gnu: bootloader: Rework chaining, add grub-efi-netboot-removable-bootloader.

This rework allows to use an (efi-bootloader-chain) like this, which is able
to boot over network or local storage, depending on whether the bootloader
target has support for symbolic links:

   (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))
        (targets '("/booti/efi"))
        …))
    …)

* doc/guix.texi (Bootloader Configuration): Describe the new
‘grub-efi-netboot-removable-bootloader’.  Mention the file names used and that
the UEFI Boot Manager is not modified.  Advise 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]: Rename to...
[efi-bootloader-profile-hook]: ... this and remove unused modules.  Do not
create the now extraneous collection directory.
(efi-bootloader-chain): Add PACKAGES and DISK-IMAGE-INSTALLER arguments.
Remove 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).  Add
a GRUB argument, fix indentation, remove previous code retrieving GRUB from
CONFIG.
(grub-configuration-file): Make use of make-grub-configuration.
(grub-efi-configuration-file): New procedure.
(grub-cfg): New variable to replace "/boot/grub/grub.cfg".
(install-grub-efi-netboot): Remove, splitting logic to...
(make-grub-efi-netboot-installer): ... this new helper procedure, as well as
to make-grub-efi-netboot, added below.
(grub-bootloader): Adjust to use the GRUB-CFG.
(grub-efi-bootloader): Likewise.  Removed inheritance and declare all fields
explicitly.
(make-grub-efi-netboot-bootloader): New procedure.
(grub-efi-netboot-bootloader): Use it.
(grub-efi-netboot-removable-bootloader): New variable.
* gnu/packages/bootloaders.scm (make-grub-efi-netboot): New procedure.

Signed-off-by: Maxim Cournoyer <maxim.cournoyer@gmail.com>
Modified-by: Maxim Cournoyer <maxim.cournoyer@gmail.com>
This commit is contained in:
Stefan 2022-11-30 19:59:09 -05:00 committed by Maxim Cournoyer
parent 1a63aea943
commit a9acbf919a
No known key found for this signature in database
GPG key ID: 1260E46482E63562
4 changed files with 323 additions and 151 deletions

View file

@ -37997,8 +37997,9 @@ The type of a bootloader configuration declaration.
@cindex BIOS, bootloader @cindex BIOS, bootloader
The bootloader to use, as a @code{bootloader} object. For now The bootloader to use, as a @code{bootloader} object. For now
@code{grub-bootloader}, @code{grub-efi-bootloader}, @code{grub-bootloader}, @code{grub-efi-bootloader},
@code{grub-efi-netboot-bootloader}, @code{grub-efi-removable-bootloader}, @code{grub-efi-removable-bootloader}, @code{grub-efi-netboot-bootloader},
@code{extlinux-bootloader} and @code{u-boot-bootloader} are supported. @code{grub-efi-netboot-removable-bootloader}, @code{extlinux-bootloader}
and @code{u-boot-bootloader} are supported.
@cindex ARM, bootloaders @cindex ARM, bootloaders
@cindex AArch64, bootloaders @cindex AArch64, bootloaders
@ -38007,15 +38008,29 @@ modules. In particular, @code{(gnu bootloader u-boot)} contains definitions
of bootloaders for a wide range of ARM and AArch64 systems, using the of bootloaders for a wide range of ARM and AArch64 systems, using the
@uref{https://www.denx.de/wiki/U-Boot/, U-Boot bootloader}. @uref{https://www.denx.de/wiki/U-Boot/, U-Boot bootloader}.
@vindex grub-bootloader
@code{grub-bootloader} allows you to boot in particular Intel-based machines
in ``legacy'' BIOS mode.
@vindex grub-efi-bootloader @vindex grub-efi-bootloader
@code{grub-efi-bootloader} allows to boot on modern systems using the @code{grub-efi-bootloader} allows to boot on modern systems using the
@dfn{Unified Extensible Firmware Interface} (UEFI). This is what you should @dfn{Unified Extensible Firmware Interface} (UEFI). This is what you should
use if the installation image contains a @file{/sys/firmware/efi} directory use if the installation image contains a @file{/sys/firmware/efi} directory
when you boot it on your system. when you boot it on your system.
@vindex grub-bootloader @vindex grub-efi-removable-bootloader
@code{grub-bootloader} allows you to boot in particular Intel-based machines @code{grub-efi-removable-bootloader} allows you to boot your system from
in ``legacy'' BIOS mode. removable media by writing the GRUB file to the UEFI-specification location of
@file{/EFI/BOOT/BOOTX64.efi} of the boot directory, usually @file{/boot/efi}.
This is also useful for some UEFI firmwares that ``forget'' their configuration
from their non-volatile storage. Like @code{grub-efi-bootloader}, this can only
be used if the @file{/sys/firmware/efi} directory is available.
@quotation Note
This @emph{will} overwrite the GRUB file from any other operating systems that
also place their GRUB file in the UEFI-specification location; making them
unbootable.
@end quotation
@vindex grub-efi-netboot-bootloader @vindex grub-efi-netboot-bootloader
@code{grub-efi-netboot-bootloader} allows you to boot your system over network @code{grub-efi-netboot-bootloader} allows you to boot your system over network
@ -38024,9 +38039,10 @@ build a diskless Guix system.
The installation of the @code{grub-efi-netboot-bootloader} generates the The installation of the @code{grub-efi-netboot-bootloader} generates the
content of the TFTP root directory at @code{targets} (@pxref{Bootloader content of the TFTP root directory at @code{targets} (@pxref{Bootloader
Configuration, @code{targets}}), to be served by a TFTP server. You may Configuration, @code{targets}}) below the sub-directory @file{efi/Guix}, to be
want to mount your TFTP server directories onto the @code{targets} to served by a TFTP server. You may want to mount your TFTP server directories
move the required files to the TFTP server automatically. 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 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 store from an NFS share), then the TFTP server needs to serve the file
@ -38055,25 +38071,34 @@ this constellation the symlinks will work.
For other constellations you will have to program your own bootloader For other constellations you will have to program your own bootloader
installer, which then takes care to make necessary files from the store installer, which then takes care to make necessary files from the store
accessible through TFTP, for example by copying them into the TFTP root accessible through TFTP, for example by copying them into the TFTP root
directory to your @code{targets}. directory for your @code{targets}.
It is important to note that symlinks pointing outside the TFTP root directory 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 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 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 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 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 over netboot possible. For all this we can currently only recommend you to look
for instructions about @acronym{PXE, Preboot eXecution Environment}. for instructions about @acronym{PXE, Preboot eXecution Environment}.
@vindex grub-efi-removable-bootloader If a local EFI System Partition (ESP) or a similar partition with a FAT
@code{grub-efi-removable-bootloader} allows you to boot your system from file system is mounted in @code{targets}, then symlinks cannot be
removable media by writing the GRUB file to the UEFI-specification location of created. In this case everything will be prepared for booting from
@file{/EFI/BOOT/BOOTX64.efi} of the boot directory, usually @file{/boot/efi}. local storage, matching the behavior of @code{grub-efi-bootloader}, with
This is also useful for some UEFI firmwares that ``forget'' their configuration the difference that all GRUB binaries are copied to @code{targets},
from their non-volatile storage. Like @code{grub-efi-bootloader}, this can only necessary for booting over the network.
be used if the @file{/sys/firmware/efi} directory is available.
@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 with the UEFI specification for removable media.
@quotation Note @quotation Note
This @emph{will} overwrite the GRUB file from any other operating systems that This @emph{will} overwrite the GRUB file from any other operating systems that

View file

@ -322,26 +322,22 @@ (define (lookup-bootloader-by-name name)
(force %bootloaders)) (force %bootloaders))
(leave (G_ "~a: no such bootloader~%") name))) (leave (G_ "~a: no such bootloader~%") name)))
(define (efi-bootloader-profile files bootloader-package hooks) (define (efi-bootloader-profile packages files hooks)
"Creates a profile with BOOTLOADER-PACKAGE and a directory collection/ with "Creates a profile from the lists of PACKAGES and FILES from the store.
links to additional FILES from the store. This collection is meant to be used This profile is meant to be used by the bootloader-installer.
by the bootloader installer.
FILES is a list of file or directory names from the store, which will be 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 '/', symlinked into the profile. If a directory name ends with '/', then the
then the directory content instead of the directory itself will be symlinked directory content instead of the directory itself will be symlinked into the
into the collection/ directory. 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. local-file, etc., or package contents produced with file-append.
HOOKS lists additional hook functions to modify the profile." HOOKS lists additional hook functions to modify the profile."
(define (bootloader-collection manifest) (define (efi-bootloader-profile-hook manifest)
(define build (define build
(with-imported-modules '((guix build utils) (with-imported-modules '((guix build utils))
(ice-9 ftw)
(srfi srfi-1)
(srfi srfi-26))
#~(begin #~(begin
(use-modules ((guix build utils) (use-modules ((guix build utils)
#:select (mkdir-p strip-store-file-name)) #:select (mkdir-p strip-store-file-name))
@ -365,8 +361,7 @@ (define name-ends-with-/? (cut string-suffix? "/" <>))
(define (name-is-store-entry? name) (define (name-is-store-entry? name)
"Return #t if NAME is a direct store entry and nothing inside." "Return #t if NAME is a direct store entry and nothing inside."
(not (string-index (strip-store-file-name name) #\/))) (not (string-index (strip-store-file-name name) #\/)))
(let* ((collection (string-append #$output "/collection")) (let* ((files '#$files)
(files '#$files)
(directories (filter name-ends-with-/? files)) (directories (filter name-ends-with-/? files))
(names-from-directories (names-from-directories
(append-map (lambda (directory) (append-map (lambda (directory)
@ -374,11 +369,11 @@ (define (name-is-store-entry? name)
directories)) directories))
(names (append names-from-directories (names (append names-from-directories
(remove name-ends-with-/? files)))) (remove name-ends-with-/? files))))
(mkdir-p collection) (mkdir-p #$output)
(if (every file-exists? names) (if (every file-exists? names)
(begin (begin
(for-each (lambda (name) (for-each (lambda (name)
(symlink-to name collection (symlink-to name #$output
(if (name-is-store-entry? name) (if (name-is-store-entry? name)
strip-store-file-name strip-store-file-name
basename))) basename)))
@ -386,57 +381,63 @@ (define (name-is-store-entry? name)
#t) #t)
#f))))) #f)))))
(gexp->derivation "bootloader-collection" (gexp->derivation "efi-bootloader-profile"
build build
#:local-build? #t #:local-build? #t
#:substitutable? #f #:substitutable? #f
#:properties #:properties
`((type . profile-hook) `((type . profile-hook)
(hook . bootloader-collection)))) (hook . efi-bootloader-profile-hook))))
(profile (content (packages->manifest (list bootloader-package))) (profile (content (packages->manifest packages))
(name "bootloader-profile") (name "efi-bootloader-profile")
(hooks (append (list bootloader-collection) hooks)) (hooks (cons efi-bootloader-profile-hook hooks))
(locales? #f) (locales? #f)
(allow-collisions? #f) (allow-collisions? #f)
(relative-symlinks? #f))) (relative-symlinks? #f)))
(define* (efi-bootloader-chain files (define* (efi-bootloader-chain final-bootloader
final-bootloader
#:key #:key
(packages '())
(files '())
(hooks '()) (hooks '())
installer) installer
"Define a bootloader chain with FINAL-BOOTLOADER as the final bootloader and disk-image-installer)
certain directories and files from the store given in the list of FILES. "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, The package of the FINAL-BOOTLOADER and all PACKAGES and FILES will be placed
local-file, etc., or package contents produced with file-append. They will be in an efi-bootloader-profile, which will be passed to the INSTALLER.
collected inside a directory collection/ inside a generated 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 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 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. 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 If the INSTALLER argument is used, then this gexp procedure will be called to
the bootloader. Otherwise the installer of the FINAL-BOOTLOADER will be called." install the efi-bootloader-profile. Otherwise the installer of the
(let* ((final-installer (or installer FINAL-BOOTLOADER will be called.
(bootloader-installer final-bootloader)))
(profile (efi-bootloader-profile files If the DISK-IMAGE-INSTALLER is used, then this gexp procedure will be called
(bootloader-package final-bootloader) to install the efi-bootloader-profile into a disk image. Otherwise the
(if (list? hooks) disk-image-installer of the FINAL-BOOTLOADER will be called."
hooks (bootloader
(list hooks))))) (inherit final-bootloader)
(bootloader (name "efi-bootloader-chain")
(inherit final-bootloader) (package
(package profile) (efi-bootloader-profile (cons (bootloader-package final-bootloader)
(installer packages)
#~(lambda (bootloader target mount-point) files
(#$final-installer bootloader target mount-point) (if (list? hooks)
(copy-recursively hooks
(string-append bootloader "/collection") (list hooks))))
(string-append mount-point target) (installer
#:follow-symlinks? #t (or installer
#:log (%make-void-port "w"))))))) (bootloader-installer final-bootloader)))
(disk-image-installer
(or disk-image-installer
(bootloader-disk-image-installer final-bootloader)))))

View file

@ -53,13 +53,14 @@ (define-module (gnu bootloader grub)
grub-theme-gfxmode grub-theme-gfxmode
install-grub-efi-removable install-grub-efi-removable
install-grub-efi-netboot make-grub-efi-netboot-installer
grub-bootloader grub-bootloader
grub-efi-bootloader grub-efi-bootloader
grub-efi-removable-bootloader grub-efi-removable-bootloader
grub-efi32-bootloader grub-efi32-bootloader
grub-efi-netboot-bootloader grub-efi-netboot-bootloader
grub-efi-netboot-removable-bootloader
grub-mkrescue-bootloader grub-mkrescue-bootloader
grub-minimal-bootloader grub-minimal-bootloader
@ -353,7 +354,7 @@ (define (grub-root-search device file)
((or #f (? string?)) ((or #f (? string?))
#~(format #f "search --file --set ~a" #$file))))) #~(format #f "search --file --set ~a" #$file)))))
(define* (grub-configuration-file config entries (define* (make-grub-configuration grub config entries
#:key #:key
(locale #f) (locale #f)
(system (%current-system)) (system (%current-system))
@ -453,9 +454,7 @@ (define (sugar)
(define locale-config (define locale-config
(let* ((entry (first all-entries)) (let* ((entry (first all-entries))
(device (menu-entry-device entry)) (device (menu-entry-device entry))
(mount-point (menu-entry-device-mount-point entry)) (mount-point (menu-entry-device-mount-point entry)))
(bootloader (bootloader-configuration-bootloader config))
(grub (bootloader-package bootloader)))
#~(let ((locale #$(and locale #~(let ((locale #$(and locale
(locale-definition-source (locale-definition-source
(locale-name->definition locale)))) (locale-name->definition locale))))
@ -481,8 +480,6 @@ (define locale-config
(define keyboard-layout-config (define keyboard-layout-config
(let* ((layout (bootloader-configuration-keyboard-layout config)) (let* ((layout (bootloader-configuration-keyboard-layout config))
(grub (bootloader-package
(bootloader-configuration-bootloader config)))
(keymap* (and layout (keymap* (and layout
(keyboard-layout-file layout #:grub grub))) (keyboard-layout-file layout #:grub grub)))
(entry (first all-entries)) (entry (first all-entries))
@ -533,6 +530,16 @@ (define builder
#:options '(#:local-build? #t #:options '(#:local-build? #t
#:substitutable? #f))) #: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")
;;; ;;;
@ -674,42 +681,31 @@ (define install-grub-efi32
((target-arm?) "--target=arm-efi")) ((target-arm?) "--target=arm-efi"))
"--efi-directory" target-esp))))) "--efi-directory" target-esp)))))
(define (install-grub-efi-netboot subdir) (define* (make-grub-efi-netboot-installer grub-efi grub-cfg subdir)
"Define a grub-efi-netboot bootloader installer for installation in SUBDIR, "Make a bootloader-installer for a grub-efi-netboot bootloader, which expects
which is usually efi/Guix or efi/boot." its files in SUBDIR and its configuration file in GRUB-CFG.
(let* ((system (string-split (nix-system->gnu-triplet
(or (%current-target-system) As a grub-efi-netboot package is already pre-installed by 'grub-mknetdir', the
(%current-system))) installer basically copies all files from the bootloader-package (or profile)
#\-)) into the bootloader-target directory.
(arch (first system))
(boot-efi-link (match system Additionally for network booting over TFTP, two relative symlinks to the store
;; These are the supportend systems and the names and to the GRUB-CFG file are necessary. Due to this a TFTP root directory must
;; defined by the UEFI standard for removable media. not be located on a FAT file-system.
(("i686" _ ...) "/bootia32.efi")
(("x86_64" _ ...) "/bootx64.efi") If the bootloader-target does not support symlinks, then it is assumed to be a
(("arm" _ ...) "/bootarm.efi") kind of EFI System Partition (ESP). In this case an intermediate configuration
(("aarch64" _ ...) "/bootaa64.efi") file is created with the help of GRUB-EFI to load the GRUB-CFG.
(("riscv" _ ...) "/bootriscv32.efi")
(("riscv64" _ ...) "/bootriscv64.efi") The installer is usable for any efi-bootloader-chain, which prepares the
;; Other systems are not supported, although defined. bootloader-profile in a way ready for copying.
;; (("riscv128" _ ...) "/bootriscv128.efi")
;; (("ia64" _ ...) "/bootia64.efi") The installer does not manipulate the system's 'UEFI Boot Manager'.
((_ ...) #f)))
(core-efi (string-append The returned installer accepts the BOOTLOADER, TARGET and MOUNT-POINT
;; This is the arch dependent file name of GRUB, e.g. arguments. Its job is to copy the BOOTLOADER, which must be a pre-installed
;; i368-efi/core.efi or arm64-efi/core.efi. grub-efi-netboot package with a SUBDIR like efi/boot or efi/Guix, below the
(match arch directory TARGET for the system whose root is mounted at MOUNT-POINT.
("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.
MOUNT-POINT is the last argument in 'guix system init /etc/config.scm mnt/point' MOUNT-POINT is the last argument in 'guix system init /etc/config.scm mnt/point'
or '/' for other 'guix system' commands. or '/' for other 'guix system' commands.
@ -719,17 +715,19 @@ (define (install-grub-efi-netboot subdir)
(operating-system (operating-system
(bootloader (bootloader-configuration (bootloader (bootloader-configuration
(targets '(\"/boot\")) (targets '(\"/boot/efi\"))
)) ))
) )
TARGET is required to be an absolute directory name, usually mounted via NFS, 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. 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 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. 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 are created. The first symlink points
relatively form MOUNT-POINT/TARGET/SUBDIR/grub.cfg to relatively form MOUNT-POINT/TARGET/SUBDIR/grub.cfg to
MOUNT-POINT/boot/grub/grub.cfg, and the second symlink points relatively from MOUNT-POINT/boot/grub/grub.cfg, and the second symlink points relatively from
MOUNT-POINT/TARGET/%store-prefix to MOUNT-POINT/%store-prefix. MOUNT-POINT/TARGET/%store-prefix to MOUNT-POINT/%store-prefix.
@ -739,34 +737,80 @@ (define (install-grub-efi-netboot subdir)
It is also important to note that both symlinks will point outside the TFTP root 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 directory and that the TARGET/%store-prefix symlink makes the whole store
accessible via TFTP. Possibly the TFTP server must be configured accessible via TFTP. Possibly the TFTP server must be configured to allow
to allow accesses outside its TFTP root directory. This may need to be accesses outside its TFTP root directory. This all may need to be considered
considered for security aspects." for security aspects. It is advised to disable any TFTP write access!
(use-modules ((guix build union) #:select (symlink-relative)))
(let* ((net-dir (string-append mount-point target "/")) The installer can also be used to prepare booting from local storage, if the
(sub-dir (string-append net-dir #$subdir "/")) underlying file-system, like FAT on an EFI System Partition (ESP), does not
(store (string-append mount-point (%store-prefix))) support symlinks. In this case the MOUNT-POINT/TARGET/SUBDIR/grub.cfg will be
(store-link (string-append net-dir (%store-prefix))) created with the help of GRUB-EFI to load the /boot/grub/grub.cfg file. A
(grub-cfg (string-append mount-point "/boot/grub/grub.cfg")) symlink to the store is not needed in this case."
(grub-cfg-link (string-append sub-dir (basename grub-cfg))) (with-imported-modules '((guix build union))
(boot-efi-link (string-append sub-dir #$boot-efi-link))) #~(lambda (bootloader target mount-point)
;; Prepare the symlink to the store. ;; In context of a disk image creation TARGET will be #f and an
(mkdir-p (dirname store-link)) ;; installer is expected to do necessary installations on MOUNT-POINT,
(false-if-exception (delete-file store-link)) ;; which will become the root file system. If TARGET is #f, this
(symlink-relative store store-link) ;; installer has nothing to do, as it only cares about the EFI System
;; Prepare the symlink to the grub.cfg, which points into the store. ;; Partition (ESP).
(mkdir-p (dirname grub-cfg-link)) (when target
(false-if-exception (delete-file grub-cfg-link)) (use-modules ((guix build union) #:select (symlink-relative))
(symlink-relative grub-cfg grub-cfg-link) (ice-9 popen)
;; Install GRUB, which refers to the grub.cfg, with support for (ice-9 rdelim))
;; encrypted partitions, (let* ((mount-point/target (string-append mount-point target "/"))
(setenv "GRUB_ENABLE_CRYPTODISK" "y") ;; When installing Guix, it is common to mount TARGET below
(invoke/quiet (string-append bootloader "/bin/grub-mknetdir") ;; MOUNT-POINT rather than the root directory.
(string-append "--net-directory=" net-dir) (bootloader-target (if (file-exists? mount-point/target)
(string-append "--subdir=" #$subdir)) mount-point/target
;; Prepare the bootloader symlink, which points to core.efi of GRUB. target))
(false-if-exception (delete-file boot-efi-link)) (store (string-append mount-point (%store-prefix)))
(symlink #$core-efi boot-efi-link)))))) (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. Add a 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")))))))))))
@ -784,7 +828,7 @@ (define grub-bootloader
(package grub) (package grub)
(installer install-grub) (installer install-grub)
(disk-image-installer install-grub-disk-image) (disk-image-installer install-grub-disk-image)
(configuration-file "/boot/grub/grub.cfg") (configuration-file grub-cfg)
(configuration-file-generator grub-configuration-file))) (configuration-file-generator grub-configuration-file)))
(define grub-minimal-bootloader (define grub-minimal-bootloader
@ -794,11 +838,12 @@ (define grub-minimal-bootloader
(define grub-efi-bootloader (define grub-efi-bootloader
(bootloader (bootloader
(inherit grub-bootloader) (name 'grub-efi)
(package grub-efi)
(installer install-grub-efi) (installer install-grub-efi)
(disk-image-installer #f) (disk-image-installer #f)
(name 'grub-efi) (configuration-file grub-cfg)
(package grub-efi))) (configuration-file-generator grub-configuration-file)))
(define grub-efi-removable-bootloader (define grub-efi-removable-bootloader
(bootloader (bootloader
@ -813,11 +858,22 @@ (define grub-efi32-bootloader
(name 'grub-efi32) (name 'grub-efi32)
(package grub-efi32))) (package grub-efi32)))
(define grub-efi-netboot-bootloader (define (make-grub-efi-netboot-bootloader name subdir)
(bootloader (bootloader
(inherit grub-efi-bootloader) (name name)
(name 'grub-efi-netboot-bootloader) (package (make-grub-efi-netboot (symbol->string name) subdir))
(installer (install-grub-efi-netboot "efi/Guix")))) (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 (define grub-mkrescue-bootloader
(bootloader (bootloader

View file

@ -16,6 +16,7 @@
;;; Copyright © 2021 Vincent Legoll <vincent.legoll@gmail.com> ;;; Copyright © 2021 Vincent Legoll <vincent.legoll@gmail.com>
;;; Copyright © 2021 Brice Waegeneire <brice@waegenei.re> ;;; Copyright © 2021 Brice Waegeneire <brice@waegenei.re>
;;; Copyright © 2022 Denis 'GNUtoo' Carikli <GNUtoo@cyberdimension.org> ;;; Copyright © 2022 Denis 'GNUtoo' Carikli <GNUtoo@cyberdimension.org>
;;; Copyright © 2021 Stefan <stefan-guix@vodafonemail.de>
;;; ;;;
;;; This file is part of GNU Guix. ;;; This file is part of GNU Guix.
;;; ;;;
@ -67,7 +68,9 @@ (define-module (gnu packages bootloaders)
#:use-module (gnu packages virtualization) #:use-module (gnu packages virtualization)
#:use-module (gnu packages xorg) #:use-module (gnu packages xorg)
#:use-module (guix build-system gnu) #:use-module (guix build-system gnu)
#:use-module (guix build-system trivial)
#:use-module (guix download) #:use-module (guix download)
#:use-module (guix gexp)
#:use-module (guix git-download) #:use-module (guix git-download)
#:use-module ((guix licenses) #:prefix license:) #:use-module ((guix licenses) #:prefix license:)
#:use-module (guix packages) #:use-module (guix packages)
@ -75,6 +78,7 @@ (define-module (gnu packages bootloaders)
#:use-module (srfi srfi-1) #:use-module (srfi srfi-1)
#:use-module (srfi srfi-26) #:use-module (srfi srfi-26)
#:use-module (ice-9 optargs) #:use-module (ice-9 optargs)
#:use-module (ice-9 match)
#:use-module (ice-9 regex)) #:use-module (ice-9 regex))
(define unifont (define unifont
@ -390,6 +394,92 @@ (define-public grub-hybrid
(scandir input-dir)) (scandir input-dir))
#t))))))))) #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")))
(list
#:modules '((guix build utils))
#:builder
#~(begin
(use-modules (guix build utils))
(let* ((bootloader #$(this-package-input "grub-efi"))
(net-dir #$output)
(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 pre-loaded 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 (list 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 (define-public syslinux
(let ((commit "bb41e935cc83c6242de24d2271e067d76af3585c")) (let ((commit "bb41e935cc83c6242de24d2271e067d76af3585c"))
(package (package