diff --git a/archinstall/lib/installer.py b/archinstall/lib/installer.py index da1941834e..fe6d0935b4 100644 --- a/archinstall/lib/installer.py +++ b/archinstall/lib/installer.py @@ -1325,6 +1325,7 @@ def _add_grub_bootloader( boot_partition: PartitionModification, root: PartitionModification | LvmVolume, efi_partition: PartitionModification | None, + uki_enabled: bool = False, bootloader_removable: bool = False, ) -> None: debug('Installing grub bootloader') @@ -1396,6 +1397,37 @@ def _add_grub_bootloader( except SysCallError as err: raise DiskError(f'Failed to install GRUB boot on {boot_partition.dev_path}: {err}') + # Add custom UKI entries if enabled + if uki_enabled and SysInfo.has_uefi() and efi_partition: + custom_entries = self.target / 'etc/grub.d/09_custom' + entries_content = ( + '#!/bin/sh\n' + 'exec tail -n +3 $0\n' + '# This file provides UKI (Unified Kernel Image) boot entries.\n' + '# Generated by archinstall. Do not modify the exec tail line above.\n' + '# Custom entries can be added below.\n\n' + ) + + uki_entries = [] + for kernel in self.kernels: + entry = textwrap.dedent( + f""" + menuentry "Arch Linux ({kernel}) UKI" {{ + uki /EFI/Linux/arch-{kernel}.efi + }} + """ + ) + uki_entries.append(entry) + + entries_content += '\n'.join(uki_entries) + custom_entries.write_text(entries_content) + custom_entries.chmod(0o755) + + # Disable 10_linux to prevent broken entries on kernel updates + linux_script = self.target / 'etc/grub.d/10_linux' + if linux_script.exists(): + linux_script.chmod(0o644) + try: self.arch_chroot( f'grub-mkconfig -o {boot_dir}/grub/grub.cfg', @@ -1669,29 +1701,28 @@ def _add_refind_bootloader( kernel_params = ' '.join(self._get_kernel_params(root)) for kernel in self.kernels: - for variant in ('', '-fallback'): - if uki_enabled: - entry = f'"Arch Linux ({kernel}{variant}) UKI" "{kernel_params}"' - else: - if boot_on_root: - # Kernels are in /boot subdirectory of root filesystem - if hasattr(root, 'btrfs_subvols') and root.btrfs_subvols: - # Root is btrfs with subvolume, find the root subvolume - root_subvol = next((sv for sv in root.btrfs_subvols if sv.is_root()), None) - if root_subvol: - subvol_name = root_subvol.name - initrd_path = f'initrd={subvol_name}\\boot\\initramfs-{kernel}{variant}.img' - else: - initrd_path = f'initrd=\\boot\\initramfs-{kernel}{variant}.img' + if uki_enabled: + entry = f'"Arch Linux ({kernel}) UKI" "{kernel_params}"' + else: + if boot_on_root: + # Kernels are in /boot subdirectory of root filesystem + if hasattr(root, 'btrfs_subvols') and root.btrfs_subvols: + # Root is btrfs with subvolume, find the root subvolume + root_subvol = next((sv for sv in root.btrfs_subvols if sv.is_root()), None) + if root_subvol: + subvol_name = root_subvol.name + initrd_path = f'initrd={subvol_name}\\boot\\initramfs-{kernel}.img' else: - # Root without btrfs subvolume - initrd_path = f'initrd=\\boot\\initramfs-{kernel}{variant}.img' + initrd_path = f'initrd=\\boot\\initramfs-{kernel}.img' else: - # Kernels are at root of their partition (ESP or separate boot partition) - initrd_path = f'initrd=\\initramfs-{kernel}{variant}.img' - entry = f'"Arch Linux ({kernel}{variant})" "{kernel_params} {initrd_path}"' + # Root without btrfs subvolume + initrd_path = f'initrd=\\boot\\initramfs-{kernel}.img' + else: + # Kernels are at root of their partition (ESP or separate boot partition) + initrd_path = f'initrd=\\initramfs-{kernel}.img' + entry = f'"Arch Linux ({kernel})" "{kernel_params} {initrd_path}"' - config_contents.append(entry) + config_contents.append(entry) config_path.write_text('\n'.join(config_contents) + '\n') @@ -1823,7 +1854,7 @@ def add_bootloader(self, bootloader: Bootloader, uki_enabled: bool = False, boot case Bootloader.Systemd: self._add_systemd_bootloader(boot_partition, root, efi_partition, uki_enabled) case Bootloader.Grub: - self._add_grub_bootloader(boot_partition, root, efi_partition, bootloader_removable) + self._add_grub_bootloader(boot_partition, root, efi_partition, uki_enabled, bootloader_removable) case Bootloader.Efistub: self._add_efistub_bootloader(boot_partition, root, uki_enabled) case Bootloader.Limine: diff --git a/archinstall/lib/models/bootloader.py b/archinstall/lib/models/bootloader.py index 0a004ae83f..22fa2c3106 100644 --- a/archinstall/lib/models/bootloader.py +++ b/archinstall/lib/models/bootloader.py @@ -20,11 +20,7 @@ class Bootloader(Enum): Refind = 'Refind' def has_uki_support(self) -> bool: - match self: - case Bootloader.Efistub | Bootloader.Limine | Bootloader.Systemd | Bootloader.Refind: - return True - case _: - return False + return self != Bootloader.NO_BOOTLOADER def has_removable_support(self) -> bool: match self: @@ -82,7 +78,8 @@ def parse_arg(cls, config: dict[str, Any], skip_boot: bool) -> BootloaderConfigu def get_default(cls) -> BootloaderConfiguration: bootloader = Bootloader.get_default() removable = SysInfo.has_uefi() and bootloader.has_removable_support() - return cls(bootloader=bootloader, uki=False, removable=removable) + uki = SysInfo.has_uefi() and bootloader.has_uki_support() + return cls(bootloader=bootloader, uki=uki, removable=removable) def preview(self) -> str: text = f'{tr("Bootloader")}: {self.bootloader.value}'