Skip to content

Commit e6344f9

Browse files
authored
Fix Limine bootloader deployment (#2216)
* Add `get_unique_path_for_device` to `DeviceHandler` * Fix Limine bootloader deployment * Fail if UKI is enabled with Limine * Support more configuration options with Limine * Fix linter errors * Fix boot partition fs_type check for Limine
1 parent f16af43 commit e6344f9

File tree

3 files changed

+81
-75
lines changed

3 files changed

+81
-75
lines changed

archinstall/lib/disk/device_handler.py

+14
Original file line numberDiff line numberDiff line change
@@ -133,6 +133,20 @@ def get_parent_device_path(self, dev_path: Path) -> Path:
133133
lsblk = get_lsblk_info(dev_path)
134134
return Path(f'/dev/{lsblk.pkname}')
135135

136+
def get_unique_path_for_device(self, dev_path: Path) -> Optional[Path]:
137+
paths = Path('/dev/disk/by-id').glob('*')
138+
linked_targets = {p.resolve(): p for p in paths}
139+
linked_wwn_targets = {p: linked_targets[p] for p in linked_targets
140+
if p.name.startswith('wwn-') or p.name.startswith('nvme-eui.')}
141+
142+
if dev_path in linked_wwn_targets:
143+
return linked_wwn_targets[dev_path]
144+
145+
if dev_path in linked_targets:
146+
return linked_targets[dev_path]
147+
148+
return None
149+
136150
def get_uuid_for_path(self, path: Path) -> Optional[str]:
137151
partition = self.find_partition(path)
138152
return partition.partuuid if partition else None

archinstall/lib/global_menu.py

+5-2
Original file line numberDiff line numberDiff line change
@@ -363,8 +363,11 @@ def _validate_bootloader(self) -> Optional[str]:
363363
if boot_partition is None:
364364
return "Boot partition not found"
365365

366-
if bootloader == Bootloader.Limine and boot_partition.fs_type == disk.FilesystemType.Btrfs:
367-
return "Limine bootloader does not support booting from BTRFS filesystem"
366+
if bootloader == Bootloader.Limine:
367+
if boot_partition.fs_type != disk.FilesystemType.Fat32:
368+
return "Limine does not support booting from filesystems other than FAT32"
369+
elif self._menu_options['uki'].current_selection:
370+
return "Limine does not support booting UKIs"
368371

369372
return None
370373

archinstall/lib/installer.py

+62-73
Original file line numberDiff line numberDiff line change
@@ -972,70 +972,54 @@ def _add_grub_bootloader(
972972
def _add_limine_bootloader(
973973
self,
974974
boot_partition: disk.PartitionModification,
975+
efi_partition: Optional[disk.PartitionModification],
975976
root_partition: disk.PartitionModification
976977
):
977978
self.pacman.strap('limine')
978-
info(f"Limine boot partition: {boot_partition.dev_path}")
979979

980-
root_uuid = root_partition.uuid
980+
info(f"Limine boot partition: {boot_partition.dev_path}")
981981

982-
def create_pacman_hook(contents: str):
983-
HOOK_DIR = "/etc/pacman.d/hooks"
984-
SysCommand(f"/usr/bin/arch-chroot {self.target} mkdir -p {HOOK_DIR}")
985-
SysCommand(f"/usr/bin/arch-chroot {self.target} sh -c \"echo '{contents}' > {HOOK_DIR}/liminedeploy.hook\"")
982+
limine_path = self.target / 'usr' / 'share' / 'limine'
983+
hook_command = None
986984

987985
if SysInfo.has_uefi():
986+
if not efi_partition:
987+
raise ValueError('Could not detect efi partition')
988+
elif not efi_partition.mountpoint:
989+
raise ValueError('EFI partition is not mounted')
990+
991+
info(f"Limine EFI partition: {efi_partition.dev_path}")
992+
988993
try:
989-
# The `limine.sys` file, contains stage 3 code.
990-
cmd = f'/usr/bin/arch-chroot' \
991-
f' {self.target}' \
992-
f' cp' \
993-
f' /usr/share/limine/BOOTX64.EFI' \
994-
f' /boot/EFI/BOOT/'
995-
996-
SysCommand(cmd, peek_output=True)
997-
except SysCallError as err:
998-
raise DiskError(f"Failed to install Limine BOOTX64.EFI on {boot_partition.dev_path}: {err}")
994+
efi_dir_path = self.target / efi_partition.mountpoint.relative_to('/') / 'EFI' / 'BOOT'
995+
efi_dir_path.mkdir(parents=True, exist_ok=True)
999996

1000-
# Create the EFI limine pacman hook.
1001-
create_pacman_hook("""
1002-
[Trigger]
1003-
Operation = Install
1004-
Operation = Upgrade
1005-
Type = Package
1006-
Target = limine
997+
for file in ('BOOTIA32.EFI', 'BOOTX64.EFI'):
998+
shutil.copy(limine_path / file, efi_dir_path)
999+
except Exception as err:
1000+
raise DiskError(f'Failed to install Limine in {self.target}{efi_partition.mountpoint}: {err}')
10071001

1008-
[Action]
1009-
Description = Deploying Limine after upgrade...
1010-
When = PostTransaction
1011-
Exec = /usr/bin/cp /usr/share/limine/BOOTX64.EFI /boot/EFI/BOOT/
1012-
""")
1002+
hook_command = f'/usr/bin/cp /usr/share/limine/BOOTIA32.EFI {efi_partition.mountpoint}/EFI/BOOT/' \
1003+
f' && /usr/bin/cp /usr/share/limine/BOOTX64.EFI {efi_partition.mountpoint}/EFI/BOOT/'
10131004
else:
10141005
parent_dev_path = disk.device_handler.get_parent_device_path(boot_partition.safe_dev_path)
10151006

1016-
try:
1017-
# The `limine.sys` file, contains stage 3 code.
1018-
cmd = f'/usr/bin/arch-chroot' \
1019-
f' {self.target}' \
1020-
f' cp' \
1021-
f' /usr/share/limine/limine-bios.sys' \
1022-
f' /boot/limine-bios.sys'
1007+
if unique_path := disk.device_handler.get_unique_path_for_device(parent_dev_path):
1008+
parent_dev_path = unique_path
10231009

1024-
SysCommand(cmd, peek_output=True)
1010+
try:
1011+
# The `limine-bios.sys` file contains stage 3 code.
1012+
shutil.copy(limine_path / 'limine-bios.sys', self.target / 'boot')
10251013

10261014
# `limine bios-install` deploys the stage 1 and 2 to the disk.
1027-
cmd = f'/usr/bin/arch-chroot' \
1028-
f' {self.target}' \
1029-
f' limine' \
1030-
f' bios-install' \
1031-
f' {parent_dev_path}'
1015+
SysCommand(f'/usr/bin/arch-chroot {self.target} limine bios-install {parent_dev_path}', peek_output=True)
1016+
except Exception as err:
1017+
raise DiskError(f'Failed to install Limine on {parent_dev_path}: {err}')
10321018

1033-
SysCommand(cmd, peek_output=True)
1034-
except SysCallError as err:
1035-
raise DiskError(f"Failed to install Limine on {boot_partition.dev_path}: {err}")
1019+
hook_command = f'/usr/bin/limine bios-install {parent_dev_path}' \
1020+
f' && /usr/bin/cp /usr/share/limine/limine-bios.sys /boot/'
10361021

1037-
create_pacman_hook(f"""
1038-
[Trigger]
1022+
hook_contents = f'''[Trigger]
10391023
Operation = Install
10401024
Operation = Upgrade
10411025
Type = Package
@@ -1044,33 +1028,38 @@ def create_pacman_hook(contents: str):
10441028
[Action]
10451029
Description = Deploying Limine after upgrade...
10461030
When = PostTransaction
1047-
# XXX: Kernel name descriptors cannot be used since they are not persistent and
1048-
# can change after each boot.
1049-
Exec = /bin/sh -c \\"/usr/bin/limine bios-install /dev/disk/by-uuid/{root_uuid} && /usr/bin/cp /usr/share/limine/limine-bios.sys /boot/\\"
1050-
""")
1031+
Exec = /bin/sh -c "{hook_command}"
1032+
'''
10511033

1052-
# Limine does not ship with a default configuration file. We are going to
1053-
# create a basic one that is similar to the one GRUB generates.
1054-
try:
1055-
config = f"""
1056-
TIMEOUT=5
1057-
1058-
:Arch Linux
1059-
PROTOCOL=linux
1060-
KERNEL_PATH=boot:///vmlinuz-linux
1061-
CMDLINE=root=UUID={root_uuid} rw rootfstype={root_partition.safe_fs_type.value} loglevel=3
1062-
MODULE_PATH=boot:///initramfs-linux.img
1063-
1064-
:Arch Linux (fallback)
1065-
PROTOCOL=linux
1066-
KERNEL_PATH=boot:///vmlinuz-linux
1067-
CMDLINE=root=UUID={root_uuid} rw rootfstype={root_partition.safe_fs_type.value} loglevel=3
1068-
MODULE_PATH=boot:///initramfs-linux-fallback.img
1069-
"""
1070-
1071-
SysCommand(f"/usr/bin/arch-chroot {self.target} sh -c \"echo '{config}' > /boot/limine.cfg\"")
1072-
except SysCallError as err:
1073-
raise DiskError(f"Could not configure Limine: {err}")
1034+
hooks_dir = self.target / 'etc' / 'pacman.d' / 'hooks'
1035+
hooks_dir.mkdir(parents=True, exist_ok=True)
1036+
1037+
hook_path = hooks_dir / '99-limine.hook'
1038+
hook_path.write_text(hook_contents)
1039+
1040+
microcode = []
1041+
1042+
if ucode := self._get_microcode():
1043+
microcode = [f'MODULE_PATH=boot:///{ucode}']
1044+
1045+
kernel_params = ' '.join(self._get_kernel_params(root_partition))
1046+
config_contents = 'TIMEOUT=5\n'
1047+
1048+
for kernel in self.kernels:
1049+
for variant in ('', '-fallback'):
1050+
entry = [
1051+
f'PROTOCOL=linux',
1052+
f'KERNEL_PATH=boot:///vmlinuz-{kernel}',
1053+
*microcode,
1054+
f'MODULE_PATH=boot:///initramfs-{kernel}{variant}.img',
1055+
f'CMDLINE={kernel_params}',
1056+
]
1057+
1058+
config_contents += f'\n:Arch Linux ({kernel}{variant})\n'
1059+
config_contents += '\n'.join([f' {it}' for it in entry]) + '\n'
1060+
1061+
config_path = self.target / 'boot' / 'limine.cfg'
1062+
config_path.write_text(config_contents)
10741063

10751064
self.helper_flags['bootloader'] = "limine"
10761065

@@ -1227,7 +1216,7 @@ def add_bootloader(self, bootloader: Bootloader, uki_enabled: bool = False):
12271216
case Bootloader.Efistub:
12281217
self._add_efistub_bootloader(boot_partition, root_partition, uki_enabled)
12291218
case Bootloader.Limine:
1230-
self._add_limine_bootloader(boot_partition, root_partition)
1219+
self._add_limine_bootloader(boot_partition, efi_partition, root_partition)
12311220

12321221
def add_additional_packages(self, packages: Union[str, List[str]]) -> bool:
12331222
return self.pacman.strap(packages)

0 commit comments

Comments
 (0)