diff --git a/archinstall/lib/bootloader/utils.py b/archinstall/lib/bootloader/utils.py index a732f4e117..2d0b5a8198 100644 --- a/archinstall/lib/bootloader/utils.py +++ b/archinstall/lib/bootloader/utils.py @@ -9,6 +9,8 @@ class BootloaderValidationFailureKind(Enum): LimineNonFatBoot = auto() LimineLayout = auto() + BootloaderRequiresUefi = auto() + EfistubNonFatBoot = auto() @dataclass(frozen=True) @@ -17,9 +19,13 @@ class BootloaderValidationFailure: description: str +_UEFI_ONLY_BOOTLOADERS = (Bootloader.Systemd, Bootloader.Efistub, Bootloader.Refind) + + def validate_bootloader_layout( bootloader_config: BootloaderConfiguration | None, disk_config: DiskLayoutConfiguration | None, + is_uefi: bool, ) -> BootloaderValidationFailure | None: """Validate bootloader configuration against disk layout. @@ -29,12 +35,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 in _UEFI_ONLY_BOOTLOADERS and not is_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 3d94f31e2b..e961b5e71e 100644 --- a/archinstall/lib/global_menu.py +++ b/archinstall/lib/global_menu.py @@ -461,8 +461,6 @@ def _validate_bootloader(self) -> str | 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,10 +488,7 @@ def _validate_bootloader(self) -> str | 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): + if failure := validate_bootloader_layout(bootloader_config, disk_config, self._uefi): return failure.description return None diff --git a/archinstall/lib/installer.py b/archinstall/lib/installer.py index 19560d75a9..c65c084310 100644 --- a/archinstall/lib/installer.py +++ b/archinstall/lib/installer.py @@ -1472,6 +1472,7 @@ def _add_limine_bootloader( if failure := validate_bootloader_layout( BootloaderConfiguration(bootloader=Bootloader.Limine, uki=uki_enabled), self._disk_config, + SysInfo.has_uefi(), ): raise DiskError(failure.description) diff --git a/archinstall/scripts/guided.py b/archinstall/scripts/guided.py index 5cdcdad9d2..32198c9b66 100644 --- a/archinstall/scripts/guided.py +++ b/archinstall/scripts/guided.py @@ -11,6 +11,7 @@ from archinstall.lib.disk.utils import disk_layouts from archinstall.lib.general.general_menu import PostInstallationAction, select_post_installation from archinstall.lib.global_menu import GlobalMenu +from archinstall.lib.hardware import SysInfo from archinstall.lib.installer import Installer, accessibility_tools_in_use, run_custom_user_commands from archinstall.lib.menu.util import delayed_warning from archinstall.lib.mirror.mirror_handler import MirrorListHandler @@ -217,6 +218,7 @@ def main(arch_config_handler: ArchConfigHandler | None = None) -> None: if failure := validate_bootloader_layout( arch_config_handler.config.bootloader_config, arch_config_handler.config.disk_config, + SysInfo.has_uefi(), ): error(failure.description) return