Add support for rEFInd boot manager (#3707)

* Add support for rEFInd boot manager

* Fix ruff formatting complaints

* Added support for different mountpoints for /efi and /boot

Also fixed issue where if /boot is located in a BTRFS root partition, the initrd path wasn't including the subvol name.

* Fix ruff formatting complaints

* Replace SysCommand with self.arch_chroot call

* Fix ruff formatting complaints

---------

Co-authored-by: Diogo Bispo <gpg.jta36@slmail.me>
This commit is contained in:
justbispo 2025-12-31 04:24:22 +00:00 committed by GitHub
parent 446d23c59d
commit cb6fe6b34b
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
3 changed files with 110 additions and 2 deletions

View File

@ -466,6 +466,10 @@ class GlobalMenu(AbstractMenu[None]):
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'
elif bootloader == Bootloader.Refind:
if not SysInfo.has_uefi():
return 'rEFInd can only be used on UEFI systems'
return None
def _prev_install_invalid_config(self, item: MenuItem) -> str | None:

View File

@ -1604,6 +1604,106 @@ class Installer:
self._helper_flags['bootloader'] = 'efistub'
def _add_refind_bootloader(
self,
boot_partition: PartitionModification,
efi_partition: PartitionModification | None,
root: PartitionModification | LvmVolume,
uki_enabled: bool = False,
) -> None:
debug('Installing rEFInd bootloader')
self.pacman.strap('refind')
if not SysInfo.has_uefi():
raise HardwareIncompatibilityError
info(f'rEFInd boot partition: {boot_partition.dev_path}')
if not efi_partition:
raise ValueError('Could not detect EFI system partition')
elif not efi_partition.mountpoint:
raise ValueError('EFI system partition is not mounted')
info(f'rEFInd EFI partition: {efi_partition.dev_path}')
try:
self.arch_chroot('refind-install')
except SysCallError as err:
raise DiskError(f'Could not install rEFInd to {self.target}{efi_partition.mountpoint}: {err}')
if not boot_partition.mountpoint:
raise ValueError('Boot partition is not mounted, cannot write rEFInd config')
boot_is_separate = boot_partition != efi_partition and boot_partition.dev_path != efi_partition.dev_path
if boot_is_separate:
# Separate boot partition (not ESP, not root)
config_path = self.target / boot_partition.mountpoint.relative_to('/') / 'refind_linux.conf'
boot_on_root = False
elif efi_partition.mountpoint == Path('/boot'):
# ESP is mounted at /boot, kernels are on ESP
config_path = self.target / 'boot' / 'refind_linux.conf'
boot_on_root = False
else:
# ESP is elsewhere (/efi, /boot/efi, etc.), kernels are on root filesystem at /boot
config_path = self.target / 'boot' / 'refind_linux.conf'
boot_on_root = True
config_contents = []
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'
else:
# Root without btrfs subvolume
initrd_path = f'initrd=\\boot\\initramfs-{kernel}{variant}.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}"'
config_contents.append(entry)
config_path.write_text('\n'.join(config_contents) + '\n')
hook_contents = textwrap.dedent(
"""\
[Trigger]
Operation = Install
Operation = Upgrade
Type = Package
Target = refind
[Action]
Description = Updating rEFInd on ESP
When = PostTransaction
Exec = /usr/bin/refind-install
"""
)
hooks_dir = self.target / 'etc' / 'pacman.d' / 'hooks'
hooks_dir.mkdir(parents=True, exist_ok=True)
hook_path = hooks_dir / '99-refind.hook'
hook_path.write_text(hook_contents)
self._helper_flags['bootloader'] = 'refind'
def _config_uki(
self,
root: PartitionModification | LvmVolume,
@ -1657,11 +1757,12 @@ class Installer:
def add_bootloader(self, bootloader: Bootloader, uki_enabled: bool = False, bootloader_removable: bool = False) -> None:
"""
Adds a bootloader to the installation instance.
Archinstall supports one of three types:
Archinstall supports one of five types:
* systemd-bootctl
* grub
* limine
* efistub (beta)
* refnd (beta)
:param bootloader: Type of bootloader to be added
:param uki_enabled: Whether to use unified kernel images
@ -1713,6 +1814,8 @@ class Installer:
self._add_efistub_bootloader(boot_partition, root, uki_enabled)
case Bootloader.Limine:
self._add_limine_bootloader(boot_partition, efi_partition, root, uki_enabled, bootloader_removable)
case Bootloader.Refind:
self._add_refind_bootloader(boot_partition, efi_partition, root, uki_enabled)
def add_additional_packages(self, packages: str | list[str]) -> None:
return self.pacman.strap(packages)

View File

@ -17,10 +17,11 @@ class Bootloader(Enum):
Grub = 'Grub'
Efistub = 'Efistub'
Limine = 'Limine'
Refind = 'Refind'
def has_uki_support(self) -> bool:
match self:
case Bootloader.Efistub | Bootloader.Limine | Bootloader.Systemd:
case Bootloader.Efistub | Bootloader.Limine | Bootloader.Systemd | Bootloader.Refind:
return True
case _:
return False