Skip to content
Merged
Show file tree
Hide file tree
Changes from 1 commit
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
6 changes: 6 additions & 0 deletions archinstall/lib/global_menu.py
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
from pathlib import Path
from typing import override

from archinstall.default_profiles.profile import GreeterType
Expand Down Expand Up @@ -492,6 +493,11 @@ def _validate_bootloader(self) -> str | None:
if bootloader == Bootloader.Limine:
if boot_partition.fs_type not in [FilesystemType.FAT12, FilesystemType.FAT16, FilesystemType.FAT32]:
return 'Limine does not support booting with a non-FAT boot partition'
if self._uefi and efi_partition and boot_partition == efi_partition and efi_partition.mountpoint != Path('/boot') and not bootloader_config.uki:
return (
f'Limine requires kernels on a FAT partition. The ESP is mounted at {efi_partition.mountpoint}, '
'enable UKI or add a separate /boot partition'
)

elif bootloader == Bootloader.Refind:
if not self._uefi:
Expand Down
16 changes: 11 additions & 5 deletions archinstall/lib/installer.py
Original file line number Diff line number Diff line change
Expand Up @@ -1466,6 +1466,16 @@ def _add_limine_bootloader(
elif not efi_partition.mountpoint:
raise ValueError('EFI partition is not mounted')

# Limine can only read FAT filesystems. When the ESP doubles as
# the boot partition but is mounted outside /boot (e.g. /efi,
# /boot/efi) and UKI is disabled, kernels end up on the root
# filesystem under /boot/ which Limine cannot access.
if boot_partition == efi_partition and efi_partition.mountpoint != Path('/boot') and not uki_enabled:
raise DiskError(
f'Limine requires kernels on a FAT partition. The ESP is mounted at {efi_partition.mountpoint}, '
'so enable UKI or add a separate /boot partition to install Limine.',
)

info(f'Limine EFI partition: {efi_partition.dev_path}')

parent_dev_path = get_parent_device_path(efi_partition.safe_dev_path)
Expand All @@ -1476,15 +1486,11 @@ def _add_limine_bootloader(
if bootloader_removable:
efi_dir_path = efi_dir_path / 'BOOT'
efi_dir_path_target = efi_dir_path_target / 'BOOT'

boot_limine_path = self.target / 'boot' / 'limine'
boot_limine_path.mkdir(parents=True, exist_ok=True)
config_path = boot_limine_path / 'limine.conf'
else:
efi_dir_path = efi_dir_path / 'arch-limine'
efi_dir_path_target = efi_dir_path_target / 'arch-limine'

config_path = efi_dir_path / 'limine.conf'
config_path = efi_dir_path / 'limine.conf'

efi_dir_path.mkdir(parents=True, exist_ok=True)

Expand Down
36 changes: 36 additions & 0 deletions archinstall/scripts/guided.py
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
import os
import sys
import time
from pathlib import Path

from archinstall.lib.applications.application_handler import ApplicationHandler
from archinstall.lib.args import ArchConfig, ArchConfigHandler
Expand Down Expand Up @@ -195,6 +196,35 @@ def perform_installation(
pass


def _check_bootloader_layout(config: ArchConfig) -> str | None:
Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

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

I think it would be better to put this into a util, something like a new lib/bootloader/utils.py would be good

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

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

And this should be checked in the other scripts as well I suppose

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.

Extracted the check into lib/bootloader/utils.validate_bootloader_layout. On "other scripts": minimal.py hardcodes Systemd-boot and only_hd.py doesn't install a bootloader, so only guided.py needed it.

While at it I noticed this overlaps with global_menu._validate_bootloader (same Limine/ESP case in two places), and several menu-side gaps remain (Systemd/Efistub on BIOS, Efistub with non-FAT boot). Feels like a good follow-up PR - consolidate all bootloader layout validation into the new util and close those gaps.

I'll research it a bit later.

"""Validate bootloader configuration against disk layout.

Returns an error message if the configuration would produce an
unbootable system, or None if it is valid.
"""
# Limine can only read FAT. When the ESP is the boot partition but
# mounted outside /boot and UKI is disabled, the kernel ends up on the
# root filesystem which Limine cannot access.
if not (config.bootloader_config and config.bootloader_config.bootloader == Bootloader.Limine and not config.bootloader_config.uki and config.disk_config):
return None

efi_part = next(
(p for m in config.disk_config.device_modifications if (p := m.get_efi_partition())),
None,
)
boot_part = next(
(p for m in config.disk_config.device_modifications if (p := m.get_boot_partition())),
None,
)

if efi_part and boot_part == efi_part and efi_part.mountpoint != Path('/boot'):
return (
f'Limine requires kernels on a FAT partition. The ESP is mounted at {efi_part.mountpoint}, '
'enable UKI or add a separate /boot partition to install Limine.'
)
return None


def main(arch_config_handler: ArchConfigHandler | None = None) -> None:
if arch_config_handler is None:
arch_config_handler = ArchConfigHandler()
Expand All @@ -211,6 +241,12 @@ def main(arch_config_handler: ArchConfigHandler | None = None) -> None:
config.write_debug()
config.save()

# Safety net for silent/config-file flow. The TUI menu blocks Install via
# GlobalMenu._validate_bootloader() before reaching this point.
if err_msg := _check_bootloader_layout(arch_config_handler.config):
error(err_msg)
return

if arch_config_handler.args.dry_run:
return

Expand Down