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
This commit is contained in:
parent
f16af43949
commit
e6344f93f7
|
|
@ -133,6 +133,20 @@ class DeviceHandler(object):
|
|||
lsblk = get_lsblk_info(dev_path)
|
||||
return Path(f'/dev/{lsblk.pkname}')
|
||||
|
||||
def get_unique_path_for_device(self, dev_path: Path) -> Optional[Path]:
|
||||
paths = Path('/dev/disk/by-id').glob('*')
|
||||
linked_targets = {p.resolve(): p for p in paths}
|
||||
linked_wwn_targets = {p: linked_targets[p] for p in linked_targets
|
||||
if p.name.startswith('wwn-') or p.name.startswith('nvme-eui.')}
|
||||
|
||||
if dev_path in linked_wwn_targets:
|
||||
return linked_wwn_targets[dev_path]
|
||||
|
||||
if dev_path in linked_targets:
|
||||
return linked_targets[dev_path]
|
||||
|
||||
return None
|
||||
|
||||
def get_uuid_for_path(self, path: Path) -> Optional[str]:
|
||||
partition = self.find_partition(path)
|
||||
return partition.partuuid if partition else None
|
||||
|
|
|
|||
|
|
@ -363,8 +363,11 @@ class GlobalMenu(AbstractMenu):
|
|||
if boot_partition is None:
|
||||
return "Boot partition not found"
|
||||
|
||||
if bootloader == Bootloader.Limine and boot_partition.fs_type == disk.FilesystemType.Btrfs:
|
||||
return "Limine bootloader does not support booting from BTRFS filesystem"
|
||||
if bootloader == Bootloader.Limine:
|
||||
if boot_partition.fs_type != disk.FilesystemType.Fat32:
|
||||
return "Limine does not support booting from filesystems other than FAT32"
|
||||
elif self._menu_options['uki'].current_selection:
|
||||
return "Limine does not support booting UKIs"
|
||||
|
||||
return None
|
||||
|
||||
|
|
|
|||
|
|
@ -972,70 +972,54 @@ class Installer:
|
|||
def _add_limine_bootloader(
|
||||
self,
|
||||
boot_partition: disk.PartitionModification,
|
||||
efi_partition: Optional[disk.PartitionModification],
|
||||
root_partition: disk.PartitionModification
|
||||
):
|
||||
self.pacman.strap('limine')
|
||||
|
||||
info(f"Limine boot partition: {boot_partition.dev_path}")
|
||||
|
||||
root_uuid = root_partition.uuid
|
||||
|
||||
def create_pacman_hook(contents: str):
|
||||
HOOK_DIR = "/etc/pacman.d/hooks"
|
||||
SysCommand(f"/usr/bin/arch-chroot {self.target} mkdir -p {HOOK_DIR}")
|
||||
SysCommand(f"/usr/bin/arch-chroot {self.target} sh -c \"echo '{contents}' > {HOOK_DIR}/liminedeploy.hook\"")
|
||||
limine_path = self.target / 'usr' / 'share' / 'limine'
|
||||
hook_command = None
|
||||
|
||||
if SysInfo.has_uefi():
|
||||
if not efi_partition:
|
||||
raise ValueError('Could not detect efi partition')
|
||||
elif not efi_partition.mountpoint:
|
||||
raise ValueError('EFI partition is not mounted')
|
||||
|
||||
info(f"Limine EFI partition: {efi_partition.dev_path}")
|
||||
|
||||
try:
|
||||
# The `limine.sys` file, contains stage 3 code.
|
||||
cmd = f'/usr/bin/arch-chroot' \
|
||||
f' {self.target}' \
|
||||
f' cp' \
|
||||
f' /usr/share/limine/BOOTX64.EFI' \
|
||||
f' /boot/EFI/BOOT/'
|
||||
efi_dir_path = self.target / efi_partition.mountpoint.relative_to('/') / 'EFI' / 'BOOT'
|
||||
efi_dir_path.mkdir(parents=True, exist_ok=True)
|
||||
|
||||
SysCommand(cmd, peek_output=True)
|
||||
except SysCallError as err:
|
||||
raise DiskError(f"Failed to install Limine BOOTX64.EFI on {boot_partition.dev_path}: {err}")
|
||||
for file in ('BOOTIA32.EFI', 'BOOTX64.EFI'):
|
||||
shutil.copy(limine_path / file, efi_dir_path)
|
||||
except Exception as err:
|
||||
raise DiskError(f'Failed to install Limine in {self.target}{efi_partition.mountpoint}: {err}')
|
||||
|
||||
# Create the EFI limine pacman hook.
|
||||
create_pacman_hook("""
|
||||
[Trigger]
|
||||
Operation = Install
|
||||
Operation = Upgrade
|
||||
Type = Package
|
||||
Target = limine
|
||||
|
||||
[Action]
|
||||
Description = Deploying Limine after upgrade...
|
||||
When = PostTransaction
|
||||
Exec = /usr/bin/cp /usr/share/limine/BOOTX64.EFI /boot/EFI/BOOT/
|
||||
""")
|
||||
hook_command = f'/usr/bin/cp /usr/share/limine/BOOTIA32.EFI {efi_partition.mountpoint}/EFI/BOOT/' \
|
||||
f' && /usr/bin/cp /usr/share/limine/BOOTX64.EFI {efi_partition.mountpoint}/EFI/BOOT/'
|
||||
else:
|
||||
parent_dev_path = disk.device_handler.get_parent_device_path(boot_partition.safe_dev_path)
|
||||
|
||||
try:
|
||||
# The `limine.sys` file, contains stage 3 code.
|
||||
cmd = f'/usr/bin/arch-chroot' \
|
||||
f' {self.target}' \
|
||||
f' cp' \
|
||||
f' /usr/share/limine/limine-bios.sys' \
|
||||
f' /boot/limine-bios.sys'
|
||||
if unique_path := disk.device_handler.get_unique_path_for_device(parent_dev_path):
|
||||
parent_dev_path = unique_path
|
||||
|
||||
SysCommand(cmd, peek_output=True)
|
||||
try:
|
||||
# The `limine-bios.sys` file contains stage 3 code.
|
||||
shutil.copy(limine_path / 'limine-bios.sys', self.target / 'boot')
|
||||
|
||||
# `limine bios-install` deploys the stage 1 and 2 to the disk.
|
||||
cmd = f'/usr/bin/arch-chroot' \
|
||||
f' {self.target}' \
|
||||
f' limine' \
|
||||
f' bios-install' \
|
||||
f' {parent_dev_path}'
|
||||
SysCommand(f'/usr/bin/arch-chroot {self.target} limine bios-install {parent_dev_path}', peek_output=True)
|
||||
except Exception as err:
|
||||
raise DiskError(f'Failed to install Limine on {parent_dev_path}: {err}')
|
||||
|
||||
SysCommand(cmd, peek_output=True)
|
||||
except SysCallError as err:
|
||||
raise DiskError(f"Failed to install Limine on {boot_partition.dev_path}: {err}")
|
||||
hook_command = f'/usr/bin/limine bios-install {parent_dev_path}' \
|
||||
f' && /usr/bin/cp /usr/share/limine/limine-bios.sys /boot/'
|
||||
|
||||
create_pacman_hook(f"""
|
||||
[Trigger]
|
||||
hook_contents = f'''[Trigger]
|
||||
Operation = Install
|
||||
Operation = Upgrade
|
||||
Type = Package
|
||||
|
|
@ -1044,33 +1028,38 @@ Target = limine
|
|||
[Action]
|
||||
Description = Deploying Limine after upgrade...
|
||||
When = PostTransaction
|
||||
# XXX: Kernel name descriptors cannot be used since they are not persistent and
|
||||
# can change after each boot.
|
||||
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/\\"
|
||||
""")
|
||||
Exec = /bin/sh -c "{hook_command}"
|
||||
'''
|
||||
|
||||
# Limine does not ship with a default configuration file. We are going to
|
||||
# create a basic one that is similar to the one GRUB generates.
|
||||
try:
|
||||
config = f"""
|
||||
TIMEOUT=5
|
||||
hooks_dir = self.target / 'etc' / 'pacman.d' / 'hooks'
|
||||
hooks_dir.mkdir(parents=True, exist_ok=True)
|
||||
|
||||
:Arch Linux
|
||||
PROTOCOL=linux
|
||||
KERNEL_PATH=boot:///vmlinuz-linux
|
||||
CMDLINE=root=UUID={root_uuid} rw rootfstype={root_partition.safe_fs_type.value} loglevel=3
|
||||
MODULE_PATH=boot:///initramfs-linux.img
|
||||
hook_path = hooks_dir / '99-limine.hook'
|
||||
hook_path.write_text(hook_contents)
|
||||
|
||||
:Arch Linux (fallback)
|
||||
PROTOCOL=linux
|
||||
KERNEL_PATH=boot:///vmlinuz-linux
|
||||
CMDLINE=root=UUID={root_uuid} rw rootfstype={root_partition.safe_fs_type.value} loglevel=3
|
||||
MODULE_PATH=boot:///initramfs-linux-fallback.img
|
||||
"""
|
||||
microcode = []
|
||||
|
||||
SysCommand(f"/usr/bin/arch-chroot {self.target} sh -c \"echo '{config}' > /boot/limine.cfg\"")
|
||||
except SysCallError as err:
|
||||
raise DiskError(f"Could not configure Limine: {err}")
|
||||
if ucode := self._get_microcode():
|
||||
microcode = [f'MODULE_PATH=boot:///{ucode}']
|
||||
|
||||
kernel_params = ' '.join(self._get_kernel_params(root_partition))
|
||||
config_contents = 'TIMEOUT=5\n'
|
||||
|
||||
for kernel in self.kernels:
|
||||
for variant in ('', '-fallback'):
|
||||
entry = [
|
||||
f'PROTOCOL=linux',
|
||||
f'KERNEL_PATH=boot:///vmlinuz-{kernel}',
|
||||
*microcode,
|
||||
f'MODULE_PATH=boot:///initramfs-{kernel}{variant}.img',
|
||||
f'CMDLINE={kernel_params}',
|
||||
]
|
||||
|
||||
config_contents += f'\n:Arch Linux ({kernel}{variant})\n'
|
||||
config_contents += '\n'.join([f' {it}' for it in entry]) + '\n'
|
||||
|
||||
config_path = self.target / 'boot' / 'limine.cfg'
|
||||
config_path.write_text(config_contents)
|
||||
|
||||
self.helper_flags['bootloader'] = "limine"
|
||||
|
||||
|
|
@ -1227,7 +1216,7 @@ TIMEOUT=5
|
|||
case Bootloader.Efistub:
|
||||
self._add_efistub_bootloader(boot_partition, root_partition, uki_enabled)
|
||||
case Bootloader.Limine:
|
||||
self._add_limine_bootloader(boot_partition, root_partition)
|
||||
self._add_limine_bootloader(boot_partition, efi_partition, root_partition)
|
||||
|
||||
def add_additional_packages(self, packages: Union[str, List[str]]) -> bool:
|
||||
return self.pacman.strap(packages)
|
||||
|
|
|
|||
Loading…
Reference in New Issue