From 125b9ff85add2dbe1098dcb459a5d9b7373358fb Mon Sep 17 00:00:00 2001 From: Dmitry Ilyin <6576495+widgetii@users.noreply.github.com> Date: Fri, 16 Oct 2026 22:55:07 +0300 Subject: [PATCH 1/3] nor: boot the split layout, FIT and squashfs in one firmware partition With CONFIG_OPENIPC_NOR_FIT_SPLIT (on for gk7205v200 and gk7205v300 here), a FIT at the NOR kernel offset means the split layout: firmware.bin, the hashed FIT and then the squashfs at the next 64 KiB boundary, written whole at 0x50000. firmware_scan() passes Linux "256k(u-boot),64k(env),-(firmware)" and the kernel divides that partition into kernel, rootfs and rootfs_data (mtdsplit). The read is the FIT's own size, and nothing is clamped to a slot. A uImage keeps today's fixed slots, unchanged. - A saved bootargs spells out root=/dev/mtdblock3, which under the split numbering (u-boot, env, firmware, kernel, rootfs, rootfs_data) is the FIT. The token is rewritten in RAM: mtdblock4 for the split layout, mtdblock3 for fixed slots. - openipc_nor_fit=1 joins the command line on either layout. sysupgrade reads it to know this bootloader boots the split layout before writing a firmware.bin for it. - bootm_size is set when a saved env lacks it, so the relocated DTB stays in the kernel's lowmem. - CONFIG_FIT and CONFIG_SHA1 for those boards; their NOR images grow from 205 KB to 216-217 KB (256 KB boot partition). - check_kernel/check_squashfs are __maybe_unused: NAND builds no longer call them. hi3516cv610 also boots a FIT from NOR, but its kernel has no mtdsplit and keeps fixed slots, so the option is off there. --- common/Kconfig | 10 +++++ common/openipc.c | 87 ++++++++++++++++++++++++++++++++++++++- openipc/xm72050200_config | 4 ++ openipc/xm72050300_config | 4 ++ 4 files changed, 103 insertions(+), 2 deletions(-) diff --git a/common/Kconfig b/common/Kconfig index 6e69bb5b0489..3bbaaf8080c8 100644 --- a/common/Kconfig +++ b/common/Kconfig @@ -1060,3 +1060,13 @@ config PRODUCT_SOCMODEL Optional board/binning model exposed as 'socmodel=' in the default environment (e.g. hi3516cv610 DDR binnings). +config OPENIPC_NOR_FIT_SPLIT + bool "OpenIPC: boot the split NOR layout (FIT + squashfs in one partition)" + depends on FIT + help + A FIT found at the NOR kernel offset is taken for the split layout: + the FIT and the squashfs back to back in one "firmware" partition, + which a kernel with mtdsplit divides at boot. Linux is passed that + one partition. Only for boards whose kernel carries mtdsplit; a FIT + kernel without it (hi3516cv610) keeps its fixed slots. + diff --git a/common/openipc.c b/common/openipc.c index 49cf81270dad..49a8df1b47a0 100644 --- a/common/openipc.c +++ b/common/openipc.c @@ -40,7 +40,7 @@ #define SQSH_MAGIC_BE 0x73717368 #define SQSH_MAGIC_LE 0x68737173 -static int check_squashfs(void *buf) { +static int __maybe_unused check_squashfs(void *buf) { uint32_t magic = get_unaligned_be32(buf); if (magic == SQSH_MAGIC_BE || magic == SQSH_MAGIC_LE) return 1; @@ -76,7 +76,7 @@ static int ubi_marker_present(void) { } #endif -static int check_kernel(void *buf) { +static int __maybe_unused check_kernel(void *buf) { struct image_header *hdr = (struct image_header *)buf; if (image_check_magic(hdr)) { return 1; @@ -495,6 +495,43 @@ int firmware_recovery(void) { } #endif +#if !defined(CONFIG_FMC_SPI_NAND) && defined(CONFIG_OPENIPC_NOR_FIT_SPLIT) +/* + * The kernel learns the NOR layout from bootargs. A saved environment carries + * its own bootargs with "root=/dev/mtdblock3" spelled out, which is right for + * fixed slots but names the FIT under the split layout, where the partitions + * are u-boot, env, firmware, then the kernel's split of firmware into kernel + * (3), rootfs (4) and rootfs_data (5). Rewrite that one token for the layout + * found, in RAM only, and add openipc_nor_fit=1: the word sysupgrade reads from + * /proc/cmdline to know this bootloader boots the split layout. + */ +static void nor_bootargs_adjust(int fit) +{ + const char *cur = env_get("bootargs"); + const char *want = fit ? "root=/dev/mtdblock4" : "root=/dev/mtdblock3"; + const char *other = fit ? "root=/dev/mtdblock3" : "root=/dev/mtdblock4"; + char buf[1024]; + const char *p; + + if (!cur || strlen(cur) + 32 >= sizeof(buf)) + return; + p = strstr(cur, other); + if (p) { + size_t head = p - cur; + + memcpy(buf, cur, head); + strcpy(buf + head, want); + strcat(buf, p + strlen(other)); + } else { + strcpy(buf, cur); + } + if (!strstr(buf, "openipc_nor_fit=1")) + strcat(buf, " openipc_nor_fit=1"); + if (strcmp(buf, cur)) + env_set("bootargs", buf); +} +#endif + int firmware_scan(void) { #ifdef CONFIG_FMC_SPI_NAND u8 buf[64]; @@ -568,7 +605,53 @@ int firmware_scan(void) { rootfs_size = 0xa00000; #endif + /* + * A FIT at the kernel offset is the split layout: FIT and squashfs back to + * back in one "firmware" partition that the kernel divides at boot + * (mtdsplit). Linux gets that one partition, the read is the FIT's own + * size, and nothing is clamped to a slot. + */ +#ifdef CONFIG_OPENIPC_NOR_FIT_SPLIT + if (kernel_off && rootfs_off) { + u8 hdr[64]; + + if (!spi_flash_read(flash, kernel_off, sizeof(hdr), hdr) && + fdt_magic(hdr) == FDT_MAGIC && fdt_check_header(hdr) == 0) { + char str[32], mtds[128], mtdparts[160]; + const char *mtdids = env_get("mtdids"); + ulong fit_size = ALIGN(fdt_totalsize(hdr), 0x10000); + + spi_flash_free(flash); + sprintf(str, "0x%08lx", kernel_off); + env_set("kernaddr", str); + sprintf(str, "0x%lx", fit_size); + env_set("kernsize", str); + sprintf(str, "0x%08lx", rootfs_off); + env_set("rootaddr", str); +#ifdef SFC + if (!mtdids || !*mtdids) + mtdids = SFC; +#endif + sprintf(mtds, "%dk(u-boot),%dk(env),-(firmware)", + CONFIG_ENV_OFFSET / 1024, CONFIG_ENV_SIZE / 1024); + env_set("mtds", mtds); + sprintf(mtdparts, "%s:%s", mtdids ? mtdids : "", mtds); + env_set("mtdparts", mtdparts); + /* The DTB stays in the kernel's lowmem (mem=${osmem}); a saved + * environment from a uImage-only bootloader has no bootm_size. */ + if (!env_get("bootm_size")) + env_set("bootm_size", "0x2000000"); + nor_bootargs_adjust(1); + env_set("bootfail", NULL); + return 0; + } + } +#endif + spi_flash_free(flash); +#ifdef CONFIG_OPENIPC_NOR_FIT_SPLIT + nor_bootargs_adjust(0); +#endif if (kernel_off) { char str[32]; diff --git a/openipc/xm72050200_config b/openipc/xm72050200_config index ae226538b520..23b060aaf0cb 100644 --- a/openipc/xm72050200_config +++ b/openipc/xm72050200_config @@ -2,3 +2,7 @@ CONFIG_SYS_LOAD_ADDR=0x42000000 CONFIG_MMC=y CONFIG_CMD_MMC=y CONFIG_MMC_SDHCI=y +# Split NOR layout: a hashed FIT kernel (board nor-fit.its in OpenIPC/firmware) +CONFIG_FIT=y +CONFIG_SHA1=y +CONFIG_OPENIPC_NOR_FIT_SPLIT=y diff --git a/openipc/xm72050300_config b/openipc/xm72050300_config index ec7c022070b8..28d78126a0f4 100644 --- a/openipc/xm72050300_config +++ b/openipc/xm72050300_config @@ -8,3 +8,7 @@ CONFIG_SYS_LOAD_ADDR=0x42000000 CONFIG_MMC=y CONFIG_CMD_MMC=y CONFIG_MMC_SDHCI=y +# Split NOR layout: a hashed FIT kernel (board nor-fit.its in OpenIPC/firmware) +CONFIG_FIT=y +CONFIG_SHA1=y +CONFIG_OPENIPC_NOR_FIT_SPLIT=y From e4abba2858f78efbe8866d0afebab5d40daad170 Mon Sep 17 00:00:00 2001 From: Dmitry Ilyin <6576495+widgetii@users.noreply.github.com> Date: Fri, 16 Oct 2026 23:28:00 +0300 Subject: [PATCH 2/3] nor: check the split layout's FIT hashes misc_init_r() set verify=n after openipc_helper() had run, and cameras upgraded from the old bootloader carry verify=n in their saved env as well. So bootm loaded the FIT without checking its crc32 and sha1. For gk7205v200 and gk7205v300 the board now sets it before openipc_helper(), and firmware_scan() drops it (RAM only) when it boots the split layout's FIT. A uImage keeps verify=n, as before. Verified on a GK7205V200 with its env saved by u-boot-gk7205v200: 'Verifying Hash Integrity ... crc32+ sha1+ OK' for the kernel and the DTB, then boot to login. --- board/xmedia/xm72050200/board.c | 4 +++- board/xmedia/xm72050300/board.c | 4 +++- common/openipc.c | 4 ++++ 3 files changed, 10 insertions(+), 2 deletions(-) diff --git a/board/xmedia/xm72050200/board.c b/board/xmedia/xm72050200/board.c index 1788b96b37b1..232abb248a3f 100755 --- a/board/xmedia/xm72050200/board.c +++ b/board/xmedia/xm72050200/board.c @@ -279,11 +279,13 @@ void clear_update_flag(int update_flag) int misc_init_r(void) { + /* Before openipc_helper(): a FIT on the split NOR layout carries + * hashes, and firmware_scan() drops this for it. */ + env_set("verify", "n"); openipc_helper(); #ifdef CONFIG_RANDOM_ETHADDR random_init_r(); #endif - env_set("verify", "n"); #ifdef CFG_MMU_HANDLEOK dcache_stop(); diff --git a/board/xmedia/xm72050300/board.c b/board/xmedia/xm72050300/board.c index bc4fe6f4bde6..1d38813606a1 100755 --- a/board/xmedia/xm72050300/board.c +++ b/board/xmedia/xm72050300/board.c @@ -278,11 +278,13 @@ void clear_update_flag(int update_flag) int misc_init_r(void) { + /* Before openipc_helper(): a FIT on the split NOR layout carries + * hashes, and firmware_scan() drops this for it. */ + env_set("verify", "n"); openipc_helper(); #ifdef CONFIG_RANDOM_ETHADDR random_init_r(); #endif - env_set("verify", "n"); #ifdef CFG_MMU_HANDLEOK dcache_stop(); diff --git a/common/openipc.c b/common/openipc.c index 49a8df1b47a0..12c8dce4a8d8 100644 --- a/common/openipc.c +++ b/common/openipc.c @@ -641,6 +641,10 @@ int firmware_scan(void) { * environment from a uImage-only bootloader has no bootm_size. */ if (!env_get("bootm_size")) env_set("bootm_size", "0x2000000"); + /* The FIT carries crc32 and sha1 for the kernel and the DTB; a + * verify=n saved by an older bootloader must not skip them. RAM + * only, like the rest of the scan. */ + env_set("verify", NULL); nor_bootargs_adjust(1); env_set("bootfail", NULL); return 0; From fc1b0eb94121cc808b50fda05e753e543f61ca0f Mon Sep 17 00:00:00 2001 From: Dmitry Ilyin <6576495+widgetii@users.noreply.github.com> Date: Sat, 17 Oct 2026 14:24:53 +0300 Subject: [PATCH 3/3] nor: keep uk/ur on fixed slots when the split layout boots The FIT path set kernsize to the FIT and rootaddr to the squashfs behind it. uk, ur and fwupd install a uImage and a squashfs, so on a split camera they wrote fixed-slot images at split offsets: ur at 0x230000 with the default rootsize, uk over the FIT only. The next scan found the rootfs at 0x230000, sized the kernel slot from it, and the mtdparts it built did not match where the rootfs is. kernsize, rootaddr and rootsize now name the fixed slots: kernel 2 MiB and rootfs 5 MiB, or 3 and 10 MiB on 16 MiB parts when the FIT needs 3; urwrite still moves to the ultimate slots for a big rootfs, as before. uk erases the whole kernel slot, so no head of the split squashfs is left for the scan to find. loadkernel reads the slot, which holds the whole FIT. Verified on GK7205V200 (U-Boot loaded into RAM with defib): the split layout still boots with the FIT hashes checked; ur then uk with rootfs.squashfs and uImage from TFTP turn the split camera into fixed slots, found at 0x50000 and 0x250000, and it boots to login. --- common/openipc.c | 28 +++++++++++++++++++++++++--- 1 file changed, 25 insertions(+), 3 deletions(-) diff --git a/common/openipc.c b/common/openipc.c index 12c8dce4a8d8..aa75fe13e754 100644 --- a/common/openipc.c +++ b/common/openipc.c @@ -620,14 +620,36 @@ int firmware_scan(void) { char str[32], mtds[128], mtdparts[160]; const char *mtdids = env_get("mtdids"); ulong fit_size = ALIGN(fdt_totalsize(hdr), 0x10000); - + ulong kern_slot, root_slot; + + /* + * kernsize is what loadkernel reads, and also what uk erases; rootaddr + * and rootsize are where ur writes. Those commands install a uImage + * and a squashfs, i.e. fixed slots, so they get the fixed slots: the + * kernel slot whole (the split squashfs starts inside it, and a head + * left there would be found by the next scan before the new rootfs), + * the rootfs slot after it. The FIT is read whole either way. + */ + if (fit_size <= 0x200000) { + kern_slot = 0x200000; + root_slot = 0x500000; + } else if (fit_size <= 0x300000 && flash->size > 0x800000) { + kern_slot = 0x300000; + root_slot = 0xa00000; + } else { + /* No fixed-slot layout holds this kernel. */ + kern_slot = fit_size; + root_slot = flash->size - kernel_off - fit_size; + } spi_flash_free(flash); sprintf(str, "0x%08lx", kernel_off); env_set("kernaddr", str); - sprintf(str, "0x%lx", fit_size); + sprintf(str, "0x%lx", kern_slot); env_set("kernsize", str); - sprintf(str, "0x%08lx", rootfs_off); + sprintf(str, "0x%08lx", kernel_off + kern_slot); env_set("rootaddr", str); + sprintf(str, "0x%lx", root_slot); + env_set("rootsize", str); #ifdef SFC if (!mtdids || !*mtdids) mtdids = SFC;