Send patches - preferably formatted by git format-patch - to patches at archlinux32 dot org.
summaryrefslogtreecommitdiff
path: root/archinstall/lib
diff options
context:
space:
mode:
Diffstat (limited to 'archinstall/lib')
-rw-r--r--archinstall/lib/global_menu.py44
-rw-r--r--archinstall/lib/installer.py112
-rw-r--r--archinstall/lib/interactions/system_conf.py4
-rw-r--r--archinstall/lib/models/bootloader.py1
4 files changed, 156 insertions, 5 deletions
diff --git a/archinstall/lib/global_menu.py b/archinstall/lib/global_menu.py
index 54b30240..5a431010 100644
--- a/archinstall/lib/global_menu.py
+++ b/archinstall/lib/global_menu.py
@@ -169,8 +169,8 @@ class GlobalMenu(AbstractMenu):
self._menu_options['install'] = \
Selector(
self._install_text(),
- exec_func=lambda n, v: True if len(self._missing_configs()) == 0 else False,
- preview_func=self._prev_install_missing_config,
+ exec_func=lambda n, v: self._is_config_valid(),
+ preview_func=self._prev_install_invalid_config,
no_store=True)
self._menu_options['abort'] = Selector(_('Abort'), exec_func=lambda n,v:exit(1))
@@ -200,6 +200,14 @@ class GlobalMenu(AbstractMenu):
return list(missing)
+ def _is_config_valid(self) -> bool:
+ """
+ Checks the validity of the current configuration.
+ """
+ if len(self._missing_configs()) != 0:
+ return False
+ return self._validate_bootloader() is None
+
def _update_install_text(self, name: str, value: str):
text = self._install_text()
self._menu_options['install'].update_description(text)
@@ -321,12 +329,42 @@ class GlobalMenu(AbstractMenu):
return disk.EncryptionType.type_to_text(current_value.encryption_type)
return ''
- def _prev_install_missing_config(self) -> Optional[str]:
+ def _validate_bootloader(self) -> Optional[str]:
+ """
+ Checks the selected bootloader is valid for the selected filesystem
+ type of the boot partition.
+
+ Returns [`None`] if the bootloader is valid, otherwise returns a
+ string with the error message.
+ """
+ bootloader = self._menu_options['bootloader'].current_selection
+ boot_partition: Optional[disk.PartitionModification] = None
+
+ if disk_config := self._menu_options['disk_config'].current_selection:
+ for layout in disk_config.device_modifications:
+ if boot_partition := layout.get_boot_partition():
+ break
+ else:
+ return "No disk layout selected"
+
+ 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"
+
+ return None
+
+ def _prev_install_invalid_config(self) -> Optional[str]:
if missing := self._missing_configs():
text = str(_('Missing configurations:\n'))
for m in missing:
text += f'- {m}\n'
return text[:-1] # remove last new line
+
+ if error := self._validate_bootloader():
+ return f"Invalid configuration: {error}"
+
return None
def _prev_users(self) -> Optional[str]:
diff --git a/archinstall/lib/installer.py b/archinstall/lib/installer.py
index ee546993..0d43b2fe 100644
--- a/archinstall/lib/installer.py
+++ b/archinstall/lib/installer.py
@@ -8,6 +8,8 @@ import time
from pathlib import Path
from typing import Any, List, Optional, TYPE_CHECKING, Union, Dict, Callable
+from ..lib.disk.device_model import get_lsblk_info
+
from . import disk
from .exceptions import DiskError, ServiceException, RequirementError, HardwareIncompatibilityError, SysCallError
from .general import SysCommand
@@ -850,6 +852,113 @@ class Installer:
self.helper_flags['bootloader'] = "grub"
+ def _add_limine_bootloader(
+ self,
+ boot_partition: disk.PartitionModification,
+ root_partition: disk.PartitionModification
+ ):
+ self.pacman.strap('limine')
+ info(f"Limine boot partition: {boot_partition.dev_path}")
+
+ # XXX: We cannot use `root_partition.uuid` since corresponds to the UUID of the root
+ # partition before the format.
+ root_uuid = get_lsblk_info(root_partition.safe_dev_path).uuid
+
+ device = disk.device_handler.get_device_by_partition_path(boot_partition.safe_dev_path)
+ if not device:
+ raise ValueError(f'Can not find block device: {boot_partition.safe_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\"")
+
+ if SysInfo.has_uefi():
+ 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/'
+ except SysCallError as err:
+ raise DiskError(f"Failed to install Limine BOOTX64.EFI on {boot_partition.dev_path}: {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/
+ """)
+ else:
+ 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'
+
+ SysCommand(cmd, peek_output=True)
+
+ # `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' {device.device_info.path}'
+
+ SysCommand(cmd, peek_output=True)
+ except SysCallError as err:
+ raise DiskError(f"Failed to install Limine on {boot_partition.dev_path}: {err}")
+
+ create_pacman_hook(f"""
+[Trigger]
+Operation = Install
+Operation = Upgrade
+Type = Package
+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/\\"
+ """)
+
+ # Limine does not ship with a default configuation 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}")
+
+ self.helper_flags['bootloader'] = "limine"
+
def _add_efistub_bootloader(
self,
boot_partition: disk.PartitionModification,
@@ -918,6 +1027,7 @@ class Installer:
Archinstall supports one of three types:
* systemd-bootctl
* grub
+ * limine (beta)
* efistub (beta)
:param bootloader: Type of bootloader to be added
@@ -948,6 +1058,8 @@ class Installer:
self._add_grub_bootloader(boot_partition, root_partition)
case Bootloader.Efistub:
self._add_efistub_bootloader(boot_partition, root_partition)
+ case Bootloader.Limine:
+ self._add_limine_bootloader(boot_partition, root_partition)
def add_additional_packages(self, packages: Union[str, List[str]]) -> bool:
return self.pacman.strap(packages)
diff --git a/archinstall/lib/interactions/system_conf.py b/archinstall/lib/interactions/system_conf.py
index 5b1bc456..0e5e0f1e 100644
--- a/archinstall/lib/interactions/system_conf.py
+++ b/archinstall/lib/interactions/system_conf.py
@@ -40,9 +40,9 @@ def select_kernel(preset: List[str] = []) -> List[str]:
def ask_for_bootloader(preset: Bootloader) -> Bootloader:
- # when the system only supports grub
+ # Systemd is UEFI only
if not SysInfo.has_uefi():
- options = [Bootloader.Grub.value]
+ options = [Bootloader.Grub.value, Bootloader.Limine.value]
default = Bootloader.Grub.value
else:
options = Bootloader.values()
diff --git a/archinstall/lib/models/bootloader.py b/archinstall/lib/models/bootloader.py
index e21cda33..be9812a0 100644
--- a/archinstall/lib/models/bootloader.py
+++ b/archinstall/lib/models/bootloader.py
@@ -12,6 +12,7 @@ class Bootloader(Enum):
Systemd = 'Systemd-boot'
Grub = 'Grub'
Efistub = 'Efistub'
+ Limine = 'Limine'
def json(self):
return self.value