Repository navigation
feat(initramfs): replace the scripted initramfs with omnect-os-init - #697
JanZachmann wants to merge 114 commits into
Conversation
Refactors GRUB's root partition identification by transitioning from a static `rootblk` parameter to a dynamic filesystem UUID. This enhances boot reliability by eliminating dependency on potentially unstable block device names. The GRUB `probe` command is leveraged to detect the boot partition's UUID and pass it to the kernel as `bootpart_fsuuid`.
Adds `util-linux-blkid` to the initramfs, providing essential tools for robust identification of block devices, which is crucial for locating and mounting the root filesystem. Introduces conditional Cargo features for the Rust-based `omnect-os-init` based on the detected bootloader (`grub` for EFI, `uboot` otherwise). This allows the init process to be compiled with bootloader-specific logic, improving its adaptability and ensuring correct behavior across different boot environments.
Refreshes the recipe to align with the latest upstream changes in the omnect-os-init repository. Bumps the SRCREV for the primary source. Significantly reduces the number of Rust crate dependencies and their checksums, resulting in a leaner dependency footprint for the project.
Ensures the `resize-data` feature is appended with a comma as a prefix, aligning with the expected comma-separated format for Cargo features. This consistency prevents incorrect parsing of build features.
Updates the `omnect-os-init` Rust component to a newer commit. Includes `e2fsprogs-e2fsck` in the initramfs image to enable filesystem checks, likely required by the updated init for enhanced system integrity and recovery.
…features Enable GPT support by default for all targets and conditionally enable features for release images and persistent logging based on build configuration.
omnect_first_boot_done, omnect_factory_reset_last_error and omnect_fsck_<part> were missing from the writeable env flags list. Unlisted variables are not guaranteed to survive a u-boot saveenv, and omnect_update_flow calls saveenv on both update paths, so the variables are lost across an update boot while omnect_extra_bootargs and the other listed ones survive. Observed effect: after an A/B update the device reports first_boot true again and repeats the first-boot work (resize preflight, extra-bootargs sync). The same applies to a stored fsck record, which is exactly the case the env is meant to carry across a reboot. The list now matches BootEnvKey::as_str() in omnect-os-init. The fsck entries name the partitions the initramfs runs fsck on; the flags syntax takes exact names, so they are enumerated. No change needed for grub: it persists named variables via save_env and keeps the rest of the block, so it has no equivalent drop. Signed-off-by: Joerg Zeidler <62105035+JoergZeidler@users.noreply.github.com>
omnect_first_boot_done, omnect_factory_reset_last_error and omnect_fsck_<part> were missing from the writeable env flags list. Unlisted variables are not guaranteed to survive a u-boot saveenv, and omnect_update_flow calls saveenv on both update paths, so the variables are lost across an update boot while omnect_extra_bootargs and the other listed ones survive. Observed effect: after an A/B update the device reports first_boot true again and repeats the first-boot work (resize preflight, extra-bootargs sync). The same applies to a stored fsck record, which is exactly the case the env is meant to carry across a reboot. The list now matches BootEnvKey::as_str() in omnect-os-init. The fsck entries name the partitions the initramfs runs fsck on; the flags syntax takes exact names, so they are enumerated. No change needed for grub: it persists named variables via save_env and keeps the rest of the block, so it has no equivalent drop. Signed-off-by: Joerg Zeidler <62105035+JoergZeidler@users.noreply.github.com>
Keep the legacy shell initramfs scripts deleted - the rust init replaces them and nothing on this branch references them any more. Take the phytec bootloader checksum from upstream. Both sides changed the u-boot env, so the merged tree needs a freshly built value; the checksum check reports it on the next tauril2 build. Signed-off-by: Jan Zachmann 50990105+JanZachmann@users.noreply.github.com
The recipe is generated by cargo-bitbake and sets S = "${WORKDIR}/git",
which do_qa_unpack in insane.bbclass rejects with a fatal error.
bitbake.conf sets S = "${UNPACKDIR}/${BP}", so assign that in the .inc,
which is included after the generated assignment.
Signed-off-by: Jan Zachmann <50990105+JanZachmann@users.noreply.github.com>
do_install moves the binary to /init and removes /usr/bin, which leaves an empty /usr in the initramfs. Signed-off-by: Jan Zachmann <50990105+JanZachmann@users.noreply.github.com>
The recorded checksum no longer matches the built bootloader, so the build fails. Signed-off-by: Jan Zachmann <50990105+JanZachmann@users.noreply.github.com>
…de 4 The mode table named the tools the legacy shell script used (dd, rm plus fstrim). The Rust init overwrites the whole partition from /dev/urandom and discards it with an ioctl, so the table now states what a mode does, not how. Mode 4 ran a customer-supplied wipe hook. It is not ported: it handed the wipe, the one step the caller asked for, to code that is not built or tested here, and modes 2 and 3 cover rotating disks and flash. A mode-4 trigger is rejected as invalid, so its row and the custom-wipe instructions are gone. Signed-off-by: Jan Zachmann <50990105+JanZachmann@users.noreply.github.com>
# Conflicts: # conf/machine/include/rpi.inc
The recipe fetched the init from a personal fork on a branch that stops before the factory-reset wipe modes. Point it at the branch under review so an image carries them, and update the AUTOINC suffix with the revision. Signed-off-by: Jan Zachmann <50990105+JanZachmann@users.noreply.github.com>
The examples and the field list still described the shell implementation: the object was named factory-reset, error carried an exit code and was always present, and status 1 meant an unsupported wipe mode. The init reports the object as factory_reset, leaves error and context out unless something went wrong, adds data_wiped, and uses status 4 for a reset that needed a second format attempt. A trigger it cannot use at all never reaches the reset, so that case is described separately. Also drops the link to the removed initramfs script directory. Signed-off-by: Jan Zachmann <50990105+JanZachmann@users.noreply.github.com>
…ne (#38) ## Summary fsck: for vfat, an exit code with bit 2 now counts as a failed check, not as "reboot required", and fsck.fat codes are logged as numbers instead of fsck(8) bit names. ext4 keeps the reboot. The boot partition record is now always written to grubenv, because a vfat check can no longer ask for a reboot. A failed fsck logs the last lines of its output, so the reason (e.g. `open: Input/output error`) reaches kmsg. Without a bootloader environment, `persist_fsck_results` warns that the fsck status is not saved to it. `output_tail` moved to `logging` for this and returns `(no output)` when nothing is left; this also changes the curl and e2image error reasons of flash modes 1 and 3. flash mode 1 (GRUB): after the boot partition is copied, `fatlabel -i -r` gives the clone a new boot volume ID. `docs/usage.md` describes both changes. ## Reason fsck.fat has no reboot exit code. It exits with 2 on a usage error and with 6 when it cannot open the device (dosfstools 4.2). The bitmask test handled both as "reboot required", so such a device rebooted on every boot. Now the boot continues as for any other failed boot partition check, and the mount decides. GRUB passes the filesystem UUID of the boot partition as `bootpart_fsuuid=` (with omnect/meta-omnect#697, which probes partition 1), and the init finds its disk with `blkid --uuid`. The clone copies the boot partition byte for byte, so it had the same volume ID as the source. With the source disk still attached, blkid could return the source, and the init mounted the source partitions under the kernel of the clone. The unit tests pin the `fatlabel` arguments, the step order, and that a failed `fatlabel` stops the clone before the EFI entry is created. Signed-off-by: Jan Zachmann <50990105+JanZachmann@users.noreply.github.com>
The recipe now builds the head of the factory reset contract change, which also has the firmware update order fix for bootloader updates. Signed-off-by: Jan Zachmann 50990105+JanZachmann@users.noreply.github.com
The initramfs writes flash-mode-url and flash-mode-url-sha256 for flash mode 3. The checksums of rpi and phytec-imx8mm change with omnect_env.h. Signed-off-by: Jan Zachmann 50990105+JanZachmann@users.noreply.github.com
The initramfs no longer creates /mnt/cert/ca and /mnt/cert/priv, so tmpfiles creates them when they are missing. Signed-off-by: Jan Zachmann 50990105+JanZachmann@users.noreply.github.com
Signed-off-by: Jan Zachmann 50990105+JanZachmann@users.noreply.github.com
Both recipes are generated with cargo bitbake. The init now keeps the behaviour of the scripted initramfs: /run hardening, create-fs-links.txt, the move of the persistent logs, and freeing the initramfs memory. Signed-off-by: Jan Zachmann 50990105+JanZachmann@users.noreply.github.com
cargo bitbake writes an AUTOINC version, and every git crate adds its revision to SRCREV_FORMAT. The ipk file name then gets too long and do_package_write_ipk fails. Only the revision of omnect-device-service goes into the version now. Signed-off-by: Jan Zachmann 50990105+JanZachmann@users.noreply.github.com
The recipe is generated with cargo bitbake. The init now writes the fsck logs and the flash mode logs readable only by root. Signed-off-by: Jan Zachmann 50990105+JanZachmann@users.noreply.github.com
## Summary A boot with `omnect_validate_update`, `omnect_validate_update_failed` or `omnect_bootloader_updated` set is no first boot any more, and an unreadable flag counts the same. On such a boot `handle_update_validation` writes `omnect_first_boot_done`, and it clears `omnect_validate_update_failed` and `omnect_bootloader_updated` only after that write worked; if the write fails, the flag stays set, the next boot tries again, and the init logs an error. `omnect_validate_update` is cleared by ODS, so on a validation boot a failed write is not retried: the boot after a successful validation is a first boot. A flash-mode boot does not write the marker. Both marker writers use one helper on `BootEnv`. The extra-bootargs sync gates on `first_boot` alone again; its own `update_pending` check and the `update_pending` field of `InitSetupCtx` are removed. `docs/usage.md` and the affected specs describe the new `first_boot` value. ## Reason A device updated from the scripted initramfs has no `omnect_first_boot_done`. The init took the first boot after the update for the first boot after flashing: it reported `first_boot: true` and ran the data resize again on a device in use. The extra-bootargs sync skipped only validation boots, but an update that also updates the bootloader is not validated: the update handlers set `omnect_bootloader_updated` instead of `omnect_validate_update_part`. The integration in omnect/meta-omnect#697 changes the bootloader on every machine, so it takes exactly this path. An update ends the first-boot phase: a device whose first-boot resize never succeeded no longer retries it after an update. The unit tests pin each update flag and a read error on each, that the marker is written before a flag is cleared and that no flag is cleared when the write fails, a failed write on a validation boot that still creates the trigger file, and over two boots on one mock that a fresh flash and each update flag end the first-boot phase after one boot. --------- Signed-off-by: Jan Zachmann <50990105+JanZachmann@users.noreply.github.com>
The recipe is generated with cargo bitbake. The init now deletes the local update validation config on boots without update validation. Signed-off-by: Jan Zachmann 50990105+JanZachmann@users.noreply.github.com
The recipes are generated with cargo bitbake from the current heads of the init and omnect-device-service branches. Signed-off-by: Jan Zachmann 50990105+JanZachmann@users.noreply.github.com
…ate validation config (#40) ## Summary Keeps behaviours of the scripted initramfs that the init did not keep, and removes a stale update validation config: - `/run` is mounted with `nosuid`, `nodev`, `strictatime` and mode 0755. - The links in `/etc/omnect/create-fs-links.txt` (one `<target> <link>` pair per line) are created once, then the file is deleted. - With `persistent-var-log`, `/var/log` is `/mnt/data/log`, the directory of the scripted initramfs. The fsck logs are written to `/mnt/data/log/fsck`. - After `switch_root` the initramfs files are deleted, so their RAM is freed. The delete does not cross into mounted filesystems, and the number of deleted entries is logged. When `/` is not a ramfs or tmpfs, the files are kept and the reason is logged. - A factory reset restore creates missing parent directories with the owner and mode from the backup. - The fsck logs and the flash mode logs are readable only by root: files 0600, the fsck log directory 0700. The scripted initramfs kept fsck output only in the status file, which has mode 0600. - `parted` runs in script mode, and a failed `parted print` is an error. - An empty `bootpart_fsuuid=` fails at once with a clear error instead of waiting for the timeout. - An fsck record in the env that does not unpack or has no exit code line (the format of the scripted initramfs) is deleted instead of staying there on every later boot. The text of a record without exit code is logged first. - On a boot without `omnect_validate_update`, `/mnt/data/var/lib/omnect-device-service/update_validation_conf.json` is deleted. A missing file is not an error, a failed delete is logged as a warning. On a validation boot the file is kept. - `tests/contract` holds the status file for every factory reset result, and a test checks that the init writes exactly these files. - `docs/usage.md` describes the symlinks, the removal of the update validation config, and that a failed EFI boot entry ends a flash mode with a fatal error. - `docs/usage.md` and the README warn that flash mode 3 does not check TLS certificates on a machine without `rtc`. - Two wrong comments are fixed: the init does not create `/mnt/cert/ca` and `/mnt/cert/priv`, and GRUB probes the boot partition of its own disk. ## Reason These are behaviours of the scripted initramfs that the init did not keep, or kept only partly. A device updated from the scripted initramfs would lose its log history and its one-time links, and run with a `/run` that allows suid binaries and device files. omnect/meta-omnect#697 builds the init into omnect-os and needs these fixes. omnect-device-service writes the update validation config before the reboot into the validation boot of an update it runs itself (`set_boot_partition`), and removes it after a successful validation. A failed validation reboots without removing it. An ADU update does not go through omnect-device-service: the swupdate handler only sets `omnect_validate_update_part` and does not write the file, but the validation reads it. So after a failed local validation, the next ADU update is validated as a local one: no IoT Hub connection check, and deviceupdate-agent is not started by omnect-device-service. A config written by hand is also deleted on every boot without validation; omnect/omnect-device-service#207 adds this to the update validation docs. The contract files close a gap that no device test can see: the status file is the interface to omnect-device-service. omnect/omnect-device-service#207 adds a copy of these files and a test that parses each one. The error-after-wipe and warning cases are built by the factory reset status code, so they hold values the init really writes. --------- Signed-off-by: Jan Zachmann 50990105+JanZachmann@users.noreply.github.com
The recipe is generated with cargo bitbake from the omnect-os-init main branch at tag 0.1.0. Signed-off-by: Jan Zachmann <50990105+JanZachmann@users.noreply.github.com>
JoergZeidler
left a comment
There was a problem hiding this comment.
Review pass on head 7df8c9c, reviewed against omnect-os-init@0.1.0 and the sibling-repo contracts. Findings are inline, most severe first. The two High items (rtc feature resolution, A/B rollback safety) need a build to settle — see the inline notes. Verified OK and not commented: SRCREV = tag 0.1.0, OMNECT_USER_ID default 15581, and the added U-Boot env keys match the init's BootEnvKey strings (but the fsck set is incomplete — see omnect_env.h).
| SRC_URI += "git://github.com/omnect/omnect-device-service.git;protocol=https;nobranch=1;branch=main" | ||
| SRCREV = "9b62e97f07e4dbbed66d6cc4b8ceb155411b763d" | ||
| # SRC_URI += "crate://crates.io/omnect-device-service/0.46.1" | ||
| SRC_URI += "git://github.com/JanZachmann/omnect-device-service.git;protocol=https;nobranch=1;branch=jz-2026-07-28-factory-reset-json-contract" |
There was a problem hiding this comment.
High (acknowledged blocker) — builds from a personal fork on a WIP branch: JanZachmann/omnect-device-service, branch jz-2026-07-28-factory-reset-json-contract, and PV still carries .AUTOINC+e9488c7e74. The PR text already says this must move to a released version before merge. Point it back to omnect/omnect-device-service on a released SRCREV (as omnect-os-init already does) and drop the AUTOINC suffix.
There was a problem hiding this comment.
Yes, this stays open until the ods PR is merged and released. Then the recipe is generated again with cargo bitbake from the omnect repo, without the AUTOINC suffix.
There was a problem hiding this comment.
Understood. Leaving this thread open as the merge gate until the ODS PR is released and the recipe is regenerated from the omnect repo.
GRUB falls back to hd0 when cmdpath names no disk, and boots with an empty bootpart_fsuuid when probe fails. Both cases now print a message, so a failed boot has a hint on the console. Signed-off-by: Jan Zachmann <50990105+JanZachmann@users.noreply.github.com>
Signed-off-by: Jan Zachmann <50990105+JanZachmann@users.noreply.github.com>
Summary
omnect-os moves from the scripted initramfs to omnect-os-init, an init written in Rust. This PR is the main integration step: it builds the init, installs it as
/initand removes the scripted initramfs (init.d/*fromomnect-os-initramfs-scripts).omnect-os-init: Cargo features follow the machine and distro (gruboruboot, the partition table fromOMNECT_PART_TABLE,factory-reset,resize-data,persistent-var-log,release-image,flash-mode-2,flash-mode-2-directwhenOMNECT_FLASH_MODE_2_DIRECT_FLASHINGis1,flash-mode-3,rtcfrom the machine feature, andearly-moduleswhenOMNECT_EARLY_MODULESis set). The partition and bootloader constants that flash mode 1 needs are exported into the build; for flash mode 2 also the boot partition offset and size andOMNECT_USER_ID.OMNECT_PART_TABLE(gptordos, defaultgpt) selects the partition table the init is built for; a machine whose wks file uses--ptable msdossetsdos.OMNECT_USER_ID(uid and gid of the omnect user) is a distro variable, so the init recipe andomnect_user.bbclassshare one value.omnect-os-initramfs: installs the tools the init calls (bashfor the debug shell on non-release images,e2fsprogs,dosfstools,util-linux-sfdisk,util-linux-blkidon GRUB machines,efibootmgron EFI machines,dhcpcdandbase-passwdfor flash modes 2 and 3,dropbearand the omnect user for flash mode 2,curlandca-certificatesfor flash mode 3). It drops the legacy script packages, the initramfs-framework modules,os-release,jq,kmod,util-linux-uuidgen,bmaptoolandxz.dhcpcdis built without udev: it runs only in the initramfs, where udevd does not run, so it no longer pulls udev into the initramfs. Thefsck.fatlinks in/sbinare created only when/sbinis not merged into/usr/sbin; on a merged image they replacedfsck.fatwith a link to itself, so the boot partition was never checked.bootpart_fsuuidinstead ofrootblk, so the init finds its disk by the filesystem UUID of the boot partition (partition 1, read withprobe). A flash mode 1 clone gets a new boot volume ID (omnect-os-init#38); the rootfs UUID would be the same on the clone and its source.$cmdpath, which needs theregexpmodule), not fromhd0.hd0is the first disk of the firmware: with a flash mode 1 clone and its source both attached, GRUB read the grubenv of the clone but loaded kernel and rootfs from the source.omnect_first_boot_done,omnect_fsck_*,omnect_factory_reset_last_error,flash-mode-url,flash-mode-url-sha256), so asaveenvinomnect_update_flowkeeps them.data-mount-optionsandresized-dataare no longer listed, because the init reads neither.u-boot-scr: the boot script no longer appends the literal textNonetobootargswhenOMNECT_APPEND_1ST_BOOTis unset, as on developer images.OMNECT_EARLY_MODULESlists the kernel modules the init loads beforeswitch_root(imx_sdmaon phytec-imx8mm). It selects theearly-modulesfeature, and the new packageomnect-os-init-early-modulesinstalls the list into the rootfs as/etc/omnect/early-modules.d/omnect.conf. Theomnect-os-initrecipe is built per machine, because its features andbuild.rsread machine settings. rpi kernels build compressed modules, so they getCONFIG_MODULE_DECOMPRESSwhen the variable is set.omnect-base-filesadds the mount point/mnt/rootCurrentPrivate, where the init bind-mounts the root filesystem. Its tmpfiles config creates/mnt/cert/caand/mnt/cert/priv(dinstead ofz), because the init does not create them.poweroffconfig is removed; the init powers off itself.omnect-os-initramfs-scripts. A layer with a.bbappendfor it no longer parses, and owninit.dscripts are no longer possible..bbappend. A request withmode4 now gets status 1.data-mount-options; the data partition is always mounted with the default options.preserve: the factory-reset request must now contain it, else it gets status 3.omnect-device-servicereads the factory-reset result the init writes (omnect-device-service#207).doc/: keep the build settings and link to the omnect-os-init docs for the behaviour of flash modes, factory reset, filesystem check and the status file.Reason
The initramfs moves from shell scripts to one Rust binary, for typed error handling, a defined result contract towards omnect-device-service, and unit tests for the boot, factory-reset and flash paths.
omnect-os-initrecipe builds the released version 0.1.0. It contains omnect-os-init#40, which keeps what the scripted initramfs did:/runwithout suid and device files, the links ofcreate-fs-links.txt, the persistent logs in/mnt/data/log, and the restored directories with their owner and mode.omnect-device-servicerecipe builds from the branch of omnect-device-service#207. It moves to a released version before this merges.omnect_env.hand the GRUB config are bootloader inputs (on phytec-imx8mm the boot script is one too); devices get a bootloader update with this image. A layer whose bootloader checksum coversrecipes-bsp/u-boot/u-boot/*needs a new expected value at the same time.