From e6344f93f7e476d05bbcd642f2ed91fdde545870 Mon Sep 17 00:00:00 2001 From: czapek <32851089+48cf@users.noreply.github.com> Date: Tue, 21 Nov 2023 10:19:17 +0100 Subject: 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 --- archinstall/lib/disk/device_handler.py | 14 ++++ archinstall/lib/global_menu.py | 7 +- archinstall/lib/installer.py | 135 +++++++++++++++------------------ 3 files changed, 81 insertions(+), 75 deletions(-) (limited to 'archinstall/lib') diff --git a/archinstall/lib/disk/device_handler.py b/archinstall/lib/disk/device_handler.py index 50e8c59c..fcf52013 100644 --- a/archinstall/lib/disk/device_handler.py +++ b/archinstall/lib/disk/device_handler.py @@ -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 diff --git a/archinstall/lib/global_menu.py b/archinstall/lib/global_menu.py index e4aa1235..d3d87603 100644 --- a/archinstall/lib/global_menu.py +++ b/archinstall/lib/global_menu.py @@ -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 diff --git a/archinstall/lib/installer.py b/archinstall/lib/installer.py index 2aa0d9dd..e2ca5e2b 100644 --- a/archinstall/lib/installer.py +++ b/archinstall/lib/installer.py @@ -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 + info(f"Limine boot partition: {boot_partition.dev_path}") - 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/' - - SysCommand(cmd, peek_output=True) - except SysCallError as err: - raise DiskError(f"Failed to install Limine BOOTX64.EFI on {boot_partition.dev_path}: {err}") + efi_dir_path = self.target / efi_partition.mountpoint.relative_to('/') / 'EFI' / 'BOOT' + efi_dir_path.mkdir(parents=True, exist_ok=True) - # Create the EFI limine pacman hook. - create_pacman_hook(""" -[Trigger] -Operation = Install -Operation = Upgrade -Type = Package -Target = limine + 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}') -[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 - -: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 - -: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 - """ - - 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}") + hooks_dir = self.target / 'etc' / 'pacman.d' / 'hooks' + hooks_dir.mkdir(parents=True, exist_ok=True) + + hook_path = hooks_dir / '99-limine.hook' + hook_path.write_text(hook_contents) + + microcode = [] + + 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) -- cgit v1.2.3-54-g00ecf