From 3ba510847baeaced14b61e5cc2ee41134af850bc Mon Sep 17 00:00:00 2001 From: Tobias Waldekranz Date: Thu, 8 Oct 2026 09:12:34 +0000 Subject: [PATCH 01/19] buildroot: Bump to get new genimage with dm-verity support Also, add a local patch for the growfs bit, which has not been upstreamed yet. --- buildroot | 2 +- ...rotective-first-option-for-hybrid-M.patch} | 33 +++---- ...upport-GROWFS-flag-on-GPT-partitions.patch | 94 +++++++++++++++++++ 3 files changed, 112 insertions(+), 17 deletions(-) rename patches/genimage/{0001-hdimage-put-GPT-protective-MBR-entry-first-in-hybrid.patch => 20/0001-hdimage-add-gpt-protective-first-option-for-hybrid-M.patch} (90%) create mode 100644 patches/genimage/20/0002-image-hd-Support-GROWFS-flag-on-GPT-partitions.patch diff --git a/buildroot b/buildroot index 2f8fda57a..93c7c33ba 160000 --- a/buildroot +++ b/buildroot @@ -1 +1 @@ -Subproject commit 2f8fda57a6a05043cc0bc3b749514d0e46dc63e5 +Subproject commit 93c7c33ba687337e32ef973bfa38701cd77312ce diff --git a/patches/genimage/0001-hdimage-put-GPT-protective-MBR-entry-first-in-hybrid.patch b/patches/genimage/20/0001-hdimage-add-gpt-protective-first-option-for-hybrid-M.patch similarity index 90% rename from patches/genimage/0001-hdimage-put-GPT-protective-MBR-entry-first-in-hybrid.patch rename to patches/genimage/20/0001-hdimage-add-gpt-protective-first-option-for-hybrid-M.patch index 2714e611f..3d24ca38d 100644 --- a/patches/genimage/0001-hdimage-put-GPT-protective-MBR-entry-first-in-hybrid.patch +++ b/patches/genimage/20/0001-hdimage-add-gpt-protective-first-option-for-hybrid-M.patch @@ -1,7 +1,7 @@ -From 7f50533b9e57f5e7d1e4dd47c3f43e2d6a9c8b1a Mon Sep 17 00:00:00 2001 +From 557bb6ebede417249e3df2a8fb7340a5cae0684e Mon Sep 17 00:00:00 2001 From: Joachim Wiberg Date: Sun, 8 Mar 2026 04:33:56 +0100 -Subject: [PATCH] hdimage: add gpt-protective-first option for hybrid MBR +Subject: [PATCH 1/2] hdimage: add gpt-protective-first option for hybrid MBR Organization: Wires In a hybrid MBR the conventional layout (used by gdisk and others) is: @@ -31,16 +31,17 @@ The default (false) preserves the existing genimage behaviour so that platforms like Raspberry Pi continue to work without any changes. Signed-off-by: Joachim Wiberg +Signed-off-by: Tobias Waldekranz --- README.rst | 10 ++++++++++ - image-hd.c | 27 ++++++++++++++++++++++----- - 2 files changed, 32 insertions(+), 5 deletions(-) + image-hd.c | 27 +++++++++++++++++++++++++-- + 2 files changed, 35 insertions(+), 2 deletions(-) diff --git a/README.rst b/README.rst -index 9a95f75..f45fb93 100644 +index 770bca8..a4c1a23 100644 --- a/README.rst +++ b/README.rst -@@ -480,6 +480,16 @@ Options: +@@ -481,6 +481,16 @@ Options: placed at 512 bytes (sector 1). Defaults to 1024 bytes (sector 2). :gpt-no-backup: Boolean. If true, then the backup partition table at the end of the image is not written. @@ -58,7 +59,7 @@ index 9a95f75..f45fb93 100644 random value. :fill: If this is set to true, then the image file will be filled diff --git a/image-hd.c b/image-hd.c -index 274c6ac..610d9d1 100644 +index 97e75a3..3ecdb98 100644 --- a/image-hd.c +++ b/image-hd.c @@ -46,6 +46,7 @@ struct hdimage { @@ -72,7 +73,7 @@ index 274c6ac..610d9d1 100644 @@ -152,6 +153,26 @@ static int hdimage_insert_mbr(struct image *image, struct list_head *partitions) memset(&mbr, 0, sizeof(mbr)); memcpy(&mbr.disk_signature, &hd->disksig, sizeof(hd->disksig)); - + + /* + * For hybrid MBR, optionally write the GPT protective entry (0xEE) at + * slot 0 first when gpt-protective-first = true. This matches the @@ -95,32 +96,32 @@ index 274c6ac..610d9d1 100644 + list_for_each_entry(part, partitions, list) { struct mbr_partition_entry *entry; - + @@ -176,13 +197,13 @@ static int hdimage_insert_mbr(struct image *image, struct list_head *partitions) i++; } - + - if (hd->table_type == TYPE_HYBRID) { + /* For hybrid MBR without gpt-protective-first, append 0xEE after data partitions. */ + if (hd->table_type == TYPE_HYBRID && !hd->gpt_protective_first) { struct mbr_partition_entry *entry; - + entry = &mbr.part_entry[i]; - + entry->boot = 0x00; - entry->partition_type = 0xee; entry->relative_sectors = 1; entry->total_sectors = hd->gpt_location / 512 + GPT_SECTORS - 2; -@@ -978,6 +999,7 @@ static int hdimage_setup(struct image *image, cfg_t *cfg) +@@ -979,6 +1000,7 @@ static int hdimage_setup(struct image *image, cfg_t *cfg) table_type = cfg_getstr(cfg, "partition-table-type"); hd->gpt_location = cfg_getint_suffix(cfg, "gpt-location"); hd->gpt_no_backup = cfg_getbool(cfg, "gpt-no-backup"); + hd->gpt_protective_first = cfg_getbool(cfg, "gpt-protective-first"); hd->fill = cfg_getbool(cfg, "fill"); - + if (is_block_device(imageoutfile(image))) { -@@ -1227,6 +1249,7 @@ static cfg_opt_t hdimage_opts[] = { +@@ -1229,6 +1251,7 @@ static cfg_opt_t hdimage_opts[] = { CFG_BOOL("gpt", cfg_false, CFGF_NODEFAULT), CFG_STR("gpt-location", NULL, CFGF_NONE), CFG_BOOL("gpt-no-backup", cfg_false, CFGF_NONE), @@ -128,6 +129,6 @@ index 274c6ac..610d9d1 100644 CFG_BOOL("fill", cfg_false, CFGF_NONE), CFG_END() }; --- +-- 2.43.0 diff --git a/patches/genimage/20/0002-image-hd-Support-GROWFS-flag-on-GPT-partitions.patch b/patches/genimage/20/0002-image-hd-Support-GROWFS-flag-on-GPT-partitions.patch new file mode 100644 index 000000000..32173632d --- /dev/null +++ b/patches/genimage/20/0002-image-hd-Support-GROWFS-flag-on-GPT-partitions.patch @@ -0,0 +1,94 @@ +From 13ebec3f62a9ee69c3211f68a0d71ad6678b3402 Mon Sep 17 00:00:00 2001 +From: Tobias Waldekranz +Date: Tue, 3 Mar 2026 10:02:34 +0000 +Subject: [PATCH 2/2] image-hd: Support GROWFS flag on GPT partitions +Organization: Wires + +This is an indication to the operating system that the contained +filesystem should be expanded to fill the entire partition. For more +information: + +https://uapi-group.org/specifications/specs/discoverable_partitions_specification/#partition-attribute-flags +Signed-off-by: Tobias Waldekranz +--- + README.rst | 2 ++ + genimage.c | 2 ++ + genimage.h | 1 + + image-hd.c | 2 ++ + 4 files changed, 7 insertions(+) + +diff --git a/README.rst b/README.rst +index a4c1a23..39f7566 100644 +--- a/README.rst ++++ b/README.rst +@@ -142,6 +142,7 @@ Partition options: + For hd images this can be used for the last partition. If set + the partition will fill the remaining space of the image. + :bootable: Boolean specifying whether to set the bootable flag. ++:growfs: Boolean specifying whether to set the growfs flag (only with GPT). + :hidden: Boolean specifying whether to set the hidden flag (only with GPT). + :no-automount: Boolean specifying whether to set the no-automount flag (only with GPT). + :read-only: Boolean specifying whether to set the read-only flag (only with GPT). +@@ -509,6 +510,7 @@ genimage configuration GPT FLAG + ====================== ============================== + read-only GPT_PE_FLAG_READ_ONLY (Bit 60) + bootable GPT_PE_FLAG_BOOTABLE (Bit 2) ++growfs GPT_PE_FLAG_GROWFS (Bit 59) + hidden GPT_PE_FLAG_HIDDEN (Bit 62) + no-automount GPT_PE_FLAG_NO_AUTO (Bit 63) + ====================== ============================== +diff --git a/genimage.c b/genimage.c +index 770a8c4..f804843 100644 +--- a/genimage.c ++++ b/genimage.c +@@ -102,6 +102,7 @@ static cfg_opt_t partition_opts[] = { + CFG_INT("partition-type", 0, CFGF_NONE), + CFG_BOOL("bootable", cfg_false, CFGF_NONE), + CFG_BOOL("forced-primary", cfg_false, CFGF_NONE), ++ CFG_BOOL("growfs", cfg_false, CFGF_NONE), + CFG_BOOL("read-only", cfg_false, CFGF_NONE), + CFG_BOOL("hidden", cfg_false, CFGF_NONE), + CFG_BOOL("no-automount", cfg_false, CFGF_NONE), +@@ -405,6 +406,7 @@ static int parse_partitions(struct image *image, cfg_t *imagesec) + part->partition_type = cfg_getint(partsec, "partition-type"); + part->bootable = cfg_getbool(partsec, "bootable"); + part->forced_primary = cfg_getbool(partsec, "forced-primary"); ++ part->growfs = cfg_getbool(partsec, "growfs"); + part->read_only = cfg_getbool(partsec, "read-only"); + part->hidden = cfg_getbool(partsec, "hidden"); + part->no_automount = cfg_getbool(partsec, "no-automount"); +diff --git a/genimage.h b/genimage.h +index 2587bc6..912239e 100644 +--- a/genimage.h ++++ b/genimage.h +@@ -46,6 +46,7 @@ struct partition { + cfg_bool_t bootable; + cfg_bool_t logical; + cfg_bool_t forced_primary; ++ cfg_bool_t growfs; + cfg_bool_t read_only; + cfg_bool_t hidden; + cfg_bool_t no_automount; +diff --git a/image-hd.c b/image-hd.c +index 3ecdb98..4513322 100644 +--- a/image-hd.c ++++ b/image-hd.c +@@ -106,6 +106,7 @@ ct_assert(sizeof(struct gpt_partition_entry) == 128); + #define GPT_REVISION_1_0 0x00010000 + + #define GPT_PE_FLAG_BOOTABLE (1ULL << 2) ++#define GPT_PE_FLAG_GROWFS (1ULL << 59) + #define GPT_PE_FLAG_READ_ONLY (1ULL << 60) + #define GPT_PE_FLAG_HIDDEN (1ULL << 62) + #define GPT_PE_FLAG_NO_AUTO (1ULL << 63) +@@ -522,6 +523,7 @@ static int hdimage_insert_gpt(struct image *image, struct list_head *partitions) + table[i].last_lba = htole64((partition_end(part)) / 512 - 1); + table[i].flags = + (part->bootable ? GPT_PE_FLAG_BOOTABLE : 0) | ++ (part->growfs ? GPT_PE_FLAG_GROWFS : 0) | + (part->read_only ? GPT_PE_FLAG_READ_ONLY : 0) | + (part->hidden ? GPT_PE_FLAG_HIDDEN : 0) | + (part->no_automount ? GPT_PE_FLAG_NO_AUTO : 0); +-- +2.43.0 + From 89fb34db0f3a109048d1aae226054de32551cddf Mon Sep 17 00:00:00 2001 From: Tobias Waldekranz Date: Thu, 26 Feb 2026 20:00:07 +0000 Subject: [PATCH 02/19] qemu: Only allow custom kernel options with QEMU's native loader This is the only mode that supports the `-append` option. It has nothing to do with "not MMC". --- board/common/qemu/Config.in.in | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/board/common/qemu/Config.in.in b/board/common/qemu/Config.in.in index 411a69620..8ac7caf38 100644 --- a/board/common/qemu/Config.in.in +++ b/board/common/qemu/Config.in.in @@ -149,7 +149,7 @@ config IX_QEMU_HOST config IX_QEMU_APPEND string "Extra kernel options" - depends on !IX_QEMU_ROOTFS_MMC + depends on IX_QEMU_LOADER_KERNEL config IX_QEMU_EXTRA string "Extra QEMU options" From 6164e415e36f1cbd4dbc4fdc83d272bbd7abe538 Mon Sep 17 00:00:00 2001 From: Tobias Waldekranz Date: Tue, 6 Oct 2026 08:29:13 +0000 Subject: [PATCH 03/19] factory: Add check logic In addition to triggering a factory-reset, add the ability to check for (and in the case of a marker file, optionally clear) a factory reset condition. With the upcoming DDI image support, the location of the marker file will move, so having the check implementation in factory will make it easier to keep them in sync. --- src/factory/factory.c | 44 +++++++++++++++++++++++++++++++++++++------ 1 file changed, 38 insertions(+), 6 deletions(-) diff --git a/src/factory/factory.c b/src/factory/factory.c index b02d90f8e..33aca3eb3 100644 --- a/src/factory/factory.c +++ b/src/factory/factory.c @@ -11,7 +11,8 @@ #include #define RESETME "/mnt/cfg/infix/.reset" -#define touch(f) mknod((f), S_IFREG|0644, 0) + +#define BOARDCHECK "/usr/libexec/infix/check-factory" int rawgetch(void) { @@ -87,12 +88,39 @@ static int run(const char *cmd) return rc; } +static int mark(void) +{ + return run("mkdir -p $(dirname " RESETME ") && touch " RESETME); +} + +static int check(int clear) +{ + struct stat st; + + if (!stat(RESETME, &st) && ((st.st_mode & S_IFMT) == S_IFREG)) { + if (clear) + unlink(RESETME); + + return 0; + } + + if (!run("grep -q 'finit.cond=factory-reset' /proc/cmdline")) + return 0; + + if (!stat(BOARDCHECK, &st) && (st.st_mode & S_IXUSR)) + return run(BOARDCHECK); + + return 1; +} + int main(int argc, char *argv[]) { struct option long_opts[] = { - { "help", 0, NULL, 'h' }, - { "no-reboot", 0, NULL, 'r' }, - { "assume-yes", 0, NULL, 'y' }, + { "check", 0, NULL, 'c' }, + { "check-clear", 0, NULL, 'C' }, + { "help", 0, NULL, 'h' }, + { "no-reboot", 0, NULL, 'r' }, + { "assume-yes", 0, NULL, 'y' }, { NULL, 0, NULL, 0 } }; int reboot = 1; @@ -100,8 +128,12 @@ int main(int argc, char *argv[]) char *tty; int c; - while ((c = getopt_long(argc, argv, "h?ry", long_opts, NULL)) != EOF) { + while ((c = getopt_long(argc, argv, "cCh?ry", long_opts, NULL)) != EOF) { switch (c) { + case 'c': + return check(0); + case 'C': + return check(1); case 'h': case '?': return usage(0); @@ -127,7 +159,7 @@ int main(int argc, char *argv[]) errx(1, "factory reset only allowed from console login!"); if (yes || yorn("Factory reset device (y/N)? ")) { - if (touch(RESETME) && errno != EEXIST) + if (mark()) err(1, "failed"); warnx("scheduled factory reset on next boot."); From a8d699b9e600e8ef3357b0de9a5ef71aee7e816e Mon Sep 17 00:00:00 2001 From: Tobias Waldekranz Date: Wed, 7 Oct 2026 10:24:22 +0000 Subject: [PATCH 04/19] board/common: nginx: Ensure existance of log directory This is needed when we move away from overlay to plain mounts of /var. --- board/common/rootfs/etc/tmpfiles.d/nginx.conf | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/board/common/rootfs/etc/tmpfiles.d/nginx.conf b/board/common/rootfs/etc/tmpfiles.d/nginx.conf index e10c9b19b..bec6a5bed 100644 --- a/board/common/rootfs/etc/tmpfiles.d/nginx.conf +++ b/board/common/rootfs/etc/tmpfiles.d/nginx.conf @@ -1,2 +1,2 @@ d /var/cache/nginx 0755 www-data www-data - +d /var/log/nginx 0755 www-data www-data From f18eb81285b108aa9969617e49076920eb0d8c23 Mon Sep 17 00:00:00 2001 From: Tobias Waldekranz Date: Sun, 1 Mar 2026 18:27:29 +0000 Subject: [PATCH 05/19] image: Allow images to contribute files to the rootfs This makes it possible to ship differing system files depending on the images being built. E.g. an ITB image might expect a different /etc/fstab than a DDI. --- board/common/image/ix-image.mk | 1 + 1 file changed, 1 insertion(+) diff --git a/board/common/image/ix-image.mk b/board/common/image/ix-image.mk index 12587b228..e5ce36909 100644 --- a/board/common/image/ix-image.mk +++ b/board/common/image/ix-image.mk @@ -22,6 +22,7 @@ $(1): $$($(2)_DEPENDENCIES) ifeq ($$($(3)),y) TARGETS_ROOTFS += $(1) +BR2_ROOTFS_OVERLAY := "$$(realpath $$(pkgdir)/rootfs) $$(call qstrip,$$(BR2_ROOTFS_OVERLAY))" endif endef From f336b2788b381e16099de6ff2851d7809086f5fd Mon Sep 17 00:00:00 2001 From: Tobias Waldekranz Date: Sun, 1 Mar 2026 18:30:12 +0000 Subject: [PATCH 06/19] image-itb-rootfs: Assume ownership of legacy filesystem setup This opens up for DDI images to ship their own implementation. Now that it is supported, defer factory-reset condition check to the factory binary instead of having a separate implementation here. --- .../image-itb-rootfs}/rootfs/etc/fstab | 0 .../rootfs/usr/libexec/infix/mnt | 21 +------------------ 2 files changed, 1 insertion(+), 20 deletions(-) rename board/common/{ => image/image-itb-rootfs}/rootfs/etc/fstab (100%) rename board/common/{ => image/image-itb-rootfs}/rootfs/usr/libexec/infix/mnt (96%) diff --git a/board/common/rootfs/etc/fstab b/board/common/image/image-itb-rootfs/rootfs/etc/fstab similarity index 100% rename from board/common/rootfs/etc/fstab rename to board/common/image/image-itb-rootfs/rootfs/etc/fstab diff --git a/board/common/rootfs/usr/libexec/infix/mnt b/board/common/image/image-itb-rootfs/rootfs/usr/libexec/infix/mnt similarity index 96% rename from board/common/rootfs/usr/libexec/infix/mnt rename to board/common/image/image-itb-rootfs/rootfs/usr/libexec/infix/mnt index 522d72f01..63974d5eb 100755 --- a/board/common/rootfs/usr/libexec/infix/mnt +++ b/board/common/image/image-itb-rootfs/rootfs/usr/libexec/infix/mnt @@ -22,25 +22,6 @@ err=0 mmc="" opt="-k" -# External button or bootloader changed kernel command line -check_factory() -{ - if [ -f /mnt/cfg/infix/.reset ]; then - return 0; - fi - - if grep -q 'finit.cond=factory-reset' /proc/cmdline; then - return 0; - fi - - # Add to your br2-external to extend factory-reset check - if [ ! -x /usr/libexec/infix/check-factory ]; then - return 1; - fi - - /usr/libexec/infix/check-factory -} - factory_reset() { find /sys/class/leds/ -type l -exec sh -c 'echo 100 > $0/brightness' {} \; @@ -393,7 +374,7 @@ if ! mount_rw cfg >/dev/null 2>&1; then vlibsrc= fi -if check_factory; then +if factory --check-clear; then factory_reset fi From 5ae545779d457ed9911742ab68006e9ed740ed17 Mon Sep 17 00:00:00 2001 From: Tobias Waldekranz Date: Wed, 7 Oct 2026 10:23:12 +0000 Subject: [PATCH 07/19] image-itb-rootfs: Assume ownership of RAUC service finit config In the upcoming DDI image format, a pre-script is required. --- .../image-itb-rootfs}/rootfs/etc/finit.d/available/rauc.conf | 0 1 file changed, 0 insertions(+), 0 deletions(-) rename board/common/{ => image/image-itb-rootfs}/rootfs/etc/finit.d/available/rauc.conf (100%) diff --git a/board/common/rootfs/etc/finit.d/available/rauc.conf b/board/common/image/image-itb-rootfs/rootfs/etc/finit.d/available/rauc.conf similarity index 100% rename from board/common/rootfs/etc/finit.d/available/rauc.conf rename to board/common/image/image-itb-rootfs/rootfs/etc/finit.d/available/rauc.conf From 323e70de83e1ed2793e14df18ef0b017a9609d23 Mon Sep 17 00:00:00 2001 From: Tobias Waldekranz Date: Tue, 3 Mar 2026 07:32:40 +0000 Subject: [PATCH 08/19] image-barebox-esp: EFI System Partition with Barebox Sign the image, allowing a UEFI firmware to load run Barebox when secure boot is enabled (provided the corresponding cert is loaded in the db, of course). --- board/common/Config.in | 3 + board/common/barebox/barebox.mk | 13 ++ board/common/barebox/env/boot/net | 24 ++++ board/common/barebox/env/init/infix | 20 +++ board/common/barebox/env/menu/mainmenu | 4 + board/common/barebox/state.dts | 79 +++++++++++ board/common/common.mk | 1 + .../common/image/image-barebox-esp/Config.in | 23 ++++ .../image/image-barebox-esp/generate.sh | 44 ++++++ .../image-barebox-esp/image-barebox-esp.mk | 9 ++ board/x86_64/barebox_defconfig | 126 ++++++++++++++++++ doc/developers-guide.md | 2 +- 12 files changed, 347 insertions(+), 1 deletion(-) create mode 100644 board/common/barebox/barebox.mk create mode 100644 board/common/barebox/env/boot/net create mode 100755 board/common/barebox/env/init/infix create mode 100644 board/common/barebox/env/menu/mainmenu create mode 100644 board/common/barebox/state.dts create mode 100644 board/common/image/image-barebox-esp/Config.in create mode 100755 board/common/image/image-barebox-esp/generate.sh create mode 100644 board/common/image/image-barebox-esp/image-barebox-esp.mk create mode 100644 board/x86_64/barebox_defconfig diff --git a/board/common/Config.in b/board/common/Config.in index f81d61451..079836db9 100644 --- a/board/common/Config.in +++ b/board/common/Config.in @@ -8,6 +8,9 @@ source "$BR2_EXTERNAL_INFIX_PATH/board/common/image/image-itb-gns3a/Config.in" source "$BR2_EXTERNAL_INFIX_PATH/board/common/image/image-itb-rauc/Config.in" source "$BR2_EXTERNAL_INFIX_PATH/board/common/image/image-ext4-rauc/Config.in" source "$BR2_EXTERNAL_INFIX_PATH/board/common/image/image-itb-dl-release/Config.in" + +comment "General" +source "$BR2_EXTERNAL_INFIX_PATH/board/common/image/image-barebox-esp/Config.in" source "$BR2_EXTERNAL_INFIX_PATH/board/common/image/image-readme/Config.in" endmenu diff --git a/board/common/barebox/barebox.mk b/board/common/barebox/barebox.mk new file mode 100644 index 000000000..42812f411 --- /dev/null +++ b/board/common/barebox/barebox.mk @@ -0,0 +1,13 @@ +ifeq ($(IX_IMAGE_BAREBOX_ESP),y) + +IX_BAREBOX_DIR := $(BR2_EXTERNAL_INFIX_PATH)/board/common/barebox +IX_BAREBOX_BUILD := $(BUILD_DIR)/infix-bareboxenv + +define BAREBOX_PRE_BUILD_CREATE_ENV + @$(call IXMSG,"Creating Barebox environment") + rsync -a $(IX_BAREBOX_DIR)/env/ $(IX_BAREBOX_BUILD) + dtc <$(IX_BAREBOX_DIR)/state.dts >$(IX_BAREBOX_BUILD)/state.dtb +endef +BAREBOX_PRE_BUILD_HOOKS += BAREBOX_PRE_BUILD_CREATE_ENV + +endif diff --git a/board/common/barebox/env/boot/net b/board/common/barebox/env/boot/net new file mode 100644 index 000000000..641a199fd --- /dev/null +++ b/board/common/barebox/env/boot/net @@ -0,0 +1,24 @@ +#!/bin/sh + +if dhcp $1; then + if [ -z "${global.dhcp.bootfile}" ]; then + echo "net: ERROR: DHCP did not supply a bootfile" + exit 1 + fi +else + echo "net: ERROR: DHCP Failed" + exit 1 +fi + +echo "net: Downloading ${global.dhcp.bootfile}..." +if tftp ${global.dhcp.bootfile} /initrd; then +else + echo "net: ERROR: Download failed" + exit 1 +fi + +global bootm.initrd=/initrd +dmsetup loop initrd /initrd && boot /dev/initrd + +echo "net: ERROR: Unable to boot from ${global.dhcp.bootfile}" +exit 1 diff --git a/board/common/barebox/env/init/infix b/board/common/barebox/env/init/infix new file mode 100755 index 000000000..d83079a9e --- /dev/null +++ b/board/common/barebox/env/init/infix @@ -0,0 +1,20 @@ +#!/bin/sh + +global allow_color=1 +global model=efi +export PS1="\e[1;32mbarebox@\h\e[0m:\e[1;36m\w\e[0m# " + +global autoboot_timeout=1 +global boot.default="bootchooser storage" +global bootchooser.reset_attempts="all-zero" +global bootchooser.retry=1 +global bootchooser.state_prefix="state.bootstate" +global bootchooser.targets="internal-primary internal-secondary net" + +global blspec.require_trust=${efi.secure_mode} +global ddi.require_trust=${efi.secure_mode} + +keys -l os @efi + +lvm activate + diff --git a/board/common/barebox/env/menu/mainmenu b/board/common/barebox/env/menu/mainmenu new file mode 100644 index 000000000..6b8c2fc52 --- /dev/null +++ b/board/common/barebox/env/menu/mainmenu @@ -0,0 +1,4 @@ +#!/bin/sh + +boot -m +reset diff --git a/board/common/barebox/state.dts b/board/common/barebox/state.dts new file mode 100644 index 000000000..2991b3af9 --- /dev/null +++ b/board/common/barebox/state.dts @@ -0,0 +1,79 @@ +/dts-v1/; + +/ { + aliases { + state = &state; + }; + + partitions { + compatible = "fixed-partitions"; + + statepart: state { + partuuid = "80ca5554-c020-11f1-ba4d-b3c49d5dbf47"; + }; + }; + + state: state { + compatible = "barebox,state"; + magic = <0x80ca5554>; + backend-type = "raw"; + backend = <&statepart>; + backend-stridesize = <0x200>; + #address-cells = <1>; + #size-cells = <1>; + + bootstate { + internal-primary { + #address-cells = <1>; + #size-cells = <1>; + remaining_attempts@0 { + reg = <0x0 0x4>; + type = "uint32"; + default = <3>; + }; + priority@4 { + reg = <0x4 0x4>; + type = "uint32"; + default = <20>; + }; + }; + + internal-secondary { + #address-cells = <1>; + #size-cells = <1>; + remaining_attempts@8 { + reg = <0x8 0x4>; + type = "uint32"; + default = <3>; + }; + priority@c { + reg = <0xc 0x4>; + type = "uint32"; + default = <10>; + }; + }; + + net { + #address-cells = <1>; + #size-cells = <1>; + remaining_attempts@10 { + reg = <0x10 0x4>; + type = "uint32"; + default = <3>; + }; + priority@14 { + reg = <0x14 0x4>; + type = "uint32"; + default = <0>; + }; + }; + + last_chosen@18 { + #address-cells = <1>; + #size-cells = <1>; + reg = <0x18 0x4>; + type = "uint32"; + }; + }; + }; +}; diff --git a/board/common/common.mk b/board/common/common.mk index e0586379d..a570f09d4 100644 --- a/board/common/common.mk +++ b/board/common/common.mk @@ -1,3 +1,4 @@ +include $(BR2_EXTERNAL_INFIX_PATH)/board/common/barebox/barebox.mk include $(BR2_EXTERNAL_INFIX_PATH)/board/common/image/image.mk include $(BR2_EXTERNAL_INFIX_PATH)/board/common/qemu/qemu.mk diff --git a/board/common/image/image-barebox-esp/Config.in b/board/common/image/image-barebox-esp/Config.in new file mode 100644 index 000000000..af12ba9d6 --- /dev/null +++ b/board/common/image/image-barebox-esp/Config.in @@ -0,0 +1,23 @@ +menuconfig IX_IMAGE_BAREBOX_ESP + bool "Barebox EFI System Partition" + depends on BR2_TARGET_BAREBOX + select BR2_PACKAGE_DT_UTILS + help + An EFI System Partition (ESP) with Barebox installed. + +config IX_IMAGE_BAREBOX_ESP_KEY + string "signing key" + depends on IX_IMAGE_BAREBOX_ESP + default "${BR2_EXTERNAL_INFIX_PATH}/board/common/signing-keys/development/infix.key" + help + Path to the private RSA key, in PKCS#8 format, used to sign + the Barebox EFI payload. + +config IX_IMAGE_BAREBOX_ESP_CERT + string "signing cert" + depends on IX_IMAGE_BAREBOX_ESP + default "${BR2_EXTERNAL_INFIX_PATH}/board/common/signing-keys/development/infix.crt" + help + Path to the X509 certificate whose associated public key can + validate the authenticity of the generated signature. + diff --git a/board/common/image/image-barebox-esp/generate.sh b/board/common/image/image-barebox-esp/generate.sh new file mode 100755 index 000000000..acec48910 --- /dev/null +++ b/board/common/image/image-barebox-esp/generate.sh @@ -0,0 +1,44 @@ +#!/bin/sh + +set -e + +case "$BR2_ARCH" in + x86_64) + BOOT_EFI=BOOTX64.EFI + ;; + *) + echo "Unknown EFI boot path for $BR2_ARCH" >&2 + exit 1 + ;; +esac + +mkdir -p "${WORKDIR}"/root +rm -rf "${WORKDIR}"/tmp +mkdir -p "${WORKDIR}"/tmp + +sbsign \ + --key "${KEY}" \ + --cert "${CERT}" \ + --output "${BINARIES_DIR}"/barebox.efi.signed \ + "${BINARIES_DIR}"/barebox.efi + +cat <"${WORKDIR}"/genimage.cfg +image barebox-esp.vfat { + size = "16M" + vfat { + file EFI/BOOT/$BOOT_EFI { + image = $BINARIES_DIR/barebox.efi.signed + } + } +} + +# Silence genimage warnings +config {} +EOF + +genimage \ + --tmppath "${WORKDIR}"/tmp \ + --rootpath "${WORKDIR}"/root \ + --inputpath "${WORKDIR}" \ + --outputpath "${BINARIES_DIR}" \ + --config "${WORKDIR}"/genimage.cfg diff --git a/board/common/image/image-barebox-esp/image-barebox-esp.mk b/board/common/image/image-barebox-esp/image-barebox-esp.mk new file mode 100644 index 000000000..758448ef1 --- /dev/null +++ b/board/common/image/image-barebox-esp/image-barebox-esp.mk @@ -0,0 +1,9 @@ +################################################################################ +# +# image-barebox-esp +# +################################################################################ + +IMAGE_BAREBOX_ESP_CONFIG_VARS := KEY CERT + +$(eval $(ix-image)) diff --git a/board/x86_64/barebox_defconfig b/board/x86_64/barebox_defconfig new file mode 100644 index 000000000..b4867734c --- /dev/null +++ b/board/x86_64/barebox_defconfig @@ -0,0 +1,126 @@ +CONFIG_NAME="efi_defconfig" +CONFIG_NVVAR=y +CONFIG_MMU=y +CONFIG_HUSH_FANCY_PROMPT=y +CONFIG_CMDLINE_EDITING=y +CONFIG_AUTO_COMPLETE=y +CONFIG_MENU=y +# CONFIG_TIMESTAMP is not set +CONFIG_BOOTM_SHOW_TYPE=y +CONFIG_BOOTM_VERBOSE=y +CONFIG_BOOTM_INITRD=y +CONFIG_BLSPEC=y +CONFIG_BOOT_DDI=y +CONFIG_FLEXIBLE_BOOTARGS=y +CONFIG_CONSOLE_ACTIVATE_ALL=y +CONFIG_PARTITION_DISK_EFI=y +# CONFIG_ENV_HANDLING is not set +CONFIG_DEFAULT_ENVIRONMENT=y +CONFIG_STATE=y +CONFIG_BOOTCHOOSER=y +CONFIG_RESET_SOURCE=y +CONFIG_DEFAULT_LOGLEVEL=4 +CONFIG_DEBUG_LL=y +# CONFIG_EFI_HANDOVER_PROTOCOL is not set +CONFIG_EFI_PAYLOAD_BOOTM=y +CONFIG_EFI_PAYLOAD_STATE_LAYOUT_PATH="/env/state.dtb" +CONFIG_CMD_DMESG=y +CONFIG_LONGHELP=y +CONFIG_CMD_IOMEM=y +CONFIG_CMD_IMD=y +CONFIG_CMD_MEMINFO=y +CONFIG_CMD_BOOT=y +CONFIG_CMD_GO=y +CONFIG_CMD_LOADB=y +CONFIG_CMD_RESET=y +CONFIG_CMD_UIMAGE=y +CONFIG_CMD_BOOTCHOOSER=y +CONFIG_CMD_PARTITION=y +CONFIG_CMD_AUTOMOUNT=y +CONFIG_CMD_DMSETUP=y +CONFIG_CMD_VERITYSETUP=y +CONFIG_CMD_LVM=y +CONFIG_CMD_NV=y +CONFIG_CMD_EXPORT=y +CONFIG_CMD_GLOBAL=y +CONFIG_CMD_PRINTENV=y +CONFIG_CMD_MAGICVAR=y +CONFIG_CMD_MAGICVAR_HELP=y +CONFIG_CMD_BASENAME=y +CONFIG_CMD_DIRNAME=y +CONFIG_CMD_FILETYPE=y +CONFIG_CMD_LN=y +CONFIG_CMD_MD5SUM=y +CONFIG_CMD_READLINK=y +CONFIG_CMD_UNCOMPRESS=y +CONFIG_CMD_GETOPT=y +CONFIG_CMD_LET=y +CONFIG_CMD_MSLEEP=y +CONFIG_CMD_READF=y +CONFIG_CMD_SLEEP=y +CONFIG_CMD_DHCP=y +CONFIG_CMD_HOST=y +CONFIG_NET_CMD_IFUP=y +CONFIG_CMD_PING=y +CONFIG_CMD_TFTP=y +CONFIG_CMD_ECHO_E=y +CONFIG_CMD_EDIT=y +CONFIG_CMD_MENU=y +CONFIG_CMD_MENUTREE=y +CONFIG_CMD_READLINE=y +CONFIG_CMD_TIMEOUT=y +CONFIG_CMD_CRC=y +CONFIG_CMD_CRC_CMP=y +CONFIG_CMD_MM=y +CONFIG_CMD_DETECT=y +CONFIG_CMD_FLASH=y +CONFIG_CMD_POWEROFF=y +CONFIG_CMD_WD=y +CONFIG_CMD_KEYS=y +CONFIG_CMD_PKCS7=y +CONFIG_CMD_2048=y +CONFIG_CMD_BAREBOX_UPDATE=y +CONFIG_CMD_OF_DIFF=y +CONFIG_CMD_OF_NODE=y +CONFIG_CMD_OF_PROPERTY=y +CONFIG_CMD_OFTREE=y +CONFIG_CMD_TIME=y +CONFIG_NET=y +CONFIG_NET_NETCONSOLE=y +CONFIG_DEEP_PROBE_DEFAULT=y +CONFIG_OFDEVICE=y +CONFIG_DRIVER_SERIAL_EFI_STDIO=y +CONFIG_DRIVER_NET_EFI_SNP=y +# CONFIG_SPI is not set +CONFIG_DISK=y +CONFIG_DISK_WRITE=y +CONFIG_DM_BLK=y +CONFIG_DM_BLK_LINEAR=y +CONFIG_DM_BLK_VERITY=y +CONFIG_DM_LVM=y +CONFIG_USB_HOST=y +CONFIG_USB_EFI_IO_PROTOCOL=y +CONFIG_VIDEO=y +CONFIG_FRAMEBUFFER_CONSOLE=y +CONFIG_DRIVER_VIDEO_EFI_GOP=y +CONFIG_FINTEK_SUPERIO=y +CONFIG_SMSC_SUPERIO=y +CONFIG_WATCHDOG=y +CONFIG_WATCHDOG_EFI=y +CONFIG_F71808E_WDT=y +CONFIG_ITCO_WDT=y +# CONFIG_PINCTRL is not set +CONFIG_PCI_EFI=y +CONFIG_FS_EXT4=y +CONFIG_FS_TFTP=y +CONFIG_FS_TFTP_MAX_WINDOW_SIZE=128 +CONFIG_FS_NFS=y +CONFIG_FS_EFI=y +CONFIG_FS_EFIVARFS=y +CONFIG_FS_FAT=y +CONFIG_FS_FAT_WRITE=y +CONFIG_FS_SQUASHFS=y +CONFIG_CRYPTO_RSA=y +CONFIG_CRYPTO_ECDSA=y +CONFIG_CRYPTO_EFI_DB=y +CONFIG_ZLIB=y diff --git a/doc/developers-guide.md b/doc/developers-guide.md index b1325692c..bb44bf262 100644 --- a/doc/developers-guide.md +++ b/doc/developers-guide.md @@ -73,7 +73,7 @@ $ sudo apt install bc binutils build-essential bzip2 cpio \ libncurses-dev libssl-dev perl patch \ python3 rsync sed tar unzip wget \ autopoint bison flex autoconf automake \ - mtools + mtools sbsigntool ``` To build an Infix image; select the target and then make: From 3a8719e0ea0123bd17641c93ad654fcadca890b5 Mon Sep 17 00:00:00 2001 From: Tobias Waldekranz Date: Thu, 4 Dec 2025 08:01:18 +0000 Subject: [PATCH 09/19] image-ddi: Add Discoverable Disk Image (DDI) --- board/common/Config.in | 5 + board/common/image/image-ddi/Config.in | 40 +++ board/common/image/image-ddi/generate.sh | 109 ++++++++ board/common/image/image-ddi/image-ddi.mk | 10 + .../image-ddi/rootfs/boot/efi-backup/.empty | 0 .../image/image-ddi/rootfs/boot/efi/.empty | 0 .../etc/finit.d/available/mark-boot-ok.conf | 1 + .../rootfs/etc/finit.d/available/rauc.conf | 5 + .../etc/finit.d/enabled/mark-boot-ok.conf | 1 + board/common/image/image-ddi/rootfs/etc/fstab | 18 ++ .../image/image-ddi/rootfs/mnt/rw/.empty | 0 .../image-ddi/rootfs/usr/libexec/infix/mnt | 253 ++++++++++++++++++ .../rootfs/usr/libexec/infix/rauc-config-gen | 87 ++++++ board/common/image/ix-image.mk | 1 + board/common/post-build.sh | 5 + board/x86_64/rootfs/loader/entries/os.conf | 4 + external.mk | 16 ++ package/factory/factory.mk | 5 +- src/factory/factory.c | 8 +- 19 files changed, 564 insertions(+), 4 deletions(-) create mode 100644 board/common/image/image-ddi/Config.in create mode 100755 board/common/image/image-ddi/generate.sh create mode 100644 board/common/image/image-ddi/image-ddi.mk create mode 100644 board/common/image/image-ddi/rootfs/boot/efi-backup/.empty create mode 100644 board/common/image/image-ddi/rootfs/boot/efi/.empty create mode 100644 board/common/image/image-ddi/rootfs/etc/finit.d/available/mark-boot-ok.conf create mode 100644 board/common/image/image-ddi/rootfs/etc/finit.d/available/rauc.conf create mode 120000 board/common/image/image-ddi/rootfs/etc/finit.d/enabled/mark-boot-ok.conf create mode 100644 board/common/image/image-ddi/rootfs/etc/fstab create mode 100644 board/common/image/image-ddi/rootfs/mnt/rw/.empty create mode 100755 board/common/image/image-ddi/rootfs/usr/libexec/infix/mnt create mode 100755 board/common/image/image-ddi/rootfs/usr/libexec/infix/rauc-config-gen create mode 100644 board/x86_64/rootfs/loader/entries/os.conf diff --git a/board/common/Config.in b/board/common/Config.in index 079836db9..e82b5fd45 100644 --- a/board/common/Config.in +++ b/board/common/Config.in @@ -1,5 +1,10 @@ + menu "Images" +comment "DDI Based" +source "$BR2_EXTERNAL_INFIX_PATH/board/common/image/image-ddi/Config.in" + +comment "ITB Based" source "$BR2_EXTERNAL_INFIX_PATH/board/common/image/image-itb-rootfs/Config.in" source "$BR2_EXTERNAL_INFIX_PATH/board/common/image/image-itb-aux/Config.in" source "$BR2_EXTERNAL_INFIX_PATH/board/common/image/image-itb-boot/Config.in" diff --git a/board/common/image/image-ddi/Config.in b/board/common/image/image-ddi/Config.in new file mode 100644 index 000000000..75b6a1c94 --- /dev/null +++ b/board/common/image/image-ddi/Config.in @@ -0,0 +1,40 @@ +menuconfig IX_IMAGE_DDI + bool "Discoverable Disk Image (DDI)" + select BR2_TARGET_ROOTFS_SQUASHFS + select BR2_PACKAGE_BTRFS_PROGS + select BR2_PACKAGE_CRYPTSETUP + select BR2_PACKAGE_GPTFDISK + select BR2_PACKAGE_GPTFDISK_SGDISK + select BR2_PACKAGE_LVM2 + select BR2_PACKAGE_MULTIPATH_TOOLS + select BR2_PACKAGE_UTIL_LINUX_BINARIES + select BR2_PACKAGE_HOST_GENIMAGE + help + Create a Discoverable Disk Image containing: + - SquashFS of the root filesystem + - dm-verity hash tree of the root filesystem + - DDI compliant JSON signature object of the hash tree + + https://uapi-group.org/specifications/specs/discoverable_disk_image/ + +config IX_IMAGE_DDI_KEY + string "signing key" + depends on IX_IMAGE_DDI + default "${BR2_EXTERNAL_INFIX_PATH}/board/common/signing-keys/development/infix.key" + help + Path to the private RSA key, in PKCS#8 format, used to sign + the dm-verity hash tree. + +config IX_IMAGE_DDI_CERT + string "signing cert" + depends on IX_IMAGE_DDI + default "${BR2_EXTERNAL_INFIX_PATH}/board/common/signing-keys/development/infix.crt" + help + Path to the X509 certificate whose associated public key can + validate the authenticity of the generated signature. + + The SHA256 fingerprint of this certificate is recorded + alongside the standard PKCS#7 signature in the JSON object, + which can be useful to locate corresponding key on systems + that lack full X509 parsing capabilities. + diff --git a/board/common/image/image-ddi/generate.sh b/board/common/image/image-ddi/generate.sh new file mode 100755 index 000000000..13d0e9723 --- /dev/null +++ b/board/common/image/image-ddi/generate.sh @@ -0,0 +1,109 @@ +#!/bin/sh + +# Generate the DDI in two phases. First we create the verity hash tree +# and signature, then we collect them together with the rootfs in the +# ddi. The reason for this is that genimage (at least up to v19) can +# not infer the size of a partition if the image to be placed in it is +# created by the same genimage instance. With the two-phase approach, +# we work around that problem and can create a DDI without any wasted +# space. + +set -e + +uuidfmt() +{ + echo $1 | \ + sed -E 's/^([0-9a-fA-F]{8})([0-9a-fA-F]{4})([0-9a-fA-F]{4})([0-9a-fA-F]{4})([0-9a-fA-F]{12})$/\1-\2-\3-\4-\5/' +} + +rm -rf "${WORKDIR}"/tmp +mkdir -p "${WORKDIR}"/tmp "${WORKDIR}"/verity + +cat <"${WORKDIR}"/genimage-verity.cfg +image rootfs.verity { + verity { + image = "rootfs.squashfs" + } +} + +image rootfs.verity-sig { + verity-sig { + image = "rootfs.verity" + cert = "${CERT}" + key = "${KEY}" + } +} + +config {} +EOF + +genimage \ + --loglevel 1 \ + --tmppath "${WORKDIR}"/tmp \ + --rootpath "${WORKDIR}" \ + --inputpath "${BINARIES_DIR}" \ + --outputpath "${WORKDIR}"/verity \ + --config "${WORKDIR}"/genimage-verity.cfg + +case "${BR2_ARCH}" in + "x86_64") + arch=x86-64 + ;; + *) + echo "ERROR: missing mapping from ${BR2_ARCH} to genimage arch" >&2 + exit 1 + ;; +esac + +# As described in the DPS spec, setting the data partition's UUID to +# the first 128 bits of the roothash and the verity partition's UUID +# to the last 128 bits of the roothash allows for auto-discovery of +# the partitions based only on the roothash. +roothash=$(tr -d '\000' <"${WORKDIR}"/verity/rootfs.verity-sig | jq -r .rootHash) +rootuuid=$(uuidfmt $(echo $roothash | cut -c -32)) +hashuuid=$(uuidfmt $(echo $roothash | rev | cut -c -32 | rev)) + +# Make sure that the final image size is aligned to a 32k byte +# boundary. This lines up with the extent size used by lvm-mkinternal, +# which means that we won't get GPT location warnings from Barebox +# when we activate the LV. +ALIGN_K=32 +blks=$((2+32+32)) +blks=$((blks + (($(stat -c%s "${BINARIES_DIR}"/rootfs.squashfs) + 511) >> 9))) +blks=$((blks + (($(stat -c%s "${WORKDIR}"/verity/rootfs.verity) + 511) >> 9))) +blks=$((blks + (($(stat -c%s "${WORKDIR}"/verity/rootfs.verity-sig) + 511) >> 9))) +size=$((((blks << 9) + ((ALIGN_K << 10) - 1)) & ~((ALIGN_K << 10) - 1))) + +cat <"${WORKDIR}"/genimage-ddi.cfg +image ${ARTIFACT}.raw { + size = ${size} + hdimage { + partition-table-type = "gpt" + } + + partition root { + partition-type-uuid = "root-${arch}" + partition-uuid = "$rootuuid" + image = "rootfs.squashfs" + } + partition root-verity { + partition-type-uuid = "root-${arch}-verity" + partition-uuid = "$hashuuid" + image = "${WORKDIR}/verity/rootfs.verity" + } + partition root-verity-sig { + partition-type-uuid = "root-${arch}-verity-sig" + image = "${WORKDIR}/verity/rootfs.verity-sig" + } +} + +config {} +EOF + +genimage \ + --loglevel 1 \ + --tmppath "${WORKDIR}"/tmp \ + --rootpath "${WORKDIR}" \ + --inputpath "${BINARIES_DIR}" \ + --outputpath "${BINARIES_DIR}" \ + --config "${WORKDIR}"/genimage-ddi.cfg diff --git a/board/common/image/image-ddi/image-ddi.mk b/board/common/image/image-ddi/image-ddi.mk new file mode 100644 index 000000000..bd51942f3 --- /dev/null +++ b/board/common/image/image-ddi/image-ddi.mk @@ -0,0 +1,10 @@ +################################################################################ +# +# image-ddi +# +################################################################################ + +IMAGE_DDI_DEPENDENCIES := host-dracut host-genimage rootfs-squashfs +IMAGE_DDI_CONFIG_VARS := KEY CERT + +$(eval $(ix-image)) diff --git a/board/common/image/image-ddi/rootfs/boot/efi-backup/.empty b/board/common/image/image-ddi/rootfs/boot/efi-backup/.empty new file mode 100644 index 000000000..e69de29bb diff --git a/board/common/image/image-ddi/rootfs/boot/efi/.empty b/board/common/image/image-ddi/rootfs/boot/efi/.empty new file mode 100644 index 000000000..e69de29bb diff --git a/board/common/image/image-ddi/rootfs/etc/finit.d/available/mark-boot-ok.conf b/board/common/image/image-ddi/rootfs/etc/finit.d/available/mark-boot-ok.conf new file mode 100644 index 000000000..ac2c5cdbe --- /dev/null +++ b/board/common/image/image-ddi/rootfs/etc/finit.d/available/mark-boot-ok.conf @@ -0,0 +1 @@ +task name:mark-boot-ok [2345] rauc status mark-good -- diff --git a/board/common/image/image-ddi/rootfs/etc/finit.d/available/rauc.conf b/board/common/image/image-ddi/rootfs/etc/finit.d/available/rauc.conf new file mode 100644 index 000000000..d79a3eac4 --- /dev/null +++ b/board/common/image/image-ddi/rootfs/etc/finit.d/available/rauc.conf @@ -0,0 +1,5 @@ +set G_MESSAGES_DEBUG=nocolor +service [2345] notify:systemd \ + pre:/usr/libexec/infix/rauc-config-gen \ + env:-/etc/default/rauc \ + rauc service $RAUC_ARGS -- Software update service diff --git a/board/common/image/image-ddi/rootfs/etc/finit.d/enabled/mark-boot-ok.conf b/board/common/image/image-ddi/rootfs/etc/finit.d/enabled/mark-boot-ok.conf new file mode 120000 index 000000000..2bdda4b62 --- /dev/null +++ b/board/common/image/image-ddi/rootfs/etc/finit.d/enabled/mark-boot-ok.conf @@ -0,0 +1 @@ +../available/mark-boot-ok.conf \ No newline at end of file diff --git a/board/common/image/image-ddi/rootfs/etc/fstab b/board/common/image/image-ddi/rootfs/etc/fstab new file mode 100644 index 000000000..9c648940d --- /dev/null +++ b/board/common/image/image-ddi/rootfs/etc/fstab @@ -0,0 +1,18 @@ +tmpfs /tmp tmpfs mode=0755,nosuid,nodev 0 0 +tmpfs /run tmpfs mode=0755,nosuid,nodev 0 0 +tmpfs /media tmpfs mode=1755,nosuid,nodev 0 0 +tmpfs /mnt/tmp tmpfs defaults 0 0 + +/dev/disk/by-partuuid/82bb760e-c08e-11f1-a791-7bd312fe0c17 /boot/efi vfat defaults 0 0 +/dev/disk/by-partuuid/835b97d8-c08e-11f1-a2f7-73ad4bbf39e9 /boot/efi-backup vfat defaults 0 0 + +# Locate and mount /cfg, /home, /root and /var from the internal +# volume, wherever that is located - or, as a last resort, setup tmpfs +# mounts at these points. Setup an ephemeral writable overlay on /etc. +/usr/libexec/infix/mnt# /mnt/rw helper none 0 0 + +# Optional, nice-to-have mounts +debugfs /sys/kernel/debug debugfs nofail 0 0 +tracefs /sys/kernel/tracing tracefs nofail 0 0 +cfgfs /config configfs nofail,noauto 0 0 +host /mnt/host 9p nofail,noauto,cache=none,msize=16384 0 0 diff --git a/board/common/image/image-ddi/rootfs/mnt/rw/.empty b/board/common/image/image-ddi/rootfs/mnt/rw/.empty new file mode 100644 index 000000000..e69de29bb diff --git a/board/common/image/image-ddi/rootfs/usr/libexec/infix/mnt b/board/common/image/image-ddi/rootfs/usr/libexec/infix/mnt new file mode 100755 index 000000000..7bec8c915 --- /dev/null +++ b/board/common/image/image-ddi/rootfs/usr/libexec/infix/mnt @@ -0,0 +1,253 @@ +#!/bin/sh +# Called from /etc/fstab to ensure we have something writable mounted +# at /cfg, /home, /root, and /var. +# +# In the normal case, the "rw" LV from the "internal" LVM VG is used +# to back all persistant storage. In the event that "internal" is +# present, but the "rw" LV is missing, one will be provisioned, along +# with the required BTRFS subvolumes. +# +# In all other scenarios where the subvolumes are not available, the +# system will fall back to temporary storage for all mount points. +# +# For /etc, a writable, tmpfs backed, layer is always overlayed on top +# of the read-only contents from the OS image. + +set -e + +nm=$(basename "$0") +need_restore= +code=0 + +logcrit() +{ + logger -k -t "$nm" -p user.crit "$@" +} + +lognote() +{ + logger -k -t "$nm" -p user.notice "$@" +} + +status() +{ + GREEN="$(printf '\033[1;32m')" + RED="$(printf '\033[1;31m')" + YELLOW="$(printf '\033[1;33m')" + BOLD="$(printf '\033[1m')" + RESET="$(printf '\033[0m')" + + case $1 in + 0) color=$GREEN; text=' OK ' ;; + 1) color=$RED; text='FAIL' ;; + 2) color=$YELLOW;text='WARN' ;; + *) color=$YELLOW;text=' ⋯ ' ;; + esac + + printf '%s[%s%s%s%s]%s ' "$BOLD" "$color" "$text" "$RESET" "$BOLD" "$RESET" +} + +print_start() +{ + printf '\r\033[K%s%s' "$(status 3)" "$*" > /dev/console + need_restore=YES +} + +print_end() +{ + rc=$1; shift + if [ $# -gt 0 ]; then + printf '\r\033[K%s%s\n' "$(status "$rc")" "$*" > /dev/console + else + printf '\r%s\n' "$(status "$rc")" > /dev/console + fi +} + +# Restore Finit's original progress message so its [ OK ] appears correctly +print_restore() +{ + [ "$need_restore" ] || return 0 + print_start "Mounting filesystems from /etc/fstab" +} + +internal_rw_factory_reset() +{ + factory --check-clear || return 0 + [ -b /dev/internal/rw ] || return 0 + + print_start "Resetting to factory defaults" + lognote "Resetting to factory defaults" + + lvm lvremove -y internal/rw || { + logcrit "Unable to remove rw volume" + print_end 1 + return 0 + } + + lognote "Factory reset complete" + print_end 0 +} + +subvols="cfg var root home" + +internal_rw_provision() +{ + # The only condition under which we do BTRFS provisioning is when + # the LV is absent. In all other failure scenarios: fall back to + # tmpfs and let the user sort it out. This way we do not risk any + # loss of data. + [ -b /dev/internal/rw ] && return 0 + + print_start "Provisioning persistent storage on internal volume" + + lvm lvcreate -y --name rw --extents 50%FREE internal || { + logcrit "No rw LV available, nor could it be created" + print_end 1 + return 1 + } + + mkfs.btrfs -L rw /dev/internal/rw || { + logcrit "Could not create BTRFS on rw LV" + print_end 1 + return 1 + } + + mount -t btrfs /dev/internal/rw /mnt/rw || { + logcrit "Failed to mount rw" + print_end 1 + return 1 + } + + for subvol in $subvols; do + btrfs subvolume create /mnt/rw/@"$subvol" || { + logcrit "Failed to create @$subvol on rw" + print_end 1 + return 1 + } + done + + umount /mnt/rw || { + logcrit "Failed to unmount rw" + print_end 1 + return 1 + } + + print_end 0 +} + +internal_secondary_provision() +{ + local _free _size + + # On the first system boot, if a primary image is available, but a + # secondary one is not, provision a duplicate of primary as the + # secondary image. This let's the distribution disk image be + # small, yet automatically deploys a redundant setup. + [ -f /dev/firstboot ] || return 0 + [ -b /dev/internal/primary ] || return 0 + [ -b /dev/internal/secondary ] && return 0 + + print_start "Provisioning redundant image" + + _free=$(lvm vgs --reportformat=json --units b internal \ + | jq -r .report[0].vg[0].vg_free \ + | tr -d B) + _size=$(lvs --reportformat=json --units b internal/primary \ + | jq -r .report[0].lv[0].lv_size \ + | tr -d B) + + [ "$_free" -ge "$_size" ] || { + logcrit "Skipping redundant $((_size >> 20))MB image, $((_free >> 20))MB available" + print_end 1 + return 0 + } + + lvm lvcreate --name secondary --size "${_size}B" internal || { + logcrit "Unable to create secondary LV" + print_end 1 + return 1 + } + + dd if=/dev/internal/primary of=/dev/internal/secondary bs=1M status=none || { + logcrit "Redundant image replication failed" + print_end 1 + return 1 + } + + kpartx -a -p-part /dev/internal/secondary || { + logcrit "Failed to setup partitions from replicated secondary image" + } + + print_end 0 +} + +internal_activate() +{ + lvm vgs internal || { + logcrit "No internal volume available" + return 1 + } + + lvm vgchange -ay internal || { + logcrit "Unable to bring internal volume online" + return 1 + } + + internal_rw_factory_reset || return 1 + internal_rw_provision || return 1 + internal_secondary_provision || return 1 +} + +internal_rw_mount() +{ + [ -b /dev/internal/rw ] || return 1 + + for subvol in $subvols; do + mount -t btrfs /dev/internal/rw /"$subvol" -o subvol=@"$subvol" || { + logcrit "Failed to mount /$subvol" + return 1 + } + done +} + +tmpfs_rw_mount() +{ + for subvol in $subvols; do + mkdir -p -m 0755 /mnt/tmp/"$subvol" + mount /mnt/tmp/"$subvol" /"$subvol" -o bind || { + logcrit "Failed to mount temporary /$subvol" + return 1 + } + done +} + +tmpfs_etc_mount() +{ + mkdir -p -m 0755 /mnt/tmp/etc/ + tar c -C /etc . | tar x -C /mnt/tmp/etc + mount /mnt/tmp/etc /etc -o bind +} + +exec >/mnt/tmp/mnt.log 2>&1 + +tmpfs_etc_mount || { + code=1 + logcrit "Failed to make /etc writable" +} + +internal_activate || { + print_end 1 "Activating internal volume" +} + +internal_rw_mount || { + code=1 + print_end 1 "Mounting persistent storage from internal volume" + logcrit "Internal volume not found, falling back to tmpfs" + + print_start "Falling back to ephemeral tmpfs storage!" + tmpfs_rw_mount + print_end $? +} + +print_restore +exit $code diff --git a/board/common/image/image-ddi/rootfs/usr/libexec/infix/rauc-config-gen b/board/common/image/image-ddi/rootfs/usr/libexec/infix/rauc-config-gen new file mode 100755 index 000000000..76fce556c --- /dev/null +++ b/board/common/image/image-ddi/rootfs/usr/libexec/infix/rauc-config-gen @@ -0,0 +1,87 @@ +#!/bin/sh + +. /etc/os-release + +{ + cat </etc/rauc/system.conf + +mkdir -p /var/lib/rauc + +[ -f /var/lib/rauc/central.raucs ] && exit + +unixepoch=$(date -d1970-01-01 -u +%FT%TZ) + +{ + cat </var/lib/rauc/central.raucs + diff --git a/board/common/image/ix-image.mk b/board/common/image/ix-image.mk index e5ce36909..d026d1c40 100644 --- a/board/common/image/ix-image.mk +++ b/board/common/image/ix-image.mk @@ -13,6 +13,7 @@ $(1): $$($(2)_DEPENDENCIES) WORKDIR=$$(BUILD_DIR)/$(1) \ BINARIES_DIR=$$(BINARIES_DIR) \ TARGET_DIR=$$(TARGET_DIR) \ + BR2_ARCH=$$(BR2_ARCH) \ BR2_EXTERNAL_INFIX_PATH=$$(BR2_EXTERNAL_INFIX_PATH) \ ARTIFACT=$$(INFIX_ARTIFACT) \ COMPATIBLE=$$(IX_COMPATIBLE) \ diff --git a/board/common/post-build.sh b/board/common/post-build.sh index 5a94fe885..4d4a709e0 100755 --- a/board/common/post-build.sh +++ b/board/common/post-build.sh @@ -91,6 +91,11 @@ rm -f "$TARGET_DIR/etc/os-release" fi } > "$TARGET_DIR/etc/os-release" +if [ -f "$TARGET_DIR"/loader/entries/os.conf ]; then + sed -i "s/@TITLE@/$IX_NAME $INFIX_VERSION/" \ + "$TARGET_DIR"/loader/entries/os.conf +fi + echo "$IX_TAGLINE $INFIX_VERSION -- $(date +"%b %e %H:%M %Z %Y")" > "$TARGET_DIR/etc/version" ixmsg "Creating /etc/version: $(cat "$TARGET_DIR/etc/version")" diff --git a/board/x86_64/rootfs/loader/entries/os.conf b/board/x86_64/rootfs/loader/entries/os.conf new file mode 100644 index 000000000..cd19524a7 --- /dev/null +++ b/board/x86_64/rootfs/loader/entries/os.conf @@ -0,0 +1,4 @@ +title @TITLE@ +linux /boot/bzImage +options console=ttyS0 console=hvc0 loglevel=4 +linux-appendroot true diff --git a/external.mk b/external.mk index 071cfee47..5609bd270 100644 --- a/external.mk +++ b/external.mk @@ -25,6 +25,22 @@ FRR_POST_BUILD_HOOKS += FRR_POST_BUILD_HOOK # NETSNMP_CONF_OPTS += --enable-read-only +# Some of these assumes the presence of systemd, so skip them. +LVM2_CONF_OPTS += --disable-udev_rules + +# +# The multipath-tools package, which we need for kpartx, installs udev +# rules that assumes the presence of systemd. There is no option to +# skip the install, but we can pick the destination. So place them in +# the root and then remove them in the post-install hook. +MULTIPATH_TOOLS_OPTS += udevrulesdir="/.mpath-trash" + +define MPATH_POST_INSTALL_CLEANUP + rm -rf $(TARGET_DIR)/.mpath-trash +endef + +MULTIPATH_TOOLS_POST_INSTALL_TARGET_HOOKS += MPATH_POST_INSTALL_CLEANUP + # # External pre-built toolchains do not carry their own license. # diff --git a/package/factory/factory.mk b/package/factory/factory.mk index 1d76d3468..bc2b6a683 100644 --- a/package/factory/factory.mk +++ b/package/factory/factory.mk @@ -11,12 +11,11 @@ FACTORY_SITE_METHOD = local FACTORY_SITE = $(BR2_EXTERNAL_INFIX_PATH)/src/factory FACTORY_REDISTRIBUTE = NO -define FACTORY_CONF_ENV -CFLAGS="$(INFIX_CFLAGS)" -endef +FACTORY_RESETME_PATH=$(if $(IX_IMAGE_DDI),/boot/efi/infix/.factory-reset,/mnt/cfg/infix.reset) define FACTORY_BUILD_CMDS $(TARGET_MAKE_ENV) $(TARGET_CONFIGURE_OPTS) $(MAKE) -C $(@D) \ + CFLAGS="$(INFIX_CFLAGS) -DRESETME_PATH=$(FACTORY_RESETME_PATH)" \ LDLIBS="$(TARGET_LDFLAGS)" endef diff --git a/src/factory/factory.c b/src/factory/factory.c index 33aca3eb3..4259f1cb8 100644 --- a/src/factory/factory.c +++ b/src/factory/factory.c @@ -10,7 +10,13 @@ #include #include -#define RESETME "/mnt/cfg/infix/.reset" +#define stringify(_x) #_x +#define xstringify(_x) stringify(_x) + +#ifndef RESETME_PATH +#error "RESETME_PATH must be specified" +#endif +#define RESETME xstringify(RESETME_PATH) #define BOARDCHECK "/usr/libexec/infix/check-factory" From 46b0ca37deb11e8c597b8c6d0710eaf9688331ab Mon Sep 17 00:00:00 2001 From: Tobias Waldekranz Date: Thu, 18 Dec 2025 00:18:42 +0000 Subject: [PATCH 10/19] image-ddi-disk: Full system image --- board/common/Config.in | 1 + board/common/image/image-ddi-disk/Config.in | 8 + board/common/image/image-ddi-disk/generate.sh | 69 ++++++ .../image/image-ddi-disk/image-ddi-disk.mk | 9 + board/common/rootfs/etc/partition-uuid | 3 + utils/lvm-mkinternal | 210 ++++++++++++++++++ 6 files changed, 300 insertions(+) create mode 100644 board/common/image/image-ddi-disk/Config.in create mode 100755 board/common/image/image-ddi-disk/generate.sh create mode 100644 board/common/image/image-ddi-disk/image-ddi-disk.mk create mode 100755 utils/lvm-mkinternal diff --git a/board/common/Config.in b/board/common/Config.in index e82b5fd45..3f28073ca 100644 --- a/board/common/Config.in +++ b/board/common/Config.in @@ -3,6 +3,7 @@ menu "Images" comment "DDI Based" source "$BR2_EXTERNAL_INFIX_PATH/board/common/image/image-ddi/Config.in" +source "$BR2_EXTERNAL_INFIX_PATH/board/common/image/image-ddi-disk/Config.in" comment "ITB Based" source "$BR2_EXTERNAL_INFIX_PATH/board/common/image/image-itb-rootfs/Config.in" diff --git a/board/common/image/image-ddi-disk/Config.in b/board/common/image/image-ddi-disk/Config.in new file mode 100644 index 000000000..7b1f60b49 --- /dev/null +++ b/board/common/image/image-ddi-disk/Config.in @@ -0,0 +1,8 @@ +menuconfig IX_IMAGE_DDI_DISK + bool "LVM2 based provisioning disk (DDI)" + depends on IX_IMAGE_DDI + select IX_IMAGE_BAREBOX_ESP + help + Compose a full disk image consisting of an EFI System + Partition (ESP) containing Barebox, and an LVM PV in which + Infix is installed as an LV. diff --git a/board/common/image/image-ddi-disk/generate.sh b/board/common/image/image-ddi-disk/generate.sh new file mode 100755 index 000000000..a4e5966c1 --- /dev/null +++ b/board/common/image/image-ddi-disk/generate.sh @@ -0,0 +1,69 @@ +#!/bin/sh + +set -e + + +mkdir -p "${WORKDIR}"/root +rm -rf "${WORKDIR}"/tmp +mkdir -p "${WORKDIR}"/tmp + +"${BR2_EXTERNAL_INFIX_PATH}"/utils/lvm-mkinternal \ + primary "${BINARIES_DIR}"/"${ARTIFACT}".raw >"${WORKDIR}"/internal.lvm + +. $BR2_EXTERNAL_INFIX_PATH/board/common/rootfs/etc/partition-uuid +[ -n "${BAREBOX_STATE_UUID}" ] +[ -n "${ESP_UUID}" ] +[ -n "${ESP_BACKUP_UUID}" ] + +cat <"${WORKDIR}"/genimage.cfg + +image ${ARTIFACT}.disk { + hdimage { + partition-table-type = "gpt" + } + + partition esp { + partition-type-uuid = "esp" + partition-uuid = "$ESP_UUID" + image = "$BINARIES_DIR/barebox-esp.vfat" + } + + partition esp-backup { + partition-type-uuid = "esp" + partition-uuid = "$ESP_BACKUP_UUID" + image = "$BINARIES_DIR/barebox-esp.vfat" + } + + partition barebox-state { + partition-type-uuid = "barebox-state" + partition-uuid = "$BAREBOX_STATE_UUID" + size = 2048 + } + + partition internal { + growfs = "true" + partition-type-uuid = "lvm" + image = "internal.lvm" + } +} + +image ${ARTIFACT}.qcow2 { + qemu { + format = "qcow2" + } + + partition disk { + image = "${ARTIFACT}.disk" + } +} + +# Silence genimage warnings +config {} +EOF + +genimage \ + --tmppath "${WORKDIR}"/tmp \ + --rootpath "${WORKDIR}"/root \ + --inputpath "${WORKDIR}" \ + --outputpath "${BINARIES_DIR}" \ + --config "${WORKDIR}"/genimage.cfg diff --git a/board/common/image/image-ddi-disk/image-ddi-disk.mk b/board/common/image/image-ddi-disk/image-ddi-disk.mk new file mode 100644 index 000000000..e666e0238 --- /dev/null +++ b/board/common/image/image-ddi-disk/image-ddi-disk.mk @@ -0,0 +1,9 @@ +################################################################################ +# +# image-ddi-disk +# +################################################################################ + +IMAGE_DDI_DISK_DEPENDENCIES := image-barebox-esp image-ddi + +$(eval $(ix-image)) diff --git a/board/common/rootfs/etc/partition-uuid b/board/common/rootfs/etc/partition-uuid index c7e162be7..92be42fef 100644 --- a/board/common/rootfs/etc/partition-uuid +++ b/board/common/rootfs/etc/partition-uuid @@ -1,3 +1,6 @@ AUX_UUID="78460f84-de84-4fe7-89bd-4c1f433b2230" +BAREBOX_STATE_UUID="80ca5554-c020-11f1-ba4d-b3c49d5dbf47" +ESP_UUID="82bb760e-c08e-11f1-a791-7bd312fe0c17" +ESP_BACKUP_UUID="835b97d8-c08e-11f1-a2f7-73ad4bbf39e9" PRIMARY_UUID="107ae911-a97b-4380-975c-7ce1a2dde1e0" SECONDARY_UUID="352bd9b2-2ca9-44e2-bdc7-edbc87ba1e02" diff --git a/utils/lvm-mkinternal b/utils/lvm-mkinternal new file mode 100755 index 000000000..14fd2d812 --- /dev/null +++ b/utils/lvm-mkinternal @@ -0,0 +1,210 @@ +#!/usr/bin/env python3 + +import os +import random +import struct + +class ID: + def __init__(self): + charset = "abcdefghijklmnopqrstuvwxyz" + charset += "ABCDEFGHIJKLMNOPQRSTUVWXYZ" + charset += "0123456789" + + b = random.choices(charset, k=32) + self.bytes = "".join(b).encode() + + def dashed(self): + return self.bytes[0:6].decode() + \ + "-" + self.bytes[6:10].decode() + \ + "-" + self.bytes[10:14].decode() + \ + "-" + self.bytes[14:18].decode() + \ + "-" + self.bytes[18:22].decode() + \ + "-" + self.bytes[22:26].decode() + \ + "-" + self.bytes[26:32].decode() + +def crc32(data: bytes) -> int: + crc = 0xf597a6cf + + for byte in data: + crc ^= byte + for _ in range(8): + if crc & 1: + crc = (crc >> 1) ^ 0xedb88320 + else: + crc >>= 1 + + return crc & 0xffffffff + +def sendfile(dst, src, count): + while count > 0: + chunk = os.sendfile(dst, src, None, count) + if chunk < 0: + raise OSError("sendfile") + + count -= chunk + +class Linear: + def __init__(self, start, align, path, name=None): + self.id = ID() + self.name = name if name else os.path.basename(path) + self.path = path + self.start = start + self.align = align + self.size = os.path.getsize(path) + self.asize = (self.size + align - 1) & ~(align - 1) + + def sendfile(self, fd): + pad = self.asize - self.size + + with open(self.path, "rb") as f: + sendfile(fd, f.fileno(), self.size) + + if pad: + os.write(fd, b"\0" * pad) + + def meta(self): + return f""" {self.name} {{ + id = "{self.id.dashed()}" + status = ["READ", "WRITE", "VISIBLE"] + segment_count = 1 + + segment1 {{ + start_extent = 0 + extent_count = {self.asize // self.align } + type = "striped" + stripe_count = 1 + stripes = [ + "pv0", {self.start // self.align } + ] + }} + }}""" + +class InternalPV: + def __init__(self, images=[], align=(32 << 10), creation_time=0): + self.pvid = ID() + self.vgid = ID() + self.align = align + self.creation_time = creation_time + + self.lvs = [] + self.dsize = 0 + for img, path in images: + lv = Linear(self.dsize, align, path, img) + self.lvs.append(lv) + self.dsize += lv.asize + + # Reserve `align` bytes for primary metadata area, the size + # used by all LVs, and another `aligned` bytes for the backup + # metadata area + self.size = align + self.dsize + align + + def write(self, dst): + for i in range(4): + dst.write(self._label(i)) + + dst.write(b"\0" * 0x800) + + dst.write(self._metadata(True)) + + for lv in self.lvs: + lv.sendfile(dst.fileno()) + + dst.write(self._metadata(False)) + + def _label(self, sector: int): + headfmt, tailfmt = "<8s Q I", "", 1, + offs, size, + 512, len(txt), crc32(txt), 0, + 0, 0, 0, 0) + b"\0" * padsize + + head = struct.pack(headfmt, crc32(tail)) + + return head + tail + txt + bytes([0] * txtpadsize) + + def _meta_txt(self): + return f"""internal {{ + id = "{self.vgid.dashed()}" + seqno = 1 + status = ["RESIZEABLE", "READ", "WRITE"] + extent_size = {self.align >> 9} + max_pv = 0 + max_lv = 0 + metadata_copies = 2 + + physical_volumes {{ + pv0 {{ + id = "{self.pvid.dashed()}" + dev = "/dev/internal" + status = ["ALLOCATABLE"] + dev_size = {self.size >> 9} + pe_start = {self.align >> 9} + pe_count = {self.dsize // self.align } + }} + }} + + logical_volumes {{ +{"\n".join([lv.meta() for lv in self.lvs])} + }} +}} + +contents = "Text Format Volume Group" +version = 1 +description = "Internal image storage" +creation_host = "infix-build-system" +creation_time = {self.creation_time} +""".encode() + + +def main(): + import argparse + import datetime + import sys + + parser = argparse.ArgumentParser(description="Create LVM PV containing a set of images") + parser.add_argument("-T", "--creation-time", metavar="TIMESTAMP", + default=int(datetime.datetime.now().timestamp())) + parser.add_argument("images", metavar="IMAGE", nargs="*") + args = parser.parse_args() + args.images = list(zip(args.images[::2], args.images[1::2])) + + random.seed(args.creation_time) + + pv = InternalPV(args.images, creation_time=args.creation_time) + pv.write(sys.stdout.buffer) + +if __name__ == "__main__": + main() From b14e07c0c1dbbbc13325243e61c03480a37a85d7 Mon Sep 17 00:00:00 2001 From: Tobias Waldekranz Date: Wed, 7 Oct 2026 14:59:44 +0000 Subject: [PATCH 11/19] image-ddi-rauc: Add RAUC update bundles for DDI images --- board/common/Config.in | 1 + board/common/image/image-ddi-rauc/Config.in | 27 ++++++++++++ board/common/image/image-ddi-rauc/generate.sh | 34 ++++++++++++++ board/common/image/image-ddi-rauc/hooks.sh | 44 +++++++++++++++++++ .../image/image-ddi-rauc/image-itb-rauc.mk | 10 +++++ 5 files changed, 116 insertions(+) create mode 100644 board/common/image/image-ddi-rauc/Config.in create mode 100755 board/common/image/image-ddi-rauc/generate.sh create mode 100755 board/common/image/image-ddi-rauc/hooks.sh create mode 100644 board/common/image/image-ddi-rauc/image-itb-rauc.mk diff --git a/board/common/Config.in b/board/common/Config.in index 3f28073ca..1ccee1a14 100644 --- a/board/common/Config.in +++ b/board/common/Config.in @@ -4,6 +4,7 @@ menu "Images" comment "DDI Based" source "$BR2_EXTERNAL_INFIX_PATH/board/common/image/image-ddi/Config.in" source "$BR2_EXTERNAL_INFIX_PATH/board/common/image/image-ddi-disk/Config.in" +source "$BR2_EXTERNAL_INFIX_PATH/board/common/image/image-ddi-rauc/Config.in" comment "ITB Based" source "$BR2_EXTERNAL_INFIX_PATH/board/common/image/image-itb-rootfs/Config.in" diff --git a/board/common/image/image-ddi-rauc/Config.in b/board/common/image/image-ddi-rauc/Config.in new file mode 100644 index 000000000..6389bf363 --- /dev/null +++ b/board/common/image/image-ddi-rauc/Config.in @@ -0,0 +1,27 @@ +menuconfig IX_IMAGE_DDI_RAUC + bool "RAUC upgrade bundle (DDI)" + select IX_IMAGE_DDI + select BR2_PACKAGE_HOST_RAUC + help + Create RAUC upgrade bundle, for targets using DDI images, + that can be used to upgrade a running system to this version + of Infix. + +config IX_IMAGE_DDI_RAUC_KEY + string "signing key" + depends on IX_IMAGE_DDI_RAUC + default "${BR2_EXTERNAL_INFIX_PATH}/board/common/signing-keys/development/infix.key" + help + Path to the private key, in PKCS#8 format, used to sign + the RAUC bundle; or a PKCS#11 URI. + +config IX_IMAGE_DDI_RAUC_CERT + string "signing certificate" + depends on IX_IMAGE_DDI_RAUC + default "${BR2_EXTERNAL_INFIX_PATH}/board/common/signing-keys/development/infix.crt" + help + Path to the X509 certificate which will be associated with + the bundle signature. + + NOTE: This cert MUST be included in the trust store of the + system on which this bundle is to be installed. diff --git a/board/common/image/image-ddi-rauc/generate.sh b/board/common/image/image-ddi-rauc/generate.sh new file mode 100755 index 000000000..1caf05118 --- /dev/null +++ b/board/common/image/image-ddi-rauc/generate.sh @@ -0,0 +1,34 @@ +#!/bin/sh + +set -e + +ddi="${BINARIES_DIR}"/"${ARTIFACT}.raw" +pkg="${BINARIES_DIR}"/"${ARTIFACT}.pkg" + +cp -f "${PKGDIR}"/hooks.sh "${WORKDIR}"/hooks.sh + +# RAUC <= 1.15 uses the file extension to find a suitable install +# handler, which must be .img in order to select the "raw" type. +cp -f "${ddi}" "${WORKDIR}"/ +ln -sf "${ARTIFACT}.raw" "${WORKDIR}"/ddi.img + +cat >"${WORKDIR}"/manifest.raucm <&2 +} + +die() +{ + echo "ERROR: $*" >&2 + exit 1 +} + +case "$1" in + slot-pre-install) + [ "$RAUC_SLOT_CLASS" = "rootfs" ] || break + + dst=/dev/mapper/"$RAUC_SLOT_BOOTNAME" \ + || die "$RAUC_SLOT_BOOTNAME is not available" + dstsize=$(lvs --reportformat=json --units b "$dst" \ + | jq -r .report[0].lv[0].lv_size \ + | tr -d B) + + src="$RAUC_BUNDLE_MOUNT_POINT"/ddi.img + [ -f "$src" ] \ + || die "No DDI in bundle" + srcsize=$(stat -L -c %s "$src") + + kpartx -d "$dst" \ + || die "Unable to tear down partitions" + + [ "$dstsize" -eq "$srcsize" ] || { + lvm lvresize --yes --size ${newsize}B "$dst" \ + || die "Unable to resize $RAUC_SLOT_BOOTNAME to ${srcsize}B" + } + + note "$RAUC_SLOT_BOOTNAME is ready to accept update" + ;; + *) + exit 1 + ;; +esac diff --git a/board/common/image/image-ddi-rauc/image-itb-rauc.mk b/board/common/image/image-ddi-rauc/image-itb-rauc.mk new file mode 100644 index 000000000..942b050b6 --- /dev/null +++ b/board/common/image/image-ddi-rauc/image-itb-rauc.mk @@ -0,0 +1,10 @@ +################################################################################ +# +# image-ddi-rauc +# +################################################################################ + +IMAGE_DDI_RAUC_DEPENDENCIES := host-rauc image-ddi +IMAGE_DDI_RAUC_CONFIG_VARS := KEY CERT + +$(eval $(ix-image)) From 7536da4ad9fd725dd440d37a33859741aa4c1a4b Mon Sep 17 00:00:00 2001 From: Tobias Waldekranz Date: Thu, 26 Feb 2026 14:25:30 +0000 Subject: [PATCH 12/19] image-lvm-stub: Base image for use with 'make run' --- board/common/Config.in | 1 + board/common/image/image-lvm-stub/Config.in | 9 +++ board/common/image/image-lvm-stub/generate.sh | 58 +++++++++++++++++++ .../image/image-lvm-stub/image-lvm-stub.mk | 9 +++ 4 files changed, 77 insertions(+) create mode 100644 board/common/image/image-lvm-stub/Config.in create mode 100755 board/common/image/image-lvm-stub/generate.sh create mode 100644 board/common/image/image-lvm-stub/image-lvm-stub.mk diff --git a/board/common/Config.in b/board/common/Config.in index 1ccee1a14..60a3b92d1 100644 --- a/board/common/Config.in +++ b/board/common/Config.in @@ -18,6 +18,7 @@ source "$BR2_EXTERNAL_INFIX_PATH/board/common/image/image-itb-dl-release/Config. comment "General" source "$BR2_EXTERNAL_INFIX_PATH/board/common/image/image-barebox-esp/Config.in" +source "$BR2_EXTERNAL_INFIX_PATH/board/common/image/image-lvm-stub/Config.in" source "$BR2_EXTERNAL_INFIX_PATH/board/common/image/image-readme/Config.in" endmenu diff --git a/board/common/image/image-lvm-stub/Config.in b/board/common/image/image-lvm-stub/Config.in new file mode 100644 index 000000000..48a31f635 --- /dev/null +++ b/board/common/image/image-lvm-stub/Config.in @@ -0,0 +1,9 @@ +config IX_IMAGE_LVM_STUB + bool "LVM2 stub image" + default y if IX_IMAGE_DDI + depends on IX_IMAGE_BAREBOX_ESP + help + A disk image with Barebox and an empty LVM2 physical volume + attached to the internal volume group. This is useful as a + base image for persistent storage in QEMU emulation + scenarios where the OS is supplied in a separate image. diff --git a/board/common/image/image-lvm-stub/generate.sh b/board/common/image/image-lvm-stub/generate.sh new file mode 100755 index 000000000..5f54f60f8 --- /dev/null +++ b/board/common/image/image-lvm-stub/generate.sh @@ -0,0 +1,58 @@ +#!/bin/sh + +set -e + + +mkdir -p "${WORKDIR}"/root +rm -rf "${WORKDIR}"/tmp +mkdir -p "${WORKDIR}"/tmp + +"${BR2_EXTERNAL_INFIX_PATH}"/utils/lvm-mkinternal >"${WORKDIR}"/stub.lvm + +. $BR2_EXTERNAL_INFIX_PATH/board/common/rootfs/etc/partition-uuid +[ -n "${BAREBOX_STATE_UUID}" ] +[ -n "${ESP_UUID}" ] +[ -n "${ESP_BACKUP_UUID}" ] + +cat <"${WORKDIR}"/genimage.cfg +image lvm-stub.disk { + hdimage { + partition-table-type = "gpt" + } + + partition esp { + partition-type-uuid = "esp" + partition-uuid = "$ESP_UUID" + image = "$BINARIES_DIR/barebox-esp.vfat" + } + + partition esp-backup { + partition-type-uuid = "esp" + partition-uuid = "$ESP_BACKUP_UUID" + image = "$BINARIES_DIR/barebox-esp.vfat" + } + + partition barebox-state { + partition-type-uuid = "barebox-state" + partition-uuid = "$BAREBOX_STATE_UUID" + size = 2048 + } + + partition internal { + growfs = "true" + image = "stub.lvm" + partition-type-uuid = "lvm" + size = 2M + } +} + +# Silence genimage warnings +config {} +EOF + +genimage \ + --tmppath "${WORKDIR}"/tmp \ + --rootpath "${WORKDIR}"/root \ + --inputpath "${WORKDIR}" \ + --outputpath "${BINARIES_DIR}" \ + --config "${WORKDIR}"/genimage.cfg diff --git a/board/common/image/image-lvm-stub/image-lvm-stub.mk b/board/common/image/image-lvm-stub/image-lvm-stub.mk new file mode 100644 index 000000000..b12dfa90e --- /dev/null +++ b/board/common/image/image-lvm-stub/image-lvm-stub.mk @@ -0,0 +1,9 @@ +################################################################################ +# +# image-lvm-stub +# +################################################################################ + +IMAGE_LVM_STUB_DEPENDENCIES := image-barebox-esp + +$(eval $(ix-image)) From 104548273c66f3ae35a5693e086f96a051362b17 Mon Sep 17 00:00:00 2001 From: Tobias Waldekranz Date: Tue, 29 Sep 2026 11:21:54 +0000 Subject: [PATCH 13/19] board/common: Support signing the Linux kernel --- board/common/Config.in | 22 ++++++++++++++++++++++ board/common/common.mk | 12 ++++++++++++ 2 files changed, 34 insertions(+) diff --git a/board/common/Config.in b/board/common/Config.in index 60a3b92d1..48e0181fa 100644 --- a/board/common/Config.in +++ b/board/common/Config.in @@ -23,6 +23,28 @@ source "$BR2_EXTERNAL_INFIX_PATH/board/common/image/image-readme/Config.in" endmenu + +menuconfig IX_KERNEL_SIGN + bool "Sign Linux Kernel" + default y if BR2_x86_64 + +config IX_KERNEL_SIGN_KEY + string "signing key" + depends on IX_KERNEL_SIGN + default "${BR2_EXTERNAL_INFIX_PATH}/board/common/signing-keys/development/infix.key" + help + Path to the private RSA key, in PKCS#8 format, used to sign + the kernel image. + +config IX_KERNEL_SIGN_CERT + string "signing cert" + depends on IX_KERNEL_SIGN + default "${BR2_EXTERNAL_INFIX_PATH}/board/common/signing-keys/development/infix.crt" + help + Path to the X509 certificate whose associated public key can + validate the authenticity of the generated signature. + + config IX_QEMU_SCRIPTS bool "QEMU scripts" default y diff --git a/board/common/common.mk b/board/common/common.mk index a570f09d4..af6b73f74 100644 --- a/board/common/common.mk +++ b/board/common/common.mk @@ -2,6 +2,18 @@ include $(BR2_EXTERNAL_INFIX_PATH)/board/common/barebox/barebox.mk include $(BR2_EXTERNAL_INFIX_PATH)/board/common/image/image.mk include $(BR2_EXTERNAL_INFIX_PATH)/board/common/qemu/qemu.mk +ifeq ($(IX_KERNEL_SIGN),y) +define LINUX_POST_BUILD_SIGN + @$(call IXMSG,"Signing $(notdir $(LINUX_IMAGE_PATH))") + sbsign \ + --key "$(IX_KERNEL_SIGN_KEY)" \ + --cert "$(IX_KERNEL_SIGN_CERT)" \ + --output "$(LINUX_IMAGE_PATH)" \ + "$(LINUX_IMAGE_PATH)" +endef +LINUX_POST_BUILD_HOOKS += LINUX_POST_BUILD_SIGN +endif + ifeq ($(IX_TRUSTED_KEYS),y) include $(BR2_EXTERNAL_INFIX_PATH)/board/common/uboot/uboot.mk From 36ba06e51be00730cddd12d1851ccf7d87522ab6 Mon Sep 17 00:00:00 2001 From: Tobias Waldekranz Date: Thu, 26 Feb 2026 12:32:46 +0000 Subject: [PATCH 14/19] board/common: ddi: Add helper tool to work with DDIs First user is the initramfs, where it is used to setup the verity device. Later on, it will be extended for use by RAUC during upgrades, to fetch image info in statd, etc. --- board/common/rootfs/usr/bin/ddi | 343 ++++++++++++++++++++++++++++++++ 1 file changed, 343 insertions(+) create mode 100755 board/common/rootfs/usr/bin/ddi diff --git a/board/common/rootfs/usr/bin/ddi b/board/common/rootfs/usr/bin/ddi new file mode 100755 index 000000000..39f795e07 --- /dev/null +++ b/board/common/rootfs/usr/bin/ddi @@ -0,0 +1,343 @@ +#!/bin/sh + +set -e + +capath=/etc/rauc/keys + +usage() +{ + local _ddi="$(basename $0)" + + cat <] [] + +Discoverable Disk Image manipulation + + Options: + + -A + Override the system architecture. + + -C + Source trusted signing keys from the supplied directory. + Default: $capath + + -h + Show this message and exit. + + Commands: + + inspect + + uuid + Print GPT disk identifier + + part [ is one of: + root The first root partition + + open [] + + Create a device mapping for the specified partition, called + . + + Options: + + -v + Use the associated dm-verity hash tree, whose root hash is + available in the associated signature partition. + + -V + Use the associated dm-verity hash tree, along with an + externally validated root hash. + + verify [] + + Verify that the specified partition exists and, optionally, + is accompanied by a trusted dm-verity hash tree. + + Options: + + -v + Require the presence of an associated dm-verity hash tree, + whose root hash is available in the associated signature + partition. + + -V + Require the presence of an associated dm-verity hash tree + whose root hash matches the provided one. + + Examples: + + Map and mount the verity protected SquashFS root partition from an + image: + $_ddi infix-x86_64.raw part root open -v infix + mount -t squashfs /dev/mapper/infix /opt + +EOF +} + +X86_64_CODE_ROOT=0x8304 +X86_64_CODE_ROOT_VERITY=0x830c +X86_64_CODE_ROOT_VERITY_SIG=0x8370 + +die() +{ + echo "ERROR: $*" >&2 + exit 1 +} + +ok() +{ + echo "$*" >&2 +} + +setup_arch() +{ + case "$1" in + x86_64) + CODE_ROOT=$X86_64_CODE_ROOT + CODE_ROOT_VERITY=$X86_64_CODE_ROOT_VERITY + CODE_ROOT_VERITY_SIG=$X86_64_CODE_ROOT_VERITY_SIG + ;; + *) + die "Unsupported architecture" + esac +} + +setup_work() +{ + local _umask + + _umask=$(umask) + umask 077 + workdir=$(mktemp -d) + umask $_umask + + trap 'rm -rf "$workdir"' EXIT INT TERM +} + +ddi_to_block() +{ + local _kind _lo + + _kind=$(stat -Lc %F "$1") + case "$_kind" in + "block special file") + echo "$1" + return + ;; + "regular file") + _lo=$(losetup -f || die "Found no free loop device for $1") + losetup -P "$_lo" "$1" || die "Failed to setup loop device of $1" + echo "$_lo" + return + ;; + esac + + die "$1 is a $_kind, only regular and block devices are supported" +} + +ddi_get_part_info() +{ + local _code _item + + case "$2" in + root) _code="$CODE_ROOT";; + root-verity) _code="$CODE_ROOT_VERITY";; + root-verity-sig) _code="$CODE_ROOT_VERITY_SIG";; + *) die "Unknown partition type \"$1\"" ;; + esac + _code=$(printf "%04X" "$_code") + _item=${3:-exists} + + sgdisk "$1" -p | awk -v code="$_code" -v item="$_item" ' + /^[ ]*[0-9]+/ { + if ($6 != code) + next; + + if (item == "exists") exit(0); + if (item == "index") { print($1); exit(0); } + + exit(1); + } + ' +} + +ddi_get_part_dev() +{ + local _dev _index _lvuuid _subsys + + _index=$(ddi_get_part_info "$1" "$2" index \ + || die "No $2 partition found on $1") + + _subsys=$(dmsetup info -c --noheadings -o subsystem "$1" 2>/dev/null || true) + if [ "$_subsys" = "LVM" ]; then + _lvuuid=$(dmsetup info -c --noheadings -o uuid "$1") + _dev=/dev/mapper/$(dmsetup info -c --noheadings -o name -u "part${_index}-$_lvuuid") + else + _dev=$(basename "$1") + _dev=$(find /sys/block/"$_dev"/ -mindepth 1 -maxdepth 1 -type d \ + -name "${_dev}$_index" -o -name "${_dev}p$_index") + _dev=/dev/$(basename "$_dev") + fi + + [ -b "$_dev" ] || die "Failed to locate $2 partition in $1" + + echo "$_dev" +} + +ddi_get_root_hash() +{ + local _subj + + command -v openssl >/dev/null || die "Missing dependency: openssl" + command -v jq >/dev/null || die "Missing dependency: jq" + + jq -r .rootHash "$1" | tr -d '\n' >"$workdir"/hash \ + || die "Invalid signature: rootHash is missing" + + jq -r .signature "$1" | base64 -d >"$workdir"/sig \ + || die "Invalid signature: signature data is missing" + + openssl cms -verify -verify_retcode -CApath "$capath" \ + -content "$workdir"/hash \ + -in "$workdir"/sig -inform DER \ + -certsout "$workdir"/certs >/dev/null 2>&1 \ + || die "Signature verification failed" + + _subj=$(openssl x509 -noout -subject <"$workdir"/certs | sed -e 's/^subject=//') + ok "Trusting root hash signature from $_subj" + + cat "$workdir"/hash +} + +ddipart_get_verity() +{ + local _hash + + while getopts "vV:" opt; do + case "$opt" in + V) + vkind="${kind}-verity" + vhash="$OPTARG" + ;; + v) + vkind="${kind}-verity" + ;; + esac + done + + [ "$vkind" ] || return + + vpart=$(ddi_get_part_dev "$img" "$vkind") + + [ "$vhash" ] && return + + vskind="${vkind}-sig" + vspart=$(ddi_get_part_dev "$img" "$vskind") + vhash=$(ddi_get_root_hash "$vspart") +} + +ddipart_open() +{ + [ "$vpart" ] || die "$kind partition is already accessible at $part" + + shift $((OPTIND - 1)) + + veritysetup open "$part" "$1" "$vpart" "$vhash" +} + +ddipart_verify() +{ + [ "$vpart" ] || return + + veritysetup verify "$part" "$vpart" "$vhash" + ok "Image in $img has $kind partition with valid dm-verity hash tree" +} + +ddipart() +{ + local _cmd + + kind="$1" + shift + + ddi_get_part_info "$img" "$kind" \ + || die "No $kind partition found on $img" + + _cmd="$1" + shift + + case "$_cmd" in + open|verify) + img=$(ddi_to_block $img) + part=$(ddi_get_part_dev "$img" "$kind") + ddipart_get_verity "$@" + + ddipart_$_cmd "$@" + ;; + *) + die "Unknown partition command \"$_cmd\"" + ;; + esac +} + +ddiinspect() +{ + case "$1" in + uuid) + sgdisk "$img" -p | awk ' + BEGIN { err = 1; } END { exit(err); } + /^Creating new GPT/ { exit; } + + /^Disk identifier \(GUID\): / { + print($4); err = 0; exit; + }' + ;; + *) + die "Unknown property: $1" + ;; + esac +} + +_arch=$(uname -m) + +while getopts "A:C:h" opt; do + case ${opt} in + A) + _arch="$OPTARG" + ;; + C) + capath="$OPTARG" + ;; + h) + usage && exit 0 + ;; + esac +done +shift $((OPTIND - 1)) + +if [ $# -lt 2 ]; then + usage && exit 1 +fi + +img="$1" +cmd="$2" +shift 2 + +setup_arch "$_arch" +setup_work + +case "$cmd" in + inspect) + ddiinspect "$@" + ;; + part) + ddipart "$@" + ;; + *) + echo "Unknown command \"$cmd\"" >2 + exit 1 + ;; +esac From cea7e1dd97b02905cf9d4528acf2ea0657be30d3 Mon Sep 17 00:00:00 2001 From: Tobias Waldekranz Date: Wed, 4 Feb 2026 18:37:20 +0000 Subject: [PATCH 15/19] initramfs: Build initramfs capable of booting DDI images --- board/common/common.mk | 1 + board/common/image/image-ddi/Config.in | 1 + board/common/initramfs/dracut.conf | 104 +++++++ board/common/initramfs/initramfs.mk | 38 +++ .../modules.d/01infix-lib64/module-setup.sh | 15 + .../initramfs/modules.d/10infix-init/init | 276 ++++++++++++++++++ .../modules.d/10infix-init/module-setup.sh | 20 ++ board/x86_64/linux_defconfig | 5 +- 8 files changed, 459 insertions(+), 1 deletion(-) create mode 100644 board/common/initramfs/dracut.conf create mode 100644 board/common/initramfs/initramfs.mk create mode 100755 board/common/initramfs/modules.d/01infix-lib64/module-setup.sh create mode 100755 board/common/initramfs/modules.d/10infix-init/init create mode 100755 board/common/initramfs/modules.d/10infix-init/module-setup.sh diff --git a/board/common/common.mk b/board/common/common.mk index af6b73f74..fd31bfb01 100644 --- a/board/common/common.mk +++ b/board/common/common.mk @@ -1,5 +1,6 @@ include $(BR2_EXTERNAL_INFIX_PATH)/board/common/barebox/barebox.mk include $(BR2_EXTERNAL_INFIX_PATH)/board/common/image/image.mk +include $(BR2_EXTERNAL_INFIX_PATH)/board/common/initramfs/initramfs.mk include $(BR2_EXTERNAL_INFIX_PATH)/board/common/qemu/qemu.mk ifeq ($(IX_KERNEL_SIGN),y) diff --git a/board/common/image/image-ddi/Config.in b/board/common/image/image-ddi/Config.in index 75b6a1c94..42a80abe6 100644 --- a/board/common/image/image-ddi/Config.in +++ b/board/common/image/image-ddi/Config.in @@ -9,6 +9,7 @@ menuconfig IX_IMAGE_DDI select BR2_PACKAGE_MULTIPATH_TOOLS select BR2_PACKAGE_UTIL_LINUX_BINARIES select BR2_PACKAGE_HOST_GENIMAGE + select BR2_PACKAGE_HOST_DRACUT help Create a Discoverable Disk Image containing: - SquashFS of the root filesystem diff --git a/board/common/initramfs/dracut.conf b/board/common/initramfs/dracut.conf new file mode 100644 index 000000000..126b38d55 --- /dev/null +++ b/board/common/initramfs/dracut.conf @@ -0,0 +1,104 @@ +#Simple dracut config for a system without systemd + +#Dracut configuration + +show_modules=yes +i18n_install_all=no +lvmconf=no +mdadmconf=no +early_microcode=no +hostonly=no +hostonly_cmdline=no +use_fstab=no +kernel_cmdline="rd.break=initqueue" +do_strip=no + +# Dracut modules needed +add_dracutmodules+=" \ +busybox \ +infix-init \ +infix-lib64 +" + +# Modules to ignore +omit_dracutmodules+=" \ +base \ +bash \ +biosdevname \ +btrfs \ +bluetooth \ +caps \ +cifs \ +connman \ +crypt \ +crypt-gpg \ +dash \ +dbus-broker \ +dbus-daemon \ +dm \ +dmraid \ +dmsquash-live-ntfs \ +dracut-systemd \ +fcoe \ +fcoe-uefi \ +fs-lib \ +iscsi \ +i18n \ +kernel-modules \ +kernel-modules-extra \ +lvmmerge \ +lvm \ +lunmask \ +mdraid \ +memstrack \ +mksh \ +modsign \ +mount-root \ +multipath \ +nbd \ +network-legacy \ +network-wicked \ +nfs \ +nvdimm \ +nvmf \ +parse-lunmask \ +pcsc \ +qemu \ +qemu-net \ +resume \ +rootfs-block \ +rngd \ +shutdown \ +systemd \ +systemd-udevd \ +systemd-coredump \ +systemd-ask-password \ +systemd-timedated \ +systemd-rfkill \ +systemd-resolved \ +systemd-hostnamed \ +systemd-initrd \ +systemd-integritysetup \ +systemd-journald \ +systemd-ldconfig \ +systemd-networkd \ +systemd-timesyncd \ +systemd-veritysetup \ +systemd-modules-load \ +systemd-pcrphase \ +systemd-portabled \ +systemd-pstore \ +systemd-sysctl \ +systemd-repart \ +systemd-sysext \ +systemd-sysusers \ +systemd-tmpfiles \ +squash \ +ssh-client \ +stratis \ +tpm2-tss \ +udev-rules \ +url-lib \ +usrmount \ +virtiofs \ +" diff --git a/board/common/initramfs/initramfs.mk b/board/common/initramfs/initramfs.mk new file mode 100644 index 000000000..1a033464a --- /dev/null +++ b/board/common/initramfs/initramfs.mk @@ -0,0 +1,38 @@ +ifeq ($(IX_IMAGE_DDI),y) + +IX_INITRAMFS_DIR := $(BR2_EXTERNAL_INFIX_PATH)/board/common/initramfs +IX_INITRAMFS_BUILD := $(BUILD_DIR)/infix-initramfs +IX_INITRAMFS_DEPS := \ + host-dracut \ + host-fakeroot \ + busybox \ + cryptsetup \ + gptfdisk \ + jq \ + lvm2 \ + multipath-tools \ + util-linux + +define LINUX_PRE_BUILD_INITRAMFS + @$(call IXMSG,"Creating Infix initramfs") + mkdir -p $(IX_INITRAMFS_BUILD) + cp -a $(IX_INITRAMFS_DIR)/modules.d/* $(HOST_DIR)/lib/dracut/modules.d/ + $(HOST_DIR)/bin/fakeroot $(HOST_DIR)/bin/dracut \ + -c $(IX_INITRAMFS_DIR)/dracut.conf \ + --kver $(LINUX_VERSION) --no-kernel \ + --sysroot $(TARGET_DIR) \ + --tmpdir $(IX_INITRAMFS_BUILD) \ + -M \ + --force \ + --no-compress \ + $(@D)/infix-initramfs.cpio +endef +LINUX_PRE_BUILD_HOOKS += LINUX_PRE_BUILD_INITRAMFS + +# We are late to the party here, buildroot/linux/linux.mk has already +# been sourced, so in addition to the dependency list, we also need to +# "manually" extend the deps for the kernel's configure target. +LINUX_DEPENDENCIES += $(IX_INITRAMFS_DEPS) +$(LINUX_TARGET_CONFIGURE): | $(IX_INITRAMFS_DEPS) + +endif diff --git a/board/common/initramfs/modules.d/01infix-lib64/module-setup.sh b/board/common/initramfs/modules.d/01infix-lib64/module-setup.sh new file mode 100755 index 000000000..790e9d6b9 --- /dev/null +++ b/board/common/initramfs/modules.d/01infix-lib64/module-setup.sh @@ -0,0 +1,15 @@ +#!/bin/bash + +check() { + return 0 +} + +depends() { + return 0 +} + +install() { + # Not sure if this should be handled by buildroot's merged-usr + # module. Fix it here for now. + ln -s "lib" "${initdir?}/lib64" +} diff --git a/board/common/initramfs/modules.d/10infix-init/init b/board/common/initramfs/modules.d/10infix-init/init new file mode 100755 index 000000000..56a1746a8 --- /dev/null +++ b/board/common/initramfs/modules.d/10infix-init/init @@ -0,0 +1,276 @@ +#!/bin/sh + +echo -e "\e[31m●\e[0m \e[33m●\e[0m \e[32m●\e[0m Infix Initramfs — @VERSION@" + +set -e + +getkopt() +{ + grep -q -E "(^| )$1( |$)" /proc/cmdline +} + +getkparam() +{ + sed -n "s/.*\<$1=\([^ ]*\).*/\1/p" /proc/cmdline +} + +debug_shell() +{ + [ "$(getkparam rd.shell)" = "$1" ] || return 0 + + echo "Starting debug shell at $1" + exec sh -i +} + +die() +{ + step="${*:-$step}" + + echo -e "\r\e[0K[\e[31mFAIL\e[0m] $step" + echo "<0>ix-initramfs: ERROR: $step" >/dev/kmsg + debug_shell error + exit 1 +} + +warn() +{ + step="${*:-$step}" + + echo -e "\r\e[0K[\e[33mWARN\e[0m] $step" + echo "<5>ix-initramfs: WARNING: $step" >/dev/kmsg + debug_shell warning +} + +ok() +{ + step="${*:-$step}" + + echo -e "\r\e[0K[\e[32m OK \e[0m] $step" + echo "<5>ix-initramfs: $step" >/dev/kmsg +} + +begin() +{ + step="$*" + echo -ne "[\e[33m ⋯ \e[0m] $step" +} + +debug() +{ + echo "<7>ix-initramfs: $*" >/dev/kmsg +} + +getdisk() +{ + local _disk _uuid _kind + + case "$1" in + PARTUUID=*) + _uuid=$(echo "$1" | sed -e 's/PARTUUID=//' | tr 'A-F' 'a-f') + _disk=/dev/disk/by-partuuid/"$_uuid" + ;; + *) + _disk="$1" + ;; + esac + + _disk=$(realpath "$_disk") + _kind=$(stat -c %F "$_disk" 2>/dev/null || true) + [ "$_kind" = "block special file" ] && echo "$_disk" || true +} + +mountcore() +{ + mount -t proc proc /proc + mount -t sysfs sys /sys + mount -t devtmpfs dev /dev +} + +mountroot() +{ + local _disk=$(getdisk $(getkparam root)) + + [ -z "$_disk" ] \ + && die "Unknown root device \"$(getkparam root)\"" + + mkdir /newroot + mount "$_disk" /newroot || \ + die "Unable to mount rootfs on $_disk" + + ok "Mounted root filesystem" +} + +switchroot() +{ + local innerinit=$(getkparam init) + + begin "Switching to root filesystem" + exec switch_root /newroot ${innerinit:-/sbin/init} + die "Unable to switch to root filesystem" +} + +internal_get_part() +{ + local _i _pv + + for _i in $(seq 50); do + _pv=$(lvm pvs --reportformat json \ + | jq -r '.report[].pv[] | select(.vg_name == "internal") | .pv_name') + if [ "$_pv" ]; then + [ $_i -gt 5 ] && ok + echo "$_pv" + return 0 + fi + + [ $_i -eq 5 ] && begin "Waiting for the internal volume to come online" + sleep .2 + done + + warn "Proceeding without the internal volume" + return 0 +} + +internal_inflate() +{ + local _dev _growfs _index _part + + getkopt rd.nointernal && return 0 + + GPT_PART_GROWFS_BIT=59 + + _part=$(internal_get_part) + [ "$_part" ] || return 0 + + _index=$(cat /sys/class/block/$(basename "$_part")/partition) + [ "$_index" ] || return 1 + + _dev=/dev/$(basename $(realpath /sys/class/block/$(basename "$_part")/../)) + [ -b "$_dev" ] || return 1 + + sgdisk "$_dev" -A "$_index":get:$GPT_PART_GROWFS_BIT \ + | grep -q "$_index:$GPT_PART_GROWFS_BIT:1" \ + || return 0 + + # Make a note to userspace that this is the first time the system + # is booting. + touch /dev/firstboot + + begin "Inflating internal VG in $_part" + + # First, expand the GPT to cover the entire disk and expand the + # LVM partition as much as possible. + if ! sgdisk "$_dev" \ + --disk-guid=random \ + --set-alignment=1 \ + --move-second-header \ + --delete="$_index" \ + --largest-new="$_index" --typecode="$_index":8e00 \ + >/tmp/internal-inflate.log 2>&1; then + warn "Failed to inflate GPT on $_dev" + return 0 + fi + + if ! lvm pvresize "$_part" >>/tmp/internal-inflate.log 2>&1; then + warn "Failed to resize PV $_part" + return 0 + fi + + ok "Inflated internal VG in $_part" +} + +initrd_probe() +{ + getkopt rd.noinitrd && return 0 + [ -f /initrd.image ] || return 0 + + begin "Creating initrd loop device" + + if ! losetup -P -f /initrd.image; then + warn "initrd_probe: losetup failed" + return 0 + fi + + ok "Created initrd loop device" +} + +partuuids_probe() +{ + mkdir -p /dev/disk/by-partuuid + + grep -H PARTUUID= /sys/class/block/*/uevent | \ + sed -E 's|^/sys/class/block/([^/]+)/uevent:PARTUUID=(.*)$|\1 \2|' | \ + while read name uuid; do + ln -s /dev/"$name" /dev/disk/by-partuuid/"$uuid" 2>/dev/null || true + done +} + +lvm_probe() +{ + getkopt rd.nolvm && return 0 + + begin "Activating logical volumes" + + if ! lvm vgchange -ay &>/dev/null; then + warn "lvm_probe: Failed to activate the $_vg VG" + return 0 + fi + + for _lv in $(dmsetup info -c --noheadings -o name); do + sfdisk -J /dev/mapper/"$_lv" >/tmp/lv 2>/dev/null || continue + jq -e '.partitiontable.label == "gpt"' /tmp/lv >/dev/null 2>&1 || continue + + jq -r '.partitiontable | select(.label == "gpt") | + .partitions[] | "\(.uuid | ascii_downcase) \(.node)"' /tmp/lv | \ + while read uuid node; do + ln -s "$node" /dev/disk/by-partuuid/"$uuid" 2>/dev/null || true + done + + kpartx -a -p-part /dev/mapper/"$_lv" || \ + warn "lvm_probe:: Unable to add partitions from $_lv" + done + + ok "Activated logical volumes" +} + +uuidfmt() +{ + echo $1 | \ + sed -E 's/^([0-9a-fA-F]{8})([0-9a-fA-F]{4})([0-9a-fA-F]{4})([0-9a-fA-F]{4})([0-9a-fA-F]{12})$/\1-\2-\3-\4-\5/' +} + +dm_verity_root_probe() +{ + local _root _hash + + [ -z "$roothash" ] && return 0 + + begin "Creating verity protected root" + + _root=$(getdisk PARTUUID=$(uuidfmt $(echo "$roothash" | cut -c 1-32))) + [ "$_root" ] || die "dm_verity_probe: Unknown verity root data device" + + _hash=$(getdisk PARTUUID=$(uuidfmt $(echo "$roothash" | cut -c 33-64))) + [ "$_hash" ] || die "dm_verity_probe: Unknown verity root hash device" + + veritysetup open "$_root" root "$_hash" "$roothash" \ + || die "dm_verity_probe: veritysetup failed" + + ok "Created verity protected root" +} + +mountcore + +debug_shell internal +internal_inflate + +debug_shell probe +initrd_probe +partuuids_probe +lvm_probe +dm_verity_root_probe + +debug_shell mount +mountroot + +debug_shell switchroot +switchroot diff --git a/board/common/initramfs/modules.d/10infix-init/module-setup.sh b/board/common/initramfs/modules.d/10infix-init/module-setup.sh new file mode 100755 index 000000000..011a90fb7 --- /dev/null +++ b/board/common/initramfs/modules.d/10infix-init/module-setup.sh @@ -0,0 +1,20 @@ +#!/bin/bash + +progs="dmsetup kpartx jq lvm sgdisk sfdisk veritysetup" + +check() { + require_binaries $progs || return 1 + + return 0 +} + +depends() { + return 0 +} + +install() { + inst_multiple $progs + + cp "$moddir/init" "${initdir?}/init" + sed -e "s/@VERSION@/${INFIX_VERSION}/" -i "${initdir?}/init" +} diff --git a/board/x86_64/linux_defconfig b/board/x86_64/linux_defconfig index 4b9f5d050..f4486904c 100644 --- a/board/x86_64/linux_defconfig +++ b/board/x86_64/linux_defconfig @@ -29,10 +29,14 @@ CONFIG_CGROUP_BPF=y CONFIG_USER_NS=y CONFIG_SCHED_AUTOGROUP=y CONFIG_BLK_DEV_INITRD=y +CONFIG_INITRAMFS_SOURCE="infix-initramfs.cpio" +CONFIG_INITRAMFS_COMPRESSION_ZSTD=y CONFIG_KALLSYMS_ALL=y CONFIG_PROFILING=y CONFIG_SMP=y CONFIG_EFI=y +CONFIG_EFI_STUB=y +# CONFIG_EFI_HANDOVER_PROTOCOL is not set CONFIG_KPROBES=y # CONFIG_GCC_PLUGINS is not set CONFIG_MODULES=y @@ -307,7 +311,6 @@ CONFIG_VIRTIO_INPUT=y CONFIG_VIRTIO_MMIO=y CONFIG_VIRTIO_MMIO_CMDLINE_DEVICES=y CONFIG_EXT2_FS=y -CONFIG_EXT2_FS_POSIX_ACL=y CONFIG_EXT4_FS=y CONFIG_EXT4_FS_POSIX_ACL=y CONFIG_BTRFS_FS=y From 78c20ab3a52c998b30ecd4647c3ad0216a7c0765 Mon Sep 17 00:00:00 2001 From: Tobias Waldekranz Date: Fri, 27 Feb 2026 23:18:09 +0000 Subject: [PATCH 16/19] qemu: Support running from DDI images run.sh has degenerated quite a bit over the years - in no small part due to the way the qemu-system argv is constructed. Cut our losses by taking a fresh look at this problem, now that lots of things need to change anyway. Keep the old script as-is for targets that still use ITB based images. --- board/common/qemu/ddi/Config.in.in | 339 +++++++++++++ board/common/qemu/ddi/run.sh | 582 +++++++++++++++++++++++ board/common/qemu/{ => itb}/Config.in.in | 0 board/common/qemu/{ => itb}/run.sh | 0 board/common/qemu/qemu.mk | 29 +- 5 files changed, 941 insertions(+), 9 deletions(-) create mode 100644 board/common/qemu/ddi/Config.in.in create mode 100755 board/common/qemu/ddi/run.sh rename board/common/qemu/{ => itb}/Config.in.in (100%) rename board/common/qemu/{ => itb}/run.sh (100%) diff --git a/board/common/qemu/ddi/Config.in.in b/board/common/qemu/ddi/Config.in.in new file mode 100644 index 000000000..79a35f28f --- /dev/null +++ b/board/common/qemu/ddi/Config.in.in @@ -0,0 +1,339 @@ +mainmenu "QEMU Virtualization" + +choice + prompt "Target Architecture" + default @ARCH@ + +config IX_QEMU_aarch64 + bool "AArch64 (little endian)" + +config IX_QEMU_arm + bool "AArch32 (little endian)" + +config IX_QEMU_riscv64 + bool "riscv64" + +config IX_QEMU_x86_64 + bool "x86_64" + +endchoice + +config IX_QEMU_ARCH + string + default "aarch64" if IX_QEMU_aarch64 + default "arm" if IX_QEMU_arm + default "riscv64" if IX_QEMU_riscv64 + default "x86_64" if IX_QEMU_x86_64 + +config IX_QEMU_VIRTIO_BUS + string + default "device" if IX_QEMU_arm + default "pci" + +config IX_QEMU_BIN_DDI + string + default "@IMAGE@" + +config IX_QEMU_BIN_DISK + string + default "@DISK@" + +config IX_QEMU_BIN_KERNEL + string + depends on IX_QEMU_LOADER_QEMU + default "../zImage" if IX_QEMU_arm + default "../Image" if IX_QEMU_aarch64 + default "../bzImage" if IX_QEMU_x86_64 + +config IX_QEMU_BIN_LVM_STUB + string + default "../lvm-stub.disk" + + +choice + prompt "Loader" + default IX_QEMU_LOADER_QEMU + +config IX_QEMU_LOADER_QEMU + bool "QEMU" + +config IX_QEMU_LOADER_OVMF + bool "OVMF (UEFI)" + +endchoice + +menu "OVMF" + depends on IX_QEMU_LOADER_OVMF + +config IX_QEMU_BIN_OVMF_CODE + string "CODE binary" + default "/usr/share/OVMF/OVMF_CODE_4M.secboot.fd" if IX_QEMU_OVMF_SB + default "/usr/share/OVMF/OVMF_CODE_4M.fd" + help + Path to the OVMF executable binary, passed as parallel FLASH + 0 (read-only). + +config IX_QEMU_BIN_OVMF_VARS + string "VARS binary" + default "/usr/share/OVMF/OVMF_VARS_4M.fd" + help + Path to the binary containing the OVMF non-volatile variable + store, passed as parallel FLASH 1. + +config IX_QEMU_OVMF_SB + bool "Secure boot" + default y + +config IX_QEMU_OVMF_SB_PK + string "Platform key (PK)" + depends on IX_QEMU_OVMF_SB + default "./infix-development.crt" + +config IX_QEMU_OVMF_SB_KEK + string "Key exchange key (KEK)" + depends on IX_QEMU_OVMF_SB + default "./infix-development.crt" + +config IX_QEMU_OVMF_SB_DB + string "Signature database keys (db)" + depends on IX_QEMU_OVMF_SB + default "./infix-development.crt" + +endmenu + +menu "Machine" + +config IX_QEMU_MACHINE + string "Machine" + default "q35,smm=on" if IX_QEMU_x86_64 + default "virt" + +config IX_QEMU_CPU + string "CPU" + default "host" if IX_QEMU_x86_64 + default "cortex-a53" if IX_QEMU_aarch64 + default "max" + +config IX_QEMU_RAM + string "RAM size (k/M/G)" + default "384M" + +choice + prompt "Console" + default IX_QEMU_CONSOLE_VIRTIO + +config IX_QEMU_CONSOLE_VIRTIO + bool "Virtio (hvc0)" + +config IX_QEMU_CONSOLE_SERIAL + bool "Serial (ttyS0/ttyAMA0)" + +endchoice + +config IX_QEMU_CONSOLE + string + default "hvc0" if IX_QEMU_CONSOLE_VIRTIO + default "ttyAMA0" if IX_QEMU_arm + default "ttyAMA0" if IX_QEMU_aarch64 + default "ttyS0" + +endmenu + +menu "Disks & Filesystems" + +config IX_QEMU_DISK_INITRD + bool "Use the DDI as initrd" + depends on IX_QEMU_LOADER_QEMU + help + Pass, and boot from, the DDI as the initial RAM disk. + + This is useful to test netbooting scenarios, e.g., in + combination with an empty system disk to simulate an initial + deployment. + +comment "System disk" + +choice + prompt "Interface" + default IX_QEMU_DISK_SYS_IF_VIRTIO + +config IX_QEMU_DISK_SYS_IF_VIRTIO + bool "virtio" + help + Attach system disks using virtio block devices. + +endchoice + +choice + prompt "Contents" + default IX_QEMU_DISK_SYS_SPLIT + +config IX_QEMU_DISK_SYS_EMPTY + bool "Empty disk" + help + Attach an empty disk. + + This is useful for testing things like installing Infix from a + USB mass storage device (see "USB Mass Storage" below). + +config IX_QEMU_DISK_SYS_FULL + bool "Full system disk" + help + Use the full system disk containing: + - EFI partition (and backup) with Barebox + - LVM PV with Infix installed + + This is useful when testing system upgrades, fallback + mechanisms related to corrupt disks etc. + + WARNING: Usage of this option is fraught with peril as + incremental builds of Infix are likely to cause corruption in + the QCoW2 layer. If you do not understand the meaning of this + you most likely to NOT want to make use of this feature. + +config IX_QEMU_DISK_SYS_NONE + bool "None" + help + Do not attach any system disk. + + This is useful for testing how Infix behaves when no persistent + storage is available - a common scenario during bringup of a + new board. + +config IX_QEMU_DISK_SYS_SPLIT + bool "Split DDI/LVM" + help + Attach one disk containing the EFI System partition and an + (initially empty) LVM stub for persistent storage; and the raw + DDI image in another disk. + + This is the simplest way of testing out incremental builds of + Infix. Since the DDI is attached as a raw read-only disk, there + is no risk of CoW corruption like with a full system disk. + + NOTE: In this setup, the size below refers to the size of the + LVM stub. + +endchoice + +config IX_QEMU_DISK_SYS_SIZE + string "Size (k/M/G)" + default "8G" + +comment "USB mass storage" + +choice + prompt "Contents" + default IX_QEMU_DISK_USB_NONE + +config IX_QEMU_DISK_USB_DISK + bool "Full system disk" + help + Attach the full system disk containing: + - EFI partition (and backup) with Barebox + - LVM PV with Infix installed + +config IX_QEMU_DISK_USB_NONE + bool "None" + help + Do not attach any disk over USB. + +endchoice + +config IX_QEMU_DISK_USB_SIZE + string "Size (k/M/G)" + depends on !IX_QEMU_DISK_USB_NONE + default "4G" + +comment "Host filesystem passthrough (9P)" + +config IX_QEMU_DISK_HOST + string "Path" + default "/tmp" + +endmenu + +menu "Networking" + +comment "Host attachment" + +choice + prompt "Mode" + default IX_QEMU_NET_USER + +config IX_QEMU_NET_BRIDGE + bool "Bridged" + +config IX_QEMU_NET_NONE + bool "None" + +config IX_QEMU_NET_ROCKER + bool "Rocker" + +config IX_QEMU_NET_TAP + bool "TAP" + +config IX_QEMU_NET_USER + bool "User" + +endchoice + +config IX_QEMU_NET_MODE + string + default "bridge" if IX_QEMU_NET_BRIDGE + default "none" if IX_QEMU_NET_NONE + default "rocker" if IX_QEMU_NET_ROCKER + default "tap" if IX_QEMU_NET_TAP + default "user" if IX_QEMU_NET_USER + + +config IX_QEMU_NET_BRIDGE_DEV + string "Bridge device" + depends on IX_QEMU_NET_BRIDGE + default "virbr0" + +config IX_QEMU_NET_USER_NETBOOT + bool "Enable netbooting" + depends on IX_QEMU_NET_USER + help + Supply the DDI as the DHCP bootfile and serve the images + directory over TFTP. + +config IX_QEMU_NET_USER_OPTS + string "User mode options" + depends on IX_QEMU_NET_USER + help + Extra -nic user, + +config IX_QEMU_NET_TAP_N + int "Number of TAPs" + depends on IX_QEMU_NET_TAP + default 1 + +config IX_QEMU_NET_PORTS + int "Number of Rocker switch ports" + depends on IX_QEMU_NET_ROCKER + default 10 + + +comment "VM attachment" + +config IX_QEMU_NET_MODEL + string "Interface model" + default "virtio-net-device" if IX_QEMU_arm + default "virtio-net-pci" + help + The default virtio NIC works for most use-cases, but if you + want to play with low-level stuff like ethtool, you might + want to test the Intel 82545EM driver, e1000. + + Note: ARM 32-bit uses virtio-net-device (MMIO) by default. + +endmenu + +config IX_QEMU_EXTRA + string "Extra QEMU options" + +config IX_QEMU_APPEND + string "Extra kernel options" + depends on IX_QEMU_LOADER_QEMU diff --git a/board/common/qemu/ddi/run.sh b/board/common/qemu/ddi/run.sh new file mode 100755 index 000000000..4f57ae7d8 --- /dev/null +++ b/board/common/qemu/ddi/run.sh @@ -0,0 +1,582 @@ +#!/bin/sh +# This script can be used to start an Infix OS image in Qemu. It reads +# either a .config, generated from Config.in, or qemu.cfg from a release +# tarball, for the required configuration data. +# +# Debian/Ubuntu users can change the configuration post-release, install +# the kconfig-frontends package: +# +# sudo apt install kconfig-frontends +# +# and then call this script with: +# +# ./run.sh -c +# +# To bring up a menuconfig dialog. Select `Exit` and save the changes. +# For more help, see:_ +# +# ./run.sh -h +# +# shellcheck disable=SC3037 + +# Add /sbin to PATH for mkfs.ext4 and such (not default in debian) +export PATH="/sbin:/usr/sbin:$PATH" + +qdir=$(dirname "$(readlink -f "$0")") +imgdir=$(readlink -f "${qdir}/..") +prognm=$(basename "$0") + +usage() +{ + cat <&2 + exit 1 +} + +binpath() +{ + case "$1" in + ./*|../*) + # Relative paths are relative to the location of .config + printf "$qdir/$1" + ;; + *) + printf "$1" + ;; + esac +} + +q() +{ + local _cmd="$1" + shift + + case "$_cmd" in + sect) + printf ' \\\n\t' >>"$qdir"/qemu.sh + + case $# in + 1) + printf -- "-$1" >>"$qdir"/qemu.sh + _delim=" " + ;; + 2) + printf -- "-$1 $2" >>"$qdir"/qemu.sh + _delim="," + ;; + *) + die "q sect: Invalid arguments" + ;; + esac + ;; + param) + [ $# -eq 2 ] || die "q param: Takes exactly two arguments" + printf -- "$_delim$1=$2" >>"$qdir"/qemu.sh + _delim="," + ;; + option) + [ $# -eq 1 ] || die "q option: Takes exactly one argument" + printf -- "$_delim$1" >>"$qdir"/qemu.sh + _delim="," + ;; + *) + die "q: Unknown command: $_cmd" + ;; + esac +} + +q_sect_device_virtio() +{ + q sect device virtio-$1-$IX_QEMU_VIRTIO_BUS +} + +q_add_disk() +{ + if [ "$IX_QEMU_DISK_SYS_IF_VIRTIO" ]; then + q_sect_device_virtio blk + q param drive "$1" + else + die "Unknown system disk interface" + fi +} + +q_sect_device_usb() +{ + if [ -z "$usb_bus_added" ]; then + q sect usb + q sect device usb-ehci + q param id ehci + usb_bus_added=YES + fi + + q sect device "$1" + q param bus ehci.0 +} + +qcowed() +{ + local _qcow="$qdir"/"$1".qcow2 + local _base _size + + if [ -f "$_qcow" ] && qemu-img check -q "$_qcow"; then + echo "$_qcow" + return + fi + + rm -f "$_qcow" + + case $# in + 2) + _size="$2" + + qemu-img create -q -f qcow2 "$_qcow" "$_size" \ + || die "Unable to create $1" + ;; + 3) + + _base="$2" + _size="$3" + + [ "$_size" = "auto" ] && _size= + + qemu-img create -q -f qcow2 -F raw \ + -o backing_file="$_base" "$_qcow" $_size \ + || die "Unable to create CoW layer for $_base" + ;; + *) + die "qcowed: usage: qcowed [] " + ;; + esac + + echo "$_qcow" +} + +append() +{ + echo -n " $*" >>"$qdir"/append +} + +appendroot() +{ + append root=/dev/mapper/root + append roothash=$(sfdisk -J $(binpath "$IX_QEMU_BIN_DDI") \ + | jq -r '.partitiontable.partitions | "\(.[0].uuid)-\(.[1].uuid)"' \ + | tr -d '-' | tr 'A-F' 'a-f') +} + +gen_machine() +{ + q sect machine + q param type "$IX_QEMU_MACHINE" + q param accel kvm:tcg + + q sect cpu "$IX_QEMU_CPU" + + q sect m + q param size "$IX_QEMU_RAM" +} + +gen_pflash_ovmf_code() +{ + q sect drive + q param id ovmf-code + q param format raw + q param if pflash + q param unit 0 + q param file $(binpath "$IX_QEMU_BIN_OVMF_CODE") + q param readonly on +} + +gen_pflash_ovmf_vars() +{ + local _src=$(binpath "$IX_QEMU_BIN_OVMF_VARS") + local _dst="$qdir"/ovmf-vars-$(grep _OVMF_ "$qdir"/.config | sha256sum | head -c 8).fd + local _guid=$(uuidgen) + + if ! [ -f "$_dst" ]; then + [ -f "$_src" ] || die "OVMF Variable template ($_src) does not exist" + cp "$_src" "$_dst" + + if [ "$IX_QEMU_OVMF_SB" ]; then + command virt-fw-vars \ + || die "Please install virt-fw-vars to support injection of OVMF secure boot variables" + + virt-fw-vars \ + --inplace "$_dst" \ + --set-pk $_guid $(binpath "$IX_QEMU_OVMF_SB_PK") \ + --add-kek $_guid $(binpath "$IX_QEMU_OVMF_SB_KEK") \ + --add-db $_guid $(binpath "$IX_QEMU_OVMF_SB_DB") \ + --secure-boot \ + || die "Failed to inject secure boot keys to OVMF variable image" + fi + fi + + q sect drive + q param id ovmf-vars + q param format raw + q param if pflash + q param unit 1 + q param file "$_dst" +} + +gen_loader() +{ + if [ "$IX_QEMU_LOADER_QEMU" ]; then + q sect kernel $(binpath "$IX_QEMU_BIN_KERNEL") + appendroot + elif [ "$IX_QEMU_LOADER_OVMF" ]; then + gen_pflash_ovmf_code + gen_pflash_ovmf_vars + + if [ "$IX_QEMU_OVMF_SB" ]; then + q sect global + q param driver cfi.pflash01 + q param property secure + q param value on + fi + fi +} + +gen_serial() +{ + q sect display none + + q_sect_device_virtio serial + + q sect chardev stdio + q param id console0 + q param mux on + + q sect mon + q param chardev console0 + + case "$IX_QEMU_CONSOLE" in + hvc0) + q sect device virtconsole + q param nr 0 + q param name console + q param chardev console0 + ;; + ttyS0|ttyAMA0) + q sect serial chardev:console0 + ;; + *) + die "Unsupported console: $IX_QEMU_CONSOLE" + ;; + esac + + append console="$IX_QEMU_CONSOLE" + + [ "$V" ] && append debug || append loglevel=4 +} + +gpt_uuid() +{ + sgdisk "$1" -p | awk ' + BEGIN { err = 1; } END { exit(err); } + /^Creating new GPT/ { exit; } + + /^Disk identifier \(GUID\): / { + print($4); err = 0; exit; + }' || die "Unable to determine GPT UUID of $1" +} + +gen_disk_sys_empty() +{ + q sect drive + q param id disk-sys + q param format qcow2 + q param if none + q param file $(qcowed disk-sys "$IX_QEMU_DISK_SYS_SIZE") + + q_add_disk disk-sys +} + +gen_disk_sys_full() +{ + q sect drive + q param id disk-sys + q param format qcow2 + q param if none + q param file $(qcowed disk-sys $(binpath "$IX_QEMU_BIN_DISK") \ + "$IX_QEMU_DISK_SYS_SIZE") + + q_add_disk disk-sys +} + +gen_disk_sys_split() +{ + # LVM stub + q sect drive + q param id disk-stub + q param format qcow2 + q param if none + q param file $(qcowed disk-stub $(binpath "$IX_QEMU_BIN_LVM_STUB") \ + "$IX_QEMU_DISK_SYS_SIZE") + + q_add_disk disk-stub + + # DDI (ro) + q sect drive + q param id disk-ddi + q param format raw + q param file $(binpath "$IX_QEMU_BIN_DDI") + q param if none + q param read-only on + + q_add_disk disk-ddi +} + +gen_disk_sys() +{ + [ "$IX_QEMU_DISK_SYS_NONE" ] && return + + if [ "$IX_QEMU_DISK_SYS_EMPTY" ]; then + gen_disk_sys_empty + elif [ "$IX_QEMU_DISK_SYS_FULL" ]; then + gen_disk_sys_full + elif [ "$IX_QEMU_DISK_SYS_SPLIT" ]; then + gen_disk_sys_split + else + die "Unknown system disk mode" + fi +} + +gen_disk_usb() +{ + [ "$IX_QEMU_DISK_USB_NONE" ] && return + + if [ "$IX_QEMU_DISK_USB_DISK" ]; then + q sect drive + q param id disk-usb + q param format qcow2 + q param file $(qcowed disk-usb \ + $(binpath "$IX_QEMU_BIN_DISK") \ + "$IX_QEMU_DISK_USB_SIZE") + q param if none + + q_sect_device_usb usb-storage + q param drive disk-usb + else + die "Unknown USB attachment" + fi +} + +gen_disk_host() +{ + [ -d "$IX_QEMU_DISK_HOST" ] || return + + q sect virtfs local + q param path "$IX_QEMU_DISK_HOST" + q param security_model none + q param writeout immediate + q param mount_tag host +} + +gen_disk_initrd() +{ + [ "$IX_QEMU_DISK_INITRD" ] || return + + q sect initrd $(binpath "$IX_QEMU_BIN_DDI") +} + +internal_is_available() +{ + [ "$IX_QEMU_DISK_SYS_FULL" ] || [ "$IX_QEMU_DISK_SYS_SPLIT" ] \ + || [ "$IX_QEMU_DISK_USB_DISK" ] +} + +gen_disks() +{ + gen_disk_sys + gen_disk_usb + gen_disk_host + + gen_disk_initrd + + internal_is_available || append rd.nointernal +} + +gen_net_dev() +{ + local _name="$1" + local _idx="$2" + local _mac=$(printf "02:00:00:de:ad:%02x" "$_idx") + + echo "$_name $_mac" >>"$qdir"/mactab + + q sect device "$IX_QEMU_NET_MODEL" + q param netdev "$_name" + q param mac "$_mac" +} + +gen_net_user() +{ + [ "$IX_QEMU_NET_USER" ] || return + + q sect netdev user + q param id e1 + + if [ "$IX_QEMU_NET_USER_NETBOOT" ]; then + q param tftp $(dirname $(readlink -f $(binpath "$IX_QEMU_BIN_DDI"))) + q param bootfile $(basename "$IX_QEMU_BIN_DDI") + fi + + if [ "$IX_QEMU_NET_USER_OPTS" ]; then + q option "$IX_QEMU_NET_USER_OPTS" + fi + + gen_net_dev e1 1 +} + +gen_net() +{ + :> "$qdir"/mactab + q sect fw_cfg + q param name opt/mactab + q param file "$qdir"/mactab + + gen_net_user +} + +gen_gdb() +{ + # Create a UNIX socket on the host that is connected to a virtio + # console in the guest, which gdbserver can attach to for + # userspace debugging. + q sect chardev socket + q param id gdbserver + q param path "$qdir"/gdbserver.sock + q param server on + q param wait off + + q sect device virtserialport + q param nr 1 + q param name gdbserver + q param chardev gdbserver + + # Create a UNIX socket on the host that is connected to QEMU's GDB + # stub, for bootloader/kernel debugging. + q sect chardev socket + q param id gdbqemu + q param path "$qdir"/gdbqemu.sock + q param server on + q param wait off + + q sect gdb chardev:gdbqemu +} + +gen_all() +{ + local _append + + : >"$qdir"/append + cat <"$qdir"/qemu.sh +#!/bin/sh + +echo "Starting Qemu :: Ctrl-a x -- exit | Ctrl-a c -- toggle console/monitor" + +line=\$(stty -g) +stty raw +trap 'stty "\$line"' EXIT INT TERM + +qemu-system-$IX_QEMU_ARCH -nodefaults \\ +EOF + chmod +x "$qdir"/qemu.sh + + gen_machine + gen_loader + gen_serial + gen_disks + gen_net + gen_gdb + + if [ "$IX_QEMU_LOADER_QEMU" ]; then + _append=$(cat "$qdir"/append) + q sect append "\"$_append $IX_QEMU_APPEND $*\"" + fi + + echo >>"$qdir"/qemu.sh +} + +menuconfig() +{ + command -v kconfig-mconf >/dev/null \ + || die "cannot find kconfig-mconf for menuconfig" + + CONFIG_= KCONFIG_CONFIG="$qdir"/.config \ + kconfig-mconf "$qdir"/Config.in +} + +_generate=YES + +while getopts "0cGh-" opt; do + case ${opt} in + 0) + echo "Clearing all copy-on-write layers" >&2 + rm -f "$qdir"/*.qcow2 + rm -f "$qdir"/ovmf-vars-*.fd + exit 0 + ;; + c) + menuconfig + ;; + G) + _generate= + ;; + h) + usage && exit 0 + ;; + -) + break + ;; + *) + usage && exit 1 + ;; + esac +done +shift $((OPTIND - 1)) + +# shellcheck disable=SC1090 +. "$qdir"/.config + +[ "$_generate" ] && gen_all "$*" + +exec "$qdir"/qemu.sh diff --git a/board/common/qemu/Config.in.in b/board/common/qemu/itb/Config.in.in similarity index 100% rename from board/common/qemu/Config.in.in rename to board/common/qemu/itb/Config.in.in diff --git a/board/common/qemu/run.sh b/board/common/qemu/itb/run.sh similarity index 100% rename from board/common/qemu/run.sh rename to board/common/qemu/itb/run.sh diff --git a/board/common/qemu/qemu.mk b/board/common/qemu/qemu.mk index bdee41a05..694c38d6b 100644 --- a/board/common/qemu/qemu.mk +++ b/board/common/qemu/qemu.mk @@ -4,12 +4,16 @@ # ################################################################################ -QEMU_SCRIPTS_DIR := $(pkgdir) -QEMU_SCRIPTS_NAME := qemu-scripts -QEMU_SCRIPTS_TYPE := rootfs +qemu-scripts-dir := $(pkgdir)/$(if $(IX_IMAGE_DDI),ddi,itb) +qemu-image := ../$(INFIX_ARTIFACT).$(if $(IX_IMAGE_DDI),raw,qcow2) +qemu-disk := $(if $(IX_IMAGE_DDI_DISK),../$(INFIX_ARTIFACT).disk) +qemu-esp := $(if $(IX_IMAGE_BAREBOX_ESP),../barebox-esp.vfat) +qemu-extra := $(if $(IX_IMAGE_DDI),$(BINARIES_DIR)/qemu/infix-development.crt) + +qemu-kconfig-prefix := $(if $(IX_IMAGE_DDI),,CONFIG_) qemu-kconfig = \ - CONFIG_="CONFIG_" \ + CONFIG_="$(qemu-kconfig-prefix)" \ BR2_CONFIG="$(BINARIES_DIR)/qemu/.config" \ $(BUILD_DIR)/buildroot-config/$(1) $(2) "$(BINARIES_DIR)/qemu/Config.in" @@ -26,23 +30,30 @@ run-menuconfig: $(BUILD_DIR)/buildroot-config/mconf qemu-scripts: \ $(BINARIES_DIR)/qemu/run.sh \ $(BINARIES_DIR)/qemu/Config.in \ - $(BINARIES_DIR)/qemu/.config + $(BINARIES_DIR)/qemu/.config \ + $(qemu-extra) -$(BINARIES_DIR)/qemu/run.sh: $(QEMU_SCRIPTS_DIR)/run.sh +$(BINARIES_DIR)/qemu/run.sh: $(qemu-scripts-dir)/run.sh @$(call IXMSG,"Installing QEMU scripts") @mkdir -p $(dir $@) @cp $< $@ -$(BINARIES_DIR)/qemu/Config.in: $(QEMU_SCRIPTS_DIR)/Config.in.in +$(BINARIES_DIR)/qemu/Config.in: $(qemu-scripts-dir)/Config.in.in @mkdir -p $(dir $@) @sed \ - -e "s:@ARCH@:IX_QEMU_$(BR2_ARCH):" \ - -e "s:@DISK_IMG@:../$(INFIX_ARTIFACT).qcow2:" \ + -e "s:@ARCH@:IX_QEMU_$(BR2_ARCH):g" \ + -e "s:@IMAGE@:$(qemu-image):g" \ + -e "s:@DISK@:$(qemu-disk):g" \ < $< >$@ $(BINARIES_DIR)/qemu/.config: $(BINARIES_DIR)/qemu/Config.in @$(call qemu-kconfig,conf,--olddefconfig) @rm -f $@.old +$(BINARIES_DIR)/qemu/infix-development.crt: \ +$(BR2_EXTERNAL_INFIX_PATH)/board/common/signing-keys/development/infix.crt + @mkdir -p $(dir $@) + @cp $< $@ + TARGETS_ROOTFS += qemu-scripts endif From 061d3858502d3ce9fe9cf09ae1c9279f4ad8511c Mon Sep 17 00:00:00 2001 From: Tobias Waldekranz Date: Thu, 8 Oct 2026 09:29:46 +0000 Subject: [PATCH 17/19] confd: Make marker directory configurable With DDI images, test-mode and other marker files are stored on /boot/efi instead of /mnt/aux. Add a --with-marker-dir configure option, and select the right directory based on IX_IMAGE_DDI. --- package/confd-test-mode/confd-test-mode.mk | 1 + package/confd/confd.mk | 1 + src/confd/bin/{gen-config => gen-config.in} | 2 +- src/confd/configure.ac | 8 ++++++++ src/confd/src/main.c | 10 +++++----- src/test-mode/configure.ac | 6 ++++++ src/test-mode/src/test-mode.c | 4 ++-- 7 files changed, 24 insertions(+), 8 deletions(-) rename src/confd/bin/{gen-config => gen-config.in} (99%) diff --git a/package/confd-test-mode/confd-test-mode.mk b/package/confd-test-mode/confd-test-mode.mk index 1a9f040c9..3c991a13c 100644 --- a/package/confd-test-mode/confd-test-mode.mk +++ b/package/confd-test-mode/confd-test-mode.mk @@ -12,6 +12,7 @@ CONFD_TEST_MODE_LICENSE_FILES = LICENSE CONFD_TEST_MODE_REDISTRIBUTE = NO CONFD_TEST_MODE_DEPENDENCIES = sysrepo libite libyang confd CONFD_TEST_MODE_AUTORECONF = YES +CONFD_TEST_MODE_CONF_OPTS = --with-marker-dir=$(if $(IX_IMAGE_DDI),/boot/efi,/mnt/aux) CONFD_TEST_MODE_SYSREPO_SHM_PREFIX = sr_buildroot$(subst /,_,$(CONFIG_DIR))_test_mode COMMON_SYSREPO_ENV = \ diff --git a/package/confd/confd.mk b/package/confd/confd.mk index 02a2249c8..2a9f5bf98 100644 --- a/package/confd/confd.mk +++ b/package/confd/confd.mk @@ -13,6 +13,7 @@ CONFD_REDISTRIBUTE = NO CONFD_DEPENDENCIES = host-sysrepo sysrepo rousette netopeer2 jansson libite sysrepo libsrx libglib2 libev sysklogd watchdogd CONFD_AUTORECONF = YES CONFD_CONF_OPTS += --disable-silent-rules --with-crypt=$(BR2_PACKAGE_CONFD_DEFAULT_CRYPT) +CONFD_CONF_OPTS += --with-marker-dir=$(if $(IX_IMAGE_DDI),/boot/efi,/mnt/aux) CONFD_SYSREPO_SHM_PREFIX = sr_buildroot$(subst /,_,$(CONFIG_DIR))_confd define CONFD_CONF_ENV diff --git a/src/confd/bin/gen-config b/src/confd/bin/gen-config.in similarity index 99% rename from src/confd/bin/gen-config rename to src/confd/bin/gen-config.in index d58e8b7b0..b2e5b1b55 100755 --- a/src/confd/bin/gen-config +++ b/src/confd/bin/gen-config.in @@ -121,7 +121,7 @@ gen_factory_cfg log "Starting up, calling gen_failure_cfg()" gen_failure_cfg -if [ -f "/mnt/aux/test-mode" ]; then +if [ -f "@MARKER_DIR@/test-mode" ]; then gen_test_cfg fi diff --git a/src/confd/configure.ac b/src/confd/configure.ac index 28f0c6e2f..3294a3209 100644 --- a/src/confd/configure.ac +++ b/src/confd/configure.ac @@ -7,6 +7,7 @@ AM_SILENT_RULES(yes) AC_CONFIG_FILES([ Makefile bin/Makefile + bin/gen-config bin/gen-version share/Makefile share/factory.d/Makefile @@ -85,6 +86,12 @@ AC_ARG_WITH(crypt, AS_HELP_STRING([--with-crypt=crypt], [Default crypt for $0$cleartext, default: yescrypt]), [crypt=$withval], [crypt="yescrypt"]) +AC_ARG_WITH([marker-dir], + [AS_HELP_STRING([--with-marker-dir=DIR], [Directory holding test-mode marker files @<:@/mnt/aux@:>@])], + [markerdir="$withval"], [markerdir=/mnt/aux]) +AC_DEFINE_UNQUOTED([MARKER_DIR], ["$markerdir"], [Directory holding test-mode marker files]) +AC_SUBST([MARKER_DIR], [$markerdir]) + AS_IF([test "x$enable_containers" = "xyes"], [ AC_DEFINE(CONTAINERS, 1, [Built with container support])]) @@ -203,6 +210,7 @@ Optional features: SNMP support .........: $enable_snmp Login shell ..........: $login_shell Default crypt algo ...: $crypt + Marker directory .....: $markerdir ------------- Compiler version -------------- $($CC --version || true) diff --git a/src/confd/src/main.c b/src/confd/src/main.c index 6e24690c4..1278bd977 100644 --- a/src/confd/src/main.c +++ b/src/confd/src/main.c @@ -54,7 +54,7 @@ #define MIGRATED_PATH "/run/confd-migrated.cfg" /* Set when startup-config fails, next boot goes to fail-secure, issue #1637 */ -#define FAILED_PATH "/mnt/aux/startup-config.failed" +#define FAILED_PATH MARKER_DIR"/startup-config.failed" /* * Set a finit condition in the usr/ namespace, e.g. @@ -603,7 +603,7 @@ static void handle_startup_failure(sr_session_ctx_t *sess, const char *failure_p */ static void maybe_enable_test_mode(void) { - if (fexist("/mnt/aux/test-mode")) { + if (fexist(MARKER_DIR "/test-mode")) { int rc; conout(3, "Enabling test mode"); @@ -635,9 +635,9 @@ static int bootstrap_config(sr_conn_ctx_t *conn, sr_session_ctx_t *sess, } /* Test mode support */ - if (fexist("/mnt/aux/test-mode")) { - if (fexist("/mnt/aux/test-override-startup")) { - unlink("/mnt/aux/test-override-startup"); + if (fexist(MARKER_DIR "/test-mode")) { + if (fexist(MARKER_DIR "/test-override-startup")) { + unlink(MARKER_DIR "/test-override-startup"); config_path = startup_path; } else { NOTE("Test mode detected, switching to test-config"); diff --git a/src/test-mode/configure.ac b/src/test-mode/configure.ac index 71280daa0..769658c01 100644 --- a/src/test-mode/configure.ac +++ b/src/test-mode/configure.ac @@ -14,6 +14,11 @@ LT_INIT PKG_PROG_PKG_CONFIG +AC_ARG_WITH([marker-dir], + [AS_HELP_STRING([--with-marker-dir=DIR], [Directory holding test-mode marker files @<:@/mnt/aux@:>@])], + [markerdir="$withval"], [markerdir=/mnt/aux]) +AC_DEFINE_UNQUOTED([MARKER_DIR], ["$markerdir"], [Directory holding test-mode marker files]) + PKG_CHECK_MODULES([sysrepo], [sysrepo >= 2.2.36]) PKG_CHECK_MODULES([libite], [libite >= 2.5.0]) @@ -53,6 +58,7 @@ cat < Date: Thu, 8 Oct 2026 11:56:03 +0000 Subject: [PATCH 18/19] test: Use DDI images for qeneth testing --- test/.env | 2 +- test/docker/Dockerfile | 3 + test/env | 16 +--- test/inject-test-mode | 108 ++++++++++++---------- test/templates/infix-bios-x86_64.mustache | 6 +- test/test.mk | 3 +- test/virt/quad/topology.dot.in | 8 +- 7 files changed, 76 insertions(+), 70 deletions(-) diff --git a/test/.env b/test/.env index d92081768..e18f2d57a 100644 --- a/test/.env +++ b/test/.env @@ -2,7 +2,7 @@ # shellcheck disable=SC2034,SC2154 # Current container image -INFIX_TEST=ghcr.io/kernelkit/infix-test:2.14 +INFIX_TEST=ghcr.io/kernelkit/infix-test:2.15 ixdir=$(readlink -f "$testdir/..") logdir=$(readlink -f "$testdir/.log") diff --git a/test/docker/Dockerfile b/test/docker/Dockerfile index 56e2dc193..0b2d49381 100644 --- a/test/docker/Dockerfile +++ b/test/docker/Dockerfile @@ -32,17 +32,20 @@ RUN apk add --no-cache \ libyang-dev \ linux-headers \ make \ + mtools \ ncurses-dev \ net-snmp-tools \ nmap \ openssh-client \ openssl \ + ovmf \ pandoc-cli \ pkgconf \ python3-dev \ qemu-img \ qemu-system-x86_64 \ ruby-mustache \ + sfdisk \ skopeo \ socat \ squashfs-tools \ diff --git a/test/env b/test/env index 2bf3fe6c0..f57da07b1 100755 --- a/test/env +++ b/test/env @@ -81,7 +81,7 @@ get_base_img() { local files="$1" local base_img_file - base_img_file=$(echo "$files" | tr ' ' '\n' | grep -- '.qcow2$') + base_img_file=$(echo "$files" | tr ' ' '\n' | grep -- '.disk$') echo "$envdir/qeneth/$(basename "$base_img_file")" } @@ -102,17 +102,11 @@ start_topology() ln -sf "$file" "$envdir/qeneth/$filename" done - base_img_qcow2=$(get_base_img "$files") - base_img_disk="${base_img_qcow2%.qcow2}.disk" + base_img_disk=$(get_base_img "$files") + test_img_disk="${base_img_disk%.disk}-test.disk" + $testdir/inject-test-mode "$base_img_disk" "$test_img_disk" - test_img_disk="${base_img_qcow2%.qcow2}-test.disk" - test_img_qcow2="${test_img_disk%.disk}.qcow2" - - qemu-img convert -f qcow2 -O raw "$base_img_qcow2" "$base_img_disk" - $testdir/inject-test-mode -b "$base_img_disk" -o "$test_img_disk" - qemu-img convert -f raw -O qcow2 "$test_img_disk" "$test_img_qcow2" - - img_name=$(basename $test_img_qcow2) + img_name=$(basename $test_img_disk) sed -i "s/qn_image=\".*\"/qn_image=\"$img_name\"/" "$envdir/qeneth/topology.dot.in" (cd "$envdir/qeneth/" && $qeneth generate && $qeneth start) diff --git a/test/inject-test-mode b/test/inject-test-mode index 2f499b5b5..138eea582 100755 --- a/test/inject-test-mode +++ b/test/inject-test-mode @@ -1,54 +1,64 @@ #!/bin/sh -# This script injects a "test-mode" file into a copy of the main disk image. -# It begins by parsing the partition table of the disk image to identify the -# 'aux' partition, which is then extracted. An empty 'test-mode' file is -# injected into the extracted partition, and the modified partition is written -# back to the output image. The output image can subsequently be used as a -# backing image for Copy-on-Write (QCOW) images utilized by Qeneth. +# This script injects a "test-mode" file into a copy of the main disk +# image. It begins by parsing the partition table of the disk image +# to identify either the 'aux' or 'esp' partition, which is then +# extracted. An empty 'test-mode' file is injected into the extracted +# partition, and the modified partition is written back to the output +# image. The output image can subsequently be used as a backing image +# for Copy-on-Write (QCOW) images utilized by Qeneth. set -e -while getopts "b:o:" opt; do - case $opt in - b) base_img="$OPTARG" ;; # Base image (Original image) - o) output_img="$OPTARG" ;; # Output image (Backing image for QCoW images) - *) echo "Usage: $0 -b -o " ; exit 1 ;; - esac -done - -if [ -z "$base_img" ] || [ -z "$output_img" ]; then - echo "Both -b (base image) and -o (output image) parameters are required." - exit 1 -fi - -rm -f "$output_img" -if ! cp "$base_img" "$output_img"; then - echo "Error: Failed to copy $base_img to $output_img" - exit 1 -fi - -if ! part_table=$(fdisk -l "$output_img" 2>/dev/null); then - echo "Error: Failed to read partition table from $output_img" - exit 1 -fi - -aux_line=$(echo "$part_table" | grep 'aux') -if [ -z "$aux_line" ]; then - echo "Error: 'aux' partition not found in $output_img" - exit 1 -fi - -start=$(echo "$aux_line" | awk '{print $2}') -end=$(echo "$aux_line" | awk '{print $3}') -count=$(($end - $start + 1)) -block_size=$(echo "$part_table" | grep "Logical sector size" | awk '{print $4}') - -dd if="$output_img" of="tmpaux" skip="$start" count="$count" bs="$block_size" status=none - -touch tmp-empty-file -e2cp tmp-empty-file tmpaux:/test-mode -rm tmp-empty-file - -dd of="$output_img" if="tmpaux" seek="$start" count="$count" bs="$block_size" status=none conv=notrunc -rm tmpaux +die() +{ + echo "ERROR: $*" >&2 + exit 1 +} + +workdir=$(mktemp -d) +trap 'rm -rf "$workdir"' EXIT INT TERM + + +baseimg="$1" +testimg="$2" + +basegpt="$workdir"/basegpt.json +sfdisk -J "$baseimg" >"$basegpt" + +lbsize=$(jq -r .partitiontable.sectorsize $basegpt) + +jq -r '.partitiontable.partitions[] + | select(.name == "aux" or .name == "esp") + | "\(.name) \(.start) \(.size)"' $basegpt | { + read part start size + + [ "$part" ] || die "Disk must contain either aux or esp partition" + + + dd status=none \ + if="$baseimg" \ + bs=$lbsize skip=$start count=$size \ + of="$workdir"/markerpart \ + || die "Unable to extract marker partition" + + touch "$workdir"/empty + + case "$part" in + aux) + e2cp "$workdir"/empty "$workdir"/markerpart:/test-mode \ + || die "Unable to inject test-mode in aux" + ;; + esp) + mcopy -i "$workdir"/markerpart "$workdir"/empty ::/test-mode \ + || die "Unable to inject test-mode in esp" + ;; + esac + + cp -f "$baseimg" "$testimg" || die "Unable to create $testimg" + dd status=none conv=notrunc \ + if="$workdir"/markerpart \ + bs=$lbsize seek=$start count=$size \ + of="$testimg" \ + || die "Unable to inject markerpartition in $testimg" +} diff --git a/test/templates/infix-bios-x86_64.mustache b/test/templates/infix-bios-x86_64.mustache index eddecec07..2666380cc 100644 --- a/test/templates/infix-bios-x86_64.mustache +++ b/test/templates/infix-bios-x86_64.mustache @@ -4,12 +4,12 @@ con=hvc0 tty -s && con=hvc2 img={{#qn_image}}{{qn_image}}{{/qn_image}}{{^qn_image}}infix-x86_64-disk.img{{/qn_image}} -bios={{#qn_bios}}{{qn_bios}}{{/qn_bios}}{{^qn_bios}}OVMF.fd{{/qn_bios}} +bios={{#qn_bios}}{{qn_bios}}{{/qn_bios}}{{^qn_bios}}/usr/share/OVMF/OVMF.fd{{/qn_bios}} imgdir=. {{> inc/infix-usb}} origimg=$(realpath $img) -qemu-img create -f qcow2 -o backing_file=$origimg -F qcow2 {{name}}.qcow2 +qemu-img create -b $origimg -F raw -f qcow2 {{name}}.qcow2 4G {{> inc/infix-mactab}} @@ -20,7 +20,7 @@ mkfifo /tmp/{{name}}-gps.in /tmp/{{name}}-gps.out 2>/dev/null mkfifo /tmp/{{name}}-gps1.in /tmp/{{name}}-gps1.out 2>/dev/null {{/qn_gps}} -exec qemu-system-x86_64 -M pc,accel=kvm:tcg -cpu max \ +exec qemu-system-x86_64 -M q35,smm=on,accel=kvm:tcg -cpu max \ -m {{#qn_mem}}{{qn_mem}}{{/qn_mem}}{{^qn_mem}}256M{{/qn_mem}} \ {{> ../qeneth/templates/inc/qemu-links}} {{> inc/infix-fwcfg}} diff --git a/test/test.mk b/test/test.mk index fdc10ff27..8973f963e 100644 --- a/test/test.mk +++ b/test/test.mk @@ -24,8 +24,7 @@ mode-run := -t $(BINARIES_DIR)/qemu.dot mode := $(mode-$(TEST_MODE)) pkg-$(ARCH) := -p $(O)/images/$(INFIX_ARTIFACT).pkg -binaries-$(ARCH) := $(INFIX_ARTIFACT).qcow2 -binaries-x86_64 += OVMF.fd +binaries-$(ARCH) := $(INFIX_ARTIFACT).disk binaries := $(foreach bin,$(binaries-$(ARCH)),-f $(BINARIES_DIR)/$(bin)) # Common transport override for minimal defconfigs diff --git a/test/virt/quad/topology.dot.in b/test/virt/quad/topology.dot.in index f42e9c22c..32305e7b3 100644 --- a/test/virt/quad/topology.dot.in +++ b/test/virt/quad/topology.dot.in @@ -24,7 +24,7 @@ graph "quad" { label="{ e1 | e2 | e3 | e4 } | dut1 | { e5 | e6 | e7 | e8 } | { radio0 | radio1 | radio2 | radio3 }", pos="10,30!", provides="infix watchdog gps", - expected_boot="primary", + expected_boot="internal-primary", qn_console=9001, qn_mem="384M", qn_usb="dut1.usb", @@ -40,7 +40,7 @@ graph "quad" { label="{ e1 | e2 | e3 | e4 } | dut2 | { e5 | e6 | e7 | e8 } | { radio0 | radio1 | radio2 }", pos="0,20!", provides="infix watchdog", - expected_boot="primary", + expected_boot="internal-primary", qn_console=9002, qn_mem="384M", qn_usb="dut2.usb" @@ -49,7 +49,7 @@ graph "quad" { label="{ e1 | e2 | e3 | e4 } | dut3 | { e5 | e6 | e7 | e8 } | { radio0 | radio1 }", pos="0,10!", provides="infix watchdog", - expected_boot="primary", + expected_boot="internal-primary", qn_console=9003, qn_mem="384M", qn_usb="dut3.usb" @@ -59,7 +59,7 @@ graph "quad" { label="{ e1 | e2 | e3 | e4 } | dut4 | { e5 | e6 | e7 | e8 } | { radio0 | radio1 }", pos="10,0!", provides="infix watchdog", - expected_boot="primary", + expected_boot="internal-primary", qn_console=9004, qn_mem="384M", qn_usb="dut4.usb" From e3bc2de8393fa969ee98f7f64716ed8b0373c156 Mon Sep 17 00:00:00 2001 From: Tobias Waldekranz Date: Thu, 8 Oct 2026 08:48:20 +0000 Subject: [PATCH 19/19] defconfig: x86_64: Switch to DDI images Until we have a Barebox release which includes the upstream LVM support, build from a GIT tag on the kernelkit branch. --- configs/x86_64_defconfig | 22 ++++++++++------------ configs/x86_64_minimal_defconfig | 22 ++++++++++------------ external.mk | 5 +++++ patches/barebox/git.hash | 1 + 4 files changed, 26 insertions(+), 24 deletions(-) create mode 100644 patches/barebox/git.hash diff --git a/configs/x86_64_defconfig b/configs/x86_64_defconfig index 6cac556ff..e39a21223 100644 --- a/configs/x86_64_defconfig +++ b/configs/x86_64_defconfig @@ -125,21 +125,20 @@ BR2_PACKAGE_LESS=y BR2_PACKAGE_MG=y BR2_PACKAGE_NANO=y # BR2_TARGET_ROOTFS_TAR is not set -BR2_TARGET_EDK2=y -BR2_TARGET_GRUB2=y -BR2_TARGET_GRUB2_X86_64_EFI=y -BR2_TARGET_GRUB2_BUILTIN_MODULES_EFI="boot linux ext2 squash4 part_gpt normal efi_gop configfile loadenv test echo reboot net efinet tftp loopback cat search" -BR2_TARGET_GRUB2_BUILTIN_CONFIG_EFI="${BR2_EXTERNAL_INFIX_PATH}/board/x86_64/grub-embed.cfg" -BR2_TARGET_GRUB2_INSTALL_TOOLS=y +BR2_TARGET_BAREBOX=y +BR2_TARGET_BAREBOX_CUSTOM_GIT=y +BR2_TARGET_BAREBOX_CUSTOM_GIT_REPO_URL="https://github.com/kernelkit/barebox.git" +BR2_TARGET_BAREBOX_CUSTOM_GIT_VERSION="kkit-next-ddi1" +BR2_TARGET_BAREBOX_USE_CUSTOM_CONFIG=y +BR2_TARGET_BAREBOX_CUSTOM_CONFIG_FILE="${BR2_EXTERNAL_INFIX_PATH}/board/x86_64/barebox_defconfig" +BR2_TARGET_BAREBOX_IMAGE_FILE="barebox.efi" +BR2_TARGET_BAREBOX_CUSTOM_EMBEDDED_ENV_PATH="${BUILD_DIR}/infix-bareboxenv" BR2_PACKAGE_HOST_DOSFSTOOLS=y BR2_PACKAGE_HOST_E2FSPROGS=y BR2_PACKAGE_HOST_ENVIRONMENT_SETUP=y BR2_PACKAGE_HOST_GENEXT2FS=y BR2_PACKAGE_HOST_GO_BIN=y BR2_PACKAGE_HOST_MTOOLS=y -BR2_PACKAGE_HOST_UBOOT_TOOLS_FIT_SUPPORT=y -BR2_PACKAGE_HOST_UBOOT_TOOLS_FIT_SIGNATURE_SUPPORT=y -BR2_PACKAGE_HOST_UBOOT_TOOLS_FDT_ADD_PUBKEY=y IX_VENDOR_HOME="https://www.kernelkit.org" IX_DESC="Infix is an immutable, friendly, and secure operating system that turns any ARM or x86 device into a powerful, manageable network appliance. Deploy on anything from $35 Raspberry Pi boards to enterprise switches as routers, IoT gateways, or edge devices. Infix models Linux networking features using YANG so you can manage your devices using NETCONF/RESTCONF APIs and focus on your business logic running in isolated containers." IX_HOME="https://github.com/kernelkit/infix/" @@ -188,9 +187,8 @@ BR2_PACKAGE_WEBUI=y BR2_PACKAGE_RAUC_INSTALLATION_STATUS=y BR2_PACKAGE_HOST_PYTHON_YANGDOC=y BR2_PACKAGE_PCIUTILS=y -IX_IMAGE_ITB_AUX=y -IX_IMAGE_ITB_QCOW=y -IX_IMAGE_ITB_RAUC=y +IX_IMAGE_DDI_DISK=y +IX_IMAGE_DDI_RAUC=y IX_IMAGE_README=y IX_TRUSTED_KEYS=y IX_TRUSTED_KEYS_DEVELOPMENT=y diff --git a/configs/x86_64_minimal_defconfig b/configs/x86_64_minimal_defconfig index f3db8f305..0b1a592c6 100644 --- a/configs/x86_64_minimal_defconfig +++ b/configs/x86_64_minimal_defconfig @@ -102,20 +102,19 @@ BR2_PACKAGE_LESS=y BR2_PACKAGE_MG=y BR2_PACKAGE_NANO=y # BR2_TARGET_ROOTFS_TAR is not set -BR2_TARGET_EDK2=y -BR2_TARGET_GRUB2=y -BR2_TARGET_GRUB2_X86_64_EFI=y -BR2_TARGET_GRUB2_BUILTIN_MODULES_EFI="boot linux ext2 squash4 part_gpt normal efi_gop configfile loadenv test echo reboot net efinet tftp loopback cat search" -BR2_TARGET_GRUB2_BUILTIN_CONFIG_EFI="${BR2_EXTERNAL_INFIX_PATH}/board/x86_64/grub-embed.cfg" -BR2_TARGET_GRUB2_INSTALL_TOOLS=y +BR2_TARGET_BAREBOX=y +BR2_TARGET_BAREBOX_CUSTOM_GIT=y +BR2_TARGET_BAREBOX_CUSTOM_GIT_REPO_URL="https://github.com/kernelkit/barebox.git" +BR2_TARGET_BAREBOX_CUSTOM_GIT_VERSION="kkit-next-ddi1" +BR2_TARGET_BAREBOX_USE_CUSTOM_CONFIG=y +BR2_TARGET_BAREBOX_CUSTOM_CONFIG_FILE="${BR2_EXTERNAL_INFIX_PATH}/board/x86_64/barebox_defconfig" +BR2_TARGET_BAREBOX_IMAGE_FILE="barebox.efi" +BR2_TARGET_BAREBOX_CUSTOM_EMBEDDED_ENV_PATH="${BUILD_DIR}/infix-bareboxenv" BR2_PACKAGE_HOST_DOSFSTOOLS=y BR2_PACKAGE_HOST_E2FSPROGS=y BR2_PACKAGE_HOST_ENVIRONMENT_SETUP=y BR2_PACKAGE_HOST_GENEXT2FS=y BR2_PACKAGE_HOST_MTOOLS=y -BR2_PACKAGE_HOST_UBOOT_TOOLS_FIT_SUPPORT=y -BR2_PACKAGE_HOST_UBOOT_TOOLS_FIT_SIGNATURE_SUPPORT=y -BR2_PACKAGE_HOST_UBOOT_TOOLS_FDT_ADD_PUBKEY=y IX_VENDOR_HOME="https://www.kernelkit.org" IX_DESC="Infix is an immutable, friendly, and secure operating system that turns any ARM or x86 device into a powerful, manageable network appliance. Deploy on anything from $35 Raspberry Pi boards to enterprise switches as routers, IoT gateways, or edge devices. Infix models Linux networking features using YANG so you can manage your devices using NETCONF/RESTCONF APIs and focus on your business logic running in isolated containers." IX_HOME="https://github.com/kernelkit/infix/" @@ -147,9 +146,8 @@ BR2_PACKAGE_MDNS_ALIAS=y BR2_PACKAGE_ONIEPROM=y BR2_PACKAGE_ROUSETTE=y BR2_PACKAGE_RAUC_INSTALLATION_STATUS=y -IX_IMAGE_ITB_AUX=y -IX_IMAGE_ITB_QCOW=y -IX_IMAGE_ITB_RAUC=y +IX_IMAGE_DDI_DISK=y +IX_IMAGE_DDI_RAUC=y IX_IMAGE_README=y IX_TRUSTED_KEYS=y IX_TRUSTED_KEYS_DEVELOPMENT=y diff --git a/external.mk b/external.mk index 5609bd270..ee3f41311 100644 --- a/external.mk +++ b/external.mk @@ -8,6 +8,11 @@ local.mk: @$(call IXMSG,"Installing local override for certain packages") @(cd $O && ln -s $(BR2_EXTERNAL_INFIX_PATH)/local.mk .) +# TEMPORARY +# Until the lvm series gets into a released version and we can create +# a proper release from kernelkit/barebox, we build from a GIT tag. +BAREBOX_HASH_FILES += $(BR2_EXTERNAL_INFIX_PATH)/patches/barebox/git.hash + # # Buildroot package extensions # diff --git a/patches/barebox/git.hash b/patches/barebox/git.hash new file mode 100644 index 000000000..92f7c3fcb --- /dev/null +++ b/patches/barebox/git.hash @@ -0,0 +1 @@ +sha256 b11e120c0b9d08900bdab1931e9c18f849ed76f262db6fd66f819e3312c7637d barebox-kkit-next-ddi1-git4.tar.gz