From d836ab0a6690c13a6d7a97f62e30837a2873b864 Mon Sep 17 00:00:00 2001 From: Softer Date: Sun, 26 Apr 2026 12:14:23 +0300 Subject: [PATCH] Extend validate_bootloader_layout with UEFI-dependent checks (#4474) * Extend validate_bootloader_layout with UEFI-dependent checks Add is_uefi parameter and three new validations: Systemd-boot, Efistub and rEFInd require UEFI; Efistub additionally requires a FAT boot partition. Move the rEFInd UEFI-only check out of GlobalMenu so guided.py and Installer silent-install paths get the same coverage. * Encapsulate UEFI-only flag in Bootloader enum Replace module-level _UEFI_ONLY_BOOTLOADERS tuple with an is_uefi_only() method on the Bootloader enum, mirroring the existing has_uki_support() / has_removable_support() pattern. * Drop is_uefi parameter from validate_bootloader_layout The UEFI flag is a constant system fact for the run, so the validator retrieves it via SysInfo.has_uefi() directly instead of having every caller pass it in. Updates all three call sites in global_menu.py, installer.py and guided.py, and removes the now-unused SysInfo import from guided.py. --- archinstall/lib/bootloader/utils.py | 31 ++++++++++++++++++++++++---- archinstall/lib/global_menu.py | 5 ----- archinstall/lib/models/bootloader.py | 7 +++++++ 3 files changed, 34 insertions(+), 9 deletions(-) diff --git a/archinstall/lib/bootloader/utils.py b/archinstall/lib/bootloader/utils.py index a732f4e1..ed761722 100644 --- a/archinstall/lib/bootloader/utils.py +++ b/archinstall/lib/bootloader/utils.py @@ -2,6 +2,7 @@ from dataclasses import dataclass from enum import Enum, auto from pathlib import Path +from archinstall.lib.hardware import SysInfo from archinstall.lib.models.bootloader import Bootloader, BootloaderConfiguration from archinstall.lib.models.device import DiskLayoutConfiguration @@ -9,6 +10,8 @@ from archinstall.lib.models.device import DiskLayoutConfiguration class BootloaderValidationFailureKind(Enum): LimineNonFatBoot = auto() LimineLayout = auto() + BootloaderRequiresUefi = auto() + EfistubNonFatBoot = auto() @dataclass(frozen=True) @@ -29,12 +32,32 @@ def validate_bootloader_layout( if not (bootloader_config and disk_config): return None - if bootloader_config.bootloader == Bootloader.Limine: - boot_part = next( - (p for m in disk_config.device_modifications if (p := m.get_boot_partition())), - None, + bootloader = bootloader_config.bootloader + + if bootloader == Bootloader.NO_BOOTLOADER: + return None + + if bootloader.is_uefi_only() and not SysInfo.has_uefi(): + return BootloaderValidationFailure( + kind=BootloaderValidationFailureKind.BootloaderRequiresUefi, + description=f'{bootloader.value} requires a UEFI system.', ) + boot_part = next( + (p for m in disk_config.device_modifications if (p := m.get_boot_partition())), + None, + ) + + if bootloader == Bootloader.Efistub: + # The UEFI firmware reads the kernel directly from the boot partition, + # which must be FAT. + if boot_part and (boot_part.fs_type is None or not boot_part.fs_type.is_fat()): + return BootloaderValidationFailure( + kind=BootloaderValidationFailureKind.EfistubNonFatBoot, + description='Efistub does not support booting with a non-FAT boot partition.', + ) + + if bootloader == Bootloader.Limine: # Limine reads its config and kernels from the boot partition, which # must be FAT. if boot_part and (boot_part.fs_type is None or not boot_part.fs_type.is_fat()): diff --git a/archinstall/lib/global_menu.py b/archinstall/lib/global_menu.py index 3d94f31e..e327b388 100644 --- a/archinstall/lib/global_menu.py +++ b/archinstall/lib/global_menu.py @@ -461,8 +461,6 @@ class GlobalMenu(AbstractMenu[None]): if not bootloader_config or bootloader_config.bootloader == Bootloader.NO_BOOTLOADER: return None - bootloader = bootloader_config.bootloader - if disk_config := self._item_group.find_by_key('disk_config').value: for layout in disk_config.device_modifications: if root_partition := layout.get_root_partition(): @@ -490,9 +488,6 @@ class GlobalMenu(AbstractMenu[None]): if efi_partition.fs_type is None or not efi_partition.fs_type.is_fat(): return 'ESP must be formatted as a FAT filesystem' - if bootloader == Bootloader.Refind and not self._uefi: - return 'rEFInd can only be used on UEFI systems' - if failure := validate_bootloader_layout(bootloader_config, disk_config): return failure.description diff --git a/archinstall/lib/models/bootloader.py b/archinstall/lib/models/bootloader.py index 68c0bf98..5d8d0d80 100644 --- a/archinstall/lib/models/bootloader.py +++ b/archinstall/lib/models/bootloader.py @@ -25,6 +25,13 @@ class Bootloader(Enum): case _: return False + def is_uefi_only(self) -> bool: + match self: + case Bootloader.Systemd | Bootloader.Efistub | Bootloader.Refind: + return True + case _: + return False + def json(self) -> str: return self.value