- Pinecube board support
- 64-bit FEL support
- mkimage support for eGON images (superseding mksunxiboot)
- Bluetooth BD address generation
- some fixes
This commit is contained in:
Tom Rini 2021-01-11 21:23:59 -05:00
commit f71e574cf9
18 changed files with 508 additions and 163 deletions

View file

@ -1045,6 +1045,7 @@ config ARCH_SUNXI
select SPL_USE_TINY_PRINTF
select USE_PREBOOT
select SYS_RELOC_GD_ENV_ADDR
imply BOARD_LATE_INIT
imply CMD_DM
imply CMD_GPT
imply CMD_UBI if MTD_RAW_NAND

View file

@ -27,6 +27,8 @@ obj-$(CONFIG_ARM_SMCCC) += smccc-call.o
ifndef CONFIG_SPL_BUILD
obj-$(CONFIG_ARMV8_SPIN_TABLE) += spin_table.o spin_table_v8.o
else
obj-$(CONFIG_ARCH_SUNXI) += fel_utils.o
endif
obj-$(CONFIG_$(SPL_)ARMV8_SEC_FIRMWARE_SUPPORT) += sec_firmware.o sec_firmware_asm.o

View file

@ -0,0 +1,78 @@
/*
* Utility functions for FEL mode, when running SPL in AArch64.
*
* Copyright (c) 2017 Arm Ltd.
*
* SPDX-License-Identifier: GPL-2.0+
*/
#include <asm-offsets.h>
#include <config.h>
#include <asm/system.h>
#include <linux/linkage.h>
/*
* We don't overwrite save_boot_params() here, to save the FEL state upon
* entry, since this would run *after* the RMR reset, which clobbers that
* state.
* Instead we store the state _very_ early in the boot0 hook, *before*
* resetting to AArch64.
*/
/*
* The FEL routines in BROM run in AArch32.
* Reset back into 32-bit mode here and restore the saved FEL state
* afterwards.
* Resetting back into AArch32/EL3 using the RMR always enters the BROM,
* but we can use the CPU hotplug mechanism to branch back to our code
* immediately.
*/
ENTRY(return_to_fel)
/*
* the RMR reset will clear all registers, so save the arguments
* (LR and SP) in the fel_stash structure, which we read anyways later
*/
adr x2, fel_stash
str w0, [x2]
str w1, [x2, #4]
adr x1, fel_stash_addr // to find the fel_stash address in AA32
str w2, [x1]
ldr x0, =0xfa50392f // CPU hotplug magic
#ifdef CONFIG_MACH_SUN50I_H6
ldr x2, =(SUNXI_RTC_BASE + 0x1b8) // BOOT_CPU_HP_FLAG_REG
str w0, [x2], #0x4
#else
ldr x2, =(SUNXI_CPUCFG_BASE + 0x1a4) // offset for CPU hotplug base
str w0, [x2, #0x8]
#endif
adr x0, back_in_32
str w0, [x2]
dsb sy
isb sy
mov x0, #2 // RMR reset into AArch32
dsb sy
msr RMR_EL3, x0
isb sy
1: wfi
b 1b
/* AArch32 code to restore the state from fel_stash and return back to FEL. */
back_in_32:
.word 0xe59f0028 // ldr r0, [pc, #40] ; load fel_stash address
.word 0xe5901008 // ldr r1, [r0, #8]
.word 0xe129f001 // msr CPSR_fc, r1
.word 0xf57ff06f // isb
.word 0xe590d000 // ldr sp, [r0]
.word 0xe590e004 // ldr lr, [r0, #4]
.word 0xe5901010 // ldr r1, [r0, #16]
.word 0xee0c1f10 // mcr 15, 0, r1, cr12, cr0, {0} ; VBAR
.word 0xe590100c // ldr r1, [r0, #12]
.word 0xee011f10 // mcr 15, 0, r1, cr1, cr0, {0} ; SCTLR
.word 0xf57ff06f // isb
.word 0xe12fff1e // bx lr ; return to FEL
fel_stash_addr:
.word 0x00000000 // receives fel_stash addr, by AA64 code above
ENDPROC(return_to_fel)

View file

@ -590,6 +590,7 @@ dtb-$(CONFIG_MACH_SUN8I_R40) += \
sun8i-r40-bananapi-m2-ultra.dtb \
sun8i-v40-bananapi-m2-berry.dtb
dtb-$(CONFIG_MACH_SUN8I_V3S) += \
sun8i-s3-pinecube.dtb \
sun8i-v3s-licheepi-zero.dtb
dtb-$(CONFIG_MACH_SUN50I_H5) += \
sun50i-h5-bananapi-m2-plus.dtb \

View file

@ -15,6 +15,19 @@
tst x0, x0 // this is "b #0x84" in ARM
b reset
.space 0x7c
.word 0xe28f0058 // add r0, pc, #88
.word 0xe59f1054 // ldr r1, [pc, #84]
.word 0xe0800001 // add r0, r0, r1
.word 0xe580d000 // str sp, [r0]
.word 0xe580e004 // str lr, [r0, #4]
.word 0xe10fe000 // mrs lr, CPSR
.word 0xe580e008 // str lr, [r0, #8]
.word 0xee11ef10 // mrc 15, 0, lr, cr1, cr0, {0}
.word 0xe580e00c // str lr, [r0, #12]
.word 0xee1cef10 // mrc 15, 0, lr, cr12, cr0, {0}
.word 0xe580e010 // str lr, [r0, #16]
.word 0xe59f1024 // ldr r1, [pc, #36] ; 0x170000a0
.word 0xe59f0024 // ldr r0, [pc, #36] ; CONFIG_*_TEXT_BASE
.word 0xe5810000 // str r0, [r1]
@ -36,6 +49,7 @@
#else
.word CONFIG_SYS_TEXT_BASE
#endif
.word fel_stash - .
#else
/* normal execution */
b reset

View file

@ -7,19 +7,7 @@
#ifndef _ASM_ARCH_SPL_H_
#define _ASM_ARCH_SPL_H_
#define BOOT0_MAGIC "eGON.BT0"
#define SPL_SIGNATURE "SPL" /* marks "sunxi" SPL header */
#define SPL_MAJOR_BITS 3
#define SPL_MINOR_BITS 5
#define SPL_VERSION(maj, min) \
((((maj) & ((1U << SPL_MAJOR_BITS) - 1)) << SPL_MINOR_BITS) | \
((min) & ((1U << SPL_MINOR_BITS) - 1)))
#define SPL_HEADER_VERSION SPL_VERSION(0, 2)
#define SPL_ENV_HEADER_VERSION SPL_VERSION(0, 1)
#define SPL_DT_HEADER_VERSION SPL_VERSION(0, 2)
#define SPL_DRAM_HEADER_VERSION SPL_VERSION(0, 3)
#include <sunxi_image.h>
#define SPL_ADDR CONFIG_SUNXI_SRAM_ADDRESS
@ -31,59 +19,7 @@
#define SUNXI_BOOTED_FROM_MMC0_HIGH 0x10
#define SUNXI_BOOTED_FROM_MMC2_HIGH 0x12
/* boot head definition from sun4i boot code */
struct boot_file_head {
uint32_t b_instruction; /* one intruction jumping to real code */
uint8_t magic[8]; /* ="eGON.BT0" or "eGON.BT1", not C-style str */
uint32_t check_sum; /* generated by PC */
uint32_t length; /* generated by PC */
/*
* We use a simplified header, only filling in what is needed
* by the boot ROM. To be compatible with Allwinner tools we
* would need to implement the proper fields here instead of
* padding.
*
* Actually we want the ability to recognize our "sunxi" variant
* of the SPL. To do so, let's place a special signature into the
* "pub_head_size" field. We can reasonably expect Allwinner's
* boot0 to always have the upper 16 bits of this set to 0 (after
* all the value shouldn't be larger than the limit imposed by
* SRAM size).
* If the signature is present (at 0x14), then we know it's safe
* to use the remaining 8 bytes (at 0x18) for our own purposes.
* (E.g. sunxi-tools "fel" utility can pass information there.)
*/
union {
uint32_t pub_head_size;
uint8_t spl_signature[4];
};
uint32_t fel_script_address; /* since v0.1, set by sunxi-fel */
/*
* If the fel_uEnv_length member below is set to a non-zero value,
* it specifies the size (byte count) of data at fel_script_address.
* At the same time this indicates that the data is in uEnv.txt
* compatible format, ready to be imported via "env import -t".
*/
uint32_t fel_uEnv_length; /* since v0.1, set by sunxi-fel */
/*
* 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; /* since v0.2, set by mksunxiboot */
uint32_t dram_size; /* in MiB, since v0.3, set by SPL */
uint32_t boot_media; /* written here by the boot ROM */
/* A padding area (may be used for storing text strings) */
uint32_t string_pool[13]; /* since v0.2, filled by mksunxiboot */
/* 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)
#define is_boot0_magic(addr) (memcmp((void *)(addr), BOOT0_MAGIC, 8) == 0)
uint32_t sunxi_get_boot_device(void);

View file

@ -1016,4 +1016,15 @@ config PINEPHONE_DT_SELECTION
Enable this option to automatically select the device tree for the
correct PinePhone hardware revision during boot.
config BLUETOOTH_DT_DEVICE_FIXUP
string "Fixup the Bluetooth controller address"
default ""
help
This option specifies the DT compatible name of the Bluetooth
controller for which to set the "local-bd-address" property.
Set this option if your device ships with the Bluetooth controller
default address.
The used address is "bdaddr" if set, and "ethaddr" with the LSB
flipped elsewise.
endif

View file

@ -37,9 +37,9 @@
static void mctl_sys_init(struct dram_para *para);
static void mctl_com_init(struct dram_para *para);
static void mctl_channel_init(struct dram_para *para);
static bool mctl_channel_init(struct dram_para *para);
static void mctl_core_init(struct dram_para *para)
static bool mctl_core_init(struct dram_para *para)
{
mctl_sys_init(para);
mctl_com_init(para);
@ -51,7 +51,7 @@ static void mctl_core_init(struct dram_para *para)
default:
panic("Unsupported DRAM type!");
};
mctl_channel_init(para);
return mctl_channel_init(para);
}
/* PHY initialisation */
@ -411,7 +411,7 @@ static void mctl_bit_delay_set(struct dram_para *para)
}
}
static void mctl_channel_init(struct dram_para *para)
static bool mctl_channel_init(struct dram_para *para)
{
struct sunxi_mctl_com_reg * const mctl_com =
(struct sunxi_mctl_com_reg *)SUNXI_DRAM_COM_BASE;
@ -528,46 +528,15 @@ static void mctl_channel_init(struct dram_para *para)
clrbits_le32(&mctl_phy->dx[i].gcr[3], ~0x3ffff);
udelay(10);
if (readl(&mctl_phy->pgsr[0]) & 0x400000)
{
/* Check for single rank and optionally half DQ. */
if ((readl(&mctl_phy->dx[0].rsr[0]) & 0x3) == 2 &&
(readl(&mctl_phy->dx[1].rsr[0]) & 0x3) == 2) {
para->ranks = 1;
if ((readl(&mctl_phy->dx[2].rsr[0]) & 0x3) != 2 ||
(readl(&mctl_phy->dx[3].rsr[0]) & 0x3) != 2)
para->bus_full_width = 0;
/* Restart DRAM initialization from scratch. */
mctl_core_init(para);
return;
}
/*
* Check for dual rank and half DQ. NOTE: This combination
* is highly unlikely and was not tested. Condition is the
* same as in libdram, though.
*/
if ((readl(&mctl_phy->dx[0].rsr[0]) & 0x3) == 0 &&
(readl(&mctl_phy->dx[1].rsr[0]) & 0x3) == 0) {
para->bus_full_width = 0;
/* Restart DRAM initialization from scratch. */
mctl_core_init(para);
return;
}
panic("This DRAM setup is currently not supported.\n");
}
if (readl(&mctl_phy->pgsr[0]) & 0xff00000) {
/* Oops! There's something wrong! */
debug("PLL = %x\n", readl(0x3001010));
debug("DRAM PHY PGSR0 = %x\n", readl(&mctl_phy->pgsr[0]));
for (i = 0; i < 4; i++)
debug("DRAM PHY DX%dRSR0 = %x\n", i, readl(&mctl_phy->dx[i].rsr[0]));
panic("Error while initializing DRAM PHY!\n");
debug("Error while initializing DRAM PHY!\n");
return false;
}
if (sunxi_dram_is_lpddr(para->type))
@ -582,13 +551,59 @@ static void mctl_channel_init(struct dram_para *para)
writel(0xffffffff, &mctl_com->maer0);
writel(0x7ff, &mctl_com->maer1);
writel(0xffff, &mctl_com->maer2);
return true;
}
static void mctl_auto_detect_rank_width(struct dram_para *para)
{
/* this is minimum size that it's supported */
para->cols = 8;
para->rows = 13;
/*
* Previous versions of this driver tried to auto detect the rank
* and width by looking at controller registers. However this proved
* to be not reliable, so this approach here is the more robust
* solution. Check the git history for details.
*
* Strategy here is to test most demanding combination first and least
* demanding last, otherwise HW might not be fully utilized. For
* example, half bus width and rank = 1 combination would also work
* on HW with full bus width and rank = 2, but only 1/4 RAM would be
* visible.
*/
debug("testing 32-bit width, rank = 2\n");
para->bus_full_width = 1;
para->ranks = 2;
if (mctl_core_init(para))
return;
debug("testing 32-bit width, rank = 1\n");
para->bus_full_width = 1;
para->ranks = 1;
if (mctl_core_init(para))
return;
debug("testing 16-bit width, rank = 2\n");
para->bus_full_width = 0;
para->ranks = 2;
if (mctl_core_init(para))
return;
debug("testing 16-bit width, rank = 1\n");
para->bus_full_width = 0;
para->ranks = 1;
if (mctl_core_init(para))
return;
panic("This DRAM setup is currently not supported.\n");
}
static void mctl_auto_detect_dram_size(struct dram_para *para)
{
/* TODO: non-(LP)DDR3 */
/* Detect rank number and half DQ by the code in mctl_channel_init. */
mctl_core_init(para);
/* detect row address bits */
para->cols = 8;
@ -652,10 +667,6 @@ unsigned long sunxi_dram_init(void)
(struct sunxi_mctl_com_reg *)SUNXI_DRAM_COM_BASE;
struct dram_para para = {
.clk = CONFIG_DRAM_CLK,
.ranks = 2,
.cols = 11,
.rows = 14,
.bus_full_width = 1,
#ifdef CONFIG_SUNXI_DRAM_H6_LPDDR3
.type = SUNXI_DRAM_TYPE_LPDDR3,
.dx_read_delays = SUN50I_H6_LPDDR3_DX_READ_DELAYS,
@ -673,6 +684,7 @@ unsigned long sunxi_dram_init(void)
setbits_le32(0x7010310, BIT(8));
clrbits_le32(0x7010318, 0x3f);
mctl_auto_detect_rank_width(&para);
mctl_auto_detect_dram_size(&para);
mctl_core_init(&para);

View file

@ -440,6 +440,11 @@ M: Vasily Khoruzhick <anarsoul@gmail.com>
S: Maintained
F: configs/pinebook_defconfig
PINECUBE BOARD:
M: Icenowy Zheng <icenowy@aosc.io>
S: Maintained
F: configs/pinecube_defconfig
PINE64 BOARDS
M: Andre Przywara <andre.przywara@arm.com>
S: Maintained

View file

@ -39,6 +39,7 @@
#include <u-boot/crc.h>
#include <env_internal.h>
#include <linux/libfdt.h>
#include <fdt_support.h>
#include <nand.h>
#include <net.h>
#include <spl.h>
@ -789,6 +790,38 @@ static void parse_spl_header(const uint32_t spl_addr)
env_set_hex("fel_scriptaddr", spl->fel_script_address);
}
static bool get_unique_sid(unsigned int *sid)
{
if (sunxi_get_sid(sid) != 0)
return false;
if (!sid[0])
return false;
/*
* The single words 1 - 3 of the SID have quite a few bits
* which are the same on many models, so we take a crc32
* of all 3 words, to get a more unique value.
*
* Note we only do this on newer SoCs as we cannot change
* the algorithm on older SoCs since those have been using
* fixed mac-addresses based on only using word 3 for a
* long time and changing a fixed mac-address with an
* u-boot update is not good.
*/
#if !defined(CONFIG_MACH_SUN4I) && !defined(CONFIG_MACH_SUN5I) && \
!defined(CONFIG_MACH_SUN6I) && !defined(CONFIG_MACH_SUN7I) && \
!defined(CONFIG_MACH_SUN8I_A23) && !defined(CONFIG_MACH_SUN8I_A33)
sid[3] = crc32(0, (unsigned char *)&sid[1], 12);
#endif
/* Ensure the NIC specific bytes of the mac are not all 0 */
if ((sid[3] & 0xffffff) == 0)
sid[3] |= 0x800000;
return true;
}
/*
* Note this function gets called multiple times.
* It must not make any changes to env variables which already exist.
@ -799,61 +832,40 @@ static void setup_environment(const void *fdt)
unsigned int sid[4];
uint8_t mac_addr[6];
char ethaddr[16];
int i, ret;
int i;
ret = sunxi_get_sid(sid);
if (ret == 0 && sid[0] != 0) {
/*
* The single words 1 - 3 of the SID have quite a few bits
* which are the same on many models, so we take a crc32
* of all 3 words, to get a more unique value.
*
* Note we only do this on newer SoCs as we cannot change
* the algorithm on older SoCs since those have been using
* fixed mac-addresses based on only using word 3 for a
* long time and changing a fixed mac-address with an
* u-boot update is not good.
*/
#if !defined(CONFIG_MACH_SUN4I) && !defined(CONFIG_MACH_SUN5I) && \
!defined(CONFIG_MACH_SUN6I) && !defined(CONFIG_MACH_SUN7I) && \
!defined(CONFIG_MACH_SUN8I_A23) && !defined(CONFIG_MACH_SUN8I_A33)
sid[3] = crc32(0, (unsigned char *)&sid[1], 12);
#endif
if (!get_unique_sid(sid))
return;
/* Ensure the NIC specific bytes of the mac are not all 0 */
if ((sid[3] & 0xffffff) == 0)
sid[3] |= 0x800000;
for (i = 0; i < 4; i++) {
sprintf(ethaddr, "ethernet%d", i);
if (!fdt_get_alias(fdt, ethaddr))
continue;
for (i = 0; i < 4; i++) {
sprintf(ethaddr, "ethernet%d", i);
if (!fdt_get_alias(fdt, ethaddr))
continue;
if (i == 0)
strcpy(ethaddr, "ethaddr");
else
sprintf(ethaddr, "eth%daddr", i);
if (i == 0)
strcpy(ethaddr, "ethaddr");
else
sprintf(ethaddr, "eth%daddr", i);
if (env_get(ethaddr))
continue;
if (env_get(ethaddr))
continue;
/* Non OUI / registered MAC address */
mac_addr[0] = (i << 4) | 0x02;
mac_addr[1] = (sid[0] >> 0) & 0xff;
mac_addr[2] = (sid[3] >> 24) & 0xff;
mac_addr[3] = (sid[3] >> 16) & 0xff;
mac_addr[4] = (sid[3] >> 8) & 0xff;
mac_addr[5] = (sid[3] >> 0) & 0xff;
/* Non OUI / registered MAC address */
mac_addr[0] = (i << 4) | 0x02;
mac_addr[1] = (sid[0] >> 0) & 0xff;
mac_addr[2] = (sid[3] >> 24) & 0xff;
mac_addr[3] = (sid[3] >> 16) & 0xff;
mac_addr[4] = (sid[3] >> 8) & 0xff;
mac_addr[5] = (sid[3] >> 0) & 0xff;
eth_env_set_enetaddr(ethaddr, mac_addr);
}
eth_env_set_enetaddr(ethaddr, mac_addr);
}
if (!env_get("serial#")) {
snprintf(serial_string, sizeof(serial_string),
"%08x%08x", sid[0], sid[3]);
if (!env_get("serial#")) {
snprintf(serial_string, sizeof(serial_string),
"%08x%08x", sid[0], sid[3]);
env_set("serial#", serial_string);
}
env_set("serial#", serial_string);
}
}
@ -890,6 +902,11 @@ int misc_init_r(void)
setup_environment(gd->fdt_blob);
return 0;
}
int board_late_init(void)
{
#ifdef CONFIG_USB_ETHER
usb_ether_init();
#endif
@ -897,6 +914,38 @@ int misc_init_r(void)
return 0;
}
static void bluetooth_dt_fixup(void *blob)
{
/* Some devices ship with a Bluetooth controller default address.
* Set a valid address through the device tree.
*/
uchar tmp[ETH_ALEN], bdaddr[ETH_ALEN];
unsigned int sid[4];
int i;
if (!CONFIG_BLUETOOTH_DT_DEVICE_FIXUP[0])
return;
if (eth_env_get_enetaddr("bdaddr", tmp)) {
/* Convert between the binary formats of the corresponding stacks */
for (i = 0; i < ETH_ALEN; ++i)
bdaddr[i] = tmp[ETH_ALEN - i - 1];
} else {
if (!get_unique_sid(sid))
return;
bdaddr[0] = ((sid[3] >> 0) & 0xff) ^ 1;
bdaddr[1] = (sid[3] >> 8) & 0xff;
bdaddr[2] = (sid[3] >> 16) & 0xff;
bdaddr[3] = (sid[3] >> 24) & 0xff;
bdaddr[4] = (sid[0] >> 0) & 0xff;
bdaddr[5] = 0x02;
}
do_fixup_by_compat(blob, CONFIG_BLUETOOTH_DT_DEVICE_FIXUP,
"local-bd-address", bdaddr, ETH_ALEN, 1);
}
int ft_board_setup(void *blob, struct bd_info *bd)
{
int __maybe_unused r;
@ -907,6 +956,8 @@ int ft_board_setup(void *blob, struct bd_info *bd)
*/
setup_environment(blob);
bluetooth_dt_fixup(blob);
#ifdef CONFIG_VIDEO_DT_SIMPLEFB
r = sunxi_simplefb_setup(blob);
if (r)

View file

@ -189,6 +189,7 @@ static const table_entry_t uimage_type[] = {
{ IH_TYPE_STM32IMAGE, "stm32image", "STMicroelectronics STM32 Image" },
{ IH_TYPE_MTKIMAGE, "mtk_image", "MediaTek BootROM loadable Image" },
{ IH_TYPE_COPRO, "copro", "Coprocessor Image"},
{ IH_TYPE_SUNXI_EGON, "sunxi_egon", "Allwinner eGON Boot Image" },
{ -1, "", "", },
};

View file

@ -0,0 +1,15 @@
CONFIG_ARM=y
CONFIG_ARCH_SUNXI=y
CONFIG_SPL=y
CONFIG_MACH_SUN8I_V3S=y
CONFIG_SUNXI_DRAM_DDR3_1333=y
CONFIG_DRAM_CLK=504
CONFIG_DRAM_ODT_EN=y
CONFIG_I2C0_ENABLE=y
CONFIG_DEFAULT_DEVICE_TREE="sun8i-s3-pinecube"
CONFIG_SPL_I2C_SUPPORT=y
# CONFIG_NETDEVICES is not set
CONFIG_AXP209_POWER=y
CONFIG_AXP_DCDC2_VOLT=1250
CONFIG_AXP_DCDC3_VOLT=3300
CONFIG_CONS_INDEX=3

View file

@ -163,9 +163,7 @@
#define CONFIG_SYS_MONITOR_LEN (768 << 10) /* 768 KiB */
#ifndef CONFIG_ARM64 /* AArch64 FEL support is not ready yet */
#define CONFIG_SPL_BOARD_LOAD_IMAGE
#endif
/*
* We cannot use expressions here, because expressions won't be evaluated in

View file

@ -308,6 +308,7 @@ enum {
IH_TYPE_IMX8MIMAGE, /* Freescale IMX8MBoot Image */
IH_TYPE_IMX8IMAGE, /* Freescale IMX8Boot Image */
IH_TYPE_COPRO, /* Coprocessor Image for remoteproc*/
IH_TYPE_SUNXI_EGON, /* Allwinner eGON Boot Image */
IH_TYPE_COUNT, /* Number of image types */
};

82
include/sunxi_image.h Normal file
View file

@ -0,0 +1,82 @@
/* SPDX-License-Identifier: GPL-2.0+ */
/*
* (C) Copyright 2007-2011
* Allwinner Technology Co., Ltd. <www.allwinnertech.com>
* Tom Cubie <tangliang@allwinnertech.com>
*
* Constants and data structures used in Allwinner "eGON" images, as
* parsed by the Boot-ROM.
*
* Shared between mkimage and the SPL.
*/
#ifndef SUNXI_IMAGE_H
#define SUNXI_IMAGE_H
#define BOOT0_MAGIC "eGON.BT0"
#define BROM_STAMP_VALUE 0x5f0a6c39
#define SPL_SIGNATURE "SPL" /* marks "sunxi" SPL header */
#define SPL_MAJOR_BITS 3
#define SPL_MINOR_BITS 5
#define SPL_VERSION(maj, min) \
((((maj) & ((1U << SPL_MAJOR_BITS) - 1)) << SPL_MINOR_BITS) | \
((min) & ((1U << SPL_MINOR_BITS) - 1)))
#define SPL_HEADER_VERSION SPL_VERSION(0, 2)
#define SPL_ENV_HEADER_VERSION SPL_VERSION(0, 1)
#define SPL_DT_HEADER_VERSION SPL_VERSION(0, 2)
#define SPL_DRAM_HEADER_VERSION SPL_VERSION(0, 3)
/* boot head definition from sun4i boot code */
struct boot_file_head {
uint32_t b_instruction; /* one intruction jumping to real code */
uint8_t magic[8]; /* ="eGON.BT0" or "eGON.BT1", not C-style str */
uint32_t check_sum; /* generated by PC */
uint32_t length; /* generated by PC */
/*
* We use a simplified header, only filling in what is needed
* by the boot ROM. To be compatible with Allwinner tools we
* would need to implement the proper fields here instead of
* padding.
*
* Actually we want the ability to recognize our "sunxi" variant
* of the SPL. To do so, let's place a special signature into the
* "pub_head_size" field. We can reasonably expect Allwinner's
* boot0 to always have the upper 16 bits of this set to 0 (after
* all the value shouldn't be larger than the limit imposed by
* SRAM size).
* If the signature is present (at 0x14), then we know it's safe
* to use the remaining 8 bytes (at 0x18) for our own purposes.
* (E.g. sunxi-tools "fel" utility can pass information there.)
*/
union {
uint32_t pub_head_size;
uint8_t spl_signature[4];
};
uint32_t fel_script_address; /* since v0.1, set by sunxi-fel */
/*
* If the fel_uEnv_length member below is set to a non-zero value,
* it specifies the size (byte count) of data at fel_script_address.
* At the same time this indicates that the data is in uEnv.txt
* compatible format, ready to be imported via "env import -t".
*/
uint32_t fel_uEnv_length; /* since v0.1, set by sunxi-fel */
/*
* 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; /* since v0.2, set by mksunxiboot */
uint32_t dram_size; /* in MiB, since v0.3, set by SPL */
uint32_t boot_media; /* written here by the boot ROM */
/* A padding area (may be used for storing text strings) */
uint32_t string_pool[13]; /* since v0.2, filled by mksunxiboot */
/* 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)];
#endif

View file

@ -386,11 +386,11 @@ endif
$(obj)/$(SPL_BIN).sfp: $(obj)/$(SPL_BIN).bin FORCE
$(call if_changed,mkimage)
quiet_cmd_mksunxiboot = MKSUNXI $@
cmd_mksunxiboot = $(objtree)/tools/mksunxiboot \
--default-dt $(CONFIG_DEFAULT_DEVICE_TREE) $< $@
MKIMAGEFLAGS_sunxi-spl.bin = -T sunxi_egon \
-n $(CONFIG_DEFAULT_DEVICE_TREE)
$(obj)/sunxi-spl.bin: $(obj)/$(SPL_BIN).bin FORCE
$(call if_changed,mksunxiboot)
$(call if_changed,mkimage)
quiet_cmd_sunxi_spl_image_builder = SUNXI_SPL_IMAGE_BUILDER $@
cmd_sunxi_spl_image_builder = $(objtree)/tools/sunxi-spl-image-builder \

View file

@ -104,6 +104,7 @@ dumpimage-mkimage-objs := aisimage.o \
stm32image.o \
$(ROCKCHIP_OBS) \
socfpgaimage.o \
sunxi_egon.o \
lib/crc16.o \
lib/sha1.o \
lib/sha256.o \

136
tools/sunxi_egon.c Normal file
View file

@ -0,0 +1,136 @@
// SPDX-License-Identifier: GPL-2.0+
/*
* (C) Copyright 2018 Arm Ltd.
*/
#include "imagetool.h"
#include <image.h>
#include <sunxi_image.h>
/*
* NAND requires 8K padding. SD/eMMC gets away with 512 bytes,
* but let's use the larger padding to cover both.
*/
#define PAD_SIZE 8192
static int egon_check_params(struct image_tool_params *params)
{
/* We just need a binary image file. */
return !params->dflag;
}
static int egon_verify_header(unsigned char *ptr, int image_size,
struct image_tool_params *params)
{
const struct boot_file_head *header = (void *)ptr;
uint32_t length;
/* First 4 bytes must be an ARM branch instruction. */
if ((le32_to_cpu(header->b_instruction) & 0xff000000) != 0xea000000)
return EXIT_FAILURE;
if (memcmp(header->magic, BOOT0_MAGIC, sizeof(header->magic)))
return EXIT_FAILURE;
length = le32_to_cpu(header->length);
/* Must be at least 512 byte aligned. */
if (length & 511)
return EXIT_FAILURE;
/*
* Image could also contain U-Boot proper, so could be bigger.
* But it must not be shorter.
*/
if (image_size < length)
return EXIT_FAILURE;
return EXIT_SUCCESS;
}
static void egon_print_header(const void *buf)
{
const struct boot_file_head *header = buf;
printf("Allwinner eGON image, size: %d bytes\n",
le32_to_cpu(header->length));
if (memcmp(header->spl_signature, SPL_SIGNATURE, 3))
return;
printf("\tSPL header version %d.%d\n",
header->spl_signature[3] >> SPL_MINOR_BITS,
header->spl_signature[3] & ((1U << SPL_MINOR_BITS) - 1));
if (header->spl_signature[3] >= SPL_DT_HEADER_VERSION) {
uint32_t dt_name_offs = le32_to_cpu(header->dt_name_offset);
if (dt_name_offs > 0)
printf("\tDT name: %s\n", (char *)buf + dt_name_offs);
}
}
static void egon_set_header(void *buf, struct stat *sbuf, int infd,
struct image_tool_params *params)
{
struct boot_file_head *header = buf;
uint32_t *buf32 = buf;
uint32_t checksum = 0, value;
int i;
/* Generate an ARM branch instruction to jump over the header. */
value = 0xea000000 | (sizeof(struct boot_file_head) / 4 - 2);
header->b_instruction = cpu_to_le32(value);
memcpy(header->magic, BOOT0_MAGIC, sizeof(header->magic));
header->check_sum = cpu_to_le32(BROM_STAMP_VALUE);
header->length = cpu_to_le32(params->file_size);
memcpy(header->spl_signature, SPL_SIGNATURE, 3);
header->spl_signature[3] = SPL_ENV_HEADER_VERSION;
/* If an image name has been provided, use it as the DT name. */
if (params->imagename && params->imagename[0]) {
if (strlen(params->imagename) > sizeof(header->string_pool) - 1)
printf("WARNING: DT name too long for SPL header!\n");
else {
strcpy((char *)header->string_pool, params->imagename);
value = offsetof(struct boot_file_head, string_pool);
header->dt_name_offset = cpu_to_le32(value);
header->spl_signature[3] = SPL_DT_HEADER_VERSION;
}
}
/* Calculate the checksum. Yes, it's that simple. */
for (i = 0; i < sbuf->st_size / 4; i++)
checksum += le32_to_cpu(buf32[i]);
header->check_sum = cpu_to_le32(checksum);
}
static int egon_check_image_type(uint8_t type)
{
return type == IH_TYPE_SUNXI_EGON ? 0 : 1;
}
static int egon_vrec_header(struct image_tool_params *params,
struct image_type_params *tparams)
{
tparams->hdr = calloc(sizeof(struct boot_file_head), 1);
/* Return padding to 8K blocks. */
return ALIGN(params->file_size, PAD_SIZE) - params->file_size;
}
U_BOOT_IMAGE_TYPE(
sunxi_egon,
"Allwinner eGON Boot Image support",
sizeof(struct boot_file_head),
NULL,
egon_check_params,
egon_verify_header,
egon_print_header,
egon_set_header,
NULL,
egon_check_image_type,
NULL,
egon_vrec_header
);