From 7b9aa4a57404d39f2aed940aef2026667bfca34e Mon Sep 17 00:00:00 2001 From: Stefan Date: Wed, 13 Apr 2022 21:02:07 +0200 Subject: [PATCH v3 1/8] gnu: bootloader: Rework chaining, add grub-efi-netboot-removable-bootloader. MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * doc/guix.texi (Bootloader Configuration): Describe the new ‘grub-efi-netboot-removable-bootloader’. Mention used sub-directories and that the UEFI Boot Manager is not modified. Advice to disable write-access over TFTP. * gnu/bootloader.scm (efi-bootloader-profile): Allow a list of packages and collect everything directly in the profile, avoiding a separate collection directory. Renamed the profile from "bootloader-profile" to "efi-bootloader-profile". [bootloader-collection]: Renamed to … [efi-bootloader-profile-hook]: … this and removed unused modules and the creation of the now unneeded collection directory. (efi-bootloader-chain): Added packages and disk-image-installer arguments. Removed handling of the collection directory, now only calling the given installer procedure. * gnu/bootloader/grub.scm (make-grub-efi-netboot-installer): New helper. (make-grub-configuration): New helper based on (grub-configuration-file). Adding grub argument, fixed indentation, removend code to get grub. (grub-configuration-file): Now using (make-grub-configuration). (grub-efi-configuration-file): New function using (make-grub-configuration). Instead of getting the grub-efi package from the bootloader-configuration this function refers to the grub-efi package directly. (grub-cfg): New variable to replace "/boot/grub/grub.cfg". (install-grub-efi-netboot): Removed, the functionality got moved. (make-grub-efi-netboot-installer): New helper function to return a customized installer for a certain efi-sub-directory. The installer basically copies a pre-installed efi-bootloader-profile, and adds needed symlinks for booting over network, or – on an ESP – an intermediate grub-cfg to load the final grub-cfg file. (grub-bootloader): Now using the grub-cfg variable. (grub-efi-bootloader): Now using the grub-cfg variable. Removed inheritance, giving complete set of fields. (make-grub-efi-netboot-bootloader): New helper function. (grub-efi-netboot-bootloader): Now using the helper. (grub-efi-netboot-removable-bootloader): New bootloader using the helper. It uses the efi-sub-directory "efi/boot" for removable media. * gnu/packages/bootloaders.scm (make-grub-efi-netboot): New function to return a grub-efi package pre-installed via grub-mknetdir, customized for an efi-sub-directory and able to boot via network and local storage. The rework allows to use an (efi-bootloader-chain) like this, which is able to boot over network or local storage, depending on the symlink-support at the bootloader-target: (operating-system (bootloader (bootloader-configuration (bootloader (efi-bootloader-chain grub-efi-netboot-removable-bootloader #:packages (list my-firmware-package my-u-boot-package) #:files (list (plain-file "config.txt" "kernel=u-boot.bin")) #:hooks my-special-bootloader-profile-manipulator)) (target "/booti/efi") …)) …) ) diff --git a/doc/guix.texi b/doc/guix.texi index a865b2e2e4..4b35142e95 100644 --- a/doc/guix.texi +++ b/doc/guix.texi @@ -35710,8 +35710,9 @@ The type of a bootloader configuration declaration. @cindex BIOS, bootloader The bootloader to use, as a @code{bootloader} object. For now @code{grub-bootloader}, @code{grub-efi-bootloader}, -@code{grub-efi-netboot-bootloader}, @code{extlinux-bootloader} and -@code{u-boot-bootloader} are supported. +@code{grub-efi-netboot-bootloader}, +@code{grub-efi-netboot-removable-bootloader}, +@code{extlinux-bootloader} and @code{u-boot-bootloader} are supported. @cindex ARM, bootloaders @cindex AArch64, bootloaders @@ -35737,9 +35738,10 @@ build a diskless Guix system. The installation of the @code{grub-efi-netboot-bootloader} generates the content of the TFTP root directory at @code{targets} (@pxref{Bootloader -Configuration, @code{targets}}), to be served by a TFTP server. You may -want to mount your TFTP server directories onto the @code{targets} to -move the required files to the TFTP server automatically. +Configuration, @code{targets}}) below the sub-directory @file{efi/Guix}, to be +served by a TFTP server. You may want to mount your TFTP server directories +onto the @code{targets} to move the required files to the TFTP server +automatically during installation. If you plan to use an NFS root file system as well (actually if you mount the store from an NFS share), then the TFTP server needs to serve the file @@ -35773,13 +35775,23 @@ directory to your @code{targets}. It is important to note that symlinks pointing outside the TFTP root directory may need to be allowed in the configuration of your TFTP server. Further the store link exposes the whole store through TFTP@. Both points need to be -considered carefully for security aspects. +considered carefully for security aspects. It is advised to disable any TFTP +write access! + +Please note, that this bootloader will not modify the ‘UEFI Boot Manager’ of +the system. Beside the @code{grub-efi-netboot-bootloader}, the already mentioned TFTP and NFS servers, you also need a properly configured DHCP server to make the booting over netboot possible. For all this we can currently only recommend you to look for instructions about @acronym{PXE, Preboot eXecution Environment}. +@vindex grub-efi-netboot-removable-bootloader +@code{grub-efi-netboot-removable-bootloader} is identical to +@code{grub-efi-netboot-bootloader} with the exception that the sub-directory +@file{efi/boot} will be used instead of @file{efi/Guix} to comply to the UEFI +specification for removable media. + @item @code{targets} This is a list of strings denoting the targets onto which to install the bootloader. diff --git a/gnu/bootloader.scm b/gnu/bootloader.scm index 9cf5457873..797908e24a 100644 --- a/gnu/bootloader.scm +++ b/gnu/bootloader.scm @@ -259,26 +259,22 @@ (define (lookup-bootloader-by-name name) (force %bootloaders)) (leave (G_ "~a: no such bootloader~%") name))) -(define (efi-bootloader-profile files bootloader-package hooks) - "Creates a profile with BOOTLOADER-PACKAGE and a directory collection/ with -links to additional FILES from the store. This collection is meant to be used -by the bootloader installer. +(define (efi-bootloader-profile packages files hooks) + "Creates a profile from the lists of PACKAGES and FILES from the store. +This profile is meant to be used by the bootloader-installer. FILES is a list of file or directory names from the store, which will be -symlinked into the collection/ directory. If a directory name ends with '/', -then the directory content instead of the directory itself will be symlinked -into the collection/ directory. +symlinked into the profile. If a directory name ends with '/', then the +directory content instead of the directory itself will be symlinked into the +profile. -FILES may contain file like objects produced by functions like plain-file, +FILES may contain file like objects produced by procedures like plain-file, local-file, etc., or package contents produced with file-append. HOOKS lists additional hook functions to modify the profile." - (define (bootloader-collection manifest) + (define (efi-bootloader-profile-hook manifest) (define build - (with-imported-modules '((guix build utils) - (ice-9 ftw) - (srfi srfi-1) - (srfi srfi-26)) + (with-imported-modules '((guix build utils)) #~(begin (use-modules ((guix build utils) #:select (mkdir-p strip-store-file-name)) @@ -302,7 +298,7 @@ (define name-ends-with-/? (cut string-suffix? "/" <>)) (define (name-is-store-entry? name) "Return #t if NAME is a direct store entry and nothing inside." (not (string-index (strip-store-file-name name) #\/))) - (let* ((collection (string-append #$output "/collection")) + (let* ((output #$output) (files '#$files) (directories (filter name-ends-with-/? files)) (names-from-directories @@ -311,11 +307,11 @@ (define (name-is-store-entry? name) directories)) (names (append names-from-directories (remove name-ends-with-/? files)))) - (mkdir-p collection) + (mkdir-p output) (if (every file-exists? names) (begin (for-each (lambda (name) - (symlink-to name collection + (symlink-to name output (if (name-is-store-entry? name) strip-store-file-name basename))) @@ -323,57 +319,63 @@ (define (name-is-store-entry? name) #t) #f))))) - (gexp->derivation "bootloader-collection" + (gexp->derivation "efi-bootloader-profile" build #:local-build? #t #:substitutable? #f #:properties `((type . profile-hook) - (hook . bootloader-collection)))) + (hook . efi-bootloader-profile-hook)))) - (profile (content (packages->manifest (list bootloader-package))) - (name "bootloader-profile") - (hooks (append (list bootloader-collection) hooks)) + (profile (content (packages->manifest packages)) + (name "efi-bootloader-profile") + (hooks (cons efi-bootloader-profile-hook hooks)) (locales? #f) (allow-collisions? #f) (relative-symlinks? #f))) -(define* (efi-bootloader-chain files - final-bootloader +(define* (efi-bootloader-chain final-bootloader #:key + (packages '()) + (files '()) (hooks '()) - installer) - "Define a bootloader chain with FINAL-BOOTLOADER as the final bootloader and -certain directories and files from the store given in the list of FILES. + installer + disk-image-installer) + "Define a chain of bootloaders with the FINAL-BOOTLOADER, optional PACKAGES, +and optional directories and files from the store given in the list of FILES. -FILES may contain file like objects produced by functions like plain-file, -local-file, etc., or package contents produced with file-append. They will be -collected inside a directory collection/ inside a generated bootloader profile, -which will be passed to the INSTALLER. +The package of the FINAL-BOOTLOADER and all PACKAGES and FILES will be placed +in an efi-bootloader-profile, which will be passed to the INSTALLER. + +FILES may contain file like objects produced by procedures like plain-file, +local-file, etc., or package contents produced with file-append. If a directory name in FILES ends with '/', then the directory content instead -of the directory itself will be symlinked into the collection/ directory. +of the directory itself will be symlinked into the efi-bootloader-profile. The procedures in the HOOKS list can be used to further modify the bootloader profile. It is possible to pass a single function instead of a list. -If the INSTALLER argument is used, then this function will be called to install -the bootloader. Otherwise the installer of the FINAL-BOOTLOADER will be called." - (let* ((final-installer (or installer - (bootloader-installer final-bootloader))) - (profile (efi-bootloader-profile files - (bootloader-package final-bootloader) - (if (list? hooks) - hooks - (list hooks))))) - (bootloader - (inherit final-bootloader) - (package profile) - (installer - #~(lambda (bootloader target mount-point) - (#$final-installer bootloader target mount-point) - (copy-recursively - (string-append bootloader "/collection") - (string-append mount-point target) - #:follow-symlinks? #t - #:log (%make-void-port "w"))))))) +If the INSTALLER argument is used, then this gexp procedure will be called to +install the efi-bootloader-profile. Otherwise the installer of the +FINAL-BOOTLOADER will be called. + +If the DISK-IMAGE-INSTALLER is used, then this gexp procedure will be called +to install the efi-bootloader-profile into a disk-image. Otherwise the +disk-image-installer of the FINAL-BOOTLOADER will be called." + (bootloader + (inherit final-bootloader) + (name "efi-bootloader-chain") + (package + (efi-bootloader-profile (cons (bootloader-package final-bootloader) + packages) + files + (if (list? hooks) + hooks + (list hooks)))) + (installer + (or installer + (bootloader-installer final-bootloader))) + (disk-image-installer + (or disk-image-installer + (bootloader-disk-image-installer final-bootloader))))) diff --git a/gnu/bootloader/grub.scm b/gnu/bootloader/grub.scm index 120cd55012..0ba6aa4dad 100644 --- a/gnu/bootloader/grub.scm +++ b/gnu/bootloader/grub.scm @@ -50,11 +50,12 @@ (define-module (gnu bootloader grub) grub-theme-color-highlight grub-theme-gfxmode - install-grub-efi-netboot + make-grub-efi-netboot-installer grub-bootloader grub-efi-bootloader grub-efi-netboot-bootloader + grub-efi-netboot-removable-bootloader grub-mkrescue-bootloader grub-minimal-bootloader @@ -348,7 +349,7 @@ (define (grub-root-search device file) ((or #f (? string?)) #~(format #f "search --file --set ~a" #$file))))) -(define* (grub-configuration-file config entries +(define* (make-grub-configuration grub config entries #:key (locale #f) (system (%current-system)) @@ -378,27 +379,27 @@ (define (menu-entry->gexp entry) (initrd (normalize-file (menu-entry-initrd entry) device-mount-point store-directory-prefix))) - ;; Here DEVICE is the store and DEVICE-MOUNT-POINT is its mount point. - ;; Use the right file names for LINUX and INITRD in case - ;; DEVICE-MOUNT-POINT is not "/", meaning that the store is on a - ;; separate partition. - - ;; When BTRFS-SUBVOLUME-FILE-NAME is defined, prepend it the linux and - ;; initrd paths, to allow booting from a Btrfs subvolume. - #~(format port "menuentry ~s { + ;; Here DEVICE is the store and DEVICE-MOUNT-POINT is its mount point. + ;; Use the right file names for LINUX and INITRD in case + ;; DEVICE-MOUNT-POINT is not "/", meaning that the store is on a + ;; separate partition. + + ;; When BTRFS-SUBVOLUME-FILE-NAME is defined, prepend it the linux and + ;; initrd paths, to allow booting from a Btrfs subvolume. + #~(format port "menuentry ~s { ~a linux ~a ~a initrd ~a }~%" - #$label - #$(grub-root-search device linux) - #$linux (string-join (list #$@arguments)) - #$initrd)) + #$label + #$(grub-root-search device linux) + #$linux (string-join (list #$@arguments)) + #$initrd)) (let ((kernel (menu-entry-multiboot-kernel entry)) (arguments (menu-entry-multiboot-arguments entry)) (modules (menu-entry-multiboot-modules entry)) (root-index 1)) ; XXX EFI will need root-index 2 - #~(format port " + #~(format port " menuentry ~s { multiboot ~a root=device:hd0s~a~a~a }~%" @@ -435,9 +436,7 @@ (define (sugar) (define locale-config (let* ((entry (first all-entries)) (device (menu-entry-device entry)) - (mount-point (menu-entry-device-mount-point entry)) - (bootloader (bootloader-configuration-bootloader config)) - (grub (bootloader-package bootloader))) + (mount-point (menu-entry-device-mount-point entry))) #~(let ((locale #$(and locale (locale-definition-source (locale-name->definition locale)))) @@ -463,8 +462,6 @@ (define locale-config (define keyboard-layout-config (let* ((layout (bootloader-configuration-keyboard-layout config)) - (grub (bootloader-package - (bootloader-configuration-bootloader config))) (keymap* (and layout (keyboard-layout-file layout #:grub grub))) (entry (first all-entries)) @@ -515,6 +512,16 @@ (define builder #:options '(#:local-build? #t #:substitutable? #f))) +(define (grub-configuration-file config . args) + (let* ((bootloader (bootloader-configuration-bootloader config)) + (grub (bootloader-package bootloader))) + (apply make-grub-configuration grub config args))) + +(define (grub-efi-configuration-file . args) + (apply make-grub-configuration grub-efi args)) + +(define grub-cfg "/boot/grub/grub.cfg") + ;;; @@ -608,42 +615,31 @@ (define install-grub-efi "--bootloader-id=Guix" "--efi-directory" target-esp))))) -(define (install-grub-efi-netboot subdir) - "Define a grub-efi-netboot bootloader installer for installation in SUBDIR, -which is usually efi/Guix or efi/boot." - (let* ((system (string-split (nix-system->gnu-triplet - (or (%current-target-system) - (%current-system))) - #\-)) - (arch (first system)) - (boot-efi-link (match system - ;; These are the supportend systems and the names - ;; defined by the UEFI standard for removable media. - (("i686" _ ...) "/bootia32.efi") - (("x86_64" _ ...) "/bootx64.efi") - (("arm" _ ...) "/bootarm.efi") - (("aarch64" _ ...) "/bootaa64.efi") - (("riscv" _ ...) "/bootriscv32.efi") - (("riscv64" _ ...) "/bootriscv64.efi") - ;; Other systems are not supported, although defined. - ;; (("riscv128" _ ...) "/bootriscv128.efi") - ;; (("ia64" _ ...) "/bootia64.efi") - ((_ ...) #f))) - (core-efi (string-append - ;; This is the arch dependent file name of GRUB, e.g. - ;; i368-efi/core.efi or arm64-efi/core.efi. - (match arch - ("i686" "i386") - ("aarch64" "arm64") - ("riscv" "riscv32") - (_ arch)) - "-efi/core.efi"))) - (with-imported-modules - '((guix build union)) - #~(lambda (bootloader target mount-point) - "Install the BOOTLOADER, which must be the package grub, as e.g. -bootx64.efi or bootaa64.efi into SUBDIR, which is usually efi/Guix or efi/boot, -below the directory TARGET for the system whose root is mounted at MOUNT-POINT. +(define* (make-grub-efi-netboot-installer grub-efi grub-cfg subdir) + "Make a bootloader-installer for a grub-efi-netboot bootloader, which expects +its files in SUBDIR and its configuration file in GRUB-CFG. + +As a grub-efi-netboot package is already preinstalled by 'grub-mknetdir', the +installer basically copies all files from the bootloader-package (or profile) +into the bootloader-target directory. + +Additionally for network booting over TFTP, two relative symlinks to the store +and to the GRUB-CFG file are necessary. Due to this a TFTP root directory must +not be located on a FAT file-system. + +If the bootloader-target does not support symlinks, then it is assumed to be a +kind of EFI System Partition (ESP). In this case an intermediate configuration +file is created with the help of GRUB-EFI to load the GRUB-CFG. + +The installer is usable for any efi-bootloader-chain, which prepares the +bootloader-profile in a way ready for copying. + +The installer does not manipulate the system's 'UEFI Boot Manager'." + (with-imported-modules '((guix build union)) + #~(lambda (bootloader target mount-point) + "Copy the BOOTLOADER, which must be a preinstalled grub-efi-netboot +package with a SUBDIR like efi/boot or efi/Guix, below the directory +TARGET for the system whose root is mounted at MOUNT-POINT. MOUNT-POINT is the last argument in 'guix system init /etc/config.scm mnt/point' or '/' for other 'guix system' commands. @@ -653,17 +649,18 @@ (define (install-grub-efi-netboot subdir) (operating-system (bootloader (bootloader-configuration - (targets '(\"/boot\")) + (targets '(\"/boot/efi\")) …)) …) TARGET is required to be an absolute directory name, usually mounted via NFS, and finally needs to be provided by a TFTP server as the TFTP root directory. +Usually the installer will be used to prepare network booting over TFTP. Then GRUB will load tftp://server/SUBDIR/grub.cfg and this file will instruct it to load more files from the store like tftp://server/gnu/store/…-linux…/Image. -To make this possible two symlinks will be created. The first symlink points +To make this possible two symlinks will be created. The first symlink points relatively form MOUNT-POINT/TARGET/SUBDIR/grub.cfg to MOUNT-POINT/boot/grub/grub.cfg, and the second symlink points relatively from MOUNT-POINT/TARGET/%store-prefix to MOUNT-POINT/%store-prefix. @@ -673,34 +670,78 @@ (define (install-grub-efi-netboot subdir) It is also important to note that both symlinks will point outside the TFTP root directory and that the TARGET/%store-prefix symlink makes the whole store -accessible via TFTP. Possibly the TFTP server must be configured -to allow accesses outside its TFTP root directory. This may need to be -considered for security aspects." - (use-modules ((guix build union) #:select (symlink-relative))) - (let* ((net-dir (string-append mount-point target "/")) - (sub-dir (string-append net-dir #$subdir "/")) - (store (string-append mount-point (%store-prefix))) - (store-link (string-append net-dir (%store-prefix))) - (grub-cfg (string-append mount-point "/boot/grub/grub.cfg")) - (grub-cfg-link (string-append sub-dir (basename grub-cfg))) - (boot-efi-link (string-append sub-dir #$boot-efi-link))) - ;; Prepare the symlink to the store. - (mkdir-p (dirname store-link)) - (false-if-exception (delete-file store-link)) - (symlink-relative store store-link) - ;; Prepare the symlink to the grub.cfg, which points into the store. - (mkdir-p (dirname grub-cfg-link)) - (false-if-exception (delete-file grub-cfg-link)) - (symlink-relative grub-cfg grub-cfg-link) - ;; Install GRUB, which refers to the grub.cfg, with support for - ;; encrypted partitions, - (setenv "GRUB_ENABLE_CRYPTODISK" "y") - (invoke/quiet (string-append bootloader "/bin/grub-mknetdir") - (string-append "--net-directory=" net-dir) - (string-append "--subdir=" #$subdir)) - ;; Prepare the bootloader symlink, which points to core.efi of GRUB. - (false-if-exception (delete-file boot-efi-link)) - (symlink #$core-efi boot-efi-link)))))) +accessible via TFTP. Possibly the TFTP server must be configured to allow +accesses outside its TFTP root directory. This all may need to be considered +for security aspects. It is advised to disable any TFTP write access! + +The installer can also be used to prepare booting from local storages, if the +underlying file-system, like FAT on an EFI System Partition (ESP), does not +support symlinks. In this case the MOUNT-POINT/TARGET/SUBDIR/grub.cfg will be +created with the help of GRUB-EFI to load the /boot/grub/grub.cfg file. A +symlink to the store is not needed in this case." + ;; In context of a disk image creation TARGET will be #f and an + ;; installer is expected to do necessary installations on MOUNT-POINT, + ;; which will become the root file system. + ;; If TARGET is #f, this installer has nothing to do, as it only cares + ;; about the EFI System Partition (ESP). + (when target + (use-modules ((guix build union) #:select (symlink-relative)) + (ice-9 popen) + (ice-9 rdelim)) + (let* ((mount-point/target (string-append mount-point target "/")) + ;; When installing Guix, it is common to mount TARGET below + ;; MOUNT-POINT rather than the root directory. + (bootloader-target (if (file-exists? mount-point/target) + mount-point/target + target)) + (store (string-append mount-point (%store-prefix))) + (store-link (string-append bootloader-target (%store-prefix))) + (grub-cfg (string-append mount-point #$grub-cfg)) + (grub-cfg-link (string-append bootloader-target + #$subdir "/" + (basename grub-cfg)))) + ;; Copy the bootloader into the bootloader-target directory. + ;; Should we beforehand recursively delete any existing file? + (copy-recursively bootloader bootloader-target + #:follow-symlinks? #t + #:log (%make-void-port "w")) + ;; For TFTP we need to install additional relative symlinks. + ;; If we install on an EFI System Partition (ESP) or some other FAT + ;; file-system, then symlinks cannot be created and are not needed. + ;; Therefore we ignore exceptions when trying. + ;; Prepare the symlink to the grub.cfg. + (mkdir-p (dirname grub-cfg-link)) + (false-if-exception (delete-file grub-cfg-link)) + (if (unspecified? + (false-if-exception (symlink-relative grub-cfg grub-cfg-link))) + ;; Symlinks are supported. + (begin + ;; Prepare the symlink to the store. + (mkdir-p (dirname store-link)) + (false-if-exception (delete-file store-link)) + (symlink-relative store store-link)) + ;; Creating symlinks does not seem to be supported. + ;; Probably an ESP is used. + ;; Instead we can script to search and load the actual grub.cfg. + (let* ((probe #$(file-append grub-efi "/sbin/grub-probe")) + (port + (open-pipe* OPEN_READ probe "--target=fs_uuid" grub-cfg)) + (search-root + (match (read-line port) + ((? eof-object?) + ;; There is no UUID available. As a fallback search + ;; everywhere for the grub.cfg. + (string-append "search --file --set " #$grub-cfg)) + (fs-uuid + ;; The UUID to load the grub.cfg from is known. + (string-append "search --fs-uuid --set " fs-uuid)))) + (load-grub-cfg (string-append "configfile " #$grub-cfg))) + (close-pipe port) + (with-output-to-file grub-cfg-link + (lambda () + (display (string-join (list search-root + load-grub-cfg) + "\n"))))))))))) @@ -718,7 +759,7 @@ (define grub-bootloader (package grub) (installer install-grub) (disk-image-installer install-grub-disk-image) - (configuration-file "/boot/grub/grub.cfg") + (configuration-file grub-cfg) (configuration-file-generator grub-configuration-file))) (define grub-minimal-bootloader @@ -728,17 +769,29 @@ (define grub-minimal-bootloader (define grub-efi-bootloader (bootloader - (inherit grub-bootloader) + (name 'grub-efi) + (package grub-efi) (installer install-grub-efi) (disk-image-installer #f) - (name 'grub-efi) - (package grub-efi))) + (configuration-file grub-cfg) + (configuration-file-generator grub-configuration-file))) -(define grub-efi-netboot-bootloader +(define (make-grub-efi-netboot-bootloader name subdir) (bootloader - (inherit grub-efi-bootloader) - (name 'grub-efi-netboot-bootloader) - (installer (install-grub-efi-netboot "efi/Guix")))) + (name name) + (package (make-grub-efi-netboot (symbol->string name) subdir)) + (installer (make-grub-efi-netboot-installer grub-efi grub-cfg subdir)) + (disk-image-installer #f) + (configuration-file grub-cfg) + (configuration-file-generator grub-efi-configuration-file))) + +(define grub-efi-netboot-bootloader + (make-grub-efi-netboot-bootloader 'grub-efi-netboot-bootloader + "efi/Guix")) + +(define grub-efi-netboot-removable-bootloader + (make-grub-efi-netboot-bootloader 'grub-efi-netboot-removable-bootloader + "efi/boot")) (define grub-mkrescue-bootloader (bootloader diff --git a/gnu/packages/bootloaders.scm b/gnu/packages/bootloaders.scm index 7ea6f5a647..6876ab17b9 100644 --- a/gnu/packages/bootloaders.scm +++ b/gnu/packages/bootloaders.scm @@ -15,6 +15,7 @@ ;;; Copyright © 2020, 2021 Pierre Langlois ;;; Copyright © 2021 Vincent Legoll ;;; Copyright © 2021 Brice Waegeneire +;;; Copyright © 2021 Stefan ;;; ;;; This file is part of GNU Guix. ;;; @@ -66,13 +67,17 @@ (define-module (gnu packages bootloaders) #:use-module (gnu packages virtualization) #:use-module (gnu packages xorg) #:use-module (guix build-system gnu) + #:use-module (guix build-system trivial) #:use-module (guix download) + #:use-module (guix gexp) #:use-module (guix git-download) #:use-module ((guix licenses) #:prefix license:) #:use-module (guix packages) #:use-module (guix utils) #:use-module (srfi srfi-1) #:use-module (srfi srfi-26) + #:use-module (ice-9 match) + #:use-module (ice-9 optargs) #:use-module (ice-9 regex)) (define unifont @@ -366,6 +371,91 @@ (define-public grub-hybrid (scandir input-dir)) #t))))))))) +(define-public (make-grub-efi-netboot name subdir) + "Make a grub-efi-netboot package named NAME, which will be able to boot over +network via TFTP by accessing its files in the SUBDIR of a TFTP root directory. +This package is also able to boot from local storage devices. + +A bootloader-installer basically needs to copy the package content into the +bootloader-target directory, which will usually be the TFTP root, as +'grub-mknetdir' will be invoked already during the package creation. + +Alternatively the bootloader-target directory can be a mounted EFI System +Partition (ESP), or a similar partition with a FAT file system, for booting +from local storage devices. + +The name of the GRUB EFI binary will conform to the UEFI specification for +removable media. Depending on the system it will be e.g. bootx64.efi or +bootaa64.efi below SUBDIR. + +The SUBDIR argument needs to be set to \"efi/boot\" to create a package which +conforms to the UEFI specification for removable media. + +The SUBDIR argument defaults to \"efi/Guix\", as it is also the case for +'grub-efi-bootloader'." + (package + (name name) + (version (package-version grub-efi)) + ;; Source is not needed, but it cannot be omitted. + (source #f) + (build-system trivial-build-system) + (arguments + (let* ((system (string-split (nix-system->gnu-triplet + (or (%current-target-system) + (%current-system))) + #\-)) + (arch (first system)) + (boot-efi + (match system + ;; These are the supportend systems and the names defined by + ;; the UEFI standard for removable media. + (("i686" _ ...) "/bootia32.efi") + (("x86_64" _ ...) "/bootx64.efi") + (("arm" _ ...) "/bootarm.efi") + (("aarch64" _ ...) "/bootaa64.efi") + (("riscv" _ ...) "/bootriscv32.efi") + (("riscv64" _ ...) "/bootriscv64.efi") + ;; Other systems are not supported, although defined. + ;; (("riscv128" _ ...) "/bootriscv128.efi") + ;; (("ia64" _ ...) "/bootia64.efi") + ((_ ...) #f))) + (core-efi (string-append + ;; This is the arch dependent file name of GRUB, e.g. + ;; i368-efi/core.efi or arm64-efi/core.efi. + (match arch + ("i686" "i386") + ("aarch64" "arm64") + ("riscv" "riscv32") + (_ arch)) + "-efi/core.efi"))) + `(#:modules ((guix build utils)) + #:builder + (begin + (use-modules (guix build utils)) + (let* ((bootloader (assoc-ref %build-inputs "grub-efi")) + (net-dir (assoc-ref %outputs "out")) + (sub-dir (string-append net-dir "/" ,subdir "/")) + (boot-efi (string-append sub-dir ,boot-efi)) + (core-efi (string-append sub-dir ,core-efi))) + ;; Install GRUB, which refers to the grub.cfg, with support for + ;; encrypted partitions, + (setenv "GRUB_ENABLE_CRYPTODISK" "y") + (invoke/quiet (string-append bootloader "/bin/grub-mknetdir") + (string-append "--net-directory=" net-dir) + (string-append "--subdir=" ,subdir) + ;; These modules must be preloaded to allow booting + ;; from an ESP or a similar partition with a FAT + ;; file system. + (string-append "--modules=part_msdos part_gpt fat")) + ;; Move GRUB's core.efi to the removable media name. + (false-if-exception (delete-file boot-efi)) + (rename-file core-efi boot-efi)))))) + (inputs `(("grub-efi" ,grub-efi))) + (synopsis (package-synopsis grub-efi)) + (description (package-description grub-efi)) + (home-page (package-home-page grub-efi)) + (license (package-license grub-efi)))) + (define-public syslinux (let ((commit "bb41e935cc83c6242de24d2271e067d76af3585c")) (package -- 2.35.1