Skip to content

Prepare to support UKIs - #1007

Draft
arnaldo2792 wants to merge 8 commits into
bottlerocket-os:developfrom
arnaldo2792:uki-support/core-kit
Draft

Prepare to support UKIs#1007
arnaldo2792 wants to merge 8 commits into
bottlerocket-os:developfrom
arnaldo2792:uki-support/core-kit

Conversation

@arnaldo2792

Copy link
Copy Markdown
Contributor

Description of changes:

This series prepares the core kit to support Unified Kernel Images.

Build systemd-boot and systemd-stub for systemd-257. A patch for systemd was needed to prevent it from building these binaries for the wrong CPU architectures (32 bits).

The release package was updated to conditionally include grub, since now there is another package that provides a bootloader.

Given that in-place upgrades aren't supported for the time being, signpost is skipped entirely as a binary. However, the library component was updated to find the correct FAT partition that includes the UKI to mount it at /boot. This is needed for other parts of the system, like the FIPS check.

User-space PCR9 measurements are skipped for UKI-based images, since PCR9 is measured at the kernel level when systemd-boot is used.

Testing done:

As part of: bottlerocket-os/bottlerocket-kernel-kit#523

  • Booted UKI
  • Confirmed cmdline measurements are skipped in UKI images and ran in GRUB based images
  • /boot partition mounted for both formats
  • Build fails when in-place-upgrades + uki are used

Terms of contribution:

By submitting this pull request, I agree that this contribution is dual-licensed under the terms of both the Apache License, version 2.0, and the MIT license.

@arnaldo2792
arnaldo2792 force-pushed the uki-support/core-kit branch from 7871ec2 to 96f69ca Compare August 11, 2026 01:07
@arnaldo2792

Copy link
Copy Markdown
Contributor Author

(Force push to fix commit messages)

@arnaldo2792
arnaldo2792 force-pushed the uki-support/core-kit branch from 96f69ca to 43887e4 Compare August 11, 2026 01:08
@arnaldo2792

Copy link
Copy Markdown
Contributor Author

(Forced push to fix commit messages, for real)

@arnaldo2792

Copy link
Copy Markdown
Contributor Author

(Forced push includes missing systemd service)

Comment on lines +5 to +6
# Note that this file is measured into TPM PCR 5 before parsing: editing
# it changes PCR 5 and invalidates anything sealed against it.

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'd suggest this being the first line of the file, seems like a significant callout. Maybe a "DO NOT EDIT" line, or "WARNING" line too?

Comment thread packages/systemd-257/systemd-257.spec Outdated
# shim EFI binaries, so image builds find all ESP loaders in one place.
%global efidir /boot/efi/EFI/BOOT

# systemd-boot reads its own configuration from \loader\loader.conf on the

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

did you mean /loader/loader.conf?

https://www.freedesktop.org/software/systemd/man/latest/loader.conf.html

Suggested change
# systemd-boot reads its own configuration from \loader\loader.conf on the
# systemd-boot reads its own configuration from /loader/loader.conf on the

Comment thread packages/systemd-257/loader.conf Outdated
# No PC-speaker beeping during the countdown.
beep no

# Never scan \loader\keys or enroll Secure Boot keys from the ESP.

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
# Never scan \loader\keys or enroll Secure Boot keys from the ESP.
# Never scan /loader/keys or enroll Secure Boot keys from the ESP.

https://www.freedesktop.org/software/systemd/man/latest/loader.conf.html

Comment thread packages/systemd-257/systemd-257.spec Outdated
%{buildroot}%{efidir}/systemd-boot%{_cross_efi_arch}.efi

# Ship sensible systemd-boot defaults. This has to land on the ESP rather than
# the boot partition, since systemd-boot only reads \loader\loader.conf from

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
# the boot partition, since systemd-boot only reads \loader\loader.conf from
# the boot partition, since systemd-boot only reads /loader/loader.conf from

Comment thread sources/api/prairiedog/src/main.rs Outdated
// A UKI image boots from the XBOOTLDR partition and has no A/B partition sets to fall back
// on, so a missing partition means /boot can never be populated. Fail loudly instead of
// leaving it unmounted, which would silently break everything that reads /boot.
None if uki_image() => BootAction::MissingXbootldr,

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

can't we just

Suggested change
None if uki_image() => BootAction::MissingXbootldr,
None if uki_image_from_env() => BootAction::MissingXbootldr,

and bring this function's signature to just

fn boot_action(xbootldr: Option<PathBuf>) -> BootAction

? The callsite in prepare_boot is the only instance of using uki_image_from_env(), which is just wired through to this method.

Comment thread sources/api/prairiedog/src/main.rs Outdated
match boot_action(xbootldr, uki_image_from_env) {
BootAction::MountXbootldr(boot_partition_path) => {
info!(
"Found XBOOTLDR partition {} on the OS disk, so this is a UKI image",

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
"Found XBOOTLDR partition {} on the OS disk, so this is a UKI image",
"UKI image detected (XBOOTLDR partition '{}')",

Comment thread sources/updater/signpost/src/state.rs Outdated
Comment on lines +415 to +500
pub fn xbootldr_partition() -> Result<Option<PathBuf>, Error> {
// The root filesystem is a dm-verity device. We want to determine what disk and partition
// the backing data is part of. Look up the device major and minor via stat(2):
let root_fs = BlockDevice::from_device_path("/")
.context(error::BlockDeviceFromPathSnafu { device: "/" })?;
// Get the first lower device from this one, and determine what disk it belongs to.
let active_partition = root_fs
.lower_devices()
.and_then(|mut iter| iter.next().transpose())
.context(error::RootLowerDevicesSnafu {
root: root_fs.path(),
})?
.context(error::RootHasNoLowerDevicesSnafu {
root: root_fs.path(),
})?;
let os_disk = active_partition
.disk()
.context(error::DiskFromPartitionSnafu {
device: root_fs.path(),
})?
.context(error::RootNotPartitionSnafu {
device: root_fs.path(),
})?;

// Parse the partition table on the disk.
let table = GPT::find_from(&mut File::open(os_disk.path()).context(error::OpenSnafu {
path: os_disk.path(),
what: "reading",
})?)
.map_err(error::GPTError)
.context(error::GPTFindSnafu {
device: os_disk.path(),
})?;

// Loads the path to partition number `num` on the OS disk.
let device_from_part_num = |num| -> Result<PathBuf, Error> {
Ok(os_disk
.partition(num)
.context(error::PartitionFromDiskSnafu {
device: os_disk.path(),
})?
.context(error::PartitionNotFoundOnDeviceSnafu {
num,
device: os_disk.path(),
})?
.path())
};

let mut partitions = table
.iter()
.filter(|(_, p)| p.is_used() && p.partition_type_guid == XBOOTLDR)
.map(|(num, _)| device_from_part_num(num))
.collect::<Result<Vec<_>, Error>>()?;

ensure!(
partitions.len() <= 1,
error::MultiplePartitionsOfTypeSnafu {
partition_type: {
let g = XBOOTLDR;
format!(
"{:02x}{:02x}{:02x}{:02x}-{:02x}{:02x}-{:02x}{:02x}-{:02x}{:02x}-\
{:02x}{:02x}{:02x}{:02x}{:02x}{:02x}",
g[3],
g[2],
g[1],
g[0],
g[5],
g[4],
g[7],
g[6],
g[8],
g[9],
g[10],
g[11],
g[12],
g[13],
g[14],
g[15],
)
},
partitions,
}
);

Ok(partitions.pop())
}

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

[P2] Deduplicate disk/GPT lookup shared by xbootldr_partition and State::load

xbootldr_partition() (lines ~418–461) is a near line-for-line copy of the first ~60 lines of State::load() (lines 71–131): the BlockDevice::from_device_path("/") walk, the lower_devices() selection, the disk() unwrap, the GPT::find_from open, and the device_from_part_num closure are all repeated verbatim.

Why it matters. prepare_boot in prairiedog calls both signpost::xbootldr_partition() and signpost::State::load() back-to-back in the GRUB path (main.rs:243 and main.rs:274), so the same disk is stat-walked and its GPT parsed twice on every boot. More importantly, any future change to how the OS disk is discovered has to be applied in two places, and drift here is silent.

Failure scenario. A follow-up change that teaches State::load to unwrap an extra dm layer (say, "boot from LUKS") applied only to State::load leaves xbootldr_partition looking at the wrong disk. It returns None, prairiedog silently takes the MountActiveSet branch on what is really a UKI image, and boot tries to mount an ext4 boot partition on a disk that no longer has one.

Fix. Extract a private helper like fn parse_os_disk_table() -> Result<(Disk, GPT), Error> and have both entry points call it. Alternatively, expose the XBOOTLDR lookup as a method on State so a single scan feeds both.

Comment thread sources/updater/signpost/src/state.rs Outdated

let mut partitions = table
.iter()
.filter(|(_, p)| p.is_used() && p.partition_type_guid == XBOOTLDR)

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

[P2] xbootldr_partition filters on is_used() but State::load does not

xbootldr_partition filters entries with p.is_used() && p.partition_type_guid == XBOOTLDR (this line). State::load's nth_guid closure (lines 104–110) filters only on p.partition_type_guid == guid — no is_used() check.

In the current schema this cannot matter, because unused entries have an all-zero type GUID. But the asymmetry is exactly the kind of subtle drift the "read the GPT once" comment in main.rs:239–242 is trying to avoid: if a future change ever ties partition identity to something other than the type GUID (a partition-name convention, an "in transition" tombstone), the two functions will disagree.

Also: the test ignores_unused_and_other_types (line 583) exercises partition_nums_with_type — a test helper — not State::load's actual nth_guid filter.

Fix. Add p.is_used() to State::load's nth_guid filter for consistency, or extract a shared helper.

Comment thread sources/updater/signpost/src/state.rs Outdated
Comment on lines +469 to +497
ensure!(
partitions.len() <= 1,
error::MultiplePartitionsOfTypeSnafu {
partition_type: {
let g = XBOOTLDR;
format!(
"{:02x}{:02x}{:02x}{:02x}-{:02x}{:02x}-{:02x}{:02x}-{:02x}{:02x}-\
{:02x}{:02x}{:02x}{:02x}{:02x}{:02x}",
g[3],
g[2],
g[1],
g[0],
g[5],
g[4],
g[7],
g[6],
g[8],
g[9],
g[10],
g[11],
g[12],
g[13],
g[14],
g[15],
)
},
partitions,
}
);

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

[P2] MultiplePartitionsOfType builds the GUID string at runtime with a 16-arg format!

The XBOOTLDR type GUID is a compile-time constant — its canonical form is already in a comment three lines above the array literal (line 23) and in the MissingXbootldrPartition / ScanForXbootldrPartition display messages in prairiedog (error.rs:29, error.rs:47).

Three separate places now hand-serialize the same 128-bit constant, and one of them does it with a 16-argument positional format! that will silently produce the wrong string if the byte-swap indices are ever transcribed incorrectly. There is a byte-order test (xbootldr_guid_byte_order, line 597), but no test for the formatting.

Fix. Since XBOOTLDR is the only type GUID this call currently reports, just pass the canonical string literal: partition_type: "bc13c2ff-59e6-4262-a352-b275fd6f7172".to_string(). If more type GUIDs need reporting later, introduce a fn guid_to_string(g: [u8; 16]) -> String helper with its own test.

Comment thread sources/api/prairiedog/src/main.rs Outdated
Comment on lines +243 to +278
let (xbootldr, scan_error) = match signpost::xbootldr_partition() {
Ok(partition) => (partition, None),
Err(e) => {
// Not fatal by itself: a GRUB image still has the boot partition of its active
// partition set to mount. Log it here so the cause reaches the journal on every
// path, and hold on to it so a UKI image can report it as the underlying cause
// rather than claiming the partition is absent.
warn!("Unable to scan the OS disk for an XBOOTLDR partition: {e}");
(None, Some(e))
}
};

match boot_action(xbootldr, uki_image_from_env) {
BootAction::MountXbootldr(boot_partition_path) => {
info!(
"Found XBOOTLDR partition {} on the OS disk, so this is a UKI image",
boot_partition_path.display()
);
mount_boot_partition(&boot_partition_path, BOOT_FS_VFAT, Some(VFAT_MOUNT_DATA))
}

BootAction::MissingXbootldr => match scan_error {
// The disk may well have an XBOOTLDR partition that we were unable to see, so report
// why the scan failed instead of reporting the partition as absent.
Some(source) => Err(source).context(error::ScanForXbootldrPartitionSnafu),
None => error::MissingXbootldrPartitionSnafu.fail(),
},

BootAction::MountActiveSet => {
info!("No XBOOTLDR partition found on the OS disk; using the boot partition of the active partition set");
// Get the current partitions state
let state = signpost::State::load().context(error::LoadStateSnafu)?;

mount_boot_partition(&state.active_set().boot, BOOT_FS_EXT4, None)
}
}

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

[P2] Multiple XBOOTLDR partitions on a "GRUB" image silently fall back to mounting the A-set ext4 boot partition

xbootldr_partition() returns Err(MultiplePartitionsOfType) when more than one XBOOTLDR partition is on the OS disk (state.rs:469–497). Here in prepare_boot, that error is stashed as scan_error and downgraded to a warn! log (lines 250–252). Then boot_action(None, uki_image_from_env) is called: if UKI_IMAGE is unset or "false", control flows to BootAction::MountActiveSet and prairiedog mounts the A-set ext4 boot partition (lines 271–277).

Why it matters. "Two XBOOTLDR partitions on disk" is a structural corruption of the partition table, not a legitimate GRUB layout — a plain GRUB image has zero of them. Suppressing this to a warning on the GRUB path lets a broken disk boot as if nothing is wrong; if that disk was meant to be a UKI image but image-format.env got lost, the operator sees a successful /boot mount that contains none of the UKI's kernels. The failure only surfaces later, when systemd-boot or a kernel update tool trips over it.

Fix. In the Err arm at line 245, distinguish "no XBOOTLDR to report" (log and continue) from "the scan found a real problem" (return the error unconditionally). Only demote truly ambiguous scan errors (I/O failures on the raw disk) and propagate MultiplePartitionsOfType as a hard failure. Alternatively, keep the scan-error stash but check its variant when choosing the fallback.

Comment on lines +282 to +295
fn uki_image_from_env() -> bool {
match env::var(UKI_IMAGE_ENV) {
Ok(value) => {
info!("{UKI_IMAGE_ENV} is set to '{value}' in the environment");
value.trim() == "true"
}
Err(_) => {
info!(
"{UKI_IMAGE_ENV} is not set in the environment; assuming this is not a UKI image"
);
false
}
}
}

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

[P2] uki_image_from_env treats non-UTF-8 and misspelled values as "not a UKI image" without warning

uki_image_from_env uses env::var(UKI_IMAGE_ENV):

  • Ok(value) where value.trim() != "true" (including "TRUE", "1", "yes", or an accidental leading #) silently returns false after logging the value at info.
  • Err(NotUnicode(_)) (garbage bytes in the env var) is collapsed into the same "not set" branch as Err(NotPresent), so the operator sees "UKI_IMAGE is not set in the environment" even though it was set — just unreadably.

Why it matters. This env variable is the sole fallback used to decide whether missing partitions are fatal (line 232). A UKI image whose image-format.env was corrupted or transcoded (e.g. an editor added a BOM, or a build emitted UKI_IMAGE=True) will be misclassified as a GRUB image. On a UKI image with a transient GPT read error at boot, the operator loses the useful ScanForXbootldrPartition error message and gets a misleading State::load failure instead.

Fix. Either

  1. Case-insensitive parse (value.trim().eq_ignore_ascii_case("true")) and explicitly reject unrecognized values with a warning, or
  2. Return Result<bool, Error> and propagate parse/NotUnicode errors so prepare_boot can decide.

At minimum, log warn! (not info!) when the value is set but not exactly "true" — right now that case is indistinguishable from a legitimate UKI_IMAGE=false in the journal.

Comment on lines +195 to +196
Conflicts: %{_cross_os}image-feature(standalone-image)
Conflicts: %{_cross_os}image-feature(in-place-updates)

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

[P1] Conflicts: image-feature(in-place-updates) / image-feature(standalone-image) reference feature names that do not exist

The image-feature naming convention throughout the repository is the negative form. A grep for image-feature( across the tree shows:

  • image-feature(no-in-place-updates) — used across os.spec as the "off" flag.
  • image-feature(in-place-updates)not defined or used anywhere else.
  • image-feature(standalone-image)not defined or used anywhere else.

Why it matters. These are dead conflict clauses. The intended safety guard — "the systemd-boot bootloader package must never be installed alongside the signpost/GRUB update flow" — is silently inert. If a future variant is misconfigured to include both signpost/GRUB and the UKI bootloader package, rpm/dnf won't catch it.

Fix. Either invert the polarity to match the codebase convention, or drop these lines since Requires: image-feature(uki-image) on line 192 combined with Conflicts: image-feature(no-uki-image) on line 194 already prevents installation on any non-UKI image. If two new positive feature names are being introduced by this PR series, they need to actually be defined on the image side and referenced consistently.

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Not accurate again, the image features exist in Twoliter.

Comment thread packages/release/measure-cmdline.service
Comment thread packages/os/os.spec
Comment on lines +24 to +52
+# Bottlerocket SDK fix: the cross sysroot reports cpu_family 'x86' even for
+# x86_64 targets, which makes efi_arch resolve to 'ia32'. Force x64 when the
+# real target cpu is x86_64.
+if efi_arch == 'ia32' and host_machine.cpu() == 'x86_64'
+ efi_arch = 'x64'
+endif
+
+# The same SDK quirk applies on 64-bit ARM: cpu_family is reported as 'arm',
+# so efi_arch resolves to the 32-bit 'arm' EFI target and sd-boot is emitted as
+# systemd-bootarm.efi with EFI_MACHINE_TYPE_NAME=arm. Force aa64 when the real
+# target cpu is aarch64, so the binary is named and typed per the EFI spec and
+# matches the bootaa64.efi/grubaa64.efi convention used by shim and GRUB.
+# Note: efi_cpu_family is deliberately left resolving to 'arm' below, since the
+# 'arm' and 'aarch64' entries of efi_arch_c_args are identical
+# (-mgeneral-regs-only) and the extra 'arm' link arg
+# (-Wl,--no-wchar-size-warning) is harmless here.
+if efi_arch == 'arm' and host_machine.cpu() == 'aarch64'
+ efi_arch = 'aa64'
+endif
+
+# Single corrected arch key for all EFI flag lookups, so no flag site can
+# silently regress to the -m32 (ia32) path on this SDK.
+if efi_arch == 'x64'
+ efi_cpu_family = 'x86_64'
+elif efi_arch == 'ia32'
+ efi_cpu_family = 'x86'
+else
+ efi_cpu_family = host_machine.cpu_family()
+endif

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

[P2] Overriding efi_arch in systemd's meson.build masks a real SDK bug and is fragile against upstream refactors

The patch works around a Bottlerocket-SDK quirk where host_machine.cpu_family() returns 'x86' on x86_64 and 'arm' on aarch64, and post-processes the value here.

  1. Root cause is elsewhere. host_machine.cpu_family() being wrong is a meson cross-file / SDK configuration bug ([host_machine] cpu_family = 'x86_64' / 'aarch64' are the correct settings). Patching every consumer of cpu_family in systemd is whack-a-mole — the SDK cross-file should be fixed so every build sees the right value. If the SDK fix ever lands, this patch becomes actively misleading.

  2. Incomplete coverage. The patch only replaces host_machine.cpu_family() at two call sites in src/boot/meson.build. Other consumers inside systemd 257 (e.g., arch-specific seccomp lists, syscall filter tables, ukify machinery) will still see the wrong value. The commit message says "no flag site can silently regress" but only sd-boot's efi_archspecs was fixed.

  3. efi_arch_alt sentinel semantics. The patch removes the if have and efi_arch == 'x64' and cc.links(…) block but leaves the efi_arch_alt = '' initializer. Please confirm that efi_archspecs guards the _alt entries with if efi_arch_alt != '' — otherwise meson may still try to build a nameless alt binary.

  4. Aarch64 asserts an unenforced invariant. The comment says "efi_cpu_family is deliberately left resolving to 'arm'" for aarch64, relying on efi_arch_c_args.get('arm', []) == efi_arch_c_args.get('aarch64', []). Any future systemd change that diverges the two entries will silently be lost.

Fix.

  • Preferred: fix the SDK meson cross-file and drop this patch.
  • If the patch must stay: set efi_cpu_family = 'aarch64' explicitly when efi_arch == 'aa64', and add a message()/error() guard that asserts efi_arch_c_args.get('arm') == efi_arch_c_args.get('aarch64') at configure time.

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The SDK fix, we can do in a subsequent release. The patch must stay so I'll fix the suggestion.

Comment thread packages/release/measure-cmdline.service
arnaldo2792 and others added 8 commits August 13, 2026 00:44
Enable systemd-boot and systemd-stub in the systemd-257 spec to
provide the bootloader and EFI stub required for Unified Kernel
Images (UKI).

Add a patch to skip building 32-bit (ia32) EFI binaries on
architectures that do not need them, and ship a loader.conf
with sensible default settings.

Signed-off-by: Arnaldo Garcia Rincon <agarrcia@amazon.com>
Add EnvironmentFile=-/usr/share/bottlerocket/image-format.env to
prepare-boot.service so prairiedog can read image-format variables
at boot preparation time.

Signed-off-by: Arnaldo Garcia Rincon <agarrcia@amazon.com>
On UKI-based images, the kernel command line is measured into PCR9
as part of the UKI PE binary at boot, so the separate user-space
measurement in measure-cmdline.service is redundant. Gate the
service with an ExecCondition on UKI_IMAGE so it only runs on
non-UKI images.

Signed-off-by: Arnaldo Garcia Rincon <agarrcia@amazon.com>
Prior to the introduction of systemd-boot, GRUB was the only supported
bootloader and was always explicitly required. With systemd-boot now
available, the bootloader must be selected based on the format of the
built image.

Require the (bootloader-efi) capability, which is satisfied by either
grub or systemd-boot, whichever is installed based on the variant's
enabled feature flags.

Default to grub when the (uki-image) bconds are absent, preserving
backwards compatibility with older twoliter versions.

Signed-off-by: Arnaldo Garcia Rincon <agarrcia@amazon.com>
UKIs do not yet support in-place upgrades. Skip installing signpost
on UKI-based variants, since marking partitions with successful boots
only applies to the GRUB-based boot format.

Signed-off-by: Arnaldo Garcia Rincon <agarrcia@amazon.com>
Add xbootldr_partition(), which scans the OS disk's GPT for a
partition of the XBOOTLDR type GUID and returns its path. Returns
None when no such partition exists, which is the case on GRUB
images, and fails if more than one is found since there would be no
way to choose between them.

A prior pass introduced generic disk- and partition-type-searching
abstractions (disk.rs, partition_types.rs) to support this lookup,
but a single-purpose function is simpler and sufficient for the one
caller that needs it, so those files are removed in favor of this
helper living alongside the rest of the partition-table logic in
state.rs.

Signed-off-by: Arnaldo Garcia Rincon <agarrcia@amazon.com>
prepare_boot previously always mounted the active partition set's
ext4 boot partition. On UKI images, the boot partition is XBOOTLDR,
a FAT filesystem that systemd-boot's firmware loader must be able to
read.

Scan the disk layout and check the UKI_IMAGE environment variable,
which prepare-boot.service sets from image-format.env. If it is a
UKI image, look up the XBOOTLDR partition and mount it as vfat with
the mount options systemd-boot expects (umask, shortname, iocharset,
and an SELinux context, since FAT has no extended attributes to
carry a label). Otherwise, fall back to the existing ext4 mount of
the active set's boot partition.

Signed-off-by: Arnaldo Garcia Rincon <agarrcia@amazon.com>
Backport of upstream bb19b6104978b5ede792fa3f0cfc74272f20bf9c.

Signed-off-by: Maher Homsi <maherhom@amazon.com>
@arnaldo2792
arnaldo2792 force-pushed the uki-support/core-kit branch from 337e9c8 to 53b84ce Compare August 13, 2026 17:40
Requires: %{_cross_os}image-feature(uki-image)
Provides: %{_cross_os}bootloader(efi)
Conflicts: %{_cross_os}image-feature(no-uki-image)
Conflicts: %{_cross_os}image-feature(standalone-image)

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

standalone-image feature should be compatible with systemd-bootloader

Suggested change
Conflicts: %{_cross_os}image-feature(standalone-image)

Comment on lines +96 to +97
Patch9019: 9019-boot-remove-SMBIOS-Type-11-kernel-cmdline-extra-mech.patch
Patch9020: 9020-boot-vmspawn-finish-removing-SMBIOS-cmdline-extra-bi.patch

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Do these need to be 2 separate patches? Looks like they're modifying the same file.

The SDK cross sysroot mis-reports host_machine.cpu_family(), so systemd's
efi_arch lookup resolves to the wrong EFI target: 'ia32' on x86_64 and 'arm' on
aarch64. Correct both from host_machine.cpu(), and drop the IA-32 mixed-mode
alternate build, which cannot link in this sysroot.

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This patch (as well as 9019 and 9020) is missing a Signed-off-by: line

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

6 participants