All of lore.kernel.org
 help / color / mirror / Atom feed
* [PATCH v2 0/4] sunxi: TOC0 image type support
@ 2021-08-22  4:46 Samuel Holland
  2021-08-22  4:46 ` [PATCH v2 1/4] tools: Separate image types which depend on OpenSSL Samuel Holland
                   ` (3 more replies)
  0 siblings, 4 replies; 13+ messages in thread
From: Samuel Holland @ 2021-08-22  4:46 UTC (permalink / raw)
  To: u-boot, Jagan Teki, Andre Przywara
  Cc: Samuel Holland, AKASHI Takahiro, Alexandru Gagniuc, Baruch Siach,
	Bharat Gooty, Chris Packham, Fabio Estevam, Frieder Schrempf,
	Jernej Skrabec, Marek Behún, NXP i.MX U-Boot Team,
	Naoki Hayama, Pali Rohár, Patrick Delaunay, Priyanka Jain,
	Rayagonda Kokatanur, Simon Glass, Stefan Roese, Stefano Babic,
	Sughosh Ganu, Trevor Woerner, lauri.hintsala

This series adds support for the TOC0 image format used by the Allwinner
secure boot ROM (SBROM). This series has been tested on the following
SoCs/boards, with the eFuse burnt to enable secure mode:
 - A50: Ainol Q88 Tablet
 - A64: Pine A64 Plus
 - H5: Orange Pi Zero Plus
 - H6: Pine H64 Model B
 - H616: Orange Pi Zero 2

Due to a small conflict in Makefile.spl, the last patch depends on:
https://patchwork.ozlabs.org/project/uboot/list/?series=249757

Changes in v2:
 - Refactored the first patch on top of TOOLS_LIBCRYPTO
 - Moved certificate and key item structures out of sunxi_image.h
 - Renamed "main" and "item" variables for clarity
 - Improved error messages, and added a hint about key generation
 - Added a comment explaining the purpose of the various key files
 - Mentioned testing this code on A50 in the commit message
 - Moved SPL header signature checks out of sunxi_image.h
 - Refactored SPL header signature checks to use fewer casts
 - Rebase on top of Icenowy's RISC-V support series
 - Rename Kconfig symbols to include the full image type name

Samuel Holland (4):
  tools: Separate image types which depend on OpenSSL
  tools: mkimage: Add Allwinner TOC0 support
  sunxi: Support SPL in both eGON and TOC0 images
  sunxi: Support building a SPL as a TOC0 image

 arch/arm/Kconfig                      |   1 +
 arch/arm/include/asm/arch-sunxi/spl.h |   2 -
 arch/arm/mach-imx/mxs/Kconfig         |   2 +
 arch/arm/mach-mvebu/Kconfig           |   1 +
 arch/arm/mach-sunxi/Kconfig           |   2 +
 arch/arm/mach-sunxi/board.c           |  50 +-
 board/sunxi/Kconfig                   |  24 +
 common/image.c                        |   1 +
 include/image.h                       |   1 +
 include/sunxi_image.h                 |  36 +
 scripts/Makefile.spl                  |   5 +-
 scripts/config_whitelist.txt          |   1 -
 tools/Makefile                        |  20 +-
 tools/mxsimage.c                      |   3 -
 tools/sunxi_toc0.c                    | 907 ++++++++++++++++++++++++++
 15 files changed, 1028 insertions(+), 28 deletions(-)
 create mode 100644 board/sunxi/Kconfig
 create mode 100644 tools/sunxi_toc0.c

-- 
2.31.1


^ permalink raw reply	[flat|nested] 13+ messages in thread

* [PATCH v2 1/4] tools: Separate image types which depend on OpenSSL
  2021-08-22  4:46 [PATCH v2 0/4] sunxi: TOC0 image type support Samuel Holland
@ 2021-08-22  4:46 ` Samuel Holland
  2021-08-22  9:58   ` Pali Rohár
  2021-08-22  4:46 ` [PATCH v2 2/4] tools: mkimage: Add Allwinner TOC0 support Samuel Holland
                   ` (2 subsequent siblings)
  3 siblings, 1 reply; 13+ messages in thread
From: Samuel Holland @ 2021-08-22  4:46 UTC (permalink / raw)
  To: u-boot, Jagan Teki, Andre Przywara
  Cc: Samuel Holland, AKASHI Takahiro, Alexandru Gagniuc, Baruch Siach,
	Bharat Gooty, Chris Packham, Fabio Estevam, Frieder Schrempf,
	Jernej Skrabec, Marek Behún, NXP i.MX U-Boot Team,
	Naoki Hayama, Pali Rohár, Patrick Delaunay, Priyanka Jain,
	Rayagonda Kokatanur, Simon Glass, Stefan Roese, Stefano Babic,
	Sughosh Ganu, Trevor Woerner, lauri.hintsala

Some image types always depend on OpenSSL, so they can only be included
in mkimage when TOOLS_LIBCRYPTO is selected. Instead of using arbitrary
preprocessor definitions, conditionally link the files.

When building for platforms which use those image types, automatically
select TOOLS_LIBCRYPTO, it is required for the build to complete.

Signed-off-by: Samuel Holland <samuel@sholland.org>
---

Changes in v2:
 - Refactored the first patch on top of TOOLS_LIBCRYPTO

 arch/arm/mach-imx/mxs/Kconfig |  2 ++
 arch/arm/mach-mvebu/Kconfig   |  1 +
 scripts/config_whitelist.txt  |  1 -
 tools/Makefile                | 19 +++++--------------
 tools/mxsimage.c              |  3 ---
 5 files changed, 8 insertions(+), 18 deletions(-)

diff --git a/arch/arm/mach-imx/mxs/Kconfig b/arch/arm/mach-imx/mxs/Kconfig
index 9f48ffda414..24d452942a5 100644
--- a/arch/arm/mach-imx/mxs/Kconfig
+++ b/arch/arm/mach-imx/mxs/Kconfig
@@ -3,6 +3,7 @@ if ARCH_MX23
 config MX23
 	bool
 	default y
+	select TOOLS_LIBCRYPTO
 
 choice
 	prompt "MX23 board select"
@@ -34,6 +35,7 @@ if ARCH_MX28
 config MX28
 	bool
 	default y
+	select TOOLS_LIBCRYPTO
 
 choice
 	prompt "MX28 board select"
diff --git a/arch/arm/mach-mvebu/Kconfig b/arch/arm/mach-mvebu/Kconfig
index 89737a37ad9..c02521c71bc 100644
--- a/arch/arm/mach-mvebu/Kconfig
+++ b/arch/arm/mach-mvebu/Kconfig
@@ -29,6 +29,7 @@ config ARMADA_38X
 	bool
 	select ARMADA_32BIT
 	select HAVE_MVEBU_EFUSE
+	select TOOLS_LIBCRYPTO
 
 config ARMADA_38X_HS_IMPEDANCE_THRESH
 	hex  "Armada 38x USB 2.0 High-Speed Impedance Threshold (0x0 - 0x7)"
diff --git a/scripts/config_whitelist.txt b/scripts/config_whitelist.txt
index 2d70bf5da7c..5caaad0c3dc 100644
--- a/scripts/config_whitelist.txt
+++ b/scripts/config_whitelist.txt
@@ -1000,7 +1000,6 @@ CONFIG_MXC_UART_BASE
 CONFIG_MXC_USB_FLAGS
 CONFIG_MXC_USB_PORT
 CONFIG_MXC_USB_PORTSC
-CONFIG_MXS
 CONFIG_MXS_AUART
 CONFIG_MXS_AUART_BASE
 CONFIG_MXS_OCOTP
diff --git a/tools/Makefile b/tools/Makefile
index 4a86321f646..ad0e9cf9490 100644
--- a/tools/Makefile
+++ b/tools/Makefile
@@ -94,9 +94,11 @@ ECDSA_OBJS-$(CONFIG_TOOLS_LIBCRYPTO) := $(addprefix lib/ecdsa/, ecdsa-libcrypto.
 AES_OBJS-$(CONFIG_TOOLS_LIBCRYPTO) := $(addprefix lib/aes/, \
 					aes-encrypt.o aes-decrypt.o)
 
-# Cryptographic helpers that depend on openssl/libcrypto
-LIBCRYPTO_OBJS-$(CONFIG_TOOLS_LIBCRYPTO) := $(addprefix lib/, \
-					fdt-libcrypto.o)
+# Cryptographic helpers and image types that depend on openssl/libcrypto
+LIBCRYPTO_OBJS-$(CONFIG_TOOLS_LIBCRYPTO) := \
+			lib/fdt-libcrypto.o \
+			kwbimage.o \
+			mxsimage.o
 
 ROCKCHIP_OBS = lib/rc4.o rkcommon.o rkimage.o rksd.o rkspi.o
 
@@ -117,10 +119,8 @@ dumpimage-mkimage-objs := aisimage.o \
 			imximage.o \
 			imx8image.o \
 			imx8mimage.o \
-			kwbimage.o \
 			lib/md5.o \
 			lpc32xximage.o \
-			mxsimage.o \
 			omapimage.o \
 			os_support.o \
 			pblimage.o \
@@ -155,22 +155,13 @@ fit_info-objs   := $(dumpimage-mkimage-objs) fit_info.o
 fit_check_sign-objs   := $(dumpimage-mkimage-objs) fit_check_sign.o
 file2include-objs := file2include.o
 
-ifneq ($(CONFIG_MX23)$(CONFIG_MX28)$(CONFIG_TOOLS_LIBCRYPTO),)
-# Add CONFIG_MXS into host CFLAGS, so we can check whether or not register
-# the mxsimage support within tools/mxsimage.c .
-HOSTCFLAGS_mxsimage.o += -DCONFIG_MXS
-endif
-
 ifdef CONFIG_TOOLS_LIBCRYPTO
 # This affects include/image.h, but including the board config file
 # is tricky, so manually define this options here.
 HOST_EXTRACFLAGS	+= -DCONFIG_FIT_SIGNATURE
 HOST_EXTRACFLAGS	+= -DCONFIG_FIT_SIGNATURE_MAX_SIZE=0xffffffff
 HOST_EXTRACFLAGS	+= -DCONFIG_FIT_CIPHER
-endif
 
-# MXSImage needs LibSSL
-ifneq ($(CONFIG_MX23)$(CONFIG_MX28)$(CONFIG_ARMADA_38X)$(CONFIG_TOOLS_LIBCRYPTO),)
 HOSTCFLAGS_kwbimage.o += \
 	$(shell pkg-config --cflags libssl libcrypto 2> /dev/null || echo "")
 HOSTLDLIBS_mkimage += \
diff --git a/tools/mxsimage.c b/tools/mxsimage.c
index 002f4b525aa..2bfbb421eb6 100644
--- a/tools/mxsimage.c
+++ b/tools/mxsimage.c
@@ -5,8 +5,6 @@
  * Copyright (C) 2012-2013 Marek Vasut <marex@denx.de>
  */
 
-#ifdef CONFIG_MXS
-
 #include <errno.h>
 #include <fcntl.h>
 #include <stdio.h>
@@ -2363,4 +2361,3 @@ U_BOOT_IMAGE_TYPE(
 	NULL,
 	mxsimage_generate
 );
-#endif
-- 
2.31.1


^ permalink raw reply related	[flat|nested] 13+ messages in thread

* [PATCH v2 2/4] tools: mkimage: Add Allwinner TOC0 support
  2021-08-22  4:46 [PATCH v2 0/4] sunxi: TOC0 image type support Samuel Holland
  2021-08-22  4:46 ` [PATCH v2 1/4] tools: Separate image types which depend on OpenSSL Samuel Holland
@ 2021-08-22  4:46 ` Samuel Holland
  2021-08-22 10:07   ` Pali Rohár
  2021-09-06  0:29   ` Andre Przywara
  2021-08-22  4:46 ` [PATCH v2 3/4] sunxi: Support SPL in both eGON and TOC0 images Samuel Holland
  2021-08-22  4:46 ` [PATCH v2 4/4] sunxi: Support building a SPL as a TOC0 image Samuel Holland
  3 siblings, 2 replies; 13+ messages in thread
From: Samuel Holland @ 2021-08-22  4:46 UTC (permalink / raw)
  To: u-boot, Jagan Teki, Andre Przywara
  Cc: Samuel Holland, AKASHI Takahiro, Alexandru Gagniuc, Baruch Siach,
	Bharat Gooty, Chris Packham, Fabio Estevam, Frieder Schrempf,
	Jernej Skrabec, Marek Behún, NXP i.MX U-Boot Team,
	Naoki Hayama, Pali Rohár, Patrick Delaunay, Priyanka Jain,
	Rayagonda Kokatanur, Simon Glass, Stefan Roese, Stefano Babic,
	Sughosh Ganu, Trevor Woerner, lauri.hintsala

Most Allwinner sunxi SoCs have separate boot ROMs in non-secure and
secure mode. The "non-secure" or "normal" boot ROM (NBROM) uses the
existing sunxi_egon image type. The secure boot ROM (SBROM) uses a
completely different image type, known as TOC0.

A TOC0 image is composed of a header and two or more items. One item
is the firmware binary. The others form a chain linking the firmware
signature to the root-of-trust public key (ROTPK), which has its hash
burned in the SoC's eFuses. Signatures are made using RSA-2048 + SHA256.

The pseudo-ASN.1 structure is manually assembled; this is done to work
around bugs/quirks in the boot ROM, which vary between SoCs. This TOC0
implementation has been verified to work with the A50, A64, H5, H6,
and H616 SBROMs, and it may work with other SoCs.

Signed-off-by: Samuel Holland <samuel@sholland.org>
---

Changes in v2:
 - Moved certificate and key item structures out of sunxi_image.h
 - Renamed "main" and "item" variables for clarity
 - Improved error messages, and added a hint about key generation
 - Added a comment explaining the purpose of the various key files
 - Mentioned testing this code on A50 in the commit message

 arch/arm/Kconfig      |   1 +
 common/image.c        |   1 +
 include/image.h       |   1 +
 include/sunxi_image.h |  36 ++
 tools/Makefile        |   3 +-
 tools/sunxi_toc0.c    | 907 ++++++++++++++++++++++++++++++++++++++++++
 6 files changed, 948 insertions(+), 1 deletion(-)
 create mode 100644 tools/sunxi_toc0.c

diff --git a/arch/arm/Kconfig b/arch/arm/Kconfig
index d692139199c..799fe7d43af 100644
--- a/arch/arm/Kconfig
+++ b/arch/arm/Kconfig
@@ -1014,6 +1014,7 @@ config ARCH_SUNXI
 	select SUNXI_GPIO
 	select SYS_NS16550
 	select SYS_THUMB_BUILD if !ARM64
+	select TOOLS_LIBCRYPTO
 	select USB if DISTRO_DEFAULTS
 	select USB_KEYBOARD if DISTRO_DEFAULTS && USB_HOST
 	select USB_STORAGE if DISTRO_DEFAULTS && USB_HOST
diff --git a/common/image.c b/common/image.c
index 59c52a1f9ad..8f1634c1206 100644
--- a/common/image.c
+++ b/common/image.c
@@ -191,6 +191,7 @@ static const table_entry_t uimage_type[] = {
 	{	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" },
+	{	IH_TYPE_SUNXI_TOC0, "sunxi_toc0",  "Allwinner TOC0 Boot Image" },
 	{	-1,		    "",		  "",			},
 };
 
diff --git a/include/image.h b/include/image.h
index e20f0b69d58..a4efc090309 100644
--- a/include/image.h
+++ b/include/image.h
@@ -284,6 +284,7 @@ enum {
 	IH_TYPE_IMX8IMAGE,		/* Freescale IMX8Boot Image	*/
 	IH_TYPE_COPRO,			/* Coprocessor Image for remoteproc*/
 	IH_TYPE_SUNXI_EGON,		/* Allwinner eGON Boot Image */
+	IH_TYPE_SUNXI_TOC0,		/* Allwinner TOC0 Boot Image */
 
 	IH_TYPE_COUNT,			/* Number of image types */
 };
diff --git a/include/sunxi_image.h b/include/sunxi_image.h
index 5b2055c0af3..399ad0be999 100644
--- a/include/sunxi_image.h
+++ b/include/sunxi_image.h
@@ -9,9 +9,12 @@
  *
  * Shared between mkimage and the SPL.
  */
+
 #ifndef	SUNXI_IMAGE_H
 #define	SUNXI_IMAGE_H
 
+#include <linux/types.h>
+
 #define BOOT0_MAGIC		"eGON.BT0"
 #define BROM_STAMP_VALUE	0x5f0a6c39
 #define SPL_SIGNATURE		"SPL" /* marks "sunxi" SPL header */
@@ -79,4 +82,37 @@ struct boot_file_head {
 /* 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];
+};
+
+#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];
+};
+
+#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/tools/Makefile b/tools/Makefile
index ad0e9cf9490..fc3b66af6af 100644
--- a/tools/Makefile
+++ b/tools/Makefile
@@ -98,7 +98,8 @@ AES_OBJS-$(CONFIG_TOOLS_LIBCRYPTO) := $(addprefix lib/aes/, \
 LIBCRYPTO_OBJS-$(CONFIG_TOOLS_LIBCRYPTO) := \
 			lib/fdt-libcrypto.o \
 			kwbimage.o \
-			mxsimage.o
+			mxsimage.o \
+			sunxi_toc0.o
 
 ROCKCHIP_OBS = lib/rc4.o rkcommon.o rkimage.o rksd.o rkspi.o
 
diff --git a/tools/sunxi_toc0.c b/tools/sunxi_toc0.c
new file mode 100644
index 00000000000..5aa39cba775
--- /dev/null
+++ b/tools/sunxi_toc0.c
@@ -0,0 +1,907 @@
+// SPDX-License-Identifier: GPL-2.0+
+/*
+ * (C) Copyright 2018 Arm Ltd.
+ * (C) Copyright 2020-2021 Samuel Holland <samuel@sholland.org>
+ */
+
+#include <assert.h>
+#include <stdint.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+
+#include <openssl/asn1t.h>
+#include <openssl/pem.h>
+#include <openssl/rsa.h>
+
+#include <image.h>
+#include <sunxi_image.h>
+
+#include "imagetool.h"
+#include "mkimage.h"
+
+/*
+ * NAND requires 8K padding. For other devices, BROM requires only
+ * 512B padding, but let's use the larger padding to cover everything.
+ */
+#define PAD_SIZE		8192
+
+#define pr_fmt(fmt)		"mkimage (TOC0): %s: " fmt
+#define pr_err(fmt, args...)	fprintf(stderr, pr_fmt(fmt), "error", ##args)
+#define pr_warn(fmt, args...)	fprintf(stderr, pr_fmt(fmt), "warning", ##args)
+#define pr_info(fmt, args...)	fprintf(stderr, pr_fmt(fmt), "info", ##args)
+
+struct toc0_key_item {
+	__le32  vendor_id;
+	__le32  key0_n_len;
+	__le32  key0_e_len;
+	__le32  key1_n_len;
+	__le32  key1_e_len;
+	__le32  sig_len;
+	uint8_t key0[512];
+	uint8_t key1[512];
+	uint8_t reserved[32];
+	uint8_t sig[256];
+};
+
+/*
+ * This looks somewhat like an X.509 certificate, but it is not valid BER.
+ *
+ * Some differences:
+ *  - Some X.509 certificate fields are missing or rearranged.
+ *  - Some sequences have the wrong tag.
+ *  - Zero-length sequences are accepted.
+ *  - Large strings and integers must be an even number of bytes long.
+ *  - Positive integers are not zero-extended to maintain their sign.
+ *
+ * See https://linux-sunxi.org/TOC0 for more information.
+ */
+struct toc0_small_tag {
+	uint8_t tag;
+	uint8_t length;
+};
+
+typedef struct toc0_small_tag toc0_small_int;
+typedef struct toc0_small_tag toc0_small_oct;
+typedef struct toc0_small_tag toc0_small_seq;
+typedef struct toc0_small_tag toc0_small_exp;
+
+#define TOC0_SMALL_INT(len) { 0x02, (len) }
+#define TOC0_SMALL_SEQ(len) { 0x30, (len) }
+#define TOC0_SMALL_EXP(tag, len) { 0xa0 | (tag), len }
+
+struct toc0_large_tag {
+	uint8_t tag;
+	uint8_t prefix;
+	uint8_t length_hi;
+	uint8_t length_lo;
+};
+
+typedef struct toc0_large_tag toc0_large_int;
+typedef struct toc0_large_tag toc0_large_bit;
+typedef struct toc0_large_tag toc0_large_seq;
+
+#define TOC0_LARGE_INT(len) { 0x02, 0x82, (len) >> 8, (len) & 0xff }
+#define TOC0_LARGE_BIT(len) { 0x03, 0x82, (len) >> 8, (len) & 0xff }
+#define TOC0_LARGE_SEQ(len) { 0x30, 0x82, (len) >> 8, (len) & 0xff }
+
+struct toc0_cert_item {
+	toc0_large_seq tag_totalSequence;
+	struct toc0_totalSequence {
+		toc0_large_seq tag_mainSequence;
+		struct toc0_mainSequence {
+			toc0_small_exp tag_explicit0;
+			struct toc0_explicit0 {
+				toc0_small_int tag_version;
+				uint8_t version;
+			} explicit0;
+			toc0_small_int tag_serialNumber;
+			uint8_t serialNumber;
+			toc0_small_seq tag_signature;
+			toc0_small_seq tag_issuer;
+			toc0_small_seq tag_validity;
+			toc0_small_seq tag_subject;
+			toc0_large_seq tag_subjectPublicKeyInfo;
+			struct toc0_subjectPublicKeyInfo {
+				toc0_small_seq tag_algorithm;
+				toc0_large_seq tag_publicKey;
+				struct toc0_publicKey {
+					toc0_large_int tag_n;
+					uint8_t n[256];
+					toc0_small_int tag_e;
+					uint8_t e[3];
+				} publicKey;
+			} subjectPublicKeyInfo;
+			toc0_small_exp tag_explicit3;
+			struct toc0_explicit3 {
+				toc0_small_seq tag_extension;
+				struct toc0_extension {
+					toc0_small_int tag_digest;
+					uint8_t digest[32];
+				} extension;
+			} explicit3;
+		} mainSequence;
+		toc0_large_bit tag_sigSequence;
+		struct toc0_sigSequence {
+			toc0_small_seq tag_algorithm;
+			toc0_large_bit tag_signature;
+			uint8_t signature[256];
+		} sigSequence;
+	} totalSequence;
+};
+
+#define sizeof_field(TYPE, MEMBER) sizeof((((TYPE *)0)->MEMBER))
+
+static const struct toc0_cert_item cert_item_template = {
+	TOC0_LARGE_SEQ(sizeof(struct toc0_totalSequence)),
+	{
+		TOC0_LARGE_SEQ(sizeof(struct toc0_mainSequence)),
+		{
+			TOC0_SMALL_EXP(0, sizeof(struct toc0_explicit0)),
+			{
+				TOC0_SMALL_INT(sizeof_field(struct toc0_explicit0, version)),
+				0,
+			},
+			TOC0_SMALL_INT(sizeof_field(struct toc0_mainSequence, serialNumber)),
+			0,
+			TOC0_SMALL_SEQ(0),
+			TOC0_SMALL_SEQ(0),
+			TOC0_SMALL_SEQ(0),
+			TOC0_SMALL_SEQ(0),
+			TOC0_LARGE_SEQ(sizeof(struct toc0_subjectPublicKeyInfo)),
+			{
+				TOC0_SMALL_SEQ(0),
+				TOC0_LARGE_SEQ(sizeof(struct toc0_publicKey)),
+				{
+					TOC0_LARGE_INT(sizeof_field(struct toc0_publicKey, n)),
+					{},
+					TOC0_SMALL_INT(sizeof_field(struct toc0_publicKey, e)),
+					{},
+				},
+			},
+			TOC0_SMALL_EXP(3, sizeof(struct toc0_explicit3)),
+			{
+				TOC0_SMALL_SEQ(sizeof(struct toc0_extension)),
+				{
+					TOC0_SMALL_INT(sizeof_field(struct toc0_extension, digest)),
+					{},
+				},
+			},
+		},
+		TOC0_LARGE_BIT(sizeof(struct toc0_sigSequence)),
+		{
+			TOC0_SMALL_SEQ(0),
+			TOC0_LARGE_BIT(sizeof_field(struct toc0_sigSequence, signature)),
+			{},
+		},
+	},
+};
+
+#define TOC0_DEFAULT_NUM_ITEMS		3
+#define TOC0_DEFAULT_HEADER_LEN						  \
+	ALIGN(								  \
+		sizeof(struct toc0_main_info)				+ \
+		sizeof(struct toc0_item_info) *	TOC0_DEFAULT_NUM_ITEMS	+ \
+		sizeof(struct toc0_cert_item)				+ \
+		sizeof(struct toc0_key_item),				  \
+	32)
+
+static char *fw_key_file   = "fw_key.pem";
+static char *key_item_file = "key_item.bin";
+static char *root_key_file = "root_key.pem";
+
+/*
+ * Create a key item in @buf, containing the public keys @root_key and @fw_key,
+ * and signed by the RSA key @root_key.
+ */
+static int toc0_create_key_item(uint8_t *buf, uint32_t *len,
+				RSA *root_key, RSA *fw_key)
+{
+	struct toc0_key_item *key_item = (void *)buf;
+	uint8_t digest[SHA256_DIGEST_LENGTH];
+	int ret = EXIT_FAILURE;
+	unsigned int sig_len;
+	int n_len, e_len;
+
+	/* Store key 0. */
+	n_len = BN_bn2bin(RSA_get0_n(root_key), key_item->key0);
+	e_len = BN_bn2bin(RSA_get0_e(root_key), key_item->key0 + n_len);
+	if (n_len + e_len > sizeof(key_item->key0)) {
+		pr_err("Root key is too big for key item\n");
+		goto err;
+	}
+	key_item->key0_n_len = cpu_to_le32(n_len);
+	key_item->key0_e_len = cpu_to_le32(e_len);
+
+	/* Store key 1. */
+	n_len = BN_bn2bin(RSA_get0_n(fw_key), key_item->key1);
+	e_len = BN_bn2bin(RSA_get0_e(fw_key), key_item->key1 + n_len);
+	if (n_len + e_len > sizeof(key_item->key1)) {
+		pr_err("Firmware key is too big for key item\n");
+		goto err;
+	}
+	key_item->key1_n_len = cpu_to_le32(n_len);
+	key_item->key1_e_len = cpu_to_le32(e_len);
+
+	/* Sign the key item. */
+	key_item->sig_len = cpu_to_le32(RSA_size(root_key));
+	SHA256(buf, key_item->sig - buf, digest);
+	if (!RSA_sign(NID_sha256, digest, sizeof(digest),
+		      key_item->sig, &sig_len, root_key)) {
+		pr_err("Failed to sign key item\n");
+		goto err;
+	}
+	if (sig_len != sizeof(key_item->sig)) {
+		pr_err("Bad key item signature length\n");
+		goto err;
+	}
+
+	*len = sizeof(*key_item);
+	ret = EXIT_SUCCESS;
+
+err:
+	return ret;
+}
+
+/*
+ * Verify the key item in @buf, containing two public keys @key0 and @key1,
+ * and signed by the RSA key @key0. If @root_key is provided, only signatures
+ * by that key will be accepted. @key1 is returned in @key.
+ */
+static int toc0_verify_key_item(const uint8_t *buf, uint32_t len,
+				RSA *root_key, RSA **fw_key)
+{
+	struct toc0_key_item *key_item = (void *)buf;
+	uint8_t digest[SHA256_DIGEST_LENGTH];
+	int ret = EXIT_FAILURE;
+	int n_len, e_len;
+	RSA *key0 = NULL;
+	RSA *key1 = NULL;
+	BIGNUM *n, *e;
+
+	if (len < sizeof(*key_item))
+		goto err;
+
+	/* Load key 0. */
+	n_len = le32_to_cpu(key_item->key0_n_len);
+	e_len = le32_to_cpu(key_item->key0_e_len);
+	if (n_len + e_len > sizeof(key_item->key0)) {
+		pr_err("Bad root key size in key item\n");
+		goto err;
+	}
+	n = BN_bin2bn(key_item->key0, n_len, NULL);
+	e = BN_bin2bn(key_item->key0 + n_len, e_len, NULL);
+	key0 = RSA_new();
+	if (!key0)
+		goto err;
+	if (!RSA_set0_key(key0, n, e, NULL))
+		goto err;
+
+	/* If a root key was provided, compare it to key 0. */
+	if (root_key && (BN_cmp(n, RSA_get0_n(root_key)) ||
+			 BN_cmp(e, RSA_get0_e(root_key)))) {
+		pr_err("Wrong root key in key item\n");
+		goto err;
+	}
+
+	/* Verify the key item signature. */
+	SHA256(buf, key_item->sig - buf, digest);
+	if (!RSA_verify(NID_sha256, digest, sizeof(digest),
+			key_item->sig, le32_to_cpu(key_item->sig_len), key0)) {
+		pr_err("Bad key item signature\n");
+		goto err;
+	}
+
+	if (fw_key) {
+		/* Load key 1. */
+		n_len = le32_to_cpu(key_item->key1_n_len);
+		e_len = le32_to_cpu(key_item->key1_e_len);
+		if (n_len + e_len > sizeof(key_item->key1)) {
+			pr_err("Bad firmware key size in key item\n");
+			goto err;
+		}
+		n = BN_bin2bn(key_item->key1, n_len, NULL);
+		e = BN_bin2bn(key_item->key1 + n_len, e_len, NULL);
+		key1 = RSA_new();
+		if (!key1)
+			goto err;
+		if (!RSA_set0_key(key1, n, e, NULL))
+			goto err;
+
+		if (*fw_key) {
+			/* If a FW key was provided, compare it to key 1. */
+			if (BN_cmp(n, RSA_get0_n(*fw_key)) ||
+			    BN_cmp(e, RSA_get0_e(*fw_key))) {
+				pr_err("Wrong firmware key in key item\n");
+				goto err;
+			}
+		} else {
+			/* Otherwise, send key1 back to the caller. */
+			*fw_key = key1;
+			key1 = NULL;
+		}
+	}
+
+	ret = EXIT_SUCCESS;
+
+err:
+	RSA_free(key0);
+	RSA_free(key1);
+
+	return ret;
+}
+
+/*
+ * Create a certificate in @buf, describing the firmware with SHA256 digest
+ * @digest, and signed by the RSA key @fw_key.
+ */
+static int toc0_create_cert_item(uint8_t *buf, uint32_t *len, RSA *fw_key,
+				 uint8_t digest[static SHA256_DIGEST_LENGTH])
+{
+	struct toc0_cert_item *cert_item = (void *)buf;
+	uint8_t cert_digest[SHA256_DIGEST_LENGTH];
+	struct toc0_totalSequence *totalSequence;
+	struct toc0_sigSequence *sigSequence;
+	struct toc0_extension *extension;
+	struct toc0_publicKey *publicKey;
+	int ret = EXIT_FAILURE;
+	unsigned int sig_len;
+
+	memcpy(cert_item, &cert_item_template, sizeof(*cert_item));
+	*len = sizeof(*cert_item);
+
+	/*
+	 * Fill in the public key.
+	 *
+	 * Only 2048-bit RSA keys are supported. Since this uses a fixed-size
+	 * structure, it may fail for non-standard exponents.
+	 */
+	totalSequence = &cert_item->totalSequence;
+	publicKey = &totalSequence->mainSequence.subjectPublicKeyInfo.publicKey;
+	if (BN_bn2binpad(RSA_get0_n(fw_key), publicKey->n, sizeof(publicKey->n)) < 0 ||
+	    BN_bn2binpad(RSA_get0_e(fw_key), publicKey->e, sizeof(publicKey->e)) < 0) {
+		pr_err("Firmware key is too big for certificate\n");
+		goto err;
+	}
+
+	/* Fill in the firmware digest. */
+	extension = &totalSequence->mainSequence.explicit3.extension;
+	memcpy(&extension->digest, digest, SHA256_DIGEST_LENGTH);
+
+	/*
+	 * Sign the certificate.
+	 *
+	 * In older SBROM versions (and by default in newer versions),
+	 * the last 4 bytes of the certificate are not signed.
+	 *
+	 * (The buffer passed to SHA256 starts at tag_mainSequence, but
+	 *  the buffer size does not include the length of that tag.)
+	 */
+	SHA256((uint8_t *)totalSequence, sizeof(struct toc0_mainSequence), cert_digest);
+	sigSequence = &totalSequence->sigSequence;
+	if (!RSA_sign(NID_sha256, cert_digest, SHA256_DIGEST_LENGTH,
+		      sigSequence->signature, &sig_len, fw_key)) {
+		pr_err("Failed to sign certificate\n");
+		goto err;
+	}
+	if (sig_len != sizeof(sigSequence->signature)) {
+		pr_err("Bad certificate signature length\n");
+		goto err;
+	}
+
+	ret = EXIT_SUCCESS;
+
+err:
+	return ret;
+}
+
+/*
+ * Verify the certificate in @buf, describing the firmware with SHA256 digest
+ * @digest, and signed by the RSA key contained within. If @fw_key is provided,
+ * only that key will be accepted.
+ *
+ * This function is only expected to work with images created by mkimage.
+ */
+static int toc0_verify_cert_item(const uint8_t *buf, uint32_t len, RSA *fw_key,
+				 uint8_t digest[static SHA256_DIGEST_LENGTH])
+{
+	const struct toc0_cert_item *cert_item = (const void *)buf;
+	uint8_t cert_digest[SHA256_DIGEST_LENGTH];
+	const struct toc0_totalSequence *totalSequence;
+	const struct toc0_sigSequence *sigSequence;
+	const struct toc0_extension *extension;
+	const struct toc0_publicKey *publicKey;
+	int ret = EXIT_FAILURE;
+	RSA *key = NULL;
+	BIGNUM *n, *e;
+
+	/* Extract the public key from the certificate. */
+	totalSequence = &cert_item->totalSequence;
+	publicKey = &totalSequence->mainSequence.subjectPublicKeyInfo.publicKey;
+	n = BN_bin2bn(publicKey->n, sizeof(publicKey->n), NULL);
+	e = BN_bin2bn(publicKey->e, sizeof(publicKey->e), NULL);
+	key = RSA_new();
+	if (!key)
+		goto err;
+	if (!RSA_set0_key(key, n, e, NULL))
+		goto err;
+
+	/* If a key was provided, compare it to the embedded key. */
+	if (fw_key && (BN_cmp(RSA_get0_n(key), RSA_get0_n(fw_key)) ||
+		       BN_cmp(RSA_get0_e(key), RSA_get0_e(fw_key)))) {
+		pr_err("Wrong firmware key in certificate\n");
+		goto err;
+	}
+
+	/* If a digest was provided, compare it to the embedded digest. */
+	extension = &totalSequence->mainSequence.explicit3.extension;
+	if (digest && memcmp(&extension->digest, digest, SHA256_DIGEST_LENGTH)) {
+		pr_err("Wrong firmware digest in certificate\n");
+		goto err;
+	}
+
+	/* Verify the certificate's signature. See the comment above. */
+	SHA256((uint8_t *)totalSequence, sizeof(struct toc0_mainSequence), cert_digest);
+	sigSequence = &totalSequence->sigSequence;
+	if (!RSA_verify(NID_sha256, cert_digest, SHA256_DIGEST_LENGTH,
+			sigSequence->signature,
+			sizeof(sigSequence->signature), key)) {
+		pr_err("Bad certificate signature\n");
+		goto err;
+	}
+
+	ret = EXIT_SUCCESS;
+
+err:
+	RSA_free(key);
+
+	return ret;
+}
+
+/*
+ * Always create a TOC0 containing 3 items. The extra item will be ignored on
+ * SoCs which do not support it.
+ */
+static int toc0_create(uint8_t *buf, uint32_t len, RSA *root_key, RSA *fw_key,
+		       uint8_t *key_item, uint32_t key_item_len,
+		       uint8_t *fw_item, uint32_t fw_item_len, uint32_t fw_addr)
+{
+	struct toc0_main_info *main_info = (void *)buf;
+	struct toc0_item_info *item_info = (void *)(main_info + 1);
+	uint8_t digest[SHA256_DIGEST_LENGTH];
+	uint32_t *buf32 = (void *)buf;
+	RSA *orig_fw_key = fw_key;
+	int ret = EXIT_FAILURE;
+	uint32_t checksum = 0;
+	uint32_t item_offset;
+	uint32_t item_length;
+	int i;
+
+	/* Hash the firmware for inclusion in the certificate. */
+	SHA256(fw_item, fw_item_len, digest);
+
+	/* Create the main TOC0 header, containing three items. */
+	memcpy(main_info->name, TOC0_MAIN_INFO_NAME, sizeof(main_info->name));
+	main_info->magic	= cpu_to_le32(TOC0_MAIN_INFO_MAGIC);
+	main_info->checksum	= cpu_to_le32(BROM_STAMP_VALUE);
+	main_info->num_items	= cpu_to_le32(TOC0_DEFAULT_NUM_ITEMS);
+	memcpy(main_info->end, TOC0_MAIN_INFO_END, sizeof(main_info->end));
+
+	/* The first item links the ROTPK to the signing key. */
+	item_offset = sizeof(*main_info) +
+		      sizeof(*item_info) * TOC0_DEFAULT_NUM_ITEMS;
+	/* Using an existing key item avoids needing the root private key. */
+	if (key_item) {
+		item_length = sizeof(*key_item);
+		if (toc0_verify_key_item(key_item, item_length,
+					 root_key, &fw_key))
+			goto err;
+		memcpy(buf + item_offset, key_item, item_length);
+	} else if (toc0_create_key_item(buf + item_offset, &item_length,
+					root_key, fw_key)) {
+		goto err;
+	}
+
+	item_info->name		= cpu_to_le32(TOC0_ITEM_INFO_NAME_KEY);
+	item_info->offset	= cpu_to_le32(item_offset);
+	item_info->length	= cpu_to_le32(item_length);
+	memcpy(item_info->end, TOC0_ITEM_INFO_END, sizeof(item_info->end));
+
+	/* The second item contains a certificate signed by the firmware key. */
+	item_offset = item_offset + item_length;
+	if (toc0_create_cert_item(buf + item_offset, &item_length,
+				  fw_key, digest))
+		goto err;
+
+	item_info++;
+	item_info->name		= cpu_to_le32(TOC0_ITEM_INFO_NAME_CERT);
+	item_info->offset	= cpu_to_le32(item_offset);
+	item_info->length	= cpu_to_le32(item_length);
+	memcpy(item_info->end, TOC0_ITEM_INFO_END, sizeof(item_info->end));
+
+	/* The third item contains the actual boot code. */
+	item_offset = ALIGN(item_offset + item_length, 32);
+	item_length = fw_item_len;
+	if (buf + item_offset != fw_item)
+		memmove(buf + item_offset, fw_item, item_length);
+
+	item_info++;
+	item_info->name		= cpu_to_le32(TOC0_ITEM_INFO_NAME_FIRMWARE);
+	item_info->offset	= cpu_to_le32(item_offset);
+	item_info->length	= cpu_to_le32(item_length);
+	item_info->load_addr	= cpu_to_le32(fw_addr);
+	memcpy(item_info->end, TOC0_ITEM_INFO_END, sizeof(item_info->end));
+
+	/* Pad to the required block size with 0xff to be flash-friendly. */
+	item_offset = item_offset + item_length;
+	item_length = ALIGN(item_offset, PAD_SIZE) - item_offset;
+	memset(buf + item_offset, 0xff, item_length);
+
+	/* Fill in the total padded file length. */
+	item_offset = item_offset + item_length;
+	main_info->length = cpu_to_le32(item_offset);
+
+	/* Verify enough space was provided when creating the image. */
+	assert(len >= item_offset);
+
+	/* Calculate the checksum. Yes, it's that simple. */
+	for (i = 0; i < item_offset / 4; ++i)
+		checksum += le32_to_cpu(buf32[i]);
+	main_info->checksum = cpu_to_le32(checksum);
+
+	ret = EXIT_SUCCESS;
+
+err:
+	if (fw_key != orig_fw_key)
+		RSA_free(fw_key);
+
+	return ret;
+}
+
+static const struct toc0_item_info *
+toc0_find_item(const struct toc0_main_info *main_info, uint32_t name,
+	       uint32_t *offset, uint32_t *length)
+{
+	const struct toc0_item_info *item_info = (void *)(main_info + 1);
+	uint32_t item_offset, item_length;
+	uint32_t num_items, main_length;
+	int i;
+
+	num_items   = le32_to_cpu(main_info->num_items);
+	main_length = le32_to_cpu(main_info->length);
+
+	for (i = 0; i < num_items; ++i, ++item_info) {
+		if (le32_to_cpu(item_info->name) != name)
+			continue;
+
+		item_offset = le32_to_cpu(item_info->offset);
+		item_length = le32_to_cpu(item_info->length);
+
+		if (item_offset > main_length ||
+		    item_length > main_length - item_offset)
+			continue;
+
+		*offset = item_offset;
+		*length = item_length;
+
+		return item_info;
+	}
+
+	return NULL;
+}
+
+static int toc0_verify(const uint8_t *buf, uint32_t len, RSA *root_key)
+{
+	const struct toc0_main_info *main_info = (void *)buf;
+	const struct toc0_item_info *item_info;
+	uint8_t digest[SHA256_DIGEST_LENGTH];
+	uint32_t main_length = le32_to_cpu(main_info->length);
+	uint32_t checksum = BROM_STAMP_VALUE;
+	uint32_t *buf32 = (void *)buf;
+	uint32_t length, offset;
+	int ret = EXIT_FAILURE;
+	RSA *fw_key = NULL;
+	int i;
+
+	if (len < main_length)
+		goto err;
+
+	/* Verify the main header. */
+	if (memcmp(main_info->name, TOC0_MAIN_INFO_NAME, sizeof(main_info->name)))
+		goto err;
+	if (le32_to_cpu(main_info->magic) != TOC0_MAIN_INFO_MAGIC)
+		goto err;
+	/* Verify the checksum without modifying the buffer. */
+	for (i = 0; i < main_length / 4; ++i)
+		checksum += le32_to_cpu(buf32[i]);
+	if (checksum != 2 * le32_to_cpu(main_info->checksum))
+		goto err;
+	/* The length must be at least 512 byte aligned. */
+	if (main_length % 512)
+		goto err;
+	if (memcmp(main_info->end, TOC0_MAIN_INFO_END, sizeof(main_info->end)))
+		goto err;
+
+	/* Verify the key item if present (it is optional). */
+	item_info = toc0_find_item(main_info, TOC0_ITEM_INFO_NAME_KEY,
+				   &offset, &length);
+	if (!item_info)
+		fw_key = root_key;
+	else if (toc0_verify_key_item(buf + offset, length, root_key, &fw_key))
+		goto err;
+
+	/* Hash the firmware to compare with the certificate. */
+	item_info = toc0_find_item(main_info, TOC0_ITEM_INFO_NAME_FIRMWARE,
+				   &offset, &length);
+	if (!item_info) {
+		pr_err("Missing firmware item\n");
+		goto err;
+	}
+	SHA256(buf + offset, length, digest);
+
+	/* Verify the certificate item. */
+	item_info = toc0_find_item(main_info, TOC0_ITEM_INFO_NAME_CERT,
+				   &offset, &length);
+	if (!item_info) {
+		pr_err("Missing certificate item\n");
+		goto err;
+	}
+	if (toc0_verify_cert_item(buf + offset, length, fw_key, digest))
+		goto err;
+
+	ret = EXIT_SUCCESS;
+
+err:
+	if (fw_key != root_key)
+		RSA_free(fw_key);
+
+	return ret;
+}
+
+static int toc0_check_params(struct image_tool_params *params)
+{
+	if (!params->dflag)
+		return -EINVAL;
+
+	/*
+	 * If a key directory was provided, look for key files there.
+	 * Otherwise, look for them in the current directory. The key files are
+	 * the "quoted" terms in the description below.
+	 *
+	 * A summary of the chain of trust on most SoCs:
+	 *  1) eFuse contains a SHA256 digest of the public "root key".
+	 *  2) Private "root key" signs the certificate item (generated here).
+	 *  3) Certificate item contains a SHA256 digest of the firmware item.
+	 *
+	 * A summary of the chain of trust on the H6 (by default; a bit in the
+	 * BROM_CONFIG eFuse makes it work like above):
+	 *  1) eFuse contains a SHA256 digest of the public "root key".
+	 *  2) Private "root key" signs the "key item" (generated here).
+	 *  3) "Key item" contains the public "root key" and public "fw key".
+	 *  4) Private "fw key" signs the certificate item (generated here).
+	 *  5) Certificate item contains a SHA256 digest of the firmware item.
+	 *
+	 * This means there are three valid ways to generate a TOC0:
+	 *  1) Provide the private "root key" only. This works everywhere.
+	 *     For H6, the "root key" will also be used as the "fw key".
+	 *  2) FOR H6 ONLY: Provide the private "root key" and a separate
+	 *     private "fw key".
+	 *  3) FOR H6 ONLY: Provide the private "fw key" and a pre-existing
+	 *     "key item" containing the corresponding  public "fw key".
+	 *     In this case, the private "root key" can be kept offline. The
+	 *     "key item" can be extracted from a TOC0 image generated using
+	 *     method #2 above.
+	 *
+	 *  Note that until the ROTPK_HASH eFuse is programmed, any "root key"
+	 *  will be accepted by the BROM.
+	 */
+	if (params->keydir) {
+		if (asprintf(&fw_key_file, "%s/%s", params->keydir, fw_key_file) < 0)
+			return -ENOMEM;
+		if (asprintf(&key_item_file, "%s/%s", params->keydir, key_item_file) < 0)
+			return -ENOMEM;
+		if (asprintf(&root_key_file, "%s/%s", params->keydir, root_key_file) < 0)
+			return -ENOMEM;
+	}
+
+	return 0;
+}
+
+static int toc0_verify_header(unsigned char *buf, int image_size,
+			      struct image_tool_params *params)
+{
+	int ret = EXIT_FAILURE;
+	RSA *root_key = NULL;
+	FILE *fp;
+
+	/* A root public key is optional. */
+	fp = fopen(root_key_file, "rb");
+	if (fp) {
+		pr_info("Verifying image with existing root key\n");
+		root_key = PEM_read_RSAPrivateKey(fp, NULL, NULL, NULL);
+		if (!root_key)
+			root_key = PEM_read_RSAPublicKey(fp, NULL, NULL, NULL);
+		fclose(fp);
+		if (!root_key) {
+			pr_err("Failed to read public key from '%s'\n",
+			       root_key_file);
+			goto err;
+		}
+	}
+
+	ret = toc0_verify(buf, image_size, root_key);
+
+err:
+	RSA_free(root_key);
+
+	return ret;
+}
+
+static const char *toc0_item_name(uint32_t name)
+{
+	if (name == TOC0_ITEM_INFO_NAME_CERT)
+		return "Certificate";
+	if (name == TOC0_ITEM_INFO_NAME_FIRMWARE)
+		return "Firmware";
+	if (name == TOC0_ITEM_INFO_NAME_KEY)
+		return "Key";
+	return "(unknown)";
+}
+
+static void toc0_print_header(const void *buf)
+{
+	const struct toc0_main_info *main_info = buf;
+	const struct toc0_item_info *item_info = (void *)(main_info + 1);
+	uint32_t head_length, main_length, num_items;
+	uint32_t item_offset, item_length, item_name;
+	int load_addr = -1;
+	int i;
+
+	num_items   = le32_to_cpu(main_info->num_items);
+	head_length = sizeof(*main_info) + num_items * sizeof(*item_info);
+	main_length = le32_to_cpu(main_info->length);
+
+	printf("Allwinner TOC0 Image\n"
+	       "Size: %d bytes\n"
+	       "Contents: %d items\n"
+	       " 00000000:%08x Headers\n",
+	       main_length, num_items, head_length);
+
+	for (i = 0; i < num_items; ++i, ++item_info) {
+		item_offset = le32_to_cpu(item_info->offset);
+		item_length = le32_to_cpu(item_info->length);
+		item_name   = le32_to_cpu(item_info->name);
+
+		if (item_name == TOC0_ITEM_INFO_NAME_FIRMWARE)
+			load_addr = le32_to_cpu(item_info->load_addr);
+
+		printf(" %08x:%08x %s\n",
+		       item_offset, item_length,
+		       toc0_item_name(item_name));
+	}
+
+	if (num_items && item_offset + item_length < main_length) {
+		item_offset = item_offset + item_length;
+		item_length = main_length - item_offset;
+
+		printf(" %08x:%08x Padding\n",
+		       item_offset, item_length);
+	}
+
+	if (load_addr != -1)
+		printf("Load address: 0x%08x\n", load_addr);
+}
+
+static void toc0_set_header(void *buf, struct stat *sbuf, int ifd,
+			    struct image_tool_params *params)
+{
+	uint32_t key_item_len = 0;
+	uint8_t *key_item = NULL;
+	int ret = EXIT_FAILURE;
+	RSA *root_key = NULL;
+	RSA *fw_key = NULL;
+	FILE *fp;
+
+	/* Either a key item or the root private key is required. */
+	fp = fopen(key_item_file, "rb");
+	if (fp) {
+		pr_info("Creating image using existing key item\n");
+		key_item_len = sizeof(struct toc0_key_item);
+		key_item = OPENSSL_malloc(key_item_len);
+		if (!key_item || fread(key_item, key_item_len, 1, fp) != 1) {
+			pr_err("Failed to read key item from '%s'\n",
+			       root_key_file);
+			goto err;
+		}
+		fclose(fp);
+		fp = NULL;
+	}
+
+	fp = fopen(root_key_file, "rb");
+	if (fp) {
+		root_key = PEM_read_RSAPrivateKey(fp, NULL, NULL, NULL);
+		if (!root_key)
+			root_key = PEM_read_RSAPublicKey(fp, NULL, NULL, NULL);
+		fclose(fp);
+		fp = NULL;
+	}
+
+	/* When using an existing key item, the root key is optional. */
+	if (!key_item && (!root_key || !RSA_get0_d(root_key))) {
+		pr_err("Failed to read private key from '%s'\n",
+		       root_key_file);
+		pr_info("Try 'openssl genrsa -out root_key.pem'\n");
+		goto err;
+	}
+
+	/* The certificate/firmware private key is always required. */
+	fp = fopen(fw_key_file, "rb");
+	if (fp) {
+		fw_key = PEM_read_RSAPrivateKey(fp, NULL, NULL, NULL);
+		fclose(fp);
+		fp = NULL;
+	}
+	if (!fw_key) {
+		/* If the root key is a private key, it can be used instead. */
+		if (root_key && RSA_get0_d(root_key)) {
+			pr_info("Using root key as firmware key\n");
+			fw_key = root_key;
+		} else {
+			pr_err("Failed to read private key from '%s'\n",
+			       fw_key_file);
+			goto err;
+		}
+	}
+
+	/* Warn about potential compatibility issues. */
+	if (key_item || fw_key != root_key)
+		pr_warn("Only H6 supports separate root and firmware keys\n");
+
+	ret = toc0_create(buf, params->file_size, root_key, fw_key,
+			  key_item, key_item_len,
+			  buf + TOC0_DEFAULT_HEADER_LEN,
+			  params->orig_file_size, params->addr);
+
+err:
+	OPENSSL_free(key_item);
+	OPENSSL_free(root_key);
+	if (fw_key != root_key)
+		OPENSSL_free(fw_key);
+	if (fp)
+		fclose(fp);
+
+	if (ret != EXIT_SUCCESS)
+		exit(ret);
+}
+
+static int toc0_check_image_type(uint8_t type)
+{
+	return type == IH_TYPE_SUNXI_TOC0 ? 0 : 1;
+}
+
+static int toc0_vrec_header(struct image_tool_params *params,
+			    struct image_type_params *tparams)
+{
+	tparams->hdr = calloc(tparams->header_size, 1);
+
+	/* Save off the unpadded data size for SHA256 calculation. */
+	params->orig_file_size = params->file_size - TOC0_DEFAULT_HEADER_LEN;
+
+	/* Return padding to 8K blocks. */
+	return ALIGN(params->file_size, PAD_SIZE) - params->file_size;
+}
+
+U_BOOT_IMAGE_TYPE(
+	sunxi_toc0,
+	"Allwinner TOC0 Boot Image support",
+	TOC0_DEFAULT_HEADER_LEN,
+	NULL,
+	toc0_check_params,
+	toc0_verify_header,
+	toc0_print_header,
+	toc0_set_header,
+	NULL,
+	toc0_check_image_type,
+	NULL,
+	toc0_vrec_header
+);
-- 
2.31.1


^ permalink raw reply related	[flat|nested] 13+ messages in thread

* [PATCH v2 3/4] sunxi: Support SPL in both eGON and TOC0 images
  2021-08-22  4:46 [PATCH v2 0/4] sunxi: TOC0 image type support Samuel Holland
  2021-08-22  4:46 ` [PATCH v2 1/4] tools: Separate image types which depend on OpenSSL Samuel Holland
  2021-08-22  4:46 ` [PATCH v2 2/4] tools: mkimage: Add Allwinner TOC0 support Samuel Holland
@ 2021-08-22  4:46 ` Samuel Holland
  2021-09-06  0:30   ` Andre Przywara
  2021-08-22  4:46 ` [PATCH v2 4/4] sunxi: Support building a SPL as a TOC0 image Samuel Holland
  3 siblings, 1 reply; 13+ messages in thread
From: Samuel Holland @ 2021-08-22  4:46 UTC (permalink / raw)
  To: u-boot, Jagan Teki, Andre Przywara
  Cc: Samuel Holland, AKASHI Takahiro, Alexandru Gagniuc, Baruch Siach,
	Bharat Gooty, Chris Packham, Fabio Estevam, Frieder Schrempf,
	Jernej Skrabec, Marek Behún, NXP i.MX U-Boot Team,
	Naoki Hayama, Pali Rohár, Patrick Delaunay, Priyanka Jain,
	Rayagonda Kokatanur, Simon Glass, Stefan Roese, Stefano Babic,
	Sughosh Ganu, Trevor Woerner, lauri.hintsala

SPL uses the image header to detect the boot device and to find the
offset of the next U-Boot stage. Since this information is stored
differently in the eGON and TOC0 image headers, add code to find the
correct value based on the image type currently in use.

Signed-off-by: Samuel Holland <samuel@sholland.org>
---

Changes in v2:
 - Moved SPL header signature checks out of sunxi_image.h
 - Refactored SPL header signature checks to use fewer casts

 arch/arm/include/asm/arch-sunxi/spl.h |  2 --
 arch/arm/mach-sunxi/board.c           | 50 +++++++++++++++++++++++----
 2 files changed, 43 insertions(+), 9 deletions(-)

diff --git a/arch/arm/include/asm/arch-sunxi/spl.h b/arch/arm/include/asm/arch-sunxi/spl.h
index 58cdf806d9a..157b11e4897 100644
--- a/arch/arm/include/asm/arch-sunxi/spl.h
+++ b/arch/arm/include/asm/arch-sunxi/spl.h
@@ -19,8 +19,6 @@
 #define SUNXI_BOOTED_FROM_MMC0_HIGH	0x10
 #define SUNXI_BOOTED_FROM_MMC2_HIGH	0x12
 
-#define is_boot0_magic(addr)	(memcmp((void *)(addr), BOOT0_MAGIC, 8) == 0)
-
 uint32_t sunxi_get_boot_device(void);
 
 #endif
diff --git a/arch/arm/mach-sunxi/board.c b/arch/arm/mach-sunxi/board.c
index d9b04f75fc4..b6f92bdc5e7 100644
--- a/arch/arm/mach-sunxi/board.c
+++ b/arch/arm/mach-sunxi/board.c
@@ -244,12 +244,40 @@ void s_init(void)
 
 #define SUNXI_INVALID_BOOT_SOURCE	-1
 
-static int sunxi_get_boot_source(void)
+static struct boot_file_head *sunxi_egon_get_head(void)
 {
-	if (!is_boot0_magic(SPL_ADDR + 4)) /* eGON.BT0 */
-		return SUNXI_INVALID_BOOT_SOURCE;
+	struct boot_file_head *egon_head = (void *)SPL_ADDR;
+
+	if (memcmp(egon_head, BOOT0_MAGIC, 8)) /* eGON.BT0 */
+		return NULL;
+
+	return egon_head;
+}
+
+static struct toc0_main_info *sunxi_toc0_get_info(void)
+{
+	struct toc0_main_info *toc0_info = (void *)SPL_ADDR;
+
+	if (memcmp(toc0_info->name, TOC0_MAIN_INFO_NAME, 8)) /* TOC0.GLH */
+		return NULL;
 
-	return readb(SPL_ADDR + 0x28);
+	return toc0_info;
+}
+
+static int sunxi_get_boot_source(void)
+{
+	struct boot_file_head *egon_head;
+	struct toc0_main_info *toc0_info;
+
+	egon_head = sunxi_egon_get_head();
+	if (egon_head)
+		return readb(&egon_head->boot_media);
+	toc0_info = sunxi_toc0_get_info();
+	if (toc0_info)
+		return readb(&toc0_info->platform[0]);
+
+	/* Not a valid image, so we must have been booted via FEL. */
+	return SUNXI_INVALID_BOOT_SOURCE;
 }
 
 /* The sunxi internal brom will try to loader external bootloader
@@ -297,10 +325,18 @@ uint32_t sunxi_get_boot_device(void)
 #ifdef CONFIG_SPL_BUILD
 static u32 sunxi_get_spl_size(void)
 {
-	if (!is_boot0_magic(SPL_ADDR + 4)) /* eGON.BT0 */
-		return 0;
+	struct boot_file_head *egon_head;
+	struct toc0_main_info *toc0_info;
+
+	egon_head = sunxi_egon_get_head();
+	if (egon_head)
+		return readl(&egon_head->length);
+	toc0_info = sunxi_toc0_get_info();
+	if (toc0_info)
+		return readl(&toc0_info->length);
 
-	return readl(SPL_ADDR + 0x10);
+	/* Not a valid image, so use the default U-Boot offset. */
+	return 0;
 }
 
 /*
-- 
2.31.1


^ permalink raw reply related	[flat|nested] 13+ messages in thread

* [PATCH v2 4/4] sunxi: Support building a SPL as a TOC0 image
  2021-08-22  4:46 [PATCH v2 0/4] sunxi: TOC0 image type support Samuel Holland
                   ` (2 preceding siblings ...)
  2021-08-22  4:46 ` [PATCH v2 3/4] sunxi: Support SPL in both eGON and TOC0 images Samuel Holland
@ 2021-08-22  4:46 ` Samuel Holland
  3 siblings, 0 replies; 13+ messages in thread
From: Samuel Holland @ 2021-08-22  4:46 UTC (permalink / raw)
  To: u-boot, Jagan Teki, Andre Przywara
  Cc: Samuel Holland, AKASHI Takahiro, Alexandru Gagniuc, Baruch Siach,
	Bharat Gooty, Chris Packham, Fabio Estevam, Frieder Schrempf,
	Jernej Skrabec, Marek Behún, NXP i.MX U-Boot Team,
	Naoki Hayama, Pali Rohár, Patrick Delaunay, Priyanka Jain,
	Rayagonda Kokatanur, Simon Glass, Stefan Roese, Stefano Babic,
	Sughosh Ganu, Trevor Woerner, lauri.hintsala

Now that mkimage can generate TOC0 images, and the SPL can interpret
them, hook up the build infrastructure so the user can choose which
image type to build.

Signed-off-by: Samuel Holland <samuel@sholland.org>
---

Changes in v2:
 - Rebase on top of Icenowy's RISC-V support series
 - Rename Kconfig symbols to include the full image type name

 arch/arm/mach-sunxi/Kconfig |  2 ++
 board/sunxi/Kconfig         | 24 ++++++++++++++++++++++++
 scripts/Makefile.spl        |  5 ++++-
 3 files changed, 30 insertions(+), 1 deletion(-)
 create mode 100644 board/sunxi/Kconfig

diff --git a/arch/arm/mach-sunxi/Kconfig b/arch/arm/mach-sunxi/Kconfig
index 49f94f095c1..b0191d00808 100644
--- a/arch/arm/mach-sunxi/Kconfig
+++ b/arch/arm/mach-sunxi/Kconfig
@@ -1091,6 +1091,8 @@ config BLUETOOTH_DT_DEVICE_FIXUP
 	  The used address is "bdaddr" if set, and "ethaddr" with the LSB
 	  flipped elsewise.
 
+source "board/sunxi/Kconfig"
+
 endif
 
 config CHIP_DIP_SCAN
diff --git a/board/sunxi/Kconfig b/board/sunxi/Kconfig
new file mode 100644
index 00000000000..084a8b0c6ca
--- /dev/null
+++ b/board/sunxi/Kconfig
@@ -0,0 +1,24 @@
+choice
+	prompt "SPL Image Type"
+	default SPL_IMAGE_TYPE_SUNXI_EGON
+
+config SPL_IMAGE_TYPE_SUNXI_EGON
+	bool "eGON (normal)"
+	help
+	  Select this option to embed the SPL binary in an eGON.BT0 image,
+	  which is compatible with the normal boot ROM (NBROM).
+
+	  This is usually the correct option to choose.
+
+config SPL_IMAGE_TYPE_SUNXI_TOC0
+	bool "TOC0 (secure)"
+	help
+	  Select this option to embed the SPL binary in a TOC0 image,
+	  which is compatible with the secure boot ROM (SBROM).
+
+endchoice
+
+config SPL_IMAGE_TYPE
+	string
+	default "sunxi_egon" if SPL_IMAGE_TYPE_SUNXI_EGON
+	default "sunxi_toc0" if SPL_IMAGE_TYPE_SUNXI_TOC0
diff --git a/scripts/Makefile.spl b/scripts/Makefile.spl
index c90d692c500..53e3ef164c6 100644
--- a/scripts/Makefile.spl
+++ b/scripts/Makefile.spl
@@ -414,7 +414,10 @@ endif
 $(obj)/$(SPL_BIN).sfp: $(obj)/$(SPL_BIN).bin FORCE
 	$(call if_changed,mkimage)
 
-MKIMAGEFLAGS_sunxi-spl.bin = -A $(ARCH) -T sunxi_egon \
+MKIMAGEFLAGS_sunxi-spl.bin = \
+	-A $(ARCH) \
+	-T $(CONFIG_SPL_IMAGE_TYPE) \
+	-a $(CONFIG_SPL_TEXT_BASE) \
 	-n $(CONFIG_DEFAULT_DEVICE_TREE)
 
 OBJCOPYFLAGS_u-boot-spl-dtb.hex := -I binary -O ihex --change-address=$(CONFIG_SPL_TEXT_BASE)
-- 
2.31.1


^ permalink raw reply related	[flat|nested] 13+ messages in thread

* Re: [PATCH v2 1/4] tools: Separate image types which depend on OpenSSL
  2021-08-22  4:46 ` [PATCH v2 1/4] tools: Separate image types which depend on OpenSSL Samuel Holland
@ 2021-08-22  9:58   ` Pali Rohár
  2021-08-22 17:32     ` Samuel Holland
  0 siblings, 1 reply; 13+ messages in thread
From: Pali Rohár @ 2021-08-22  9:58 UTC (permalink / raw)
  To: Samuel Holland
  Cc: u-boot, Jagan Teki, Andre Przywara, AKASHI Takahiro,
	Alexandru Gagniuc, Baruch Siach, Bharat Gooty, Chris Packham,
	Fabio Estevam, Frieder Schrempf, Jernej Skrabec,
	Marek Behún, NXP i.MX U-Boot Team, Naoki Hayama,
	Patrick Delaunay, Priyanka Jain, Rayagonda Kokatanur,
	Simon Glass, Stefan Roese, Stefano Babic, Sughosh Ganu,
	Trevor Woerner, lauri.hintsala

Hello!

On Saturday 21 August 2021 23:46:45 Samuel Holland wrote:
> Some image types always depend on OpenSSL, so they can only be included
> in mkimage when TOOLS_LIBCRYPTO is selected. Instead of using arbitrary
> preprocessor definitions, conditionally link the files.
> 
> When building for platforms which use those image types, automatically
> select TOOLS_LIBCRYPTO, it is required for the build to complete.
> 
> Signed-off-by: Samuel Holland <samuel@sholland.org>
> ---
> 
> Changes in v2:
>  - Refactored the first patch on top of TOOLS_LIBCRYPTO
> 
>  arch/arm/mach-imx/mxs/Kconfig |  2 ++
>  arch/arm/mach-mvebu/Kconfig   |  1 +
>  scripts/config_whitelist.txt  |  1 -
>  tools/Makefile                | 19 +++++--------------
>  tools/mxsimage.c              |  3 ---
>  5 files changed, 8 insertions(+), 18 deletions(-)
> 
> diff --git a/arch/arm/mach-imx/mxs/Kconfig b/arch/arm/mach-imx/mxs/Kconfig
> index 9f48ffda414..24d452942a5 100644
> --- a/arch/arm/mach-imx/mxs/Kconfig
> +++ b/arch/arm/mach-imx/mxs/Kconfig
> @@ -3,6 +3,7 @@ if ARCH_MX23
>  config MX23
>  	bool
>  	default y
> +	select TOOLS_LIBCRYPTO
>  
>  choice
>  	prompt "MX23 board select"
> @@ -34,6 +35,7 @@ if ARCH_MX28
>  config MX28
>  	bool
>  	default y
> +	select TOOLS_LIBCRYPTO
>  
>  choice
>  	prompt "MX28 board select"
> diff --git a/arch/arm/mach-mvebu/Kconfig b/arch/arm/mach-mvebu/Kconfig
> index 89737a37ad9..c02521c71bc 100644
> --- a/arch/arm/mach-mvebu/Kconfig
> +++ b/arch/arm/mach-mvebu/Kconfig
> @@ -29,6 +29,7 @@ config ARMADA_38X
>  	bool
>  	select ARMADA_32BIT
>  	select HAVE_MVEBU_EFUSE
> +	select TOOLS_LIBCRYPTO

kwbimage format is used also by A375 and AXP platform. So rather put
this select into ARMADA_32BIT section.

Moreover kwbimage format is used also by orion and kirkwood platforms.
So this dependency needs to be enabled on more places.

>  config ARMADA_38X_HS_IMPEDANCE_THRESH
>  	hex  "Armada 38x USB 2.0 High-Speed Impedance Threshold (0x0 - 0x7)"
> diff --git a/scripts/config_whitelist.txt b/scripts/config_whitelist.txt
> index 2d70bf5da7c..5caaad0c3dc 100644
> --- a/scripts/config_whitelist.txt
> +++ b/scripts/config_whitelist.txt
> @@ -1000,7 +1000,6 @@ CONFIG_MXC_UART_BASE
>  CONFIG_MXC_USB_FLAGS
>  CONFIG_MXC_USB_PORT
>  CONFIG_MXC_USB_PORTSC
> -CONFIG_MXS
>  CONFIG_MXS_AUART
>  CONFIG_MXS_AUART_BASE
>  CONFIG_MXS_OCOTP
> diff --git a/tools/Makefile b/tools/Makefile
> index 4a86321f646..ad0e9cf9490 100644
> --- a/tools/Makefile
> +++ b/tools/Makefile
> @@ -94,9 +94,11 @@ ECDSA_OBJS-$(CONFIG_TOOLS_LIBCRYPTO) := $(addprefix lib/ecdsa/, ecdsa-libcrypto.
>  AES_OBJS-$(CONFIG_TOOLS_LIBCRYPTO) := $(addprefix lib/aes/, \
>  					aes-encrypt.o aes-decrypt.o)
>  
> -# Cryptographic helpers that depend on openssl/libcrypto
> -LIBCRYPTO_OBJS-$(CONFIG_TOOLS_LIBCRYPTO) := $(addprefix lib/, \
> -					fdt-libcrypto.o)
> +# Cryptographic helpers and image types that depend on openssl/libcrypto
> +LIBCRYPTO_OBJS-$(CONFIG_TOOLS_LIBCRYPTO) := \
> +			lib/fdt-libcrypto.o \
> +			kwbimage.o \
> +			mxsimage.o
>  
>  ROCKCHIP_OBS = lib/rc4.o rkcommon.o rkimage.o rksd.o rkspi.o
>  
> @@ -117,10 +119,8 @@ dumpimage-mkimage-objs := aisimage.o \
>  			imximage.o \
>  			imx8image.o \
>  			imx8mimage.o \
> -			kwbimage.o \
>  			lib/md5.o \
>  			lpc32xximage.o \
> -			mxsimage.o \
>  			omapimage.o \
>  			os_support.o \
>  			pblimage.o \
> @@ -155,22 +155,13 @@ fit_info-objs   := $(dumpimage-mkimage-objs) fit_info.o
>  fit_check_sign-objs   := $(dumpimage-mkimage-objs) fit_check_sign.o
>  file2include-objs := file2include.o
>  
> -ifneq ($(CONFIG_MX23)$(CONFIG_MX28)$(CONFIG_TOOLS_LIBCRYPTO),)
> -# Add CONFIG_MXS into host CFLAGS, so we can check whether or not register
> -# the mxsimage support within tools/mxsimage.c .
> -HOSTCFLAGS_mxsimage.o += -DCONFIG_MXS
> -endif
> -
>  ifdef CONFIG_TOOLS_LIBCRYPTO
>  # This affects include/image.h, but including the board config file
>  # is tricky, so manually define this options here.
>  HOST_EXTRACFLAGS	+= -DCONFIG_FIT_SIGNATURE
>  HOST_EXTRACFLAGS	+= -DCONFIG_FIT_SIGNATURE_MAX_SIZE=0xffffffff
>  HOST_EXTRACFLAGS	+= -DCONFIG_FIT_CIPHER
> -endif
>  
> -# MXSImage needs LibSSL
> -ifneq ($(CONFIG_MX23)$(CONFIG_MX28)$(CONFIG_ARMADA_38X)$(CONFIG_TOOLS_LIBCRYPTO),)
>  HOSTCFLAGS_kwbimage.o += \
>  	$(shell pkg-config --cflags libssl libcrypto 2> /dev/null || echo "")
>  HOSTLDLIBS_mkimage += \
> diff --git a/tools/mxsimage.c b/tools/mxsimage.c
> index 002f4b525aa..2bfbb421eb6 100644
> --- a/tools/mxsimage.c
> +++ b/tools/mxsimage.c
> @@ -5,8 +5,6 @@
>   * Copyright (C) 2012-2013 Marek Vasut <marex@denx.de>
>   */
>  
> -#ifdef CONFIG_MXS
> -
>  #include <errno.h>
>  #include <fcntl.h>
>  #include <stdio.h>
> @@ -2363,4 +2361,3 @@ U_BOOT_IMAGE_TYPE(
>  	NULL,
>  	mxsimage_generate
>  );
> -#endif
> -- 
> 2.31.1
> 

^ permalink raw reply	[flat|nested] 13+ messages in thread

* Re: [PATCH v2 2/4] tools: mkimage: Add Allwinner TOC0 support
  2021-08-22  4:46 ` [PATCH v2 2/4] tools: mkimage: Add Allwinner TOC0 support Samuel Holland
@ 2021-08-22 10:07   ` Pali Rohár
  2021-08-22 17:44     ` Samuel Holland
  2021-09-06  0:29   ` Andre Przywara
  1 sibling, 1 reply; 13+ messages in thread
From: Pali Rohár @ 2021-08-22 10:07 UTC (permalink / raw)
  To: Samuel Holland
  Cc: u-boot, Jagan Teki, Andre Przywara, AKASHI Takahiro,
	Alexandru Gagniuc, Baruch Siach, Bharat Gooty, Chris Packham,
	Fabio Estevam, Frieder Schrempf, Jernej Skrabec,
	Marek Behún, NXP i.MX U-Boot Team, Naoki Hayama,
	Patrick Delaunay, Priyanka Jain, Rayagonda Kokatanur,
	Simon Glass, Stefan Roese, Stefano Babic, Sughosh Ganu,
	Trevor Woerner, lauri.hintsala

Hello!

On Saturday 21 August 2021 23:46:46 Samuel Holland wrote:
> Most Allwinner sunxi SoCs have separate boot ROMs in non-secure and
> secure mode. The "non-secure" or "normal" boot ROM (NBROM) uses the
> existing sunxi_egon image type. The secure boot ROM (SBROM) uses a
> completely different image type, known as TOC0.
> 
> A TOC0 image is composed of a header and two or more items. One item
> is the firmware binary. The others form a chain linking the firmware
> signature to the root-of-trust public key (ROTPK), which has its hash
> burned in the SoC's eFuses. Signatures are made using RSA-2048 + SHA256.
> 
> The pseudo-ASN.1 structure is manually assembled; this is done to work
> around bugs/quirks in the boot ROM, which vary between SoCs. This TOC0
> implementation has been verified to work with the A50, A64, H5, H6,
> and H616 SBROMs, and it may work with other SoCs.
> 
> Signed-off-by: Samuel Holland <samuel@sholland.org>
> ---
> 
> Changes in v2:
>  - Moved certificate and key item structures out of sunxi_image.h
>  - Renamed "main" and "item" variables for clarity
>  - Improved error messages, and added a hint about key generation
>  - Added a comment explaining the purpose of the various key files
>  - Mentioned testing this code on A50 in the commit message
> 
>  arch/arm/Kconfig      |   1 +
>  common/image.c        |   1 +
>  include/image.h       |   1 +
>  include/sunxi_image.h |  36 ++
>  tools/Makefile        |   3 +-
>  tools/sunxi_toc0.c    | 907 ++++++++++++++++++++++++++++++++++++++++++
>  6 files changed, 948 insertions(+), 1 deletion(-)
>  create mode 100644 tools/sunxi_toc0.c
> 
> diff --git a/arch/arm/Kconfig b/arch/arm/Kconfig
> index d692139199c..799fe7d43af 100644
> --- a/arch/arm/Kconfig
> +++ b/arch/arm/Kconfig
> @@ -1014,6 +1014,7 @@ config ARCH_SUNXI
>  	select SUNXI_GPIO
>  	select SYS_NS16550
>  	select SYS_THUMB_BUILD if !ARM64
> +	select TOOLS_LIBCRYPTO
>  	select USB if DISTRO_DEFAULTS
>  	select USB_KEYBOARD if DISTRO_DEFAULTS && USB_HOST
>  	select USB_STORAGE if DISTRO_DEFAULTS && USB_HOST
> diff --git a/common/image.c b/common/image.c
> index 59c52a1f9ad..8f1634c1206 100644
> --- a/common/image.c
> +++ b/common/image.c
> @@ -191,6 +191,7 @@ static const table_entry_t uimage_type[] = {
>  	{	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" },
> +	{	IH_TYPE_SUNXI_TOC0, "sunxi_toc0",  "Allwinner TOC0 Boot Image" },
>  	{	-1,		    "",		  "",			},
>  };
>  
> diff --git a/include/image.h b/include/image.h
> index e20f0b69d58..a4efc090309 100644
> --- a/include/image.h
> +++ b/include/image.h
> @@ -284,6 +284,7 @@ enum {
>  	IH_TYPE_IMX8IMAGE,		/* Freescale IMX8Boot Image	*/
>  	IH_TYPE_COPRO,			/* Coprocessor Image for remoteproc*/
>  	IH_TYPE_SUNXI_EGON,		/* Allwinner eGON Boot Image */
> +	IH_TYPE_SUNXI_TOC0,		/* Allwinner TOC0 Boot Image */
>  
>  	IH_TYPE_COUNT,			/* Number of image types */
>  };
> diff --git a/include/sunxi_image.h b/include/sunxi_image.h
> index 5b2055c0af3..399ad0be999 100644
> --- a/include/sunxi_image.h
> +++ b/include/sunxi_image.h
> @@ -9,9 +9,12 @@
>   *
>   * Shared between mkimage and the SPL.
>   */
> +
>  #ifndef	SUNXI_IMAGE_H
>  #define	SUNXI_IMAGE_H
>  
> +#include <linux/types.h>
> +
>  #define BOOT0_MAGIC		"eGON.BT0"
>  #define BROM_STAMP_VALUE	0x5f0a6c39
>  #define SPL_SIGNATURE		"SPL" /* marks "sunxi" SPL header */
> @@ -79,4 +82,37 @@ struct boot_file_head {
>  /* 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)];

(Just suggestion for future, not related to this patch series: above
check could be rewritten/cleaned to use static assert)

>  
> +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];
> +};
> +
> +#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];
> +};

These structures are raw image structures, right? Therefore they should
be marked as __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/tools/Makefile b/tools/Makefile
> index ad0e9cf9490..fc3b66af6af 100644
> --- a/tools/Makefile
> +++ b/tools/Makefile
> @@ -98,7 +98,8 @@ AES_OBJS-$(CONFIG_TOOLS_LIBCRYPTO) := $(addprefix lib/aes/, \
>  LIBCRYPTO_OBJS-$(CONFIG_TOOLS_LIBCRYPTO) := \
>  			lib/fdt-libcrypto.o \
>  			kwbimage.o \
> -			mxsimage.o
> +			mxsimage.o \
> +			sunxi_toc0.o
>  
>  ROCKCHIP_OBS = lib/rc4.o rkcommon.o rkimage.o rksd.o rkspi.o
>  
> diff --git a/tools/sunxi_toc0.c b/tools/sunxi_toc0.c
> new file mode 100644
> index 00000000000..5aa39cba775
> --- /dev/null
> +++ b/tools/sunxi_toc0.c
> @@ -0,0 +1,907 @@
> +// SPDX-License-Identifier: GPL-2.0+
> +/*
> + * (C) Copyright 2018 Arm Ltd.
> + * (C) Copyright 2020-2021 Samuel Holland <samuel@sholland.org>
> + */
> +
> +#include <assert.h>
> +#include <stdint.h>
> +#include <stdio.h>
> +#include <stdlib.h>
> +#include <string.h>
> +
> +#include <openssl/asn1t.h>
> +#include <openssl/pem.h>
> +#include <openssl/rsa.h>
> +
> +#include <image.h>
> +#include <sunxi_image.h>
> +
> +#include "imagetool.h"
> +#include "mkimage.h"
> +
> +/*
> + * NAND requires 8K padding. For other devices, BROM requires only
> + * 512B padding, but let's use the larger padding to cover everything.
> + */
> +#define PAD_SIZE		8192
> +
> +#define pr_fmt(fmt)		"mkimage (TOC0): %s: " fmt
> +#define pr_err(fmt, args...)	fprintf(stderr, pr_fmt(fmt), "error", ##args)
> +#define pr_warn(fmt, args...)	fprintf(stderr, pr_fmt(fmt), "warning", ##args)
> +#define pr_info(fmt, args...)	fprintf(stderr, pr_fmt(fmt), "info", ##args)
> +
> +struct toc0_key_item {
> +	__le32  vendor_id;
> +	__le32  key0_n_len;
> +	__le32  key0_e_len;
> +	__le32  key1_n_len;
> +	__le32  key1_e_len;
> +	__le32  sig_len;
> +	uint8_t key0[512];
> +	uint8_t key1[512];
> +	uint8_t reserved[32];
> +	uint8_t sig[256];
> +};

This also looks like raw structure which should be marked as __packed.

> +/*
> + * This looks somewhat like an X.509 certificate, but it is not valid BER.
> + *
> + * Some differences:
> + *  - Some X.509 certificate fields are missing or rearranged.
> + *  - Some sequences have the wrong tag.
> + *  - Zero-length sequences are accepted.
> + *  - Large strings and integers must be an even number of bytes long.
> + *  - Positive integers are not zero-extended to maintain their sign.
> + *
> + * See https://linux-sunxi.org/TOC0 for more information.
> + */
> +struct toc0_small_tag {
> +	uint8_t tag;
> +	uint8_t length;
> +};
> +
> +typedef struct toc0_small_tag toc0_small_int;
> +typedef struct toc0_small_tag toc0_small_oct;
> +typedef struct toc0_small_tag toc0_small_seq;
> +typedef struct toc0_small_tag toc0_small_exp;
> +
> +#define TOC0_SMALL_INT(len) { 0x02, (len) }
> +#define TOC0_SMALL_SEQ(len) { 0x30, (len) }
> +#define TOC0_SMALL_EXP(tag, len) { 0xa0 | (tag), len }
> +
> +struct toc0_large_tag {
> +	uint8_t tag;
> +	uint8_t prefix;
> +	uint8_t length_hi;
> +	uint8_t length_lo;
> +};
> +
> +typedef struct toc0_large_tag toc0_large_int;
> +typedef struct toc0_large_tag toc0_large_bit;
> +typedef struct toc0_large_tag toc0_large_seq;
> +
> +#define TOC0_LARGE_INT(len) { 0x02, 0x82, (len) >> 8, (len) & 0xff }
> +#define TOC0_LARGE_BIT(len) { 0x03, 0x82, (len) >> 8, (len) & 0xff }
> +#define TOC0_LARGE_SEQ(len) { 0x30, 0x82, (len) >> 8, (len) & 0xff }
> +
> +struct toc0_cert_item {
> +	toc0_large_seq tag_totalSequence;
> +	struct toc0_totalSequence {
> +		toc0_large_seq tag_mainSequence;
> +		struct toc0_mainSequence {
> +			toc0_small_exp tag_explicit0;
> +			struct toc0_explicit0 {
> +				toc0_small_int tag_version;
> +				uint8_t version;
> +			} explicit0;
> +			toc0_small_int tag_serialNumber;
> +			uint8_t serialNumber;
> +			toc0_small_seq tag_signature;
> +			toc0_small_seq tag_issuer;
> +			toc0_small_seq tag_validity;
> +			toc0_small_seq tag_subject;
> +			toc0_large_seq tag_subjectPublicKeyInfo;
> +			struct toc0_subjectPublicKeyInfo {
> +				toc0_small_seq tag_algorithm;
> +				toc0_large_seq tag_publicKey;
> +				struct toc0_publicKey {
> +					toc0_large_int tag_n;
> +					uint8_t n[256];
> +					toc0_small_int tag_e;
> +					uint8_t e[3];
> +				} publicKey;
> +			} subjectPublicKeyInfo;
> +			toc0_small_exp tag_explicit3;
> +			struct toc0_explicit3 {
> +				toc0_small_seq tag_extension;
> +				struct toc0_extension {
> +					toc0_small_int tag_digest;
> +					uint8_t digest[32];
> +				} extension;
> +			} explicit3;
> +		} mainSequence;
> +		toc0_large_bit tag_sigSequence;
> +		struct toc0_sigSequence {
> +			toc0_small_seq tag_algorithm;
> +			toc0_large_bit tag_signature;
> +			uint8_t signature[256];
> +		} sigSequence;
> +	} totalSequence;
> +};
> +
> +#define sizeof_field(TYPE, MEMBER) sizeof((((TYPE *)0)->MEMBER))
> +
> +static const struct toc0_cert_item cert_item_template = {
> +	TOC0_LARGE_SEQ(sizeof(struct toc0_totalSequence)),
> +	{
> +		TOC0_LARGE_SEQ(sizeof(struct toc0_mainSequence)),
> +		{
> +			TOC0_SMALL_EXP(0, sizeof(struct toc0_explicit0)),
> +			{
> +				TOC0_SMALL_INT(sizeof_field(struct toc0_explicit0, version)),
> +				0,
> +			},
> +			TOC0_SMALL_INT(sizeof_field(struct toc0_mainSequence, serialNumber)),
> +			0,
> +			TOC0_SMALL_SEQ(0),
> +			TOC0_SMALL_SEQ(0),
> +			TOC0_SMALL_SEQ(0),
> +			TOC0_SMALL_SEQ(0),
> +			TOC0_LARGE_SEQ(sizeof(struct toc0_subjectPublicKeyInfo)),
> +			{
> +				TOC0_SMALL_SEQ(0),
> +				TOC0_LARGE_SEQ(sizeof(struct toc0_publicKey)),
> +				{
> +					TOC0_LARGE_INT(sizeof_field(struct toc0_publicKey, n)),
> +					{},
> +					TOC0_SMALL_INT(sizeof_field(struct toc0_publicKey, e)),
> +					{},
> +				},
> +			},
> +			TOC0_SMALL_EXP(3, sizeof(struct toc0_explicit3)),
> +			{
> +				TOC0_SMALL_SEQ(sizeof(struct toc0_extension)),
> +				{
> +					TOC0_SMALL_INT(sizeof_field(struct toc0_extension, digest)),
> +					{},
> +				},
> +			},
> +		},
> +		TOC0_LARGE_BIT(sizeof(struct toc0_sigSequence)),
> +		{
> +			TOC0_SMALL_SEQ(0),
> +			TOC0_LARGE_BIT(sizeof_field(struct toc0_sigSequence, signature)),
> +			{},
> +		},
> +	},
> +};
> +
> +#define TOC0_DEFAULT_NUM_ITEMS		3
> +#define TOC0_DEFAULT_HEADER_LEN						  \
> +	ALIGN(								  \
> +		sizeof(struct toc0_main_info)				+ \
> +		sizeof(struct toc0_item_info) *	TOC0_DEFAULT_NUM_ITEMS	+ \
> +		sizeof(struct toc0_cert_item)				+ \
> +		sizeof(struct toc0_key_item),				  \
> +	32)
> +
> +static char *fw_key_file   = "fw_key.pem";
> +static char *key_item_file = "key_item.bin";
> +static char *root_key_file = "root_key.pem";
> +
> +/*
> + * Create a key item in @buf, containing the public keys @root_key and @fw_key,
> + * and signed by the RSA key @root_key.
> + */
> +static int toc0_create_key_item(uint8_t *buf, uint32_t *len,
> +				RSA *root_key, RSA *fw_key)
> +{
> +	struct toc0_key_item *key_item = (void *)buf;
> +	uint8_t digest[SHA256_DIGEST_LENGTH];
> +	int ret = EXIT_FAILURE;
> +	unsigned int sig_len;
> +	int n_len, e_len;
> +
> +	/* Store key 0. */
> +	n_len = BN_bn2bin(RSA_get0_n(root_key), key_item->key0);
> +	e_len = BN_bn2bin(RSA_get0_e(root_key), key_item->key0 + n_len);
> +	if (n_len + e_len > sizeof(key_item->key0)) {
> +		pr_err("Root key is too big for key item\n");
> +		goto err;
> +	}
> +	key_item->key0_n_len = cpu_to_le32(n_len);
> +	key_item->key0_e_len = cpu_to_le32(e_len);
> +
> +	/* Store key 1. */
> +	n_len = BN_bn2bin(RSA_get0_n(fw_key), key_item->key1);
> +	e_len = BN_bn2bin(RSA_get0_e(fw_key), key_item->key1 + n_len);
> +	if (n_len + e_len > sizeof(key_item->key1)) {
> +		pr_err("Firmware key is too big for key item\n");
> +		goto err;
> +	}
> +	key_item->key1_n_len = cpu_to_le32(n_len);
> +	key_item->key1_e_len = cpu_to_le32(e_len);
> +
> +	/* Sign the key item. */
> +	key_item->sig_len = cpu_to_le32(RSA_size(root_key));
> +	SHA256(buf, key_item->sig - buf, digest);
> +	if (!RSA_sign(NID_sha256, digest, sizeof(digest),
> +		      key_item->sig, &sig_len, root_key)) {
> +		pr_err("Failed to sign key item\n");
> +		goto err;
> +	}
> +	if (sig_len != sizeof(key_item->sig)) {
> +		pr_err("Bad key item signature length\n");
> +		goto err;
> +	}
> +
> +	*len = sizeof(*key_item);
> +	ret = EXIT_SUCCESS;
> +
> +err:
> +	return ret;
> +}
> +
> +/*
> + * Verify the key item in @buf, containing two public keys @key0 and @key1,
> + * and signed by the RSA key @key0. If @root_key is provided, only signatures
> + * by that key will be accepted. @key1 is returned in @key.
> + */
> +static int toc0_verify_key_item(const uint8_t *buf, uint32_t len,
> +				RSA *root_key, RSA **fw_key)
> +{
> +	struct toc0_key_item *key_item = (void *)buf;
> +	uint8_t digest[SHA256_DIGEST_LENGTH];
> +	int ret = EXIT_FAILURE;
> +	int n_len, e_len;
> +	RSA *key0 = NULL;
> +	RSA *key1 = NULL;
> +	BIGNUM *n, *e;
> +
> +	if (len < sizeof(*key_item))
> +		goto err;
> +
> +	/* Load key 0. */
> +	n_len = le32_to_cpu(key_item->key0_n_len);
> +	e_len = le32_to_cpu(key_item->key0_e_len);
> +	if (n_len + e_len > sizeof(key_item->key0)) {
> +		pr_err("Bad root key size in key item\n");
> +		goto err;
> +	}
> +	n = BN_bin2bn(key_item->key0, n_len, NULL);
> +	e = BN_bin2bn(key_item->key0 + n_len, e_len, NULL);
> +	key0 = RSA_new();
> +	if (!key0)
> +		goto err;
> +	if (!RSA_set0_key(key0, n, e, NULL))
> +		goto err;
> +
> +	/* If a root key was provided, compare it to key 0. */
> +	if (root_key && (BN_cmp(n, RSA_get0_n(root_key)) ||
> +			 BN_cmp(e, RSA_get0_e(root_key)))) {
> +		pr_err("Wrong root key in key item\n");
> +		goto err;
> +	}
> +
> +	/* Verify the key item signature. */
> +	SHA256(buf, key_item->sig - buf, digest);
> +	if (!RSA_verify(NID_sha256, digest, sizeof(digest),
> +			key_item->sig, le32_to_cpu(key_item->sig_len), key0)) {
> +		pr_err("Bad key item signature\n");
> +		goto err;
> +	}
> +
> +	if (fw_key) {
> +		/* Load key 1. */
> +		n_len = le32_to_cpu(key_item->key1_n_len);
> +		e_len = le32_to_cpu(key_item->key1_e_len);
> +		if (n_len + e_len > sizeof(key_item->key1)) {
> +			pr_err("Bad firmware key size in key item\n");
> +			goto err;
> +		}
> +		n = BN_bin2bn(key_item->key1, n_len, NULL);
> +		e = BN_bin2bn(key_item->key1 + n_len, e_len, NULL);
> +		key1 = RSA_new();
> +		if (!key1)
> +			goto err;
> +		if (!RSA_set0_key(key1, n, e, NULL))
> +			goto err;
> +
> +		if (*fw_key) {
> +			/* If a FW key was provided, compare it to key 1. */
> +			if (BN_cmp(n, RSA_get0_n(*fw_key)) ||
> +			    BN_cmp(e, RSA_get0_e(*fw_key))) {
> +				pr_err("Wrong firmware key in key item\n");
> +				goto err;
> +			}
> +		} else {
> +			/* Otherwise, send key1 back to the caller. */
> +			*fw_key = key1;
> +			key1 = NULL;
> +		}
> +	}
> +
> +	ret = EXIT_SUCCESS;
> +
> +err:
> +	RSA_free(key0);
> +	RSA_free(key1);
> +
> +	return ret;
> +}
> +
> +/*
> + * Create a certificate in @buf, describing the firmware with SHA256 digest
> + * @digest, and signed by the RSA key @fw_key.
> + */
> +static int toc0_create_cert_item(uint8_t *buf, uint32_t *len, RSA *fw_key,
> +				 uint8_t digest[static SHA256_DIGEST_LENGTH])
> +{
> +	struct toc0_cert_item *cert_item = (void *)buf;
> +	uint8_t cert_digest[SHA256_DIGEST_LENGTH];
> +	struct toc0_totalSequence *totalSequence;
> +	struct toc0_sigSequence *sigSequence;
> +	struct toc0_extension *extension;
> +	struct toc0_publicKey *publicKey;
> +	int ret = EXIT_FAILURE;
> +	unsigned int sig_len;
> +
> +	memcpy(cert_item, &cert_item_template, sizeof(*cert_item));
> +	*len = sizeof(*cert_item);
> +
> +	/*
> +	 * Fill in the public key.
> +	 *
> +	 * Only 2048-bit RSA keys are supported. Since this uses a fixed-size
> +	 * structure, it may fail for non-standard exponents.
> +	 */
> +	totalSequence = &cert_item->totalSequence;
> +	publicKey = &totalSequence->mainSequence.subjectPublicKeyInfo.publicKey;
> +	if (BN_bn2binpad(RSA_get0_n(fw_key), publicKey->n, sizeof(publicKey->n)) < 0 ||
> +	    BN_bn2binpad(RSA_get0_e(fw_key), publicKey->e, sizeof(publicKey->e)) < 0) {
> +		pr_err("Firmware key is too big for certificate\n");
> +		goto err;
> +	}
> +
> +	/* Fill in the firmware digest. */
> +	extension = &totalSequence->mainSequence.explicit3.extension;
> +	memcpy(&extension->digest, digest, SHA256_DIGEST_LENGTH);
> +
> +	/*
> +	 * Sign the certificate.
> +	 *
> +	 * In older SBROM versions (and by default in newer versions),
> +	 * the last 4 bytes of the certificate are not signed.
> +	 *
> +	 * (The buffer passed to SHA256 starts at tag_mainSequence, but
> +	 *  the buffer size does not include the length of that tag.)
> +	 */
> +	SHA256((uint8_t *)totalSequence, sizeof(struct toc0_mainSequence), cert_digest);
> +	sigSequence = &totalSequence->sigSequence;
> +	if (!RSA_sign(NID_sha256, cert_digest, SHA256_DIGEST_LENGTH,
> +		      sigSequence->signature, &sig_len, fw_key)) {
> +		pr_err("Failed to sign certificate\n");
> +		goto err;
> +	}
> +	if (sig_len != sizeof(sigSequence->signature)) {
> +		pr_err("Bad certificate signature length\n");
> +		goto err;
> +	}
> +
> +	ret = EXIT_SUCCESS;
> +
> +err:
> +	return ret;
> +}
> +
> +/*
> + * Verify the certificate in @buf, describing the firmware with SHA256 digest
> + * @digest, and signed by the RSA key contained within. If @fw_key is provided,
> + * only that key will be accepted.
> + *
> + * This function is only expected to work with images created by mkimage.
> + */
> +static int toc0_verify_cert_item(const uint8_t *buf, uint32_t len, RSA *fw_key,
> +				 uint8_t digest[static SHA256_DIGEST_LENGTH])
> +{
> +	const struct toc0_cert_item *cert_item = (const void *)buf;
> +	uint8_t cert_digest[SHA256_DIGEST_LENGTH];
> +	const struct toc0_totalSequence *totalSequence;
> +	const struct toc0_sigSequence *sigSequence;
> +	const struct toc0_extension *extension;
> +	const struct toc0_publicKey *publicKey;
> +	int ret = EXIT_FAILURE;
> +	RSA *key = NULL;
> +	BIGNUM *n, *e;
> +
> +	/* Extract the public key from the certificate. */
> +	totalSequence = &cert_item->totalSequence;
> +	publicKey = &totalSequence->mainSequence.subjectPublicKeyInfo.publicKey;
> +	n = BN_bin2bn(publicKey->n, sizeof(publicKey->n), NULL);
> +	e = BN_bin2bn(publicKey->e, sizeof(publicKey->e), NULL);
> +	key = RSA_new();
> +	if (!key)
> +		goto err;
> +	if (!RSA_set0_key(key, n, e, NULL))
> +		goto err;
> +
> +	/* If a key was provided, compare it to the embedded key. */
> +	if (fw_key && (BN_cmp(RSA_get0_n(key), RSA_get0_n(fw_key)) ||
> +		       BN_cmp(RSA_get0_e(key), RSA_get0_e(fw_key)))) {
> +		pr_err("Wrong firmware key in certificate\n");
> +		goto err;
> +	}
> +
> +	/* If a digest was provided, compare it to the embedded digest. */
> +	extension = &totalSequence->mainSequence.explicit3.extension;
> +	if (digest && memcmp(&extension->digest, digest, SHA256_DIGEST_LENGTH)) {
> +		pr_err("Wrong firmware digest in certificate\n");
> +		goto err;
> +	}
> +
> +	/* Verify the certificate's signature. See the comment above. */
> +	SHA256((uint8_t *)totalSequence, sizeof(struct toc0_mainSequence), cert_digest);
> +	sigSequence = &totalSequence->sigSequence;
> +	if (!RSA_verify(NID_sha256, cert_digest, SHA256_DIGEST_LENGTH,
> +			sigSequence->signature,
> +			sizeof(sigSequence->signature), key)) {
> +		pr_err("Bad certificate signature\n");
> +		goto err;
> +	}
> +
> +	ret = EXIT_SUCCESS;
> +
> +err:
> +	RSA_free(key);
> +
> +	return ret;
> +}
> +
> +/*
> + * Always create a TOC0 containing 3 items. The extra item will be ignored on
> + * SoCs which do not support it.
> + */
> +static int toc0_create(uint8_t *buf, uint32_t len, RSA *root_key, RSA *fw_key,
> +		       uint8_t *key_item, uint32_t key_item_len,
> +		       uint8_t *fw_item, uint32_t fw_item_len, uint32_t fw_addr)
> +{
> +	struct toc0_main_info *main_info = (void *)buf;
> +	struct toc0_item_info *item_info = (void *)(main_info + 1);
> +	uint8_t digest[SHA256_DIGEST_LENGTH];
> +	uint32_t *buf32 = (void *)buf;
> +	RSA *orig_fw_key = fw_key;
> +	int ret = EXIT_FAILURE;
> +	uint32_t checksum = 0;
> +	uint32_t item_offset;
> +	uint32_t item_length;
> +	int i;
> +
> +	/* Hash the firmware for inclusion in the certificate. */
> +	SHA256(fw_item, fw_item_len, digest);
> +
> +	/* Create the main TOC0 header, containing three items. */
> +	memcpy(main_info->name, TOC0_MAIN_INFO_NAME, sizeof(main_info->name));
> +	main_info->magic	= cpu_to_le32(TOC0_MAIN_INFO_MAGIC);
> +	main_info->checksum	= cpu_to_le32(BROM_STAMP_VALUE);
> +	main_info->num_items	= cpu_to_le32(TOC0_DEFAULT_NUM_ITEMS);
> +	memcpy(main_info->end, TOC0_MAIN_INFO_END, sizeof(main_info->end));
> +
> +	/* The first item links the ROTPK to the signing key. */
> +	item_offset = sizeof(*main_info) +
> +		      sizeof(*item_info) * TOC0_DEFAULT_NUM_ITEMS;
> +	/* Using an existing key item avoids needing the root private key. */
> +	if (key_item) {
> +		item_length = sizeof(*key_item);
> +		if (toc0_verify_key_item(key_item, item_length,
> +					 root_key, &fw_key))
> +			goto err;
> +		memcpy(buf + item_offset, key_item, item_length);
> +	} else if (toc0_create_key_item(buf + item_offset, &item_length,
> +					root_key, fw_key)) {
> +		goto err;
> +	}
> +
> +	item_info->name		= cpu_to_le32(TOC0_ITEM_INFO_NAME_KEY);
> +	item_info->offset	= cpu_to_le32(item_offset);
> +	item_info->length	= cpu_to_le32(item_length);
> +	memcpy(item_info->end, TOC0_ITEM_INFO_END, sizeof(item_info->end));
> +
> +	/* The second item contains a certificate signed by the firmware key. */
> +	item_offset = item_offset + item_length;
> +	if (toc0_create_cert_item(buf + item_offset, &item_length,
> +				  fw_key, digest))
> +		goto err;
> +
> +	item_info++;
> +	item_info->name		= cpu_to_le32(TOC0_ITEM_INFO_NAME_CERT);
> +	item_info->offset	= cpu_to_le32(item_offset);
> +	item_info->length	= cpu_to_le32(item_length);
> +	memcpy(item_info->end, TOC0_ITEM_INFO_END, sizeof(item_info->end));
> +
> +	/* The third item contains the actual boot code. */
> +	item_offset = ALIGN(item_offset + item_length, 32);
> +	item_length = fw_item_len;
> +	if (buf + item_offset != fw_item)
> +		memmove(buf + item_offset, fw_item, item_length);
> +
> +	item_info++;
> +	item_info->name		= cpu_to_le32(TOC0_ITEM_INFO_NAME_FIRMWARE);
> +	item_info->offset	= cpu_to_le32(item_offset);
> +	item_info->length	= cpu_to_le32(item_length);
> +	item_info->load_addr	= cpu_to_le32(fw_addr);
> +	memcpy(item_info->end, TOC0_ITEM_INFO_END, sizeof(item_info->end));
> +
> +	/* Pad to the required block size with 0xff to be flash-friendly. */
> +	item_offset = item_offset + item_length;
> +	item_length = ALIGN(item_offset, PAD_SIZE) - item_offset;
> +	memset(buf + item_offset, 0xff, item_length);
> +
> +	/* Fill in the total padded file length. */
> +	item_offset = item_offset + item_length;
> +	main_info->length = cpu_to_le32(item_offset);
> +
> +	/* Verify enough space was provided when creating the image. */
> +	assert(len >= item_offset);
> +
> +	/* Calculate the checksum. Yes, it's that simple. */
> +	for (i = 0; i < item_offset / 4; ++i)
> +		checksum += le32_to_cpu(buf32[i]);
> +	main_info->checksum = cpu_to_le32(checksum);
> +
> +	ret = EXIT_SUCCESS;
> +
> +err:
> +	if (fw_key != orig_fw_key)
> +		RSA_free(fw_key);
> +
> +	return ret;
> +}
> +
> +static const struct toc0_item_info *
> +toc0_find_item(const struct toc0_main_info *main_info, uint32_t name,
> +	       uint32_t *offset, uint32_t *length)
> +{
> +	const struct toc0_item_info *item_info = (void *)(main_info + 1);
> +	uint32_t item_offset, item_length;
> +	uint32_t num_items, main_length;
> +	int i;
> +
> +	num_items   = le32_to_cpu(main_info->num_items);
> +	main_length = le32_to_cpu(main_info->length);
> +
> +	for (i = 0; i < num_items; ++i, ++item_info) {
> +		if (le32_to_cpu(item_info->name) != name)
> +			continue;
> +
> +		item_offset = le32_to_cpu(item_info->offset);
> +		item_length = le32_to_cpu(item_info->length);
> +
> +		if (item_offset > main_length ||
> +		    item_length > main_length - item_offset)
> +			continue;
> +
> +		*offset = item_offset;
> +		*length = item_length;
> +
> +		return item_info;
> +	}
> +
> +	return NULL;
> +}
> +
> +static int toc0_verify(const uint8_t *buf, uint32_t len, RSA *root_key)
> +{
> +	const struct toc0_main_info *main_info = (void *)buf;
> +	const struct toc0_item_info *item_info;
> +	uint8_t digest[SHA256_DIGEST_LENGTH];
> +	uint32_t main_length = le32_to_cpu(main_info->length);
> +	uint32_t checksum = BROM_STAMP_VALUE;
> +	uint32_t *buf32 = (void *)buf;
> +	uint32_t length, offset;
> +	int ret = EXIT_FAILURE;
> +	RSA *fw_key = NULL;
> +	int i;
> +
> +	if (len < main_length)
> +		goto err;
> +
> +	/* Verify the main header. */
> +	if (memcmp(main_info->name, TOC0_MAIN_INFO_NAME, sizeof(main_info->name)))
> +		goto err;
> +	if (le32_to_cpu(main_info->magic) != TOC0_MAIN_INFO_MAGIC)
> +		goto err;
> +	/* Verify the checksum without modifying the buffer. */
> +	for (i = 0; i < main_length / 4; ++i)
> +		checksum += le32_to_cpu(buf32[i]);
> +	if (checksum != 2 * le32_to_cpu(main_info->checksum))
> +		goto err;
> +	/* The length must be at least 512 byte aligned. */
> +	if (main_length % 512)
> +		goto err;
> +	if (memcmp(main_info->end, TOC0_MAIN_INFO_END, sizeof(main_info->end)))
> +		goto err;
> +
> +	/* Verify the key item if present (it is optional). */
> +	item_info = toc0_find_item(main_info, TOC0_ITEM_INFO_NAME_KEY,
> +				   &offset, &length);
> +	if (!item_info)
> +		fw_key = root_key;
> +	else if (toc0_verify_key_item(buf + offset, length, root_key, &fw_key))
> +		goto err;
> +
> +	/* Hash the firmware to compare with the certificate. */
> +	item_info = toc0_find_item(main_info, TOC0_ITEM_INFO_NAME_FIRMWARE,
> +				   &offset, &length);
> +	if (!item_info) {
> +		pr_err("Missing firmware item\n");
> +		goto err;
> +	}
> +	SHA256(buf + offset, length, digest);
> +
> +	/* Verify the certificate item. */
> +	item_info = toc0_find_item(main_info, TOC0_ITEM_INFO_NAME_CERT,
> +				   &offset, &length);
> +	if (!item_info) {
> +		pr_err("Missing certificate item\n");
> +		goto err;
> +	}
> +	if (toc0_verify_cert_item(buf + offset, length, fw_key, digest))
> +		goto err;
> +
> +	ret = EXIT_SUCCESS;
> +
> +err:
> +	if (fw_key != root_key)
> +		RSA_free(fw_key);
> +
> +	return ret;
> +}
> +
> +static int toc0_check_params(struct image_tool_params *params)
> +{
> +	if (!params->dflag)
> +		return -EINVAL;
> +
> +	/*
> +	 * If a key directory was provided, look for key files there.
> +	 * Otherwise, look for them in the current directory. The key files are
> +	 * the "quoted" terms in the description below.
> +	 *
> +	 * A summary of the chain of trust on most SoCs:
> +	 *  1) eFuse contains a SHA256 digest of the public "root key".
> +	 *  2) Private "root key" signs the certificate item (generated here).
> +	 *  3) Certificate item contains a SHA256 digest of the firmware item.
> +	 *
> +	 * A summary of the chain of trust on the H6 (by default; a bit in the
> +	 * BROM_CONFIG eFuse makes it work like above):
> +	 *  1) eFuse contains a SHA256 digest of the public "root key".
> +	 *  2) Private "root key" signs the "key item" (generated here).
> +	 *  3) "Key item" contains the public "root key" and public "fw key".
> +	 *  4) Private "fw key" signs the certificate item (generated here).
> +	 *  5) Certificate item contains a SHA256 digest of the firmware item.
> +	 *
> +	 * This means there are three valid ways to generate a TOC0:
> +	 *  1) Provide the private "root key" only. This works everywhere.
> +	 *     For H6, the "root key" will also be used as the "fw key".
> +	 *  2) FOR H6 ONLY: Provide the private "root key" and a separate
> +	 *     private "fw key".
> +	 *  3) FOR H6 ONLY: Provide the private "fw key" and a pre-existing
> +	 *     "key item" containing the corresponding  public "fw key".
> +	 *     In this case, the private "root key" can be kept offline. The
> +	 *     "key item" can be extracted from a TOC0 image generated using
> +	 *     method #2 above.
> +	 *
> +	 *  Note that until the ROTPK_HASH eFuse is programmed, any "root key"
> +	 *  will be accepted by the BROM.
> +	 */
> +	if (params->keydir) {
> +		if (asprintf(&fw_key_file, "%s/%s", params->keydir, fw_key_file) < 0)
> +			return -ENOMEM;
> +		if (asprintf(&key_item_file, "%s/%s", params->keydir, key_item_file) < 0)
> +			return -ENOMEM;
> +		if (asprintf(&root_key_file, "%s/%s", params->keydir, root_key_file) < 0)
> +			return -ENOMEM;
> +	}
> +
> +	return 0;
> +}
> +
> +static int toc0_verify_header(unsigned char *buf, int image_size,
> +			      struct image_tool_params *params)
> +{
> +	int ret = EXIT_FAILURE;
> +	RSA *root_key = NULL;
> +	FILE *fp;
> +
> +	/* A root public key is optional. */
> +	fp = fopen(root_key_file, "rb");
> +	if (fp) {
> +		pr_info("Verifying image with existing root key\n");
> +		root_key = PEM_read_RSAPrivateKey(fp, NULL, NULL, NULL);
> +		if (!root_key)
> +			root_key = PEM_read_RSAPublicKey(fp, NULL, NULL, NULL);
> +		fclose(fp);
> +		if (!root_key) {
> +			pr_err("Failed to read public key from '%s'\n",
> +			       root_key_file);
> +			goto err;
> +		}
> +	}
> +
> +	ret = toc0_verify(buf, image_size, root_key);
> +
> +err:
> +	RSA_free(root_key);
> +
> +	return ret;
> +}
> +
> +static const char *toc0_item_name(uint32_t name)
> +{
> +	if (name == TOC0_ITEM_INFO_NAME_CERT)
> +		return "Certificate";
> +	if (name == TOC0_ITEM_INFO_NAME_FIRMWARE)
> +		return "Firmware";
> +	if (name == TOC0_ITEM_INFO_NAME_KEY)
> +		return "Key";
> +	return "(unknown)";
> +}
> +
> +static void toc0_print_header(const void *buf)
> +{
> +	const struct toc0_main_info *main_info = buf;
> +	const struct toc0_item_info *item_info = (void *)(main_info + 1);
> +	uint32_t head_length, main_length, num_items;
> +	uint32_t item_offset, item_length, item_name;
> +	int load_addr = -1;
> +	int i;
> +
> +	num_items   = le32_to_cpu(main_info->num_items);
> +	head_length = sizeof(*main_info) + num_items * sizeof(*item_info);
> +	main_length = le32_to_cpu(main_info->length);
> +
> +	printf("Allwinner TOC0 Image\n"
> +	       "Size: %d bytes\n"
> +	       "Contents: %d items\n"
> +	       " 00000000:%08x Headers\n",
> +	       main_length, num_items, head_length);
> +
> +	for (i = 0; i < num_items; ++i, ++item_info) {
> +		item_offset = le32_to_cpu(item_info->offset);
> +		item_length = le32_to_cpu(item_info->length);
> +		item_name   = le32_to_cpu(item_info->name);
> +
> +		if (item_name == TOC0_ITEM_INFO_NAME_FIRMWARE)
> +			load_addr = le32_to_cpu(item_info->load_addr);
> +
> +		printf(" %08x:%08x %s\n",
> +		       item_offset, item_length,
> +		       toc0_item_name(item_name));
> +	}
> +
> +	if (num_items && item_offset + item_length < main_length) {
> +		item_offset = item_offset + item_length;
> +		item_length = main_length - item_offset;
> +
> +		printf(" %08x:%08x Padding\n",
> +		       item_offset, item_length);
> +	}
> +
> +	if (load_addr != -1)
> +		printf("Load address: 0x%08x\n", load_addr);
> +}
> +
> +static void toc0_set_header(void *buf, struct stat *sbuf, int ifd,
> +			    struct image_tool_params *params)
> +{
> +	uint32_t key_item_len = 0;
> +	uint8_t *key_item = NULL;
> +	int ret = EXIT_FAILURE;
> +	RSA *root_key = NULL;
> +	RSA *fw_key = NULL;
> +	FILE *fp;
> +
> +	/* Either a key item or the root private key is required. */
> +	fp = fopen(key_item_file, "rb");
> +	if (fp) {
> +		pr_info("Creating image using existing key item\n");
> +		key_item_len = sizeof(struct toc0_key_item);
> +		key_item = OPENSSL_malloc(key_item_len);
> +		if (!key_item || fread(key_item, key_item_len, 1, fp) != 1) {
> +			pr_err("Failed to read key item from '%s'\n",
> +			       root_key_file);
> +			goto err;
> +		}
> +		fclose(fp);
> +		fp = NULL;
> +	}
> +
> +	fp = fopen(root_key_file, "rb");
> +	if (fp) {
> +		root_key = PEM_read_RSAPrivateKey(fp, NULL, NULL, NULL);
> +		if (!root_key)
> +			root_key = PEM_read_RSAPublicKey(fp, NULL, NULL, NULL);
> +		fclose(fp);
> +		fp = NULL;
> +	}
> +
> +	/* When using an existing key item, the root key is optional. */
> +	if (!key_item && (!root_key || !RSA_get0_d(root_key))) {
> +		pr_err("Failed to read private key from '%s'\n",
> +		       root_key_file);
> +		pr_info("Try 'openssl genrsa -out root_key.pem'\n");
> +		goto err;
> +	}
> +
> +	/* The certificate/firmware private key is always required. */
> +	fp = fopen(fw_key_file, "rb");
> +	if (fp) {
> +		fw_key = PEM_read_RSAPrivateKey(fp, NULL, NULL, NULL);
> +		fclose(fp);
> +		fp = NULL;
> +	}
> +	if (!fw_key) {
> +		/* If the root key is a private key, it can be used instead. */
> +		if (root_key && RSA_get0_d(root_key)) {
> +			pr_info("Using root key as firmware key\n");
> +			fw_key = root_key;
> +		} else {
> +			pr_err("Failed to read private key from '%s'\n",
> +			       fw_key_file);
> +			goto err;
> +		}
> +	}
> +
> +	/* Warn about potential compatibility issues. */
> +	if (key_item || fw_key != root_key)
> +		pr_warn("Only H6 supports separate root and firmware keys\n");
> +
> +	ret = toc0_create(buf, params->file_size, root_key, fw_key,
> +			  key_item, key_item_len,
> +			  buf + TOC0_DEFAULT_HEADER_LEN,
> +			  params->orig_file_size, params->addr);
> +
> +err:
> +	OPENSSL_free(key_item);
> +	OPENSSL_free(root_key);
> +	if (fw_key != root_key)
> +		OPENSSL_free(fw_key);
> +	if (fp)
> +		fclose(fp);
> +
> +	if (ret != EXIT_SUCCESS)
> +		exit(ret);
> +}
> +
> +static int toc0_check_image_type(uint8_t type)
> +{
> +	return type == IH_TYPE_SUNXI_TOC0 ? 0 : 1;
> +}
> +
> +static int toc0_vrec_header(struct image_tool_params *params,
> +			    struct image_type_params *tparams)
> +{
> +	tparams->hdr = calloc(tparams->header_size, 1);
> +
> +	/* Save off the unpadded data size for SHA256 calculation. */
> +	params->orig_file_size = params->file_size - TOC0_DEFAULT_HEADER_LEN;
> +
> +	/* Return padding to 8K blocks. */
> +	return ALIGN(params->file_size, PAD_SIZE) - params->file_size;
> +}
> +
> +U_BOOT_IMAGE_TYPE(
> +	sunxi_toc0,
> +	"Allwinner TOC0 Boot Image support",
> +	TOC0_DEFAULT_HEADER_LEN,
> +	NULL,
> +	toc0_check_params,
> +	toc0_verify_header,
> +	toc0_print_header,
> +	toc0_set_header,
> +	NULL,

Are you planning to implement this missing (extract_subimage) dumpimage
function in future?

> +	toc0_check_image_type,
> +	NULL,
> +	toc0_vrec_header
> +);
> -- 
> 2.31.1
> 

^ permalink raw reply	[flat|nested] 13+ messages in thread

* Re: [PATCH v2 1/4] tools: Separate image types which depend on OpenSSL
  2021-08-22  9:58   ` Pali Rohár
@ 2021-08-22 17:32     ` Samuel Holland
  2021-08-24  0:51       ` Andre Przywara
  0 siblings, 1 reply; 13+ messages in thread
From: Samuel Holland @ 2021-08-22 17:32 UTC (permalink / raw)
  To: Pali Rohár
  Cc: u-boot, Jagan Teki, Andre Przywara, AKASHI Takahiro,
	Alexandru Gagniuc, Baruch Siach, Bharat Gooty, Chris Packham,
	Fabio Estevam, Frieder Schrempf, Jernej Skrabec,
	Marek Behún, NXP i.MX U-Boot Team, Naoki Hayama,
	Patrick Delaunay, Priyanka Jain, Rayagonda Kokatanur,
	Simon Glass, Stefan Roese, Stefano Babic, Sughosh Ganu,
	Trevor Woerner, lauri.hintsala

On 8/22/21 4:58 AM, Pali Rohár wrote:
> Hello!
> 
> On Saturday 21 August 2021 23:46:45 Samuel Holland wrote:
>> Some image types always depend on OpenSSL, so they can only be included
>> in mkimage when TOOLS_LIBCRYPTO is selected. Instead of using arbitrary
>> preprocessor definitions, conditionally link the files.
>>
>> When building for platforms which use those image types, automatically
>> select TOOLS_LIBCRYPTO, it is required for the build to complete.
>>
>> Signed-off-by: Samuel Holland <samuel@sholland.org>
>> ---
>>
>> Changes in v2:
>>  - Refactored the first patch on top of TOOLS_LIBCRYPTO
>>
>>  arch/arm/mach-imx/mxs/Kconfig |  2 ++
>>  arch/arm/mach-mvebu/Kconfig   |  1 +
>>  scripts/config_whitelist.txt  |  1 -
>>  tools/Makefile                | 19 +++++--------------
>>  tools/mxsimage.c              |  3 ---
>>  5 files changed, 8 insertions(+), 18 deletions(-)
>>
>> diff --git a/arch/arm/mach-imx/mxs/Kconfig b/arch/arm/mach-imx/mxs/Kconfig
>> index 9f48ffda414..24d452942a5 100644
>> --- a/arch/arm/mach-imx/mxs/Kconfig
>> +++ b/arch/arm/mach-imx/mxs/Kconfig
>> @@ -3,6 +3,7 @@ if ARCH_MX23
>>  config MX23
>>  	bool
>>  	default y
>> +	select TOOLS_LIBCRYPTO
>>  
>>  choice
>>  	prompt "MX23 board select"
>> @@ -34,6 +35,7 @@ if ARCH_MX28
>>  config MX28
>>  	bool
>>  	default y
>> +	select TOOLS_LIBCRYPTO
>>  
>>  choice
>>  	prompt "MX28 board select"
>> diff --git a/arch/arm/mach-mvebu/Kconfig b/arch/arm/mach-mvebu/Kconfig
>> index 89737a37ad9..c02521c71bc 100644
>> --- a/arch/arm/mach-mvebu/Kconfig
>> +++ b/arch/arm/mach-mvebu/Kconfig
>> @@ -29,6 +29,7 @@ config ARMADA_38X
>>  	bool
>>  	select ARMADA_32BIT
>>  	select HAVE_MVEBU_EFUSE
>> +	select TOOLS_LIBCRYPTO
> 
> kwbimage format is used also by A375 and AXP platform. So rather put
> this select into ARMADA_32BIT section.

I can do this.

> Moreover kwbimage format is used also by orion and kirkwood platforms.
> So this dependency needs to be enabled on more places.

Since you know where they should go, would you be willing to send a
patch adding the additional "select"s?

Side note: TOOLS_LIBCRYPTO=y is the default, so nobody seems to have
noticed, but actually the build with TOOLS_LIBCRYPTO=n is broken since
commit b4f3cc2c42d9 ("tools: kwbimage: Do not hide usage of secure
header under CONFIG_ARMADA_38X"). So I will add a Fixes: tag in v3.

Regards,
Samuel

>>  config ARMADA_38X_HS_IMPEDANCE_THRESH
>>  	hex  "Armada 38x USB 2.0 High-Speed Impedance Threshold (0x0 - 0x7)"
>> diff --git a/scripts/config_whitelist.txt b/scripts/config_whitelist.txt
>> index 2d70bf5da7c..5caaad0c3dc 100644
>> --- a/scripts/config_whitelist.txt
>> +++ b/scripts/config_whitelist.txt
>> @@ -1000,7 +1000,6 @@ CONFIG_MXC_UART_BASE
>>  CONFIG_MXC_USB_FLAGS
>>  CONFIG_MXC_USB_PORT
>>  CONFIG_MXC_USB_PORTSC
>> -CONFIG_MXS
>>  CONFIG_MXS_AUART
>>  CONFIG_MXS_AUART_BASE
>>  CONFIG_MXS_OCOTP
>> diff --git a/tools/Makefile b/tools/Makefile
>> index 4a86321f646..ad0e9cf9490 100644
>> --- a/tools/Makefile
>> +++ b/tools/Makefile
>> @@ -94,9 +94,11 @@ ECDSA_OBJS-$(CONFIG_TOOLS_LIBCRYPTO) := $(addprefix lib/ecdsa/, ecdsa-libcrypto.
>>  AES_OBJS-$(CONFIG_TOOLS_LIBCRYPTO) := $(addprefix lib/aes/, \
>>  					aes-encrypt.o aes-decrypt.o)
>>  
>> -# Cryptographic helpers that depend on openssl/libcrypto
>> -LIBCRYPTO_OBJS-$(CONFIG_TOOLS_LIBCRYPTO) := $(addprefix lib/, \
>> -					fdt-libcrypto.o)
>> +# Cryptographic helpers and image types that depend on openssl/libcrypto
>> +LIBCRYPTO_OBJS-$(CONFIG_TOOLS_LIBCRYPTO) := \
>> +			lib/fdt-libcrypto.o \
>> +			kwbimage.o \
>> +			mxsimage.o
>>  
>>  ROCKCHIP_OBS = lib/rc4.o rkcommon.o rkimage.o rksd.o rkspi.o
>>  
>> @@ -117,10 +119,8 @@ dumpimage-mkimage-objs := aisimage.o \
>>  			imximage.o \
>>  			imx8image.o \
>>  			imx8mimage.o \
>> -			kwbimage.o \
>>  			lib/md5.o \
>>  			lpc32xximage.o \
>> -			mxsimage.o \
>>  			omapimage.o \
>>  			os_support.o \
>>  			pblimage.o \
>> @@ -155,22 +155,13 @@ fit_info-objs   := $(dumpimage-mkimage-objs) fit_info.o
>>  fit_check_sign-objs   := $(dumpimage-mkimage-objs) fit_check_sign.o
>>  file2include-objs := file2include.o
>>  
>> -ifneq ($(CONFIG_MX23)$(CONFIG_MX28)$(CONFIG_TOOLS_LIBCRYPTO),)
>> -# Add CONFIG_MXS into host CFLAGS, so we can check whether or not register
>> -# the mxsimage support within tools/mxsimage.c .
>> -HOSTCFLAGS_mxsimage.o += -DCONFIG_MXS
>> -endif
>> -
>>  ifdef CONFIG_TOOLS_LIBCRYPTO
>>  # This affects include/image.h, but including the board config file
>>  # is tricky, so manually define this options here.
>>  HOST_EXTRACFLAGS	+= -DCONFIG_FIT_SIGNATURE
>>  HOST_EXTRACFLAGS	+= -DCONFIG_FIT_SIGNATURE_MAX_SIZE=0xffffffff
>>  HOST_EXTRACFLAGS	+= -DCONFIG_FIT_CIPHER
>> -endif
>>  
>> -# MXSImage needs LibSSL
>> -ifneq ($(CONFIG_MX23)$(CONFIG_MX28)$(CONFIG_ARMADA_38X)$(CONFIG_TOOLS_LIBCRYPTO),)
>>  HOSTCFLAGS_kwbimage.o += \
>>  	$(shell pkg-config --cflags libssl libcrypto 2> /dev/null || echo "")
>>  HOSTLDLIBS_mkimage += \
>> diff --git a/tools/mxsimage.c b/tools/mxsimage.c
>> index 002f4b525aa..2bfbb421eb6 100644
>> --- a/tools/mxsimage.c
>> +++ b/tools/mxsimage.c
>> @@ -5,8 +5,6 @@
>>   * Copyright (C) 2012-2013 Marek Vasut <marex@denx.de>
>>   */
>>  
>> -#ifdef CONFIG_MXS
>> -
>>  #include <errno.h>
>>  #include <fcntl.h>
>>  #include <stdio.h>
>> @@ -2363,4 +2361,3 @@ U_BOOT_IMAGE_TYPE(
>>  	NULL,
>>  	mxsimage_generate
>>  );
>> -#endif
>> -- 
>> 2.31.1
>>


^ permalink raw reply	[flat|nested] 13+ messages in thread

* Re: [PATCH v2 2/4] tools: mkimage: Add Allwinner TOC0 support
  2021-08-22 10:07   ` Pali Rohár
@ 2021-08-22 17:44     ` Samuel Holland
  2021-08-22 17:51       ` Pali Rohár
  0 siblings, 1 reply; 13+ messages in thread
From: Samuel Holland @ 2021-08-22 17:44 UTC (permalink / raw)
  To: Pali Rohár
  Cc: u-boot, Jagan Teki, Andre Przywara, AKASHI Takahiro,
	Alexandru Gagniuc, Baruch Siach, Bharat Gooty, Chris Packham,
	Fabio Estevam, Frieder Schrempf, Jernej Skrabec,
	Marek Behún, NXP i.MX U-Boot Team, Naoki Hayama,
	Patrick Delaunay, Priyanka Jain, Rayagonda Kokatanur,
	Simon Glass, Stefan Roese, Stefano Babic, Sughosh Ganu,
	Trevor Woerner, lauri.hintsala

On 8/22/21 5:07 AM, Pali Rohár wrote:
> Hello!
> 
> On Saturday 21 August 2021 23:46:46 Samuel Holland wrote:
>> Most Allwinner sunxi SoCs have separate boot ROMs in non-secure and
>> secure mode. The "non-secure" or "normal" boot ROM (NBROM) uses the
>> existing sunxi_egon image type. The secure boot ROM (SBROM) uses a
>> completely different image type, known as TOC0.
>>
>> A TOC0 image is composed of a header and two or more items. One item
>> is the firmware binary. The others form a chain linking the firmware
>> signature to the root-of-trust public key (ROTPK), which has its hash
>> burned in the SoC's eFuses. Signatures are made using RSA-2048 + SHA256.
>>
>> The pseudo-ASN.1 structure is manually assembled; this is done to work
>> around bugs/quirks in the boot ROM, which vary between SoCs. This TOC0
>> implementation has been verified to work with the A50, A64, H5, H6,
>> and H616 SBROMs, and it may work with other SoCs.
>>
>> Signed-off-by: Samuel Holland <samuel@sholland.org>
>> ---
>>
>> Changes in v2:
>>  - Moved certificate and key item structures out of sunxi_image.h
>>  - Renamed "main" and "item" variables for clarity
>>  - Improved error messages, and added a hint about key generation
>>  - Added a comment explaining the purpose of the various key files
>>  - Mentioned testing this code on A50 in the commit message
>>
>>  arch/arm/Kconfig      |   1 +
>>  common/image.c        |   1 +
>>  include/image.h       |   1 +
>>  include/sunxi_image.h |  36 ++
>>  tools/Makefile        |   3 +-
>>  tools/sunxi_toc0.c    | 907 ++++++++++++++++++++++++++++++++++++++++++
>>  6 files changed, 948 insertions(+), 1 deletion(-)
>>  create mode 100644 tools/sunxi_toc0.c
>>
>> diff --git a/arch/arm/Kconfig b/arch/arm/Kconfig
>> index d692139199c..799fe7d43af 100644
>> --- a/arch/arm/Kconfig
>> +++ b/arch/arm/Kconfig
>> @@ -1014,6 +1014,7 @@ config ARCH_SUNXI
>>  	select SUNXI_GPIO
>>  	select SYS_NS16550
>>  	select SYS_THUMB_BUILD if !ARM64
>> +	select TOOLS_LIBCRYPTO
>>  	select USB if DISTRO_DEFAULTS
>>  	select USB_KEYBOARD if DISTRO_DEFAULTS && USB_HOST
>>  	select USB_STORAGE if DISTRO_DEFAULTS && USB_HOST
>> diff --git a/common/image.c b/common/image.c
>> index 59c52a1f9ad..8f1634c1206 100644
>> --- a/common/image.c
>> +++ b/common/image.c
>> @@ -191,6 +191,7 @@ static const table_entry_t uimage_type[] = {
>>  	{	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" },
>> +	{	IH_TYPE_SUNXI_TOC0, "sunxi_toc0",  "Allwinner TOC0 Boot Image" },
>>  	{	-1,		    "",		  "",			},
>>  };
>>  
>> diff --git a/include/image.h b/include/image.h
>> index e20f0b69d58..a4efc090309 100644
>> --- a/include/image.h
>> +++ b/include/image.h
>> @@ -284,6 +284,7 @@ enum {
>>  	IH_TYPE_IMX8IMAGE,		/* Freescale IMX8Boot Image	*/
>>  	IH_TYPE_COPRO,			/* Coprocessor Image for remoteproc*/
>>  	IH_TYPE_SUNXI_EGON,		/* Allwinner eGON Boot Image */
>> +	IH_TYPE_SUNXI_TOC0,		/* Allwinner TOC0 Boot Image */
>>  
>>  	IH_TYPE_COUNT,			/* Number of image types */
>>  };
>> diff --git a/include/sunxi_image.h b/include/sunxi_image.h
>> index 5b2055c0af3..399ad0be999 100644
>> --- a/include/sunxi_image.h
>> +++ b/include/sunxi_image.h
>> @@ -9,9 +9,12 @@
>>   *
>>   * Shared between mkimage and the SPL.
>>   */
>> +
>>  #ifndef	SUNXI_IMAGE_H
>>  #define	SUNXI_IMAGE_H
>>  
>> +#include <linux/types.h>
>> +
>>  #define BOOT0_MAGIC		"eGON.BT0"
>>  #define BROM_STAMP_VALUE	0x5f0a6c39
>>  #define SPL_SIGNATURE		"SPL" /* marks "sunxi" SPL header */
>> @@ -79,4 +82,37 @@ struct boot_file_head {
>>  /* 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)];
> 
> (Just suggestion for future, not related to this patch series: above
> check could be rewritten/cleaned to use static assert)

I will send a separate patch once this is merged.

>>  
>> +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];
>> +};
>> +
>> +#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];
>> +};
> 
> These structures are raw image structures, right? Therefore they should
> be marked as __packed?

I'm not sure why I would want to do that? The structures do not have any
padding to start with, and the layout of the image format implies that
they will be naturally aligned (and in fact the boot ROM assumes this).
Marking them as packed would only cause GCC to generate less efficient code.

>> +U_BOOT_IMAGE_TYPE(
>> +	sunxi_toc0,
>> +	"Allwinner TOC0 Boot Image support",
>> +	TOC0_DEFAULT_HEADER_LEN,
>> +	NULL,
>> +	toc0_check_params,
>> +	toc0_verify_header,
>> +	toc0_print_header,
>> +	toc0_set_header,
>> +	NULL,
> 
> Are you planning to implement this missing (extract_subimage) dumpimage
> function in future?

Yes, eventually. I didn't want to delay the rest of the code for it,
since it is not needed to build U-Boot.

Regards,
Samuel

^ permalink raw reply	[flat|nested] 13+ messages in thread

* Re: [PATCH v2 2/4] tools: mkimage: Add Allwinner TOC0 support
  2021-08-22 17:44     ` Samuel Holland
@ 2021-08-22 17:51       ` Pali Rohár
  0 siblings, 0 replies; 13+ messages in thread
From: Pali Rohár @ 2021-08-22 17:51 UTC (permalink / raw)
  To: Samuel Holland
  Cc: u-boot, Jagan Teki, Andre Przywara, AKASHI Takahiro,
	Alexandru Gagniuc, Baruch Siach, Bharat Gooty, Chris Packham,
	Fabio Estevam, Frieder Schrempf, Jernej Skrabec,
	Marek Behún, NXP i.MX U-Boot Team, Naoki Hayama,
	Patrick Delaunay, Priyanka Jain, Rayagonda Kokatanur,
	Simon Glass, Stefan Roese, Stefano Babic, Sughosh Ganu,
	Trevor Woerner, lauri.hintsala

On Sunday 22 August 2021 12:44:05 Samuel Holland wrote:
> >> +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];
> >> +};
> >> +
> >> +#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];
> >> +};
> > 
> > These structures are raw image structures, right? Therefore they should
> > be marked as __packed?
> 
> I'm not sure why I would want to do that? The structures do not have any
> padding to start with, and the layout of the image format implies that
> they will be naturally aligned (and in fact the boot ROM assumes this).
> Marking them as packed would only cause GCC to generate less efficient code.

Because this code is running on the build machine, not on target
hardware where is the boot ROM. It is compiled by host compiler and
build machine may have totally different padding / layout schematics and
C compiler is free in this case to put any padding between any two
members in that structure.

And if GCC generates different or less efficient code, it means that
__packed is really needed here.

^ permalink raw reply	[flat|nested] 13+ messages in thread

* Re: [PATCH v2 1/4] tools: Separate image types which depend on OpenSSL
  2021-08-22 17:32     ` Samuel Holland
@ 2021-08-24  0:51       ` Andre Przywara
  0 siblings, 0 replies; 13+ messages in thread
From: Andre Przywara @ 2021-08-24  0:51 UTC (permalink / raw)
  To: Samuel Holland
  Cc: Pali Rohár, u-boot, Jagan Teki, AKASHI Takahiro,
	Alexandru Gagniuc, Baruch Siach, Bharat Gooty, Chris Packham,
	Fabio Estevam, Frieder Schrempf, Jernej Skrabec,
	Marek Behún, NXP i.MX U-Boot Team, Naoki Hayama,
	Patrick Delaunay, Priyanka Jain, Rayagonda Kokatanur,
	Simon Glass, Stefan Roese, Stefano Babic, Sughosh Ganu,
	Trevor Woerner, lauri.hintsala

On Sun, 22 Aug 2021 12:32:40 -0500
Samuel Holland <samuel@sholland.org> wrote:

Hi Samuel,

> On 8/22/21 4:58 AM, Pali Rohár wrote:
> > Hello!
> > 
> > On Saturday 21 August 2021 23:46:45 Samuel Holland wrote:  
> >> Some image types always depend on OpenSSL, so they can only be included
> >> in mkimage when TOOLS_LIBCRYPTO is selected. Instead of using arbitrary
> >> preprocessor definitions, conditionally link the files.
> >>
> >> When building for platforms which use those image types, automatically
> >> select TOOLS_LIBCRYPTO, it is required for the build to complete.
> >>
> >> Signed-off-by: Samuel Holland <samuel@sholland.org>
> >> ---
> >>
> >> Changes in v2:
> >>  - Refactored the first patch on top of TOOLS_LIBCRYPTO
> >>
> >>  arch/arm/mach-imx/mxs/Kconfig |  2 ++
> >>  arch/arm/mach-mvebu/Kconfig   |  1 +
> >>  scripts/config_whitelist.txt  |  1 -
> >>  tools/Makefile                | 19 +++++--------------
> >>  tools/mxsimage.c              |  3 ---
> >>  5 files changed, 8 insertions(+), 18 deletions(-)
> >>
> >> diff --git a/arch/arm/mach-imx/mxs/Kconfig b/arch/arm/mach-imx/mxs/Kconfig
> >> index 9f48ffda414..24d452942a5 100644
> >> --- a/arch/arm/mach-imx/mxs/Kconfig
> >> +++ b/arch/arm/mach-imx/mxs/Kconfig
> >> @@ -3,6 +3,7 @@ if ARCH_MX23
> >>  config MX23
> >>  	bool
> >>  	default y
> >> +	select TOOLS_LIBCRYPTO
> >>  
> >>  choice
> >>  	prompt "MX23 board select"
> >> @@ -34,6 +35,7 @@ if ARCH_MX28
> >>  config MX28
> >>  	bool
> >>  	default y
> >> +	select TOOLS_LIBCRYPTO
> >>  
> >>  choice
> >>  	prompt "MX28 board select"
> >> diff --git a/arch/arm/mach-mvebu/Kconfig b/arch/arm/mach-mvebu/Kconfig
> >> index 89737a37ad9..c02521c71bc 100644
> >> --- a/arch/arm/mach-mvebu/Kconfig
> >> +++ b/arch/arm/mach-mvebu/Kconfig
> >> @@ -29,6 +29,7 @@ config ARMADA_38X
> >>  	bool
> >>  	select ARMADA_32BIT
> >>  	select HAVE_MVEBU_EFUSE
> >> +	select TOOLS_LIBCRYPTO  
> > 
> > kwbimage format is used also by A375 and AXP platform. So rather put
> > this select into ARMADA_32BIT section.  
> 
> I can do this.
> 
> > Moreover kwbimage format is used also by orion and kirkwood platforms.
> > So this dependency needs to be enabled on more places.  
> 
> Since you know where they should go, would you be willing to send a
> patch adding the additional "select"s?
> 
> Side note: TOOLS_LIBCRYPTO=y is the default, so nobody seems to have
> noticed, but actually the build with TOOLS_LIBCRYPTO=n is broken since
> commit b4f3cc2c42d9 ("tools: kwbimage: Do not hide usage of secure
> header under CONFIG_ARMADA_38X"). So I will add a Fixes: tag in v3.

I did notice, because my host doesn't have a compatible libopenssl.
Every build fails, because kwbimage.o was unconditionally included.
My previous workaround was to disable TOOLS_LIBCRYPTO, but this didn't
help after the patch you mentioned. So your solution is a quite nice
fix, and I can stop looking for a workaround.

However this somewhat breaks again somewhat needlessly with 4/4 (when
compiling for any SUNXI board, which don't really need TOC0), but this
can be easily avoided.

Cheers,
Andre

> 
> Regards,
> Samuel
> 
> >>  config ARMADA_38X_HS_IMPEDANCE_THRESH
> >>  	hex  "Armada 38x USB 2.0 High-Speed Impedance Threshold (0x0 - 0x7)"
> >> diff --git a/scripts/config_whitelist.txt b/scripts/config_whitelist.txt
> >> index 2d70bf5da7c..5caaad0c3dc 100644
> >> --- a/scripts/config_whitelist.txt
> >> +++ b/scripts/config_whitelist.txt
> >> @@ -1000,7 +1000,6 @@ CONFIG_MXC_UART_BASE
> >>  CONFIG_MXC_USB_FLAGS
> >>  CONFIG_MXC_USB_PORT
> >>  CONFIG_MXC_USB_PORTSC
> >> -CONFIG_MXS
> >>  CONFIG_MXS_AUART
> >>  CONFIG_MXS_AUART_BASE
> >>  CONFIG_MXS_OCOTP
> >> diff --git a/tools/Makefile b/tools/Makefile
> >> index 4a86321f646..ad0e9cf9490 100644
> >> --- a/tools/Makefile
> >> +++ b/tools/Makefile
> >> @@ -94,9 +94,11 @@ ECDSA_OBJS-$(CONFIG_TOOLS_LIBCRYPTO) := $(addprefix lib/ecdsa/, ecdsa-libcrypto.
> >>  AES_OBJS-$(CONFIG_TOOLS_LIBCRYPTO) := $(addprefix lib/aes/, \
> >>  					aes-encrypt.o aes-decrypt.o)
> >>  
> >> -# Cryptographic helpers that depend on openssl/libcrypto
> >> -LIBCRYPTO_OBJS-$(CONFIG_TOOLS_LIBCRYPTO) := $(addprefix lib/, \
> >> -					fdt-libcrypto.o)
> >> +# Cryptographic helpers and image types that depend on openssl/libcrypto
> >> +LIBCRYPTO_OBJS-$(CONFIG_TOOLS_LIBCRYPTO) := \
> >> +			lib/fdt-libcrypto.o \
> >> +			kwbimage.o \
> >> +			mxsimage.o
> >>  
> >>  ROCKCHIP_OBS = lib/rc4.o rkcommon.o rkimage.o rksd.o rkspi.o
> >>  
> >> @@ -117,10 +119,8 @@ dumpimage-mkimage-objs := aisimage.o \
> >>  			imximage.o \
> >>  			imx8image.o \
> >>  			imx8mimage.o \
> >> -			kwbimage.o \
> >>  			lib/md5.o \
> >>  			lpc32xximage.o \
> >> -			mxsimage.o \
> >>  			omapimage.o \
> >>  			os_support.o \
> >>  			pblimage.o \
> >> @@ -155,22 +155,13 @@ fit_info-objs   := $(dumpimage-mkimage-objs) fit_info.o
> >>  fit_check_sign-objs   := $(dumpimage-mkimage-objs) fit_check_sign.o
> >>  file2include-objs := file2include.o
> >>  
> >> -ifneq ($(CONFIG_MX23)$(CONFIG_MX28)$(CONFIG_TOOLS_LIBCRYPTO),)
> >> -# Add CONFIG_MXS into host CFLAGS, so we can check whether or not register
> >> -# the mxsimage support within tools/mxsimage.c .
> >> -HOSTCFLAGS_mxsimage.o += -DCONFIG_MXS
> >> -endif
> >> -
> >>  ifdef CONFIG_TOOLS_LIBCRYPTO
> >>  # This affects include/image.h, but including the board config file
> >>  # is tricky, so manually define this options here.
> >>  HOST_EXTRACFLAGS	+= -DCONFIG_FIT_SIGNATURE
> >>  HOST_EXTRACFLAGS	+= -DCONFIG_FIT_SIGNATURE_MAX_SIZE=0xffffffff
> >>  HOST_EXTRACFLAGS	+= -DCONFIG_FIT_CIPHER
> >> -endif
> >>  
> >> -# MXSImage needs LibSSL
> >> -ifneq ($(CONFIG_MX23)$(CONFIG_MX28)$(CONFIG_ARMADA_38X)$(CONFIG_TOOLS_LIBCRYPTO),)
> >>  HOSTCFLAGS_kwbimage.o += \
> >>  	$(shell pkg-config --cflags libssl libcrypto 2> /dev/null || echo "")
> >>  HOSTLDLIBS_mkimage += \
> >> diff --git a/tools/mxsimage.c b/tools/mxsimage.c
> >> index 002f4b525aa..2bfbb421eb6 100644
> >> --- a/tools/mxsimage.c
> >> +++ b/tools/mxsimage.c
> >> @@ -5,8 +5,6 @@
> >>   * Copyright (C) 2012-2013 Marek Vasut <marex@denx.de>
> >>   */
> >>  
> >> -#ifdef CONFIG_MXS
> >> -
> >>  #include <errno.h>
> >>  #include <fcntl.h>
> >>  #include <stdio.h>
> >> @@ -2363,4 +2361,3 @@ U_BOOT_IMAGE_TYPE(
> >>  	NULL,
> >>  	mxsimage_generate
> >>  );
> >> -#endif
> >> -- 
> >> 2.31.1
> >>  
> 


^ permalink raw reply	[flat|nested] 13+ messages in thread

* Re: [PATCH v2 2/4] tools: mkimage: Add Allwinner TOC0 support
  2021-08-22  4:46 ` [PATCH v2 2/4] tools: mkimage: Add Allwinner TOC0 support Samuel Holland
  2021-08-22 10:07   ` Pali Rohár
@ 2021-09-06  0:29   ` Andre Przywara
  1 sibling, 0 replies; 13+ messages in thread
From: Andre Przywara @ 2021-09-06  0:29 UTC (permalink / raw)
  To: Samuel Holland
  Cc: u-boot, Jagan Teki, AKASHI Takahiro, Alexandru Gagniuc,
	Baruch Siach, Bharat Gooty, Chris Packham, Fabio Estevam,
	Frieder Schrempf, Jernej Skrabec, Marek Behún,
	NXP i.MX U-Boot Team, Naoki Hayama, Pali Rohár,
	Patrick Delaunay, Priyanka Jain, Rayagonda Kokatanur,
	Simon Glass, Stefan Roese, Stefano Babic, Sughosh Ganu,
	Trevor Woerner, lauri.hintsala

On Sat, 21 Aug 2021 23:46:46 -0500
Samuel Holland <samuel@sholland.org> wrote:

Hi Samuel,

> Most Allwinner sunxi SoCs have separate boot ROMs in non-secure and
> secure mode. The "non-secure" or "normal" boot ROM (NBROM) uses the
> existing sunxi_egon image type. The secure boot ROM (SBROM) uses a
> completely different image type, known as TOC0.
> 
> A TOC0 image is composed of a header and two or more items. One item
> is the firmware binary. The others form a chain linking the firmware
> signature to the root-of-trust public key (ROTPK), which has its hash
> burned in the SoC's eFuses. Signatures are made using RSA-2048 + SHA256.
> 
> The pseudo-ASN.1 structure is manually assembled; this is done to work
> around bugs/quirks in the boot ROM, which vary between SoCs. This TOC0
> implementation has been verified to work with the A50, A64, H5, H6,
> and H616 SBROMs, and it may work with other SoCs.

many thanks for sending this, it really opens up those secure boards in
a nice and easy manner.

> 
> Signed-off-by: Samuel Holland <samuel@sholland.org>
> ---
> 
> Changes in v2:
>  - Moved certificate and key item structures out of sunxi_image.h
>  - Renamed "main" and "item" variables for clarity
>  - Improved error messages, and added a hint about key generation
>  - Added a comment explaining the purpose of the various key files
>  - Mentioned testing this code on A50 in the commit message
> 
>  arch/arm/Kconfig      |   1 +
>  common/image.c        |   1 +
>  include/image.h       |   1 +
>  include/sunxi_image.h |  36 ++
>  tools/Makefile        |   3 +-
>  tools/sunxi_toc0.c    | 907 ++++++++++++++++++++++++++++++++++++++++++
>  6 files changed, 948 insertions(+), 1 deletion(-)
>  create mode 100644 tools/sunxi_toc0.c
> 
> diff --git a/arch/arm/Kconfig b/arch/arm/Kconfig
> index d692139199c..799fe7d43af 100644
> --- a/arch/arm/Kconfig
> +++ b/arch/arm/Kconfig
> @@ -1014,6 +1014,7 @@ config ARCH_SUNXI
>  	select SUNXI_GPIO
>  	select SYS_NS16550
>  	select SYS_THUMB_BUILD if !ARM64
> +	select TOOLS_LIBCRYPTO

Can you hold this back until we get the SPL_IMAGE_TYPE_SUNXI_TOC0
symbol in patch 4/4, and make it dependent on that, or make this
"default y if ARCH_SUNXI" in TOOLS_LIBCRYPTO's definition?
Otherwise this somewhat needlessly requires OpenSSL for *every* sunxi
board build, even though there is no board using it at the moment.

>  	select USB if DISTRO_DEFAULTS
>  	select USB_KEYBOARD if DISTRO_DEFAULTS && USB_HOST
>  	select USB_STORAGE if DISTRO_DEFAULTS && USB_HOST
> diff --git a/common/image.c b/common/image.c
> index 59c52a1f9ad..8f1634c1206 100644
> --- a/common/image.c
> +++ b/common/image.c
> @@ -191,6 +191,7 @@ static const table_entry_t uimage_type[] = {
>  	{	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" },
> +	{	IH_TYPE_SUNXI_TOC0, "sunxi_toc0",  "Allwinner TOC0 Boot Image" },
>  	{	-1,		    "",		  "",			},
>  };
>  
> diff --git a/include/image.h b/include/image.h
> index e20f0b69d58..a4efc090309 100644
> --- a/include/image.h
> +++ b/include/image.h
> @@ -284,6 +284,7 @@ enum {
>  	IH_TYPE_IMX8IMAGE,		/* Freescale IMX8Boot Image	*/
>  	IH_TYPE_COPRO,			/* Coprocessor Image for remoteproc*/
>  	IH_TYPE_SUNXI_EGON,		/* Allwinner eGON Boot Image */
> +	IH_TYPE_SUNXI_TOC0,		/* Allwinner TOC0 Boot Image */
>  
>  	IH_TYPE_COUNT,			/* Number of image types */
>  };
> diff --git a/include/sunxi_image.h b/include/sunxi_image.h
> index 5b2055c0af3..399ad0be999 100644
> --- a/include/sunxi_image.h
> +++ b/include/sunxi_image.h
> @@ -9,9 +9,12 @@
>   *
>   * Shared between mkimage and the SPL.
>   */
> +
>  #ifndef	SUNXI_IMAGE_H
>  #define	SUNXI_IMAGE_H
>  
> +#include <linux/types.h>
> +
>  #define BOOT0_MAGIC		"eGON.BT0"
>  #define BROM_STAMP_VALUE	0x5f0a6c39
>  #define SPL_SIGNATURE		"SPL" /* marks "sunxi" SPL header */
> @@ -79,4 +82,37 @@ struct boot_file_head {
>  /* 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];
> +};

I second Pali's request for "packed" here. A struct is a C language
feature to organise your data, not to model foreign data packets. Yes,
it is (ab)used for this (all over U-Boot), and the compilers we work
with happen to generate valid code here, but indeed the C standard
makes no promises, unless you use "packed". It shouldn't make
much difference here (with normal compilers), but it's safer, and
signifies that this is an externally defined data structure.


The rest is all Greek to me, but it seems to work: I could successfully
boot my secure Pine64, with just a simple config change. That's a very
nice feature!

Cheers,
Andre

> +
> +#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];
> +};
> +
> +#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/tools/Makefile b/tools/Makefile
> index ad0e9cf9490..fc3b66af6af 100644
> --- a/tools/Makefile
> +++ b/tools/Makefile
> @@ -98,7 +98,8 @@ AES_OBJS-$(CONFIG_TOOLS_LIBCRYPTO) := $(addprefix lib/aes/, \
>  LIBCRYPTO_OBJS-$(CONFIG_TOOLS_LIBCRYPTO) := \
>  			lib/fdt-libcrypto.o \
>  			kwbimage.o \
> -			mxsimage.o
> +			mxsimage.o \
> +			sunxi_toc0.o
>  
>  ROCKCHIP_OBS = lib/rc4.o rkcommon.o rkimage.o rksd.o rkspi.o
>  
> diff --git a/tools/sunxi_toc0.c b/tools/sunxi_toc0.c
> new file mode 100644
> index 00000000000..5aa39cba775
> --- /dev/null
> +++ b/tools/sunxi_toc0.c
> @@ -0,0 +1,907 @@
> +// SPDX-License-Identifier: GPL-2.0+
> +/*
> + * (C) Copyright 2018 Arm Ltd.
> + * (C) Copyright 2020-2021 Samuel Holland <samuel@sholland.org>
> + */
> +
> +#include <assert.h>
> +#include <stdint.h>
> +#include <stdio.h>
> +#include <stdlib.h>
> +#include <string.h>
> +
> +#include <openssl/asn1t.h>
> +#include <openssl/pem.h>
> +#include <openssl/rsa.h>
> +
> +#include <image.h>
> +#include <sunxi_image.h>
> +
> +#include "imagetool.h"
> +#include "mkimage.h"
> +
> +/*
> + * NAND requires 8K padding. For other devices, BROM requires only
> + * 512B padding, but let's use the larger padding to cover everything.
> + */
> +#define PAD_SIZE		8192
> +
> +#define pr_fmt(fmt)		"mkimage (TOC0): %s: " fmt
> +#define pr_err(fmt, args...)	fprintf(stderr, pr_fmt(fmt), "error", ##args)
> +#define pr_warn(fmt, args...)	fprintf(stderr, pr_fmt(fmt), "warning", ##args)
> +#define pr_info(fmt, args...)	fprintf(stderr, pr_fmt(fmt), "info", ##args)
> +
> +struct toc0_key_item {
> +	__le32  vendor_id;
> +	__le32  key0_n_len;
> +	__le32  key0_e_len;
> +	__le32  key1_n_len;
> +	__le32  key1_e_len;
> +	__le32  sig_len;
> +	uint8_t key0[512];
> +	uint8_t key1[512];
> +	uint8_t reserved[32];
> +	uint8_t sig[256];
> +};
> +
> +/*
> + * This looks somewhat like an X.509 certificate, but it is not valid BER.
> + *
> + * Some differences:
> + *  - Some X.509 certificate fields are missing or rearranged.
> + *  - Some sequences have the wrong tag.
> + *  - Zero-length sequences are accepted.
> + *  - Large strings and integers must be an even number of bytes long.
> + *  - Positive integers are not zero-extended to maintain their sign.
> + *
> + * See https://linux-sunxi.org/TOC0 for more information.
> + */
> +struct toc0_small_tag {
> +	uint8_t tag;
> +	uint8_t length;
> +};
> +
> +typedef struct toc0_small_tag toc0_small_int;
> +typedef struct toc0_small_tag toc0_small_oct;
> +typedef struct toc0_small_tag toc0_small_seq;
> +typedef struct toc0_small_tag toc0_small_exp;
> +
> +#define TOC0_SMALL_INT(len) { 0x02, (len) }
> +#define TOC0_SMALL_SEQ(len) { 0x30, (len) }
> +#define TOC0_SMALL_EXP(tag, len) { 0xa0 | (tag), len }
> +
> +struct toc0_large_tag {
> +	uint8_t tag;
> +	uint8_t prefix;
> +	uint8_t length_hi;
> +	uint8_t length_lo;
> +};
> +
> +typedef struct toc0_large_tag toc0_large_int;
> +typedef struct toc0_large_tag toc0_large_bit;
> +typedef struct toc0_large_tag toc0_large_seq;
> +
> +#define TOC0_LARGE_INT(len) { 0x02, 0x82, (len) >> 8, (len) & 0xff }
> +#define TOC0_LARGE_BIT(len) { 0x03, 0x82, (len) >> 8, (len) & 0xff }
> +#define TOC0_LARGE_SEQ(len) { 0x30, 0x82, (len) >> 8, (len) & 0xff }
> +
> +struct toc0_cert_item {
> +	toc0_large_seq tag_totalSequence;
> +	struct toc0_totalSequence {
> +		toc0_large_seq tag_mainSequence;
> +		struct toc0_mainSequence {
> +			toc0_small_exp tag_explicit0;
> +			struct toc0_explicit0 {
> +				toc0_small_int tag_version;
> +				uint8_t version;
> +			} explicit0;
> +			toc0_small_int tag_serialNumber;
> +			uint8_t serialNumber;
> +			toc0_small_seq tag_signature;
> +			toc0_small_seq tag_issuer;
> +			toc0_small_seq tag_validity;
> +			toc0_small_seq tag_subject;
> +			toc0_large_seq tag_subjectPublicKeyInfo;
> +			struct toc0_subjectPublicKeyInfo {
> +				toc0_small_seq tag_algorithm;
> +				toc0_large_seq tag_publicKey;
> +				struct toc0_publicKey {
> +					toc0_large_int tag_n;
> +					uint8_t n[256];
> +					toc0_small_int tag_e;
> +					uint8_t e[3];
> +				} publicKey;
> +			} subjectPublicKeyInfo;
> +			toc0_small_exp tag_explicit3;
> +			struct toc0_explicit3 {
> +				toc0_small_seq tag_extension;
> +				struct toc0_extension {
> +					toc0_small_int tag_digest;
> +					uint8_t digest[32];
> +				} extension;
> +			} explicit3;
> +		} mainSequence;
> +		toc0_large_bit tag_sigSequence;
> +		struct toc0_sigSequence {
> +			toc0_small_seq tag_algorithm;
> +			toc0_large_bit tag_signature;
> +			uint8_t signature[256];
> +		} sigSequence;
> +	} totalSequence;
> +};
> +
> +#define sizeof_field(TYPE, MEMBER) sizeof((((TYPE *)0)->MEMBER))
> +
> +static const struct toc0_cert_item cert_item_template = {
> +	TOC0_LARGE_SEQ(sizeof(struct toc0_totalSequence)),
> +	{
> +		TOC0_LARGE_SEQ(sizeof(struct toc0_mainSequence)),
> +		{
> +			TOC0_SMALL_EXP(0, sizeof(struct toc0_explicit0)),
> +			{
> +				TOC0_SMALL_INT(sizeof_field(struct toc0_explicit0, version)),
> +				0,
> +			},
> +			TOC0_SMALL_INT(sizeof_field(struct toc0_mainSequence, serialNumber)),
> +			0,
> +			TOC0_SMALL_SEQ(0),
> +			TOC0_SMALL_SEQ(0),
> +			TOC0_SMALL_SEQ(0),
> +			TOC0_SMALL_SEQ(0),
> +			TOC0_LARGE_SEQ(sizeof(struct toc0_subjectPublicKeyInfo)),
> +			{
> +				TOC0_SMALL_SEQ(0),
> +				TOC0_LARGE_SEQ(sizeof(struct toc0_publicKey)),
> +				{
> +					TOC0_LARGE_INT(sizeof_field(struct toc0_publicKey, n)),
> +					{},
> +					TOC0_SMALL_INT(sizeof_field(struct toc0_publicKey, e)),
> +					{},
> +				},
> +			},
> +			TOC0_SMALL_EXP(3, sizeof(struct toc0_explicit3)),
> +			{
> +				TOC0_SMALL_SEQ(sizeof(struct toc0_extension)),
> +				{
> +					TOC0_SMALL_INT(sizeof_field(struct toc0_extension, digest)),
> +					{},
> +				},
> +			},
> +		},
> +		TOC0_LARGE_BIT(sizeof(struct toc0_sigSequence)),
> +		{
> +			TOC0_SMALL_SEQ(0),
> +			TOC0_LARGE_BIT(sizeof_field(struct toc0_sigSequence, signature)),
> +			{},
> +		},
> +	},
> +};
> +
> +#define TOC0_DEFAULT_NUM_ITEMS		3
> +#define TOC0_DEFAULT_HEADER_LEN						  \
> +	ALIGN(								  \
> +		sizeof(struct toc0_main_info)				+ \
> +		sizeof(struct toc0_item_info) *	TOC0_DEFAULT_NUM_ITEMS	+ \
> +		sizeof(struct toc0_cert_item)				+ \
> +		sizeof(struct toc0_key_item),				  \
> +	32)
> +
> +static char *fw_key_file   = "fw_key.pem";
> +static char *key_item_file = "key_item.bin";
> +static char *root_key_file = "root_key.pem";
> +
> +/*
> + * Create a key item in @buf, containing the public keys @root_key and @fw_key,
> + * and signed by the RSA key @root_key.
> + */
> +static int toc0_create_key_item(uint8_t *buf, uint32_t *len,
> +				RSA *root_key, RSA *fw_key)
> +{
> +	struct toc0_key_item *key_item = (void *)buf;
> +	uint8_t digest[SHA256_DIGEST_LENGTH];
> +	int ret = EXIT_FAILURE;
> +	unsigned int sig_len;
> +	int n_len, e_len;
> +
> +	/* Store key 0. */
> +	n_len = BN_bn2bin(RSA_get0_n(root_key), key_item->key0);
> +	e_len = BN_bn2bin(RSA_get0_e(root_key), key_item->key0 + n_len);
> +	if (n_len + e_len > sizeof(key_item->key0)) {
> +		pr_err("Root key is too big for key item\n");
> +		goto err;
> +	}
> +	key_item->key0_n_len = cpu_to_le32(n_len);
> +	key_item->key0_e_len = cpu_to_le32(e_len);
> +
> +	/* Store key 1. */
> +	n_len = BN_bn2bin(RSA_get0_n(fw_key), key_item->key1);
> +	e_len = BN_bn2bin(RSA_get0_e(fw_key), key_item->key1 + n_len);
> +	if (n_len + e_len > sizeof(key_item->key1)) {
> +		pr_err("Firmware key is too big for key item\n");
> +		goto err;
> +	}
> +	key_item->key1_n_len = cpu_to_le32(n_len);
> +	key_item->key1_e_len = cpu_to_le32(e_len);
> +
> +	/* Sign the key item. */
> +	key_item->sig_len = cpu_to_le32(RSA_size(root_key));
> +	SHA256(buf, key_item->sig - buf, digest);
> +	if (!RSA_sign(NID_sha256, digest, sizeof(digest),
> +		      key_item->sig, &sig_len, root_key)) {
> +		pr_err("Failed to sign key item\n");
> +		goto err;
> +	}
> +	if (sig_len != sizeof(key_item->sig)) {
> +		pr_err("Bad key item signature length\n");
> +		goto err;
> +	}
> +
> +	*len = sizeof(*key_item);
> +	ret = EXIT_SUCCESS;
> +
> +err:
> +	return ret;
> +}
> +
> +/*
> + * Verify the key item in @buf, containing two public keys @key0 and @key1,
> + * and signed by the RSA key @key0. If @root_key is provided, only signatures
> + * by that key will be accepted. @key1 is returned in @key.
> + */
> +static int toc0_verify_key_item(const uint8_t *buf, uint32_t len,
> +				RSA *root_key, RSA **fw_key)
> +{
> +	struct toc0_key_item *key_item = (void *)buf;
> +	uint8_t digest[SHA256_DIGEST_LENGTH];
> +	int ret = EXIT_FAILURE;
> +	int n_len, e_len;
> +	RSA *key0 = NULL;
> +	RSA *key1 = NULL;
> +	BIGNUM *n, *e;
> +
> +	if (len < sizeof(*key_item))
> +		goto err;
> +
> +	/* Load key 0. */
> +	n_len = le32_to_cpu(key_item->key0_n_len);
> +	e_len = le32_to_cpu(key_item->key0_e_len);
> +	if (n_len + e_len > sizeof(key_item->key0)) {
> +		pr_err("Bad root key size in key item\n");
> +		goto err;
> +	}
> +	n = BN_bin2bn(key_item->key0, n_len, NULL);
> +	e = BN_bin2bn(key_item->key0 + n_len, e_len, NULL);
> +	key0 = RSA_new();
> +	if (!key0)
> +		goto err;
> +	if (!RSA_set0_key(key0, n, e, NULL))
> +		goto err;
> +
> +	/* If a root key was provided, compare it to key 0. */
> +	if (root_key && (BN_cmp(n, RSA_get0_n(root_key)) ||
> +			 BN_cmp(e, RSA_get0_e(root_key)))) {
> +		pr_err("Wrong root key in key item\n");
> +		goto err;
> +	}
> +
> +	/* Verify the key item signature. */
> +	SHA256(buf, key_item->sig - buf, digest);
> +	if (!RSA_verify(NID_sha256, digest, sizeof(digest),
> +			key_item->sig, le32_to_cpu(key_item->sig_len), key0)) {
> +		pr_err("Bad key item signature\n");
> +		goto err;
> +	}
> +
> +	if (fw_key) {
> +		/* Load key 1. */
> +		n_len = le32_to_cpu(key_item->key1_n_len);
> +		e_len = le32_to_cpu(key_item->key1_e_len);
> +		if (n_len + e_len > sizeof(key_item->key1)) {
> +			pr_err("Bad firmware key size in key item\n");
> +			goto err;
> +		}
> +		n = BN_bin2bn(key_item->key1, n_len, NULL);
> +		e = BN_bin2bn(key_item->key1 + n_len, e_len, NULL);
> +		key1 = RSA_new();
> +		if (!key1)
> +			goto err;
> +		if (!RSA_set0_key(key1, n, e, NULL))
> +			goto err;
> +
> +		if (*fw_key) {
> +			/* If a FW key was provided, compare it to key 1. */
> +			if (BN_cmp(n, RSA_get0_n(*fw_key)) ||
> +			    BN_cmp(e, RSA_get0_e(*fw_key))) {
> +				pr_err("Wrong firmware key in key item\n");
> +				goto err;
> +			}
> +		} else {
> +			/* Otherwise, send key1 back to the caller. */
> +			*fw_key = key1;
> +			key1 = NULL;
> +		}
> +	}
> +
> +	ret = EXIT_SUCCESS;
> +
> +err:
> +	RSA_free(key0);
> +	RSA_free(key1);
> +
> +	return ret;
> +}
> +
> +/*
> + * Create a certificate in @buf, describing the firmware with SHA256 digest
> + * @digest, and signed by the RSA key @fw_key.
> + */
> +static int toc0_create_cert_item(uint8_t *buf, uint32_t *len, RSA *fw_key,
> +				 uint8_t digest[static SHA256_DIGEST_LENGTH])
> +{
> +	struct toc0_cert_item *cert_item = (void *)buf;
> +	uint8_t cert_digest[SHA256_DIGEST_LENGTH];
> +	struct toc0_totalSequence *totalSequence;
> +	struct toc0_sigSequence *sigSequence;
> +	struct toc0_extension *extension;
> +	struct toc0_publicKey *publicKey;
> +	int ret = EXIT_FAILURE;
> +	unsigned int sig_len;
> +
> +	memcpy(cert_item, &cert_item_template, sizeof(*cert_item));
> +	*len = sizeof(*cert_item);
> +
> +	/*
> +	 * Fill in the public key.
> +	 *
> +	 * Only 2048-bit RSA keys are supported. Since this uses a fixed-size
> +	 * structure, it may fail for non-standard exponents.
> +	 */
> +	totalSequence = &cert_item->totalSequence;
> +	publicKey = &totalSequence->mainSequence.subjectPublicKeyInfo.publicKey;
> +	if (BN_bn2binpad(RSA_get0_n(fw_key), publicKey->n, sizeof(publicKey->n)) < 0 ||
> +	    BN_bn2binpad(RSA_get0_e(fw_key), publicKey->e, sizeof(publicKey->e)) < 0) {
> +		pr_err("Firmware key is too big for certificate\n");
> +		goto err;
> +	}
> +
> +	/* Fill in the firmware digest. */
> +	extension = &totalSequence->mainSequence.explicit3.extension;
> +	memcpy(&extension->digest, digest, SHA256_DIGEST_LENGTH);
> +
> +	/*
> +	 * Sign the certificate.
> +	 *
> +	 * In older SBROM versions (and by default in newer versions),
> +	 * the last 4 bytes of the certificate are not signed.
> +	 *
> +	 * (The buffer passed to SHA256 starts at tag_mainSequence, but
> +	 *  the buffer size does not include the length of that tag.)
> +	 */
> +	SHA256((uint8_t *)totalSequence, sizeof(struct toc0_mainSequence), cert_digest);
> +	sigSequence = &totalSequence->sigSequence;
> +	if (!RSA_sign(NID_sha256, cert_digest, SHA256_DIGEST_LENGTH,
> +		      sigSequence->signature, &sig_len, fw_key)) {
> +		pr_err("Failed to sign certificate\n");
> +		goto err;
> +	}
> +	if (sig_len != sizeof(sigSequence->signature)) {
> +		pr_err("Bad certificate signature length\n");
> +		goto err;
> +	}
> +
> +	ret = EXIT_SUCCESS;
> +
> +err:
> +	return ret;
> +}
> +
> +/*
> + * Verify the certificate in @buf, describing the firmware with SHA256 digest
> + * @digest, and signed by the RSA key contained within. If @fw_key is provided,
> + * only that key will be accepted.
> + *
> + * This function is only expected to work with images created by mkimage.
> + */
> +static int toc0_verify_cert_item(const uint8_t *buf, uint32_t len, RSA *fw_key,
> +				 uint8_t digest[static SHA256_DIGEST_LENGTH])
> +{
> +	const struct toc0_cert_item *cert_item = (const void *)buf;
> +	uint8_t cert_digest[SHA256_DIGEST_LENGTH];
> +	const struct toc0_totalSequence *totalSequence;
> +	const struct toc0_sigSequence *sigSequence;
> +	const struct toc0_extension *extension;
> +	const struct toc0_publicKey *publicKey;
> +	int ret = EXIT_FAILURE;
> +	RSA *key = NULL;
> +	BIGNUM *n, *e;
> +
> +	/* Extract the public key from the certificate. */
> +	totalSequence = &cert_item->totalSequence;
> +	publicKey = &totalSequence->mainSequence.subjectPublicKeyInfo.publicKey;
> +	n = BN_bin2bn(publicKey->n, sizeof(publicKey->n), NULL);
> +	e = BN_bin2bn(publicKey->e, sizeof(publicKey->e), NULL);
> +	key = RSA_new();
> +	if (!key)
> +		goto err;
> +	if (!RSA_set0_key(key, n, e, NULL))
> +		goto err;
> +
> +	/* If a key was provided, compare it to the embedded key. */
> +	if (fw_key && (BN_cmp(RSA_get0_n(key), RSA_get0_n(fw_key)) ||
> +		       BN_cmp(RSA_get0_e(key), RSA_get0_e(fw_key)))) {
> +		pr_err("Wrong firmware key in certificate\n");
> +		goto err;
> +	}
> +
> +	/* If a digest was provided, compare it to the embedded digest. */
> +	extension = &totalSequence->mainSequence.explicit3.extension;
> +	if (digest && memcmp(&extension->digest, digest, SHA256_DIGEST_LENGTH)) {
> +		pr_err("Wrong firmware digest in certificate\n");
> +		goto err;
> +	}
> +
> +	/* Verify the certificate's signature. See the comment above. */
> +	SHA256((uint8_t *)totalSequence, sizeof(struct toc0_mainSequence), cert_digest);
> +	sigSequence = &totalSequence->sigSequence;
> +	if (!RSA_verify(NID_sha256, cert_digest, SHA256_DIGEST_LENGTH,
> +			sigSequence->signature,
> +			sizeof(sigSequence->signature), key)) {
> +		pr_err("Bad certificate signature\n");
> +		goto err;
> +	}
> +
> +	ret = EXIT_SUCCESS;
> +
> +err:
> +	RSA_free(key);
> +
> +	return ret;
> +}
> +
> +/*
> + * Always create a TOC0 containing 3 items. The extra item will be ignored on
> + * SoCs which do not support it.
> + */
> +static int toc0_create(uint8_t *buf, uint32_t len, RSA *root_key, RSA *fw_key,
> +		       uint8_t *key_item, uint32_t key_item_len,
> +		       uint8_t *fw_item, uint32_t fw_item_len, uint32_t fw_addr)
> +{
> +	struct toc0_main_info *main_info = (void *)buf;
> +	struct toc0_item_info *item_info = (void *)(main_info + 1);
> +	uint8_t digest[SHA256_DIGEST_LENGTH];
> +	uint32_t *buf32 = (void *)buf;
> +	RSA *orig_fw_key = fw_key;
> +	int ret = EXIT_FAILURE;
> +	uint32_t checksum = 0;
> +	uint32_t item_offset;
> +	uint32_t item_length;
> +	int i;
> +
> +	/* Hash the firmware for inclusion in the certificate. */
> +	SHA256(fw_item, fw_item_len, digest);
> +
> +	/* Create the main TOC0 header, containing three items. */
> +	memcpy(main_info->name, TOC0_MAIN_INFO_NAME, sizeof(main_info->name));
> +	main_info->magic	= cpu_to_le32(TOC0_MAIN_INFO_MAGIC);
> +	main_info->checksum	= cpu_to_le32(BROM_STAMP_VALUE);
> +	main_info->num_items	= cpu_to_le32(TOC0_DEFAULT_NUM_ITEMS);
> +	memcpy(main_info->end, TOC0_MAIN_INFO_END, sizeof(main_info->end));
> +
> +	/* The first item links the ROTPK to the signing key. */
> +	item_offset = sizeof(*main_info) +
> +		      sizeof(*item_info) * TOC0_DEFAULT_NUM_ITEMS;
> +	/* Using an existing key item avoids needing the root private key. */
> +	if (key_item) {
> +		item_length = sizeof(*key_item);
> +		if (toc0_verify_key_item(key_item, item_length,
> +					 root_key, &fw_key))
> +			goto err;
> +		memcpy(buf + item_offset, key_item, item_length);
> +	} else if (toc0_create_key_item(buf + item_offset, &item_length,
> +					root_key, fw_key)) {
> +		goto err;
> +	}
> +
> +	item_info->name		= cpu_to_le32(TOC0_ITEM_INFO_NAME_KEY);
> +	item_info->offset	= cpu_to_le32(item_offset);
> +	item_info->length	= cpu_to_le32(item_length);
> +	memcpy(item_info->end, TOC0_ITEM_INFO_END, sizeof(item_info->end));
> +
> +	/* The second item contains a certificate signed by the firmware key. */
> +	item_offset = item_offset + item_length;
> +	if (toc0_create_cert_item(buf + item_offset, &item_length,
> +				  fw_key, digest))
> +		goto err;
> +
> +	item_info++;
> +	item_info->name		= cpu_to_le32(TOC0_ITEM_INFO_NAME_CERT);
> +	item_info->offset	= cpu_to_le32(item_offset);
> +	item_info->length	= cpu_to_le32(item_length);
> +	memcpy(item_info->end, TOC0_ITEM_INFO_END, sizeof(item_info->end));
> +
> +	/* The third item contains the actual boot code. */
> +	item_offset = ALIGN(item_offset + item_length, 32);
> +	item_length = fw_item_len;
> +	if (buf + item_offset != fw_item)
> +		memmove(buf + item_offset, fw_item, item_length);
> +
> +	item_info++;
> +	item_info->name		= cpu_to_le32(TOC0_ITEM_INFO_NAME_FIRMWARE);
> +	item_info->offset	= cpu_to_le32(item_offset);
> +	item_info->length	= cpu_to_le32(item_length);
> +	item_info->load_addr	= cpu_to_le32(fw_addr);
> +	memcpy(item_info->end, TOC0_ITEM_INFO_END, sizeof(item_info->end));
> +
> +	/* Pad to the required block size with 0xff to be flash-friendly. */
> +	item_offset = item_offset + item_length;
> +	item_length = ALIGN(item_offset, PAD_SIZE) - item_offset;
> +	memset(buf + item_offset, 0xff, item_length);
> +
> +	/* Fill in the total padded file length. */
> +	item_offset = item_offset + item_length;
> +	main_info->length = cpu_to_le32(item_offset);
> +
> +	/* Verify enough space was provided when creating the image. */
> +	assert(len >= item_offset);
> +
> +	/* Calculate the checksum. Yes, it's that simple. */
> +	for (i = 0; i < item_offset / 4; ++i)
> +		checksum += le32_to_cpu(buf32[i]);
> +	main_info->checksum = cpu_to_le32(checksum);
> +
> +	ret = EXIT_SUCCESS;
> +
> +err:
> +	if (fw_key != orig_fw_key)
> +		RSA_free(fw_key);
> +
> +	return ret;
> +}
> +
> +static const struct toc0_item_info *
> +toc0_find_item(const struct toc0_main_info *main_info, uint32_t name,
> +	       uint32_t *offset, uint32_t *length)
> +{
> +	const struct toc0_item_info *item_info = (void *)(main_info + 1);
> +	uint32_t item_offset, item_length;
> +	uint32_t num_items, main_length;
> +	int i;
> +
> +	num_items   = le32_to_cpu(main_info->num_items);
> +	main_length = le32_to_cpu(main_info->length);
> +
> +	for (i = 0; i < num_items; ++i, ++item_info) {
> +		if (le32_to_cpu(item_info->name) != name)
> +			continue;
> +
> +		item_offset = le32_to_cpu(item_info->offset);
> +		item_length = le32_to_cpu(item_info->length);
> +
> +		if (item_offset > main_length ||
> +		    item_length > main_length - item_offset)
> +			continue;
> +
> +		*offset = item_offset;
> +		*length = item_length;
> +
> +		return item_info;
> +	}
> +
> +	return NULL;
> +}
> +
> +static int toc0_verify(const uint8_t *buf, uint32_t len, RSA *root_key)
> +{
> +	const struct toc0_main_info *main_info = (void *)buf;
> +	const struct toc0_item_info *item_info;
> +	uint8_t digest[SHA256_DIGEST_LENGTH];
> +	uint32_t main_length = le32_to_cpu(main_info->length);
> +	uint32_t checksum = BROM_STAMP_VALUE;
> +	uint32_t *buf32 = (void *)buf;
> +	uint32_t length, offset;
> +	int ret = EXIT_FAILURE;
> +	RSA *fw_key = NULL;
> +	int i;
> +
> +	if (len < main_length)
> +		goto err;
> +
> +	/* Verify the main header. */
> +	if (memcmp(main_info->name, TOC0_MAIN_INFO_NAME, sizeof(main_info->name)))
> +		goto err;
> +	if (le32_to_cpu(main_info->magic) != TOC0_MAIN_INFO_MAGIC)
> +		goto err;
> +	/* Verify the checksum without modifying the buffer. */
> +	for (i = 0; i < main_length / 4; ++i)
> +		checksum += le32_to_cpu(buf32[i]);
> +	if (checksum != 2 * le32_to_cpu(main_info->checksum))
> +		goto err;
> +	/* The length must be at least 512 byte aligned. */
> +	if (main_length % 512)
> +		goto err;
> +	if (memcmp(main_info->end, TOC0_MAIN_INFO_END, sizeof(main_info->end)))
> +		goto err;
> +
> +	/* Verify the key item if present (it is optional). */
> +	item_info = toc0_find_item(main_info, TOC0_ITEM_INFO_NAME_KEY,
> +				   &offset, &length);
> +	if (!item_info)
> +		fw_key = root_key;
> +	else if (toc0_verify_key_item(buf + offset, length, root_key, &fw_key))
> +		goto err;
> +
> +	/* Hash the firmware to compare with the certificate. */
> +	item_info = toc0_find_item(main_info, TOC0_ITEM_INFO_NAME_FIRMWARE,
> +				   &offset, &length);
> +	if (!item_info) {
> +		pr_err("Missing firmware item\n");
> +		goto err;
> +	}
> +	SHA256(buf + offset, length, digest);
> +
> +	/* Verify the certificate item. */
> +	item_info = toc0_find_item(main_info, TOC0_ITEM_INFO_NAME_CERT,
> +				   &offset, &length);
> +	if (!item_info) {
> +		pr_err("Missing certificate item\n");
> +		goto err;
> +	}
> +	if (toc0_verify_cert_item(buf + offset, length, fw_key, digest))
> +		goto err;
> +
> +	ret = EXIT_SUCCESS;
> +
> +err:
> +	if (fw_key != root_key)
> +		RSA_free(fw_key);
> +
> +	return ret;
> +}
> +
> +static int toc0_check_params(struct image_tool_params *params)
> +{
> +	if (!params->dflag)
> +		return -EINVAL;
> +
> +	/*
> +	 * If a key directory was provided, look for key files there.
> +	 * Otherwise, look for them in the current directory. The key files are
> +	 * the "quoted" terms in the description below.
> +	 *
> +	 * A summary of the chain of trust on most SoCs:
> +	 *  1) eFuse contains a SHA256 digest of the public "root key".
> +	 *  2) Private "root key" signs the certificate item (generated here).
> +	 *  3) Certificate item contains a SHA256 digest of the firmware item.
> +	 *
> +	 * A summary of the chain of trust on the H6 (by default; a bit in the
> +	 * BROM_CONFIG eFuse makes it work like above):
> +	 *  1) eFuse contains a SHA256 digest of the public "root key".
> +	 *  2) Private "root key" signs the "key item" (generated here).
> +	 *  3) "Key item" contains the public "root key" and public "fw key".
> +	 *  4) Private "fw key" signs the certificate item (generated here).
> +	 *  5) Certificate item contains a SHA256 digest of the firmware item.
> +	 *
> +	 * This means there are three valid ways to generate a TOC0:
> +	 *  1) Provide the private "root key" only. This works everywhere.
> +	 *     For H6, the "root key" will also be used as the "fw key".
> +	 *  2) FOR H6 ONLY: Provide the private "root key" and a separate
> +	 *     private "fw key".
> +	 *  3) FOR H6 ONLY: Provide the private "fw key" and a pre-existing
> +	 *     "key item" containing the corresponding  public "fw key".
> +	 *     In this case, the private "root key" can be kept offline. The
> +	 *     "key item" can be extracted from a TOC0 image generated using
> +	 *     method #2 above.
> +	 *
> +	 *  Note that until the ROTPK_HASH eFuse is programmed, any "root key"
> +	 *  will be accepted by the BROM.
> +	 */
> +	if (params->keydir) {
> +		if (asprintf(&fw_key_file, "%s/%s", params->keydir, fw_key_file) < 0)
> +			return -ENOMEM;
> +		if (asprintf(&key_item_file, "%s/%s", params->keydir, key_item_file) < 0)
> +			return -ENOMEM;
> +		if (asprintf(&root_key_file, "%s/%s", params->keydir, root_key_file) < 0)
> +			return -ENOMEM;
> +	}
> +
> +	return 0;
> +}
> +
> +static int toc0_verify_header(unsigned char *buf, int image_size,
> +			      struct image_tool_params *params)
> +{
> +	int ret = EXIT_FAILURE;
> +	RSA *root_key = NULL;
> +	FILE *fp;
> +
> +	/* A root public key is optional. */
> +	fp = fopen(root_key_file, "rb");
> +	if (fp) {
> +		pr_info("Verifying image with existing root key\n");
> +		root_key = PEM_read_RSAPrivateKey(fp, NULL, NULL, NULL);
> +		if (!root_key)
> +			root_key = PEM_read_RSAPublicKey(fp, NULL, NULL, NULL);
> +		fclose(fp);
> +		if (!root_key) {
> +			pr_err("Failed to read public key from '%s'\n",
> +			       root_key_file);
> +			goto err;
> +		}
> +	}
> +
> +	ret = toc0_verify(buf, image_size, root_key);
> +
> +err:
> +	RSA_free(root_key);
> +
> +	return ret;
> +}
> +
> +static const char *toc0_item_name(uint32_t name)
> +{
> +	if (name == TOC0_ITEM_INFO_NAME_CERT)
> +		return "Certificate";
> +	if (name == TOC0_ITEM_INFO_NAME_FIRMWARE)
> +		return "Firmware";
> +	if (name == TOC0_ITEM_INFO_NAME_KEY)
> +		return "Key";
> +	return "(unknown)";
> +}
> +
> +static void toc0_print_header(const void *buf)
> +{
> +	const struct toc0_main_info *main_info = buf;
> +	const struct toc0_item_info *item_info = (void *)(main_info + 1);
> +	uint32_t head_length, main_length, num_items;
> +	uint32_t item_offset, item_length, item_name;
> +	int load_addr = -1;
> +	int i;
> +
> +	num_items   = le32_to_cpu(main_info->num_items);
> +	head_length = sizeof(*main_info) + num_items * sizeof(*item_info);
> +	main_length = le32_to_cpu(main_info->length);
> +
> +	printf("Allwinner TOC0 Image\n"
> +	       "Size: %d bytes\n"
> +	       "Contents: %d items\n"
> +	       " 00000000:%08x Headers\n",
> +	       main_length, num_items, head_length);
> +
> +	for (i = 0; i < num_items; ++i, ++item_info) {
> +		item_offset = le32_to_cpu(item_info->offset);
> +		item_length = le32_to_cpu(item_info->length);
> +		item_name   = le32_to_cpu(item_info->name);
> +
> +		if (item_name == TOC0_ITEM_INFO_NAME_FIRMWARE)
> +			load_addr = le32_to_cpu(item_info->load_addr);
> +
> +		printf(" %08x:%08x %s\n",
> +		       item_offset, item_length,
> +		       toc0_item_name(item_name));
> +	}
> +
> +	if (num_items && item_offset + item_length < main_length) {
> +		item_offset = item_offset + item_length;
> +		item_length = main_length - item_offset;
> +
> +		printf(" %08x:%08x Padding\n",
> +		       item_offset, item_length);
> +	}
> +
> +	if (load_addr != -1)
> +		printf("Load address: 0x%08x\n", load_addr);
> +}
> +
> +static void toc0_set_header(void *buf, struct stat *sbuf, int ifd,
> +			    struct image_tool_params *params)
> +{
> +	uint32_t key_item_len = 0;
> +	uint8_t *key_item = NULL;
> +	int ret = EXIT_FAILURE;
> +	RSA *root_key = NULL;
> +	RSA *fw_key = NULL;
> +	FILE *fp;
> +
> +	/* Either a key item or the root private key is required. */
> +	fp = fopen(key_item_file, "rb");
> +	if (fp) {
> +		pr_info("Creating image using existing key item\n");
> +		key_item_len = sizeof(struct toc0_key_item);
> +		key_item = OPENSSL_malloc(key_item_len);
> +		if (!key_item || fread(key_item, key_item_len, 1, fp) != 1) {
> +			pr_err("Failed to read key item from '%s'\n",
> +			       root_key_file);
> +			goto err;
> +		}
> +		fclose(fp);
> +		fp = NULL;
> +	}
> +
> +	fp = fopen(root_key_file, "rb");
> +	if (fp) {
> +		root_key = PEM_read_RSAPrivateKey(fp, NULL, NULL, NULL);
> +		if (!root_key)
> +			root_key = PEM_read_RSAPublicKey(fp, NULL, NULL, NULL);
> +		fclose(fp);
> +		fp = NULL;
> +	}
> +
> +	/* When using an existing key item, the root key is optional. */
> +	if (!key_item && (!root_key || !RSA_get0_d(root_key))) {
> +		pr_err("Failed to read private key from '%s'\n",
> +		       root_key_file);
> +		pr_info("Try 'openssl genrsa -out root_key.pem'\n");
> +		goto err;
> +	}
> +
> +	/* The certificate/firmware private key is always required. */
> +	fp = fopen(fw_key_file, "rb");
> +	if (fp) {
> +		fw_key = PEM_read_RSAPrivateKey(fp, NULL, NULL, NULL);
> +		fclose(fp);
> +		fp = NULL;
> +	}
> +	if (!fw_key) {
> +		/* If the root key is a private key, it can be used instead. */
> +		if (root_key && RSA_get0_d(root_key)) {
> +			pr_info("Using root key as firmware key\n");
> +			fw_key = root_key;
> +		} else {
> +			pr_err("Failed to read private key from '%s'\n",
> +			       fw_key_file);
> +			goto err;
> +		}
> +	}
> +
> +	/* Warn about potential compatibility issues. */
> +	if (key_item || fw_key != root_key)
> +		pr_warn("Only H6 supports separate root and firmware keys\n");
> +
> +	ret = toc0_create(buf, params->file_size, root_key, fw_key,
> +			  key_item, key_item_len,
> +			  buf + TOC0_DEFAULT_HEADER_LEN,
> +			  params->orig_file_size, params->addr);
> +
> +err:
> +	OPENSSL_free(key_item);
> +	OPENSSL_free(root_key);
> +	if (fw_key != root_key)
> +		OPENSSL_free(fw_key);
> +	if (fp)
> +		fclose(fp);
> +
> +	if (ret != EXIT_SUCCESS)
> +		exit(ret);
> +}
> +
> +static int toc0_check_image_type(uint8_t type)
> +{
> +	return type == IH_TYPE_SUNXI_TOC0 ? 0 : 1;
> +}
> +
> +static int toc0_vrec_header(struct image_tool_params *params,
> +			    struct image_type_params *tparams)
> +{
> +	tparams->hdr = calloc(tparams->header_size, 1);
> +
> +	/* Save off the unpadded data size for SHA256 calculation. */
> +	params->orig_file_size = params->file_size - TOC0_DEFAULT_HEADER_LEN;
> +
> +	/* Return padding to 8K blocks. */
> +	return ALIGN(params->file_size, PAD_SIZE) - params->file_size;
> +}
> +
> +U_BOOT_IMAGE_TYPE(
> +	sunxi_toc0,
> +	"Allwinner TOC0 Boot Image support",
> +	TOC0_DEFAULT_HEADER_LEN,
> +	NULL,
> +	toc0_check_params,
> +	toc0_verify_header,
> +	toc0_print_header,
> +	toc0_set_header,
> +	NULL,
> +	toc0_check_image_type,
> +	NULL,
> +	toc0_vrec_header
> +);


^ permalink raw reply	[flat|nested] 13+ messages in thread

* Re: [PATCH v2 3/4] sunxi: Support SPL in both eGON and TOC0 images
  2021-08-22  4:46 ` [PATCH v2 3/4] sunxi: Support SPL in both eGON and TOC0 images Samuel Holland
@ 2021-09-06  0:30   ` Andre Przywara
  0 siblings, 0 replies; 13+ messages in thread
From: Andre Przywara @ 2021-09-06  0:30 UTC (permalink / raw)
  To: Samuel Holland
  Cc: u-boot, Jagan Teki, AKASHI Takahiro, Alexandru Gagniuc,
	Baruch Siach, Bharat Gooty, Chris Packham, Fabio Estevam,
	Frieder Schrempf, Jernej Skrabec, Marek Behún,
	NXP i.MX U-Boot Team, Naoki Hayama, Pali Rohár,
	Patrick Delaunay, Priyanka Jain, Rayagonda Kokatanur,
	Simon Glass, Stefan Roese, Stefano Babic, Sughosh Ganu,
	Trevor Woerner, lauri.hintsala

On Sat, 21 Aug 2021 23:46:47 -0500
Samuel Holland <samuel@sholland.org> wrote:

Hi,

> SPL uses the image header to detect the boot device and to find the
> offset of the next U-Boot stage. Since this information is stored
> differently in the eGON and TOC0 image headers, add code to find the
> correct value based on the image type currently in use.

many thanks for determining this at runtime!

> Signed-off-by: Samuel Holland <samuel@sholland.org>
> ---
> 
> Changes in v2:
>  - Moved SPL header signature checks out of sunxi_image.h
>  - Refactored SPL header signature checks to use fewer casts
> 
>  arch/arm/include/asm/arch-sunxi/spl.h |  2 --
>  arch/arm/mach-sunxi/board.c           | 50 +++++++++++++++++++++++----
>  2 files changed, 43 insertions(+), 9 deletions(-)
> 
> diff --git a/arch/arm/include/asm/arch-sunxi/spl.h b/arch/arm/include/asm/arch-sunxi/spl.h
> index 58cdf806d9a..157b11e4897 100644
> --- a/arch/arm/include/asm/arch-sunxi/spl.h
> +++ b/arch/arm/include/asm/arch-sunxi/spl.h
> @@ -19,8 +19,6 @@
>  #define SUNXI_BOOTED_FROM_MMC0_HIGH	0x10
>  #define SUNXI_BOOTED_FROM_MMC2_HIGH	0x12
>  
> -#define is_boot0_magic(addr)	(memcmp((void *)(addr), BOOT0_MAGIC, 8) == 0)
> -
>  uint32_t sunxi_get_boot_device(void);
>  
>  #endif
> diff --git a/arch/arm/mach-sunxi/board.c b/arch/arm/mach-sunxi/board.c
> index d9b04f75fc4..b6f92bdc5e7 100644
> --- a/arch/arm/mach-sunxi/board.c
> +++ b/arch/arm/mach-sunxi/board.c
> @@ -244,12 +244,40 @@ void s_init(void)
>  
>  #define SUNXI_INVALID_BOOT_SOURCE	-1
>  
> -static int sunxi_get_boot_source(void)
> +static struct boot_file_head *sunxi_egon_get_head(void)
>  {
> -	if (!is_boot0_magic(SPL_ADDR + 4)) /* eGON.BT0 */
> -		return SUNXI_INVALID_BOOT_SOURCE;
> +	struct boot_file_head *egon_head = (void *)SPL_ADDR;
> +
> +	if (memcmp(egon_head, BOOT0_MAGIC, 8)) /* eGON.BT0 */

For eGON the magic is not at the beginning of the struct, so you need:
	memcmp(&egon_head->magic, BOOT0_MAGIC, 8)

Otherwise 99.9% of all Allwinner users will be very disappointed ;-)

And there is another problem: For 32-bit SoCs the SPL address is
literally 0 (SRAM A1), so the return value in the successful case is
NULL as well :-(

Maybe have a function to return an enum (EGON, TOC0, NONE/FEL) instead?
After all the address will always be SPL_ADDR. 

Cheers,
Andre

> +		return NULL;
> +
> +	return egon_head;
> +}
> +
> +static struct toc0_main_info *sunxi_toc0_get_info(void)
> +{
> +	struct toc0_main_info *toc0_info = (void *)SPL_ADDR;
> +
> +	if (memcmp(toc0_info->name, TOC0_MAIN_INFO_NAME, 8)) /* TOC0.GLH */
> +		return NULL;
>  
> -	return readb(SPL_ADDR + 0x28);
> +	return toc0_info;
> +}
> +
> +static int sunxi_get_boot_source(void)
> +{
> +	struct boot_file_head *egon_head;
> +	struct toc0_main_info *toc0_info;
> +
> +	egon_head = sunxi_egon_get_head();
> +	if (egon_head)
> +		return readb(&egon_head->boot_media);
> +	toc0_info = sunxi_toc0_get_info();
> +	if (toc0_info)
> +		return readb(&toc0_info->platform[0]);
> +
> +	/* Not a valid image, so we must have been booted via FEL. */
> +	return SUNXI_INVALID_BOOT_SOURCE;
>  }
>  
>  /* The sunxi internal brom will try to loader external bootloader
> @@ -297,10 +325,18 @@ uint32_t sunxi_get_boot_device(void)
>  #ifdef CONFIG_SPL_BUILD
>  static u32 sunxi_get_spl_size(void)
>  {
> -	if (!is_boot0_magic(SPL_ADDR + 4)) /* eGON.BT0 */
> -		return 0;
> +	struct boot_file_head *egon_head;
> +	struct toc0_main_info *toc0_info;
> +
> +	egon_head = sunxi_egon_get_head();
> +	if (egon_head)
> +		return readl(&egon_head->length);
> +	toc0_info = sunxi_toc0_get_info();
> +	if (toc0_info)
> +		return readl(&toc0_info->length);
>  
> -	return readl(SPL_ADDR + 0x10);
> +	/* Not a valid image, so use the default U-Boot offset. */
> +	return 0;
>  }
>  
>  /*


^ permalink raw reply	[flat|nested] 13+ messages in thread

end of thread, other threads:[~2021-09-06  0:30 UTC | newest]

Thread overview: 13+ messages (download: mbox.gz / follow: Atom feed)
-- links below jump to the message on this page --
2021-08-22  4:46 [PATCH v2 0/4] sunxi: TOC0 image type support Samuel Holland
2021-08-22  4:46 ` [PATCH v2 1/4] tools: Separate image types which depend on OpenSSL Samuel Holland
2021-08-22  9:58   ` Pali Rohár
2021-08-22 17:32     ` Samuel Holland
2021-08-24  0:51       ` Andre Przywara
2021-08-22  4:46 ` [PATCH v2 2/4] tools: mkimage: Add Allwinner TOC0 support Samuel Holland
2021-08-22 10:07   ` Pali Rohár
2021-08-22 17:44     ` Samuel Holland
2021-08-22 17:51       ` Pali Rohár
2021-09-06  0:29   ` Andre Przywara
2021-08-22  4:46 ` [PATCH v2 3/4] sunxi: Support SPL in both eGON and TOC0 images Samuel Holland
2021-09-06  0:30   ` Andre Przywara
2021-08-22  4:46 ` [PATCH v2 4/4] sunxi: Support building a SPL as a TOC0 image Samuel Holland

This is an external index of several public inboxes,
see mirroring instructions on how to clone and mirror
all data and code used by this external index.