>From 13cc73335b3d35b511087d0dc18085e9f67e03ca Mon Sep 17 00:00:00 2001 From: Marco Felsch Date: Wed, 26 Apr 2023 10:29:59 +0200 Subject: [PATCH 1/2] sunxi: add image support --- arch/riscv/Kconfig.socs | 6 +- include/mach/allwinner/sunxi_image.h | 118 +++++++++++++++ scripts/.gitignore | 1 + scripts/Kconfig | 7 + scripts/Makefile | 2 + scripts/sunxi_egon.c | 208 +++++++++++++++++++++++++++ 6 files changed, 339 insertions(+), 3 deletions(-) create mode 100644 include/mach/allwinner/sunxi_image.h create mode 100644 scripts/sunxi_egon.c diff --git a/arch/riscv/Kconfig.socs b/arch/riscv/Kconfig.socs index 2cb0716cd5..9f528df7de 100644 --- a/arch/riscv/Kconfig.socs +++ b/arch/riscv/Kconfig.socs @@ -110,13 +110,13 @@ config BOARD_BEAGLEV_BETA endif -config SOC_ALLWINNER_SUN20I - bool "Allwinner Sun20i SoCs" +config ARCH_SUNXI + bool "Allwinner sun20i SoCs" depends on ARCH_RV64I select HAS_DEBUG_LL select HAS_CACHE -if SOC_ALLWINNER_SUN20I +if ARCH_SUNXI config BOARD_ALLWINNER_D1 bool "Allwinner D1 Nezha" diff --git a/include/mach/allwinner/sunxi_image.h b/include/mach/allwinner/sunxi_image.h new file mode 100644 index 0000000000..f90ea600ad --- /dev/null +++ b/include/mach/allwinner/sunxi_image.h @@ -0,0 +1,118 @@ +/* SPDX-License-Identifier: GPL-2.0+ */ +/* + * (C) Copyright 2007-2011 + * Allwinner Technology Co., Ltd. + * Tom Cubie + * + * 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 + +#include + +#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)]; + +struct toc0_main_info { + uint8_t name[8]; + __le32 magic; + __le32 checksum; + __le32 serial; + __le32 status; + __le32 num_items; + __le32 length; + uint8_t platform[4]; + uint8_t reserved[8]; + uint8_t end[4]; +} __attribute__((packed)); + +#define TOC0_MAIN_INFO_NAME "TOC0.GLH" +#define TOC0_MAIN_INFO_MAGIC 0x89119800 +#define TOC0_MAIN_INFO_END "MIE;" + +struct toc0_item_info { + __le32 name; + __le32 offset; + __le32 length; + __le32 status; + __le32 type; + __le32 load_addr; + uint8_t reserved[4]; + uint8_t end[4]; +} __attribute__((packed)); + +#define TOC0_ITEM_INFO_NAME_CERT 0x00010101 +#define TOC0_ITEM_INFO_NAME_FIRMWARE 0x00010202 +#define TOC0_ITEM_INFO_NAME_KEY 0x00010303 +#define TOC0_ITEM_INFO_END "IIE;" + +#endif diff --git a/scripts/.gitignore b/scripts/.gitignore index 3ca742ac6e..3b827c293c 100644 --- a/scripts/.gitignore +++ b/scripts/.gitignore @@ -37,3 +37,4 @@ rsatoc stm32image mvebuimg prelink-riscv +sunxi_egon diff --git a/scripts/Kconfig b/scripts/Kconfig index dcd5f32d1d..e9447fac99 100644 --- a/scripts/Kconfig +++ b/scripts/Kconfig @@ -49,6 +49,13 @@ config STM32_IMAGE help This enables building the image creation tool for STM32MP SoCs +config SUNXI_EGON_IMAGE + bool "Sunxi eGON image tool" if COMPILE_HOST_TOOLS + depends on ARCH_SUNXI || COMPILE_HOST_TOOLS + default y if ARCH_SUNXI + help + This enables building the image creation tool for Sunxi SoCs + config RK_IMAGE bool "Rockchip image tool" if COMPILE_HOST_TOOLS depends on ARCH_ROCKCHIP || COMPILE_HOST_TOOLS diff --git a/scripts/Makefile b/scripts/Makefile index 72ad9ad7a6..c335029c6d 100644 --- a/scripts/Makefile +++ b/scripts/Makefile @@ -41,6 +41,8 @@ hostprogs-always-$(CONFIG_OMAP4_HOSTTOOL_USBBOOT) += omap4_usbboot HOSTCFLAGS_rk-usb-loader.o = `pkg-config --cflags libusb-1.0` HOSTLDLIBS_rk-usb-loader = `pkg-config --libs libusb-1.0` hostprogs-always-$(CONFIG_RK_USB_LOADER) += rk-usb-loader +HOSTCFLAGS_sunxi_egon.o = -I$(srctree) -I$(srctree)/include/mach +hostprogs-always-$(CONFIG_SUNXI_EGON_IMAGE) += sunxi_egon userprogs-always-$(CONFIG_BAREBOXENV_TARGET) += bareboxenv-target userprogs-always-$(CONFIG_KERNEL_INSTALL_TARGET) += kernel-install-target diff --git a/scripts/sunxi_egon.c b/scripts/sunxi_egon.c new file mode 100644 index 0000000000..6d74a83ad8 --- /dev/null +++ b/scripts/sunxi_egon.c @@ -0,0 +1,208 @@ +// SPDX-License-Identifier: GPL-2.0+ +/* + * (C) Copyright 2018 Arm Ltd. + */ + +#include + +#include +#include + +#include + +#include +#include "compiler.h" + +/* + * NAND requires 8K padding. SD/eMMC gets away with 512 bytes, + * but let's use the larger padding by default to cover both. + */ +#define PAD_SIZE 8192 + +enum sunxi_arch { + SUNXI_ARCH_ARM, + SUNXI_ARCH_RISCV, + SUNXI_ARCH_UNKOWN +}; + +static int write_image(int infd, int outfd, enum sunxi_arch arch, + unsigned long pblsize, unsigned long ofs) +{ + struct boot_file_head *header; + uint32_t checksum = 0; + void *buf, *origbuf; + unsigned long size; + uint32_t *buf32; + uint32_t value; + int ret; + int i; + + size = ALIGN(pblsize + sizeof(*header), PAD_SIZE); + + buf = malloc(size); + if (!buf) + return 1; + + header = buf; + buf32 = buf; + + /* + * Different architectures need different first instruction to + * branch to the body. + */ + switch (arch) { + case SUNXI_ARCH_ARM: + /* Generate an ARM branch instruction to jump over the header. */ + value = 0xea000000 | (sizeof(*header) / 4 - 2); + header->b_instruction = cpu_to_le32(value); + break; + case SUNXI_ARCH_RISCV: + /* + * Generate a RISC-V JAL instruction with rd=x0 + * (pseudo instruction J, jump without side effects). + * + * The following weird bit operation maps imm[20] + * to inst[31], imm[10:1] to inst[30:21], + * imm[11] to inst[20], imm[19:12] to inst[19:12], + * and imm[0] is dropped (because 1-byte RISC-V instruction + * is not allowed). + */ + value = 0x0000006f | + ((sizeof(*header) & 0x00100000) << 11) | + ((sizeof(*header) & 0x000007fe) << 20) | + ((sizeof(*header) & 0x00000800) << 9) | + ((sizeof(*header) & 0x000ff000) << 0); + header->b_instruction = cpu_to_le32(value); + break; + default: + return 1; + } + + memcpy(header->magic, BOOT0_MAGIC, sizeof(header->magic)); + header->check_sum = cpu_to_le32(BROM_STAMP_VALUE); + header->length = cpu_to_le32(size); + + memcpy(header->spl_signature, SPL_SIGNATURE, 3); + header->spl_signature[3] = SPL_ENV_HEADER_VERSION; + + /* Calculate the checksum. Yes, it's that simple. */ + for (i = 0; i < size / 4; i++) + checksum += le32_to_cpu(buf32[i]); + header->check_sum = cpu_to_le32(checksum); + + origbuf = buf; + buf += sizeof(*header); + ret = read(infd, buf, pblsize); + if (ret > pblsize) { + printf("Error: While read: 0x%d > 0x%ld bytes!\n", + ret, pblsize); + free(origbuf); + return 1; + } + + if (ofs) + lseek(outfd, ofs, SEEK_SET); + + ret = write(outfd, origbuf, size); + if (ret != size) { + printf("Error: While write: 0x%d != 0x%ld bytes!\n", + ret, size); + free(origbuf); + return 1; + } + + free(origbuf); + + return 0; +} + +static void usage(const char *prog) +{ + printf("\nUsage: %s [options] input_file output_file", prog); + printf("\n-a architecture: riscv, arm"); + printf("\n-h this help"); + printf("\n-o offset (hex)"); + printf("\n-p barebox pbl size in bytes (hex)"); + printf("\n"); +} + +int main(int argc, char *argv[]) +{ + enum sunxi_arch arch = SUNXI_ARCH_UNKOWN; + unsigned long pblsize = 0; + unsigned long ofs = 0; + int infd, outfd; + int opt; + int ret; + + while ((opt = getopt(argc, argv, "a:o:p:h")) != -1) + { + switch (opt) { + case 'a': + if (!strcmp(optarg, "riscv")) + arch = SUNXI_ARCH_RISCV; + else if (!strcmp(optarg, "arm")) + arch = SUNXI_ARCH_ARM; + else { + printf("Error: unsupported architecture: %s\n", + optarg); + return EXIT_FAILURE; + } + break; + case 'o': + ofs = strtoul(optarg, NULL, 0); + break; + case 'p': + pblsize = strtoul(optarg, NULL, 0); + break; + case 'h': + usage(argv[0]); + return EXIT_SUCCESS; + default: + usage(argv[0]); + return EXIT_FAILURE; + } + } + + if (optind >= argc) { + printf("Error: %d\n", argc); + usage(argv[0]); + return EXIT_FAILURE; + } + + infd = open(argv[optind++], O_RDONLY); + if (infd < 0) { + printf("Error: Open input file\n"); + return EXIT_FAILURE; + } + + if (pblsize == 0) { + struct stat st; + + ret = fstat(infd, &st); + if (ret) { + ret = EXIT_FAILURE; + goto out_err_infd; + } + pblsize = st.st_size; + } + + outfd = open(argv[optind], O_WRONLY | O_CREAT, 0666); + if (outfd < 0) { + printf("Error: Open output file\n"); + ret = EXIT_FAILURE; + goto out_err_infd; + } + + ret = write_image(infd, outfd, arch, pblsize, ofs); + if (ret) + ret = EXIT_FAILURE; + + ret = EXIT_SUCCESS; + + close(outfd); +out_err_infd: + close(infd); + + return ret; +} -- 2.39.2