Repository navigation
composefs: Add support for android boot (both regular and ukiboot) - #2490
Conversation
|
Copying over composefs/composefs-rs#399 (comment) - would have been nice to have a rough sketch of a design draft for this linked and gather a bit of consensus on it. Could have basically just been fe1ea1a#diff-8df22b9d26bdc5a49b7c7557b59e35583771fb6bd56bb735479048f176cec622 filed as an issue? It may still be worth filing one as the right place to discuss high level things as opposed to implementation code concerns. |
|
@cgwalters I've done this work as much to learn about the composefs bootc backend as to do the work, because I had not looked at it before. So, it would have been hard for me to come up with the design up front. However, I agree that we need a highlevel discussion, and that will fit better in an issue, then we can do the code nitpicking here later. I'll type one up to get us started. |
fe1ea1a to
293d135
Compare
|
I moved the independent stuff to separate MRs and rebased this. |
293d135 to
1f705ac
Compare
|
I rebased on master and fixed some CI failures. I also changed things around so now we just always auto-detect the backend, no need to hand-specify a bootloader. |
This changes some APIs, and we need the corresponding changes in bootc from: bootc-dev/bootc#2490 We'll re-enable this once that is merged. Signed-off-by: Alexander Larsson <alexl@redhat.com>
1f705ac to
b8caa13
Compare
b8caa13 to
05705fe
Compare
|
I updated this to the composefs-rs 0.9.3 release and removed the draft marking. |
This changes some APIs, and we need the corresponding changes in bootc from: bootc-dev/bootc#2490 We'll re-enable this once that is merged. Signed-off-by: Alexander Larsson <alexl@redhat.com>
72e8eec to
0d03ff7
Compare
05705fe to
f39c6af
Compare
|
Rebased on main |
This is needed for aboot support. Signed-off-by: Alexander Larsson <alexl@redhat.com>
Add the Aboot boot type and map composefs-boot aboot entries to it. Reject aboot operations before modifying bootloader state until the deployment and lifecycle paths are implemented. Assisted-by: AI Signed-off-by: Alexander Larsson <alexl@redhat.com>
An addon can sort before the primary UKI and supply the wrong kernel version and command line. Ignore addon candidates when selecting the kernel; addon installation continues to use the separate boot-entry inventory. Images with only addons now report no kernel; addons alongside vmlinuz no longer force UKI backend selection. Assisted-by: AI Signed-off-by: Alexander Larsson <alexl@redhat.com>
This extends KernelType with a new Aboot version, and extends the reporting of "bootc container inspect" to give aboot info as well. Note: We're not recognizing traditional ostree aboot setups with an aboot.img in /usr/lib/modules/$kver, because we will be treating these differently, enforcing the composefs backend. Also, we want the artifacts in /boot so that we can sign them without having that affect the boot transformed digest. Assisted-by: AI Signed-off-by: Alexander Larsson <alexl@redhat.com>
This moves the code for kernel selection, composefs digest calculation, and command-line assembly so aboot builders can reuse it later. Note: This is just a code motion and no significant beheviour is changed (other than maybe some specific error message). Assisted-by: AI Signed-off-by: Alexander Larsson <alexl@redhat.com>
Kernel arguments from the image or --karg can contain stale composefs digests. Apply the computed arguments after those inputs, replacing existing values and removing duplicates so these are not duplicated, which could cause confusion. Assisted-by: AI Signed-off-by: Alexander Larsson <alexl@redhat.com>
This uses aboot-update to build Android boot or ukiboot images, similar to the "container ukify" command. Assisted-by: AI Signed-off-by: Alexander Larsson <alexl@redhat.com>
This just moves some code out, which will make later aboot changes easier to do. Assisted-by: AI Signed-off-by: Alexander Larsson <alexl@redhat.com>
Pick (or allow selecting) the right bootloader for aboot images, and install EFI files needed in case of ukiboot. Command lines in immutable images are validated. This changes how bootloader "none" is handled in the composefs backend. Before it was not supported, but now it's is used for android boot aboot images, where the bootloader is not on disk but in the board. We also need to be more careful when picking the primary boot entry, becase discover enumerates Type2 entries before the aboot, but we might have non-bootable Type2 entries (like addons), and we don't want those to cover an aboot entry. Assisted-by: AI Signed-off-by: Alexander Larsson <alexl@redhat.com>
Implement staging, finalization, and boot-time reconciliation for aboot deployments. The boot time reconciliation is done in the bootc-aboot-reconcile.service which is enbled by the bootc generator if needed (and starts once writable state is available). The reconciliation includes active slot tracking to ensure we're not GC:ing bootable images. Updates stage the boot image and optional vbmeta alongside the deployment and record their hashes in persistent pending state. At shutdown it, verifies the payloads, records the attempt, invalidates the non-booted slot mapping, and invokes aboot-deploy which will write the image to the next free boot partition. On the following boot, use the active deployment to determine whether the attempt succeeded. Clean up successful attempts while retaining failed candidates without automatically retrying them. Assisted-by: AI Signed-off-by: Alexander Larsson <alexl@redhat.com>
Document /boot-only discovery, immutable artifact command lines, artifact-selected disk layouts, and persistent A/B update behavior. Assisted-by: AI Signed-off-by: Alexander Larsson <alexl@redhat.com>
Let repart callers collect and use definitions in a chosen directory, preparing for layouts with additional required partitions. Assisted-by: AI Signed-off-by: Alexander Larsson <alexl@redhat.com>
Derive Android or ukiboot disk layouts from the source image's boot artifact when installing the running container image. With --source-imgref, skip artifact-based layout selection rather than inferring it from the installation host. Validate repart definitions and the pulled artifact against the selected layout, then initialize both slots. Keep the legacy OSTree installation path unchanged. Assisted-by: AI Signed-off-by: Alexander Larsson <alexl@redhat.com>
Exercise the native aboot installer through the existing bcvk and TMT runner on x86_64. Derive the image from the OSTree test rootfs so backend selection must follow the boot artifact, and verify the default disk layout, both payload slots, and boot completion. After that we try an update and rollback. Note: This manually installs bcvk 0.21.0, this can be dropped once bootc-dev/actions has been updated. Generated-by: AI Signed-off-by: Alexander Larsson <alexl@redhat.com>
fe824d5 to
c1ebc20
Compare
|
I see the CI failure fix was merged, lemme rebase |
|
Ok, now all the tests passed. Any chance we could get some more review and feedback on this? It kinda blocks a bunch of more work on using this on the automotive side. |
jmarrero-bot
left a comment
There was a problem hiding this comment.
A few non-blocking notes on the aboot code, inline.
Generated-by: https://github.com/jmarrero-bot#llms
| )); | ||
|
|
||
| state.record_attempt(deployment)?; | ||
| if let Some(slot) = booted_slot { |
There was a problem hiding this comment.
Two things here, neither blocking for an experimental feature:
- When
booted_slotisNone(no or ambiguousandroidboot.slot_suffix) this still records the attempt and runsaboot-deploy, but the docs ("requires an unambiguous booted slot") say it shouldn't. Bail beforerecord_attempt()instead? invalidate_other()drops the rollback slot's mapping beforeaboot-deployhas written anything. If it then fails without writing (say the binary is missing), the old rollback drops out ofbootc statusand out of the GC roots, but its slot still holds a bootable payload. And the way out of the resultingattemptedstate (bootc delete-deploymenton the staged deployment) runs a pruning GC right away, which then deletes that rollback's state. Checking foraboot-deployup front, or restoring the mapping when the spawn fails, would avoid that.
There was a problem hiding this comment.
Hmm, these are all very weird situations where the system is basically hosed from the point of view of aboot.
We have at times during development had systems which didn't properly record a boot slot, and having those act as a single-slot system has helped us make progress. So, basically I would like to leave all the slot handling magic (or at least as much as we can) to the aboot-deploy call, rather than trying to guess how it will behave. So, I think the first issue explained here is good for this development setup. In a real production environment it should not matter anyway.
For the second issue, yes, we could potentially loose and old rollback slot, but that will only happen if the current image is broken (no aboot-deploy), and I'm not sure how interesting that specific error case is. I guess you could maybe roll back to the old commit that had aboot-deploy? Any later error we can't tell if the state is safe to keep anyway. I'll add an up-front check for aboot-deploy, somewhere early in the update codepath (probably before finalize).
| origin, | ||
| verity, | ||
| missing_verity_allowed, | ||
| Bootloader::None, |
There was a problem hiding this comment.
Minor: this hardcodes Bootloader::None, so bootc status on a ukiboot host reports bootloader: none even though install picked ukiboot (image_bootloader()). The origin doesn't record the encoding today, so this probably needs the bootloader (or encoding) written at install/stage time.
There was a problem hiding this comment.
Yeah, lets add the bootloader to the origin file.
There was a problem hiding this comment.
Actually, we only ever use one bootloader on the host. I should report the host setting here, and then add some code to validate we always stay on the same bootloader on updates.
| } else { | ||
| ensure!( | ||
| bootloader != Bootloader::None, | ||
| "Bootloader set to none is not supported with the composefs backend unless using an aboot artifact" |
There was a problem hiding this comment.
This check used to be in InstallComposefsOpts validation, before anything touched the disk. Now it runs in setup_composefs_boot(), so install to-disk --composefs-backend --bootloader none with an ordinary image wipes and partitions the disk, pulls, and only then fails. At least for installs from the running container, prepare_install() already knows from find_kernel() whether the image has an aboot payload. Could the non-aboot case keep failing early there?
There was a problem hiding this comment.
I'm not worried about this. install to-disk generally runs in a VM during install. Delaying the error to after some loopback files are wiped isn't really a problem.
| option and reconciles the attempted deployment. The previous slot becomes the rollback | ||
| deployment; `bootc rollback` queues it through `aboot-deploy --rollback`. `bootc status` | ||
| indicates when a rollback is queued. A failed boot is **not** automatically retried: bootc | ||
| retains the attempted update for inspection and will not stage another update until that |
There was a problem hiding this comment.
Suggest saying how that state gets resolved. As far as I can tell, after a failed flash or a firmware fallback the only way to clear attempted and unblock bootc upgrade/switch/rollback is the hidden bootc delete-deployment <id> on the staged deployment.
There was a problem hiding this comment.
I think we should just never let the failed attempt be retried or block an update. It should just be temporarily available for inspection.
|
Approving to unblock this; the inline comments can be follow-ups. The docs are explicit that this is experimental, but the CLI doesn't gate or mark it (bootc container aboot, --bootloader ukiboot; RELEASES.md asks for an experimental marker on the command or flag, as grub-cc has). Not sure if that's on purpose @alexlarsson? Either way, this is focused work that won't affect most users at the moment: I checked that grub and systemd-boot hosts are unchanged. |
|
@jmarrero Thanks. Any chance you can merge this now? I'll work on the followup comments separately. But it is somewhate of a pain to keep this PR updated as it often conflicts with other unrelated changes. |
Add experimental aboot support to the composefs backend. This supports both
Android boot v2 images on systems with
boot_a/boot_bpartitions and no ESP,and ukiboot images on systems with an ESP.
The series adds
bootc container abootto build boot artifacts, teachescontainer inspect and composefs installation to recognize them, and implements
slot-aware status, updates, rollback, reconciliation, and garbage collection.
Updates are staged without writing a boot partition; at shutdown, bootc
verifies the artifacts and uses
aboot-deployto flash the inactive slot.There is also some generic changes:
This is marked draft atm, because it pulls in the composefs-rs branch from composefs/composefs-rs#399. Once that is landed we should do a release and update bootc to that instead.
I have follow-on work to use this to build images, which I have used to test stuff:
I was able with this to use a Dockerfile to build Fedora 44 images that boot on x86_64/aarch64 with ukiboot, and aarch64 with u-boot with android boot support. I think we can later add some integration test for this, but atm its a bit painful until all the other dependency changes have landed.