BL31=$(STAGING_DIR_IMAGE)/$(ATF) \
$(if $(TPL),ROCKCHIP_TPL=$(STAGING_DIR_IMAGE)/$(TPL))
+define Build/Configure
+ $(call Build/Configure/U-Boot)
+
+ifneq ($(filter %rk3576,$(BUILD_VARIANT)),)
+ $(TARGET_CC) -nostdlib -ffreestanding -Os -S -o $(PKG_BUILD_DIR)/rk3576-boost.S $(PKG_BUILD_DIR)/rk3576-boost.c
+ $(TARGET_CROSS)as -o $(PKG_BUILD_DIR)/rk3576-boost.o $(PKG_BUILD_DIR)/rk3576-boost.S
+ $(TARGET_CROSS)objcopy -O binary -j .text $(PKG_BUILD_DIR)/rk3576-boost.o $(PKG_BUILD_DIR)/rk3576-boost.bin
+endif
+endef
+
define Build/InstallDev
$(INSTALL_DIR) $(STAGING_DIR_IMAGE)
$(CP) $(PKG_BUILD_DIR)/u-boot-rockchip.bin $(STAGING_DIR_IMAGE)/$(BUILD_VARIANT)-u-boot-rockchip.bin
--- /dev/null
+From cb682b364eb2d49c3ee2a1f7f33258919f5da688 Mon Sep 17 00:00:00 2001
+From: Jonas Karlman <jonas@kwiboo.se>
+Date: Wed, 29 Jan 2025 22:36:27 +0000
+Subject: [PATCH 1/7] rockchip: mkimage: Split size_and_off and size_and_nimage
+
+Split 32-bit size_and_off and size_and_nimage fields of the v2 image
+format header into their own 16-bit size, offset and num_images fields.
+
+Set num_images based on number of images passed by the datafile
+parameter and size based on the offset to the hash field to fix using a
+single init data file and no boot data file for the v2 image format.
+
+Signed-off-by: Jonas Karlman <jonas@kwiboo.se>
+---
+
+--- a/tools/rkcommon.c
++++ b/tools/rkcommon.c
+@@ -34,15 +34,16 @@ enum hash_type {
+ /**
+ * struct image_entry
+ *
+- * @size_and_off: [31:16]image size;[15:0]image offset
+- * @address: default as 0xFFFFFFFF
++ * @offset: image offset (unit as 512 byte blocks)
++ * @size: image size (unit as 512 byte blocks)
++ * @address: load address (default as 0xFFFFFFFF)
+ * @flag: no use
+ * @counter: no use
+ * @hash: hash of image
+- *
+ */
+ struct image_entry {
+- uint32_t size_and_off;
++ uint16_t offset;
++ uint16_t size;
+ uint32_t address;
+ uint32_t flag;
+ uint32_t counter;
+@@ -56,16 +57,17 @@ struct image_entry {
+ * This is stored at SD card block 64 (where each block is 512 bytes)
+ *
+ * @magic: Magic (must be RK_MAGIC_V2)
+- * @size_and_nimage: [31:16]number of images;[15:0]
+- * offset to hash field of header(unit as 4Byte)
+- * @boot_flag: [3:0]hash type(0:none,1:sha256,2:sha512)
+- * @signature: hash or signature for header info
+- *
++ * @size: offset to hash field of header (unit as 4 bytes)
++ * @num_images: number of images
++ * @boot_flag: [3:0] hash type (0:none, 1:sha256, 2:sha512)
++ * @images: images
++ * @hash: hash or signature for header info
+ */
+ struct header0_info_v2 {
+ uint32_t magic;
+ uint8_t reserved[4];
+- uint32_t size_and_nimage;
++ uint16_t size;
++ uint16_t num_images;
+ uint32_t boot_flag;
+ uint8_t reserved1[104];
+ struct image_entry images[4];
+@@ -350,17 +352,18 @@ static void rkcommon_set_header0_v2(void
+ printf("Image Type: Rockchip %s boot image\n",
+ rkcommon_get_spl_hdr(params));
+ memset(buf, '\0', RK_INIT_OFFSET * RK_BLK_SIZE);
+- hdr->magic = cpu_to_le32(RK_MAGIC_V2);
+- hdr->size_and_nimage = cpu_to_le32((2 << 16) + 384);
++ hdr->magic = cpu_to_le32(RK_MAGIC_V2);
+ hdr->boot_flag = cpu_to_le32(HASH_SHA256);
+ sector_offset = 4;
+ image_size_array[0] = spl_params.init_size;
+ image_size_array[1] = spl_params.boot_size;
+
+ for (i = 0; i < 2; i++) {
++ if (!image_size_array[i])
++ break;
+ image_sector_count = image_size_array[i] / RK_BLK_SIZE;
+- hdr->images[i].size_and_off = cpu_to_le32((image_sector_count
+- << 16) + sector_offset);
++ hdr->images[i].offset = cpu_to_le16(sector_offset);
++ hdr->images[i].size = cpu_to_le16(image_sector_count);
+ hdr->images[i].address = 0xFFFFFFFF;
+ hdr->images[i].counter = cpu_to_le32(i + 1);
+ image_ptr = buf + sector_offset * RK_BLK_SIZE;
+@@ -369,6 +372,8 @@ static void rkcommon_set_header0_v2(void
+ sector_offset = sector_offset + image_sector_count;
+ }
+
++ hdr->num_images = cpu_to_le16(i);
++ hdr->size = cpu_to_le16(offsetof(typeof(*hdr), hash) / sizeof(uint32_t));
+ do_sha256_hash(buf, (void *)hdr->hash - buf, hdr->hash);
+ }
+
+@@ -515,10 +520,8 @@ void rkcommon_print_header(const void *b
+ return;
+ }
+
+- init_size = header0_v2.images[0].size_and_off >> 16;
+- init_size = init_size * RK_BLK_SIZE;
+- boot_size = header0_v2.images[1].size_and_off >> 16;
+- boot_size = boot_size * RK_BLK_SIZE;
++ init_size = le16_to_cpu(header0_v2.images[0].size) * RK_BLK_SIZE;
++ boot_size = le16_to_cpu(header0_v2.images[1].size) * RK_BLK_SIZE;
+ } else {
+ ret = rkcommon_parse_header(buf, &header0, &spl_info);
+
+@@ -532,8 +535,9 @@ void rkcommon_print_header(const void *b
+ }
+
+ image_type = ret;
+- init_size = header0.init_size * RK_BLK_SIZE;
+- boot_size = header0.init_boot_size * RK_BLK_SIZE - init_size;
++ init_size = le16_to_cpu(header0.init_size) * RK_BLK_SIZE;
++ boot_size = le16_to_cpu(header0.init_boot_size) * RK_BLK_SIZE -
++ init_size;
+
+ printf("Image Type: Rockchip %s (%s) boot image\n",
+ spl_info->spl_hdr,
--- /dev/null
+From 0085f8509d62591560901c11fa9d63e34da4fa62 Mon Sep 17 00:00:00 2001
+From: Jonas Karlman <jonas@kwiboo.se>
+Date: Wed, 29 Jan 2025 22:36:28 +0000
+Subject: [PATCH 2/7] rockchip: mkimage: Print image information for all
+ embedded images
+
+The v2 image format can embed up to 4 data files compared to the two
+init and boot data files using the older image format.
+
+Add support for displaying more of the image header information that
+exists in the v2 image format, e.g. image load address and flag.
+
+Example for v2 image format:
+
+ > tools/mkimage -l rk3576_idblock_v1.09.107.img
+ Rockchip Boot Image (v2)
+ Image 1: 4096 @ 0x1000
+ - Load address: 0x3ffc0000
+ Image 2: 77824 @ 0x2000
+ - Load address: 0x3ff81000
+ Image 3: 262144 @ 0x15000
+
+Example for older image format:
+
+ > tools/mkimage -l u-boot-rockchip.bin
+ Rockchip RK32 (SD/MMC) Boot Image
+ Init Data: 20480 @ 0x800
+ Boot Data: 112640 @ 0x5800
+
+Signed-off-by: Jonas Karlman <jonas@kwiboo.se>
+---
+
+--- a/tools/rkcommon.c
++++ b/tools/rkcommon.c
+@@ -349,8 +349,6 @@ static void rkcommon_set_header0_v2(void
+ uint8_t *image_ptr = NULL;
+ int i;
+
+- printf("Image Type: Rockchip %s boot image\n",
+- rkcommon_get_spl_hdr(params));
+ memset(buf, '\0', RK_INIT_OFFSET * RK_BLK_SIZE);
+ hdr->magic = cpu_to_le32(RK_MAGIC_V2);
+ hdr->boot_flag = cpu_to_le32(HASH_SHA256);
+@@ -504,6 +502,29 @@ int rkcommon_verify_header(unsigned char
+ return -ENOENT;
+ }
+
++static void rkcommon_print_header_v2(const struct header0_info_v2 *hdr)
++{
++ uint32_t val;
++ int i;
++
++ printf("Rockchip Boot Image (v2)\n");
++
++ for (i = 0; i < le16_to_cpu(hdr->num_images); i++) {
++ printf("Image %u: %u @ 0x%x\n",
++ le32_to_cpu(hdr->images[i].counter),
++ le16_to_cpu(hdr->images[i].size) * RK_BLK_SIZE,
++ le16_to_cpu(hdr->images[i].offset) * RK_BLK_SIZE);
++
++ val = le32_to_cpu(hdr->images[i].address);
++ if (val != 0xFFFFFFFF)
++ printf("- Load address: 0x%x\n", val);
++
++ val = le32_to_cpu(hdr->images[i].flag);
++ if (val)
++ printf("- Flag: 0x%x\n", val);
++ }
++}
++
+ void rkcommon_print_header(const void *buf, struct image_tool_params *params)
+ {
+ struct header0_info header0;
+@@ -520,8 +541,7 @@ void rkcommon_print_header(const void *b
+ return;
+ }
+
+- init_size = le16_to_cpu(header0_v2.images[0].size) * RK_BLK_SIZE;
+- boot_size = le16_to_cpu(header0_v2.images[1].size) * RK_BLK_SIZE;
++ rkcommon_print_header_v2(&header0_v2);
+ } else {
+ ret = rkcommon_parse_header(buf, &header0, &spl_info);
+
+@@ -539,15 +559,16 @@ void rkcommon_print_header(const void *b
+ boot_size = le16_to_cpu(header0.init_boot_size) * RK_BLK_SIZE -
+ init_size;
+
+- printf("Image Type: Rockchip %s (%s) boot image\n",
+- spl_info->spl_hdr,
++ printf("Rockchip %s (%s) Boot Image\n", spl_info->spl_hdr,
+ (image_type == IH_TYPE_RKSD) ? "SD/MMC" : "SPI");
+- }
+
+- printf("Init Data Size: %d bytes\n", init_size);
++ printf("Init Data: %d @ 0x%x\n", init_size,
++ le16_to_cpu(header0.init_offset) * RK_BLK_SIZE);
+
+- if (boot_size != RK_MAX_BOOT_SIZE)
+- printf("Boot Data Size: %d bytes\n", boot_size);
++ if (boot_size != RK_MAX_BOOT_SIZE)
++ printf("Boot Data: %d @ 0x%x\n", boot_size, init_size +
++ le16_to_cpu(header0.init_offset) * RK_BLK_SIZE);
++ }
+ }
+
+ void rkcommon_rc4_encode_spl(void *buf, unsigned int offset, unsigned int size)
--- /dev/null
+From 83326e18d15be1ac9b7daf7b472d0bcb8074da2b Mon Sep 17 00:00:00 2001
+From: Jonas Karlman <jonas@kwiboo.se>
+Date: Wed, 29 Jan 2025 22:36:29 +0000
+Subject: [PATCH 3/7] rockchip: mkimage: Print boot0 and boot1 parameters
+
+The v2 image format embeds boot0 and boot1 parameters, the vendor tool
+boot_merger may write these parameters based on the rkboot miniall.ini
+files.
+
+E.g. a RK3576 boot image may contain a boot1 parameter that signals
+BootROM or vendor blobs to use 1 GHz instead of the regular 24 MHz rate
+for the high precision timer.
+
+Add support for printing boot0 and boot1 parameters, e.g.:
+
+ > tools/mkimage -l rk3576_idblock_v1.09.107.img
+ Rockchip Boot Image (v2)
+ Boot1 2: 0x100
+ Image 1: 4096 @ 0x1000
+ - Load address: 0x3ffc0000
+ Image 2: 77824 @ 0x2000
+ - Load address: 0x3ff81000
+ Image 3: 262144 @ 0x15000
+
+Signed-off-by: Jonas Karlman <jonas@kwiboo.se>
+---
+
+--- a/tools/rkcommon.c
++++ b/tools/rkcommon.c
+@@ -62,6 +62,8 @@ struct image_entry {
+ * @boot_flag: [3:0] hash type (0:none, 1:sha256, 2:sha512)
+ * @images: images
+ * @hash: hash or signature for header info
++ *
++ * Other fields are not used by U-Boot
+ */
+ struct header0_info_v2 {
+ uint32_t magic;
+@@ -69,7 +71,9 @@ struct header0_info_v2 {
+ uint16_t size;
+ uint16_t num_images;
+ uint32_t boot_flag;
+- uint8_t reserved1[104];
++ uint8_t reserved1[32];
++ uint32_t boot0_param[10];
++ uint32_t boot1_param[8];
+ struct image_entry images[4];
+ uint8_t reserved2[1064];
+ uint8_t hash[512];
+@@ -509,6 +513,18 @@ static void rkcommon_print_header_v2(con
+
+ printf("Rockchip Boot Image (v2)\n");
+
++ for (i = 0; i < ARRAY_SIZE(hdr->boot0_param); i++) {
++ val = le32_to_cpu(hdr->boot0_param[i]);
++ if (val)
++ printf("Boot0 %d: 0x%x\n", i, val);
++ }
++
++ for (i = 0; i < ARRAY_SIZE(hdr->boot1_param); i++) {
++ val = le32_to_cpu(hdr->boot1_param[i]);
++ if (val)
++ printf("Boot1 %d: 0x%x\n", i, val);
++ }
++
+ for (i = 0; i < le16_to_cpu(hdr->num_images); i++) {
+ printf("Image %u: %u @ 0x%x\n",
+ le32_to_cpu(hdr->images[i].counter),
--- /dev/null
+From a9a5d21d926594a87e8db877d4b9261200c3f8fd Mon Sep 17 00:00:00 2001
+From: Jonas Karlman <jonas@kwiboo.se>
+Date: Wed, 29 Jan 2025 22:36:30 +0000
+Subject: [PATCH 4/7] rockchip: mkimage: Add option to change image offset
+ alignment
+
+The vendor boot_merger tool support a ALIGN parameter that is used to
+define offset alignment of the embedded images.
+
+Vendor use this for RK3576 to change offset alignment from the common
+2 KiB to 4 KiB, presumably it may have something to do with UFS.
+Testing with eMMC has shown that using a 512-byte alignment also work.
+
+Add support for overriding offset alignment in case this is needed for
+e.g. RK3576 in the future.
+
+Signed-off-by: Jonas Karlman <jonas@kwiboo.se>
+---
+
+--- a/tools/rkcommon.c
++++ b/tools/rkcommon.c
+@@ -140,6 +140,7 @@ struct spl_info {
+ const uint32_t spl_size;
+ const bool spl_rc4;
+ const uint32_t header_ver;
++ const uint32_t align;
+ };
+
+ static struct spl_info spl_infos[] = {
+@@ -199,14 +200,19 @@ static struct spl_info *rkcommon_get_spl
+ return NULL;
+ }
+
+-static int rkcommon_get_aligned_size(struct image_tool_params *params,
+- const char *fname)
++static bool rkcommon_is_header_v2(struct image_tool_params *params)
+ {
+- int size;
++ struct spl_info *info = rkcommon_get_spl_info(params->imagename);
+
+- size = imagetool_get_filesize(params, fname);
+- if (size < 0)
+- return -1;
++ return (info->header_ver == RK_HEADER_V2);
++}
++
++static int rkcommon_get_aligned_size(struct image_tool_params *params, int size)
++{
++ struct spl_info *info = rkcommon_get_spl_info(params->imagename);
++
++ if (info->align)
++ return ROUND(size, info->align * RK_BLK_SIZE);
+
+ /*
+ * Pad to a 2KB alignment, as required for init/boot size by the ROM
+@@ -215,6 +221,27 @@ static int rkcommon_get_aligned_size(str
+ return ROUND(size, RK_SIZE_ALIGN);
+ }
+
++static int rkcommon_get_header_size(struct image_tool_params *params)
++{
++ int header_size = rkcommon_is_header_v2(params) ?
++ sizeof(struct header0_info_v2) :
++ sizeof(struct header0_info);
++
++ return rkcommon_get_aligned_size(params, header_size);
++}
++
++static int rkcommon_get_aligned_filesize(struct image_tool_params *params,
++ const char *fname)
++{
++ int size;
++
++ size = imagetool_get_filesize(params, fname);
++ if (size < 0)
++ return -1;
++
++ return rkcommon_get_aligned_size(params, size);
++}
++
+ int rkcommon_check_params(struct image_tool_params *params)
+ {
+ int i, size;
+@@ -237,14 +264,14 @@ int rkcommon_check_params(struct image_t
+ spl_params.boot_file += 1;
+ }
+
+- size = rkcommon_get_aligned_size(params, spl_params.init_file);
++ size = rkcommon_get_aligned_filesize(params, spl_params.init_file);
+ if (size < 0)
+ return EXIT_FAILURE;
+ spl_params.init_size = size;
+
+ /* Boot file is optional, and only for back-to-bootrom functionality. */
+ if (spl_params.boot_file) {
+- size = rkcommon_get_aligned_size(params, spl_params.boot_file);
++ size = rkcommon_get_aligned_filesize(params, spl_params.boot_file);
+ if (size < 0)
+ return EXIT_FAILURE;
+ spl_params.boot_size = size;
+@@ -301,13 +328,6 @@ bool rkcommon_need_rc4_spl(struct image_
+ return info->spl_rc4;
+ }
+
+-static bool rkcommon_is_header_v2(struct image_tool_params *params)
+-{
+- struct spl_info *info = rkcommon_get_spl_info(params->imagename);
+-
+- return (info->header_ver == RK_HEADER_V2);
+-}
+-
+ static void do_sha256_hash(uint8_t *buf, uint32_t size, uint8_t *out)
+ {
+ sha256_context ctx;
+@@ -320,12 +340,13 @@ static void do_sha256_hash(uint8_t *buf,
+ static void rkcommon_set_header0(void *buf, struct image_tool_params *params)
+ {
+ struct header0_info *hdr = buf;
+- uint32_t init_boot_size;
++ uint32_t init_boot_size, init_offset;
+
+- memset(buf, '\0', RK_INIT_OFFSET * RK_BLK_SIZE);
++ init_offset = rkcommon_get_header_size(params) / RK_BLK_SIZE;
++ memset(buf, '\0', init_offset * RK_BLK_SIZE);
+ hdr->magic = cpu_to_le32(RK_MAGIC);
+ hdr->disable_rc4 = cpu_to_le32(!rkcommon_need_rc4_spl(params));
+- hdr->init_offset = cpu_to_le16(RK_INIT_OFFSET);
++ hdr->init_offset = cpu_to_le16(init_offset);
+ hdr->init_size = cpu_to_le16(spl_params.init_size / RK_BLK_SIZE);
+
+ /*
+@@ -353,10 +374,10 @@ static void rkcommon_set_header0_v2(void
+ uint8_t *image_ptr = NULL;
+ int i;
+
+- memset(buf, '\0', RK_INIT_OFFSET * RK_BLK_SIZE);
++ sector_offset = rkcommon_get_header_size(params) / RK_BLK_SIZE;
++ memset(buf, '\0', sector_offset * RK_BLK_SIZE);
+ hdr->magic = cpu_to_le32(RK_MAGIC_V2);
+ hdr->boot_flag = cpu_to_le32(HASH_SHA256);
+- sector_offset = 4;
+ image_size_array[0] = spl_params.init_size;
+ image_size_array[1] = spl_params.boot_size;
+
+@@ -382,11 +403,12 @@ static void rkcommon_set_header0_v2(void
+ void rkcommon_set_header(void *buf, struct stat *sbuf, int ifd,
+ struct image_tool_params *params)
+ {
+- struct header1_info *hdr = buf + RK_SPL_HDR_START;
+-
+ if (rkcommon_is_header_v2(params)) {
+ rkcommon_set_header0_v2(buf, params);
+ } else {
++ int header_size = rkcommon_get_header_size(params);
++ struct header1_info *hdr = buf + header_size;
++
+ rkcommon_set_header0(buf, params);
+
+ /* Set up the SPL name (i.e. copy spl_hdr over) */
+@@ -394,12 +416,12 @@ void rkcommon_set_header(void *buf, str
+ memcpy(&hdr->magic, rkcommon_get_spl_hdr(params), RK_SPL_HDR_SIZE);
+
+ if (rkcommon_need_rc4_spl(params))
+- rkcommon_rc4_encode_spl(buf, RK_SPL_HDR_START,
++ rkcommon_rc4_encode_spl(buf, header_size,
+ spl_params.init_size);
+
+ if (spl_params.boot_file) {
+ if (rkcommon_need_rc4_spl(params))
+- rkcommon_rc4_encode_spl(buf + RK_SPL_HDR_START,
++ rkcommon_rc4_encode_spl(buf + header_size,
+ spl_params.init_size,
+ spl_params.boot_size);
+ }
+@@ -624,7 +646,7 @@ int rkcommon_vrec_header(struct image_to
+ * 4 bytes of these images can safely be overwritten using the
+ * boot magic.
+ */
+- tparams->header_size = RK_SPL_HDR_START;
++ tparams->header_size = rkcommon_get_header_size(params);
+
+ /* Allocate, clear and install the header */
+ tparams->hdr = malloc(tparams->header_size);
+@@ -642,7 +664,8 @@ int rkcommon_vrec_header(struct image_to
+ params->orig_file_size = tparams->header_size +
+ spl_params.init_size + spl_params.boot_size;
+
+- params->file_size = ROUND(params->orig_file_size, RK_SIZE_ALIGN);
++ params->file_size = rkcommon_get_aligned_size(params,
++ params->orig_file_size);
+
+ /* Ignoring pad len, since we are using our own copy_image() */
+ return 0;
+--- a/tools/rkcommon.h
++++ b/tools/rkcommon.h
+@@ -10,9 +10,7 @@
+ enum {
+ RK_BLK_SIZE = 512,
+ RK_SIZE_ALIGN = 2048,
+- RK_INIT_OFFSET = 4,
+ RK_MAX_BOOT_SIZE = 512 << 10,
+- RK_SPL_HDR_START = RK_INIT_OFFSET * RK_BLK_SIZE,
+ RK_SPL_HDR_SIZE = 4,
+ };
+
--- /dev/null
+From 31255e35146c279696c51cde45facd1ea10eed28 Mon Sep 17 00:00:00 2001
+From: Jonas Karlman <jonas@kwiboo.se>
+Date: Wed, 29 Jan 2025 22:36:31 +0000
+Subject: [PATCH 5/7] rockchip: mkimage: Add support for up to 4 input files
+
+The v2 image format can support up to 4 embedded images that can be
+loaded by the BootROM using the back-to-bootrom method.
+
+Currently two input files can be passed in using the datafile parameter,
+separated by a colon (":").
+
+Extend the datafile parameter parsing to support up to 4 input files
+separated by a colon (":") for use with the v2 image format.
+
+Signed-off-by: Jonas Karlman <jonas@kwiboo.se>
+---
+
+--- a/tools/rkcommon.c
++++ b/tools/rkcommon.c
+@@ -166,17 +166,15 @@ static struct spl_info spl_infos[] = {
+ /**
+ * struct spl_params - spl params parsed in check_params()
+ *
+- * @init_file: Init data file path
+- * @init_size: Aligned size of init data in bytes
+- * @boot_file: Boot data file path
+- * @boot_size: Aligned size of boot data in bytes
++ * @file: image file path
++ * @size: aligned size of image in bytes
+ */
+
+ struct spl_params {
+- char *init_file;
+- uint32_t init_size;
+- char *boot_file;
+- uint32_t boot_size;
++ struct {
++ char *file;
++ uint32_t size;
++ } images[4];
+ };
+
+ static struct spl_params spl_params = { 0 };
+@@ -256,31 +254,32 @@ int rkcommon_check_params(struct image_t
+ if (!rkcommon_get_spl_info(params->imagename))
+ goto err_spl_info;
+
+- spl_params.init_file = params->datafile;
++ spl_params.images[0].file = params->datafile;
++ for (i = 1; i < ARRAY_SIZE(spl_params.images); i++) {
++ spl_params.images[i].file =
++ strchr(spl_params.images[i - 1].file, ':');
++ if (!spl_params.images[i].file)
++ break;
+
+- spl_params.boot_file = strchr(spl_params.init_file, ':');
+- if (spl_params.boot_file) {
+- *spl_params.boot_file = '\0';
+- spl_params.boot_file += 1;
++ *spl_params.images[i].file = '\0';
++ spl_params.images[i].file += 1;
+ }
+
+- size = rkcommon_get_aligned_filesize(params, spl_params.init_file);
+- if (size < 0)
+- return EXIT_FAILURE;
+- spl_params.init_size = size;
++ for (i = 0; i < ARRAY_SIZE(spl_params.images); i++) {
++ if (!spl_params.images[i].file)
++ break;
+
+- /* Boot file is optional, and only for back-to-bootrom functionality. */
+- if (spl_params.boot_file) {
+- size = rkcommon_get_aligned_filesize(params, spl_params.boot_file);
++ size = rkcommon_get_aligned_filesize(params,
++ spl_params.images[i].file);
+ if (size < 0)
+ return EXIT_FAILURE;
+- spl_params.boot_size = size;
++ spl_params.images[i].size = size;
+ }
+
+- if (spl_params.init_size > rkcommon_get_spl_size(params)) {
++ if (spl_params.images[0].size > rkcommon_get_spl_size(params)) {
+ fprintf(stderr,
+ "Error: SPL image is too large (size %#x than %#x)\n",
+- spl_params.init_size, rkcommon_get_spl_size(params));
++ spl_params.images[0].size, rkcommon_get_spl_size(params));
+ return EXIT_FAILURE;
+ }
+
+@@ -347,7 +346,7 @@ static void rkcommon_set_header0(void *b
+ hdr->magic = cpu_to_le32(RK_MAGIC);
+ hdr->disable_rc4 = cpu_to_le32(!rkcommon_need_rc4_spl(params));
+ hdr->init_offset = cpu_to_le16(init_offset);
+- hdr->init_size = cpu_to_le16(spl_params.init_size / RK_BLK_SIZE);
++ hdr->init_size = cpu_to_le16(spl_params.images[0].size / RK_BLK_SIZE);
+
+ /*
+ * init_boot_size needs to be set, as it is read by the BootROM
+@@ -357,10 +356,11 @@ static void rkcommon_set_header0(void *b
+ * see https://lists.denx.de/pipermail/u-boot/2017-May/293267.html
+ * for a more detailed explanation by Andy Yan
+ */
+- if (spl_params.boot_file)
+- init_boot_size = spl_params.init_size + spl_params.boot_size;
++ if (spl_params.images[1].file)
++ init_boot_size = spl_params.images[0].size +
++ spl_params.images[1].size;
+ else
+- init_boot_size = spl_params.init_size + RK_MAX_BOOT_SIZE;
++ init_boot_size = spl_params.images[0].size + RK_MAX_BOOT_SIZE;
+ hdr->init_boot_size = cpu_to_le16(init_boot_size / RK_BLK_SIZE);
+
+ rc4_encode(buf, RK_BLK_SIZE, rc4_key);
+@@ -370,7 +370,6 @@ static void rkcommon_set_header0_v2(void
+ {
+ struct header0_info_v2 *hdr = buf;
+ uint32_t sector_offset, image_sector_count;
+- uint32_t image_size_array[2];
+ uint8_t *image_ptr = NULL;
+ int i;
+
+@@ -378,19 +377,17 @@ static void rkcommon_set_header0_v2(void
+ memset(buf, '\0', sector_offset * RK_BLK_SIZE);
+ hdr->magic = cpu_to_le32(RK_MAGIC_V2);
+ hdr->boot_flag = cpu_to_le32(HASH_SHA256);
+- image_size_array[0] = spl_params.init_size;
+- image_size_array[1] = spl_params.boot_size;
+
+- for (i = 0; i < 2; i++) {
+- if (!image_size_array[i])
++ for (i = 0; i < ARRAY_SIZE(spl_params.images); i++) {
++ if (!spl_params.images[i].size)
+ break;
+- image_sector_count = image_size_array[i] / RK_BLK_SIZE;
++ image_sector_count = spl_params.images[i].size / RK_BLK_SIZE;
+ hdr->images[i].offset = cpu_to_le16(sector_offset);
+ hdr->images[i].size = cpu_to_le16(image_sector_count);
+ hdr->images[i].address = 0xFFFFFFFF;
+ hdr->images[i].counter = cpu_to_le32(i + 1);
+ image_ptr = buf + sector_offset * RK_BLK_SIZE;
+- do_sha256_hash(image_ptr, image_size_array[i],
++ do_sha256_hash(image_ptr, spl_params.images[i].size,
+ hdr->images[i].hash);
+ sector_offset = sector_offset + image_sector_count;
+ }
+@@ -417,13 +414,13 @@ void rkcommon_set_header(void *buf, str
+
+ if (rkcommon_need_rc4_spl(params))
+ rkcommon_rc4_encode_spl(buf, header_size,
+- spl_params.init_size);
++ spl_params.images[0].size);
+
+- if (spl_params.boot_file) {
++ if (spl_params.images[1].file) {
+ if (rkcommon_need_rc4_spl(params))
+ rkcommon_rc4_encode_spl(buf + header_size,
+- spl_params.init_size,
+- spl_params.boot_size);
++ spl_params.images[0].size,
++ spl_params.images[1].size);
+ }
+ }
+ }
+@@ -661,8 +658,9 @@ int rkcommon_vrec_header(struct image_to
+ * We need to store the original file-size (i.e. before padding), as
+ * imagetool does not set this during its adjustment of file_size.
+ */
+- params->orig_file_size = tparams->header_size +
+- spl_params.init_size + spl_params.boot_size;
++ params->orig_file_size = tparams->header_size;
++ for (int i = 0; i < ARRAY_SIZE(spl_params.images); i++)
++ params->orig_file_size += spl_params.images[i].size;
+
+ params->file_size = rkcommon_get_aligned_size(params,
+ params->orig_file_size);
+@@ -749,16 +747,13 @@ err_close:
+
+ int rockchip_copy_image(int ifd, struct image_tool_params *params)
+ {
+- int ret;
+-
+- ret = copy_file(params, ifd, spl_params.init_file,
+- spl_params.init_size);
+- if (ret)
+- return ret;
++ int i, ret;
+
+- if (spl_params.boot_file) {
+- ret = copy_file(params, ifd, spl_params.boot_file,
+- spl_params.boot_size);
++ for (i = 0; i < ARRAY_SIZE(spl_params.images); i++) {
++ if (!spl_params.images[i].size)
++ break;
++ ret = copy_file(params, ifd, spl_params.images[i].file,
++ spl_params.images[i].size);
+ if (ret)
+ return ret;
+ }
--- /dev/null
+From 3e252cae2b5b95c483104318120338d5a51a6b4d Mon Sep 17 00:00:00 2001
+From: Jonas Karlman <jonas@kwiboo.se>
+Date: Wed, 29 Jan 2025 22:36:32 +0000
+Subject: [PATCH 6/7] rockchip: mkimage: Add option for image load address and
+ flag
+
+The v2 image format supports defining a load address and flag for each
+embedded image.
+
+Add initial support for writing the image load address and flag to the
+v2 image format header.
+
+This may later be used for RK3576 to embed a minimal initial image that
+if required to fix booting from SD-card due to a BootROM issue.
+
+Signed-off-by: Jonas Karlman <jonas@kwiboo.se>
+---
+
+--- a/tools/rkcommon.c
++++ b/tools/rkcommon.c
+@@ -168,12 +168,16 @@ static struct spl_info spl_infos[] = {
+ *
+ * @file: image file path
+ * @size: aligned size of image in bytes
++ * @address: image load address
++ * @flag: no use
+ */
+
+ struct spl_params {
+ struct {
+ char *file;
+ uint32_t size;
++ uint32_t address;
++ uint32_t flag;
+ } images[4];
+ };
+
+@@ -384,7 +388,8 @@ static void rkcommon_set_header0_v2(void
+ image_sector_count = spl_params.images[i].size / RK_BLK_SIZE;
+ hdr->images[i].offset = cpu_to_le16(sector_offset);
+ hdr->images[i].size = cpu_to_le16(image_sector_count);
+- hdr->images[i].address = 0xFFFFFFFF;
++ hdr->images[i].address = spl_params.images[i].address ?: 0xFFFFFFFF;
++ hdr->images[i].flag = spl_params.images[i].flag;
+ hdr->images[i].counter = cpu_to_le32(i + 1);
+ image_ptr = buf + sector_offset * RK_BLK_SIZE;
+ do_sha256_hash(image_ptr, spl_params.images[i].size,
--- /dev/null
+From f6f1066339a58444d515f1ed72a172610995d090 Mon Sep 17 00:00:00 2001
+From: Jonas Karlman <jonas@kwiboo.se>
+Date: Tue, 28 Jan 2025 01:30:12 +0000
+Subject: [PATCH 7/7] WIP: rockchip: mkimage: Add rk3576 align and sd-card
+ workaround
+
+The BootROM on RK3576 has an issue loading boot images from an SD-card.
+This issue can be worked around by injecting an initial boot image
+before TPL that:
+
+ writel(0x3ffff800, 0x3ff803b0)
+
+Prepend an image containing binary code that does this and return to
+BootROM to load next image, TPL.
+
+TODO: embed the binary code into rkcommon.c
+
+Signed-off-by: Jonas Karlman <jonas@kwiboo.se>
+---
+
+--- /dev/null
++++ b/rk3576-boost.c
+@@ -0,0 +1,49 @@
++// SPDX-License-Identifier: GPL-2.0-or-later
++// Copyright Contributors to the U-Boot project.
++
++/*
++ * Generate assembly code for the c code in this file:
++ * aarch64-linux-gnu-gcc -nostdlib -ffreestanding -Os -S -o rk3576-boost.S rk3576-boost.c
++ *
++ * Compile assembly code and extract the AArch64 binary code:
++ * aarch64-linux-gnu-as -o rk3576-boost.o rk3576-boost.S
++ * aarch64-linux-gnu-objcopy -O binary -j .text rk3576-boost.o rk3576-boost.bin
++ */
++
++#include <stdint.h>
++
++#define SYS_SRAM_BASE 0x3ff80000
++#define OFFSET 0x03b0
++
++int _start(void)
++{
++ uint32_t *sram = (void*)(SYS_SRAM_BASE + OFFSET);
++
++ /* set unknown value in sram to fix boot from sdmmc */
++ *(sram) = 0x3ffff800;
++
++ return 0;
++}
++
++/*
++ .arch armv8-a
++ .file "rk3576-boost.c"
++ .text
++ .align 2
++ .global _start
++ .type _start, %function
++_start:
++.LFB0:
++ .cfi_startproc
++ mov x0, 944
++ mov w1, 1073739776
++ movk x0, 0x3ff8, lsl 16
++ str w1, [x0]
++ mov w0, 0
++ ret
++ .cfi_endproc
++.LFE0:
++ .size _start, .-_start
++ .ident "GCC: (Ubuntu 13.3.0-6ubuntu2~24.04) 13.3.0"
++ .section .note.GNU-stack,"",@progbits
++*/
+--- a/tools/rkcommon.c
++++ b/tools/rkcommon.c
+@@ -159,7 +159,7 @@ static struct spl_info spl_infos[] = {
+ { "rv1126", "110B", 0x10000 - 0x1000, false, RK_HEADER_V1 },
+ { "rk3528", "RK35", 0x10000 - 0x1000, false, RK_HEADER_V2 },
+ { "rk3568", "RK35", 0x10000 - 0x1000, false, RK_HEADER_V2 },
+- { "rk3576", "RK35", 0x80000 - 0x1000, false, RK_HEADER_V2 },
++ { "rk3576", "RK35", 0x80000 - 0x1000, false, RK_HEADER_V2, 8 },
+ { "rk3588", "RK35", 0x100000 - 0x1000, false, RK_HEADER_V2 },
+ };
+
+@@ -287,6 +287,22 @@ int rkcommon_check_params(struct image_t
+ return EXIT_FAILURE;
+ }
+
++ if (!strcmp(params->imagename, "rk3576")) {
++ size = rkcommon_get_aligned_filesize(params, "rk3576-boost.bin");
++ if (size < 0)
++ return EXIT_SUCCESS;
++
++ for (i = ARRAY_SIZE(spl_params.images) - 1; i > 0; i--) {
++ spl_params.images[i] = spl_params.images[i - 1];
++ }
++
++ spl_params.images[0].file = "rk3576-boost.bin";
++ spl_params.images[0].size = size;
++
++ spl_params.images[0].address = 0x3ffc0000;
++ spl_params.images[1].address = 0x3ff81000;
++ }
++
+ return EXIT_SUCCESS;
+
+ err_spl_info: