summary refs log tree commit diff
path: root/gnu/build
diff options
context:
space:
mode:
Diffstat (limited to 'gnu/build')
-rw-r--r--gnu/build/bootloader.scm56
-rw-r--r--gnu/build/file-systems.scm45
-rw-r--r--gnu/build/image.scm273
-rw-r--r--gnu/build/install.scm21
-rw-r--r--gnu/build/vm.scm175
5 files changed, 390 insertions, 180 deletions
diff --git a/gnu/build/bootloader.scm b/gnu/build/bootloader.scm
index 9570d6dd18..498022f6db 100644
--- a/gnu/build/bootloader.scm
+++ b/gnu/build/bootloader.scm
@@ -18,8 +18,12 @@
 ;;; along with GNU Guix.  If not, see <http://www.gnu.org/licenses/>.
 
 (define-module (gnu build bootloader)
+  #:use-module (guix build utils)
+  #:use-module (guix utils)
   #:use-module (ice-9 binary-ports)
-  #:export (write-file-on-device))
+  #:use-module (ice-9 format)
+  #:export (write-file-on-device
+            install-efi-loader))
 
 
 ;;;
@@ -36,3 +40,53 @@
             (seek output offset SEEK_SET)
             (put-bytevector output bv))
           #:binary #t)))))
+
+
+;;;
+;;; EFI bootloader.
+;;;
+
+(define (install-efi grub grub-config esp)
+  "Write a self-contained GRUB EFI loader to the mounted ESP using GRUB-CONFIG."
+  (let* ((system %host-type)
+         ;; Hard code the output location to a well-known path recognized by
+         ;; compliant firmware. See "3.5.1.1 Removable Media Boot Behaviour":
+         ;; http://www.uefi.org/sites/default/files/resources/UEFI%20Spec%202_6.pdf
+         (grub-mkstandalone (string-append grub "/bin/grub-mkstandalone"))
+         (efi-directory (string-append esp "/EFI/BOOT"))
+         ;; Map grub target names to boot file names.
+         (efi-targets (cond ((string-prefix? "x86_64" system)
+                             '("x86_64-efi" . "BOOTX64.EFI"))
+                            ((string-prefix? "i686" system)
+                             '("i386-efi" . "BOOTIA32.EFI"))
+                            ((string-prefix? "armhf" system)
+                             '("arm-efi" . "BOOTARM.EFI"))
+                            ((string-prefix? "aarch64" system)
+                             '("arm64-efi" . "BOOTAA64.EFI")))))
+    ;; grub-mkstandalone requires a TMPDIR to prepare the firmware image.
+    (setenv "TMPDIR" esp)
+
+    (mkdir-p efi-directory)
+    (invoke grub-mkstandalone "-O" (car efi-targets)
+            "-o" (string-append efi-directory "/"
+                                (cdr efi-targets))
+            ;; Graft the configuration file onto the image.
+            (string-append "boot/grub/grub.cfg=" grub-config))))
+
+(define (install-efi-loader grub-efi esp)
+  "Install in ESP directory the given GRUB-EFI bootloader.  Configure it to
+load the Grub bootloader located in the 'Guix_image' root partition."
+  (let ((grub-config "grub.cfg"))
+    (call-with-output-file grub-config
+      (lambda (port)
+        ;; Create a tiny configuration file telling the embedded grub where to
+        ;; load the real thing.  XXX This is quite fragile, and can prevent
+        ;; the image from booting when there's more than one volume with this
+        ;; label present.  Reproducible almost-UUIDs could reduce the risk
+        ;; (not eliminate it).
+        (format port
+                "insmod part_msdos~@
+               search --set=root --label Guix_image~@
+               configfile /boot/grub/grub.cfg~%")))
+    (install-efi grub-efi grub-config esp)
+    (delete-file grub-config)))
diff --git a/gnu/build/file-systems.scm b/gnu/build/file-systems.scm
index bbea4c766e..b920e8fc62 100644
--- a/gnu/build/file-systems.scm
+++ b/gnu/build/file-systems.scm
@@ -98,6 +98,47 @@ takes a bytevector and returns #t when it's a valid superblock."
 (define null-terminated-latin1->string
   (cut latin1->string <> zero?))
 
+(define (bytevector-utf16-length bv)
+  "Given a bytevector BV containing a NUL-terminated UTF16-encoded string,
+determine where the NUL terminator is and return its index.  If there's no
+NUL terminator, return the size of the bytevector."
+  (let ((length (bytevector-length bv)))
+    (let loop ((index 0))
+      (if (< index length)
+          (if (zero? (bytevector-u16-ref bv index 'little))
+              index
+              (loop (+ index 2)))
+          length))))
+
+(define* (bytevector->u16-list bv endianness #:optional (index 0))
+  (if (< index (bytevector-length bv))
+      (cons (bytevector-u16-ref bv index endianness)
+            (bytevector->u16-list bv endianness (+ index 2)))
+      '()))
+
+;; The initrd doesn't have iconv data, so do the conversion ourselves.
+(define (utf16->string bv endianness)
+  (list->string
+   (map integer->char
+        (reverse
+         (let loop ((remainder (bytevector->u16-list bv endianness))
+                    (result '()))
+             (match remainder
+              (() result)
+              ((a) (cons a result))
+              ((a b x ...)
+               (if (and (>= a #xD800) (< a #xDC00) ; high surrogate
+                        (>= b #xDC00) (< b #xE000)) ; low surrogate
+                   (loop x (cons (+ #x10000
+                                    (* #x400 (- a #xD800))
+                                    (- b #xDC00))
+                                 result))
+                   (loop (cons b x) (cons a result))))))))))
+
+(define (null-terminated-utf16->string bv endianness)
+  (utf16->string (sub-bytevector bv 0 (bytevector-utf16-length bv))
+                 endianness))
+
 
 ;;;
 ;;; Ext2 file systems.
@@ -377,7 +418,9 @@ if DEVICE does not contain an F2FS file system."
 (define (f2fs-superblock-volume-name sblock)
   "Return the volume name of SBLOCK as a string of at most 512 characters, or
 #f if SBLOCK has no volume name."
-  (utf16->string (sub-bytevector sblock (- (+ #x470 12) #x400) 512) %f2fs-endianness))
+  (null-terminated-utf16->string
+   (sub-bytevector sblock (- (+ #x470 12) #x400) 512)
+   %f2fs-endianness))
 
 (define (check-f2fs-file-system device)
   "Return the health of a F2FS file system on DEVICE."
diff --git a/gnu/build/image.scm b/gnu/build/image.scm
new file mode 100644
index 0000000000..fe8e11aa1b
--- /dev/null
+++ b/gnu/build/image.scm
@@ -0,0 +1,273 @@
+;;; GNU Guix --- Functional package management for GNU
+;;; Copyright © 2013, 2014, 2015, 2016, 2017, 2018, 2019, 2020 Ludovic Courtès <ludo@gnu.org>
+;;; Copyright © 2016 Christopher Allan Webber <cwebber@dustycloud.org>
+;;; Copyright © 2016, 2017 Leo Famulari <leo@famulari.name>
+;;; Copyright © 2017 Marius Bakke <mbakke@fastmail.com>
+;;; Copyright © 2020 Tobias Geerinckx-Rice <me@tobias.gr>
+;;; Copyright © 2020 Mathieu Othacehe <m.othacehe@gmail.com>
+;;;
+;;; This file is part of GNU Guix.
+;;;
+;;; GNU Guix is free software; you can redistribute it and/or modify it
+;;; under the terms of the GNU General Public License as published by
+;;; the Free Software Foundation; either version 3 of the License, or (at
+;;; your option) any later version.
+;;;
+;;; GNU Guix is distributed in the hope that it will be useful, but
+;;; WITHOUT ANY WARRANTY; without even the implied warranty of
+;;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+;;; GNU General Public License for more details.
+;;;
+;;; You should have received a copy of the GNU General Public License
+;;; along with GNU Guix.  If not, see <http://www.gnu.org/licenses/>.
+
+(define-module (gnu build image)
+  #:use-module (guix build store-copy)
+  #:use-module (guix build syscalls)
+  #:use-module (guix build utils)
+  #:use-module (guix store database)
+  #:use-module (gnu build bootloader)
+  #:use-module (gnu build install)
+  #:use-module (gnu build linux-boot)
+  #:use-module (gnu image)
+  #:use-module (gnu system uuid)
+  #:use-module (ice-9 ftw)
+  #:use-module (ice-9 match)
+  #:use-module (srfi srfi-19)
+  #:use-module (srfi srfi-34)
+  #:use-module (srfi srfi-35)
+  #:export (make-partition-image
+            genimage
+            initialize-efi-partition
+            initialize-root-partition
+
+            make-iso9660-image))
+
+(define (sexp->partition sexp)
+  "Take SEXP, a tuple as returned by 'partition->gexp', and turn it into a
+<partition> record."
+  (match sexp
+    ((size file-system label uuid)
+     (partition (size size)
+                (file-system file-system)
+                (label label)
+                (uuid uuid)))))
+
+(define (size-in-kib size)
+  "Convert SIZE expressed in bytes, to kilobytes and return it as a string."
+  (number->string
+   (inexact->exact (ceiling (/ size 1024)))))
+
+(define (estimate-partition-size root)
+  "Given the ROOT directory, evalute and return its size.  As this doesn't
+take the partition metadata size into account, take a 25% margin."
+  (* 1.25 (file-size root)))
+
+(define* (make-ext4-image partition target root
+                          #:key
+                          (owner-uid 0)
+                          (owner-gid 0))
+  "Handle the creation of EXT4 partition images. See 'make-partition-image'."
+  (let ((size (partition-size partition))
+        (label (partition-label partition))
+        (uuid (partition-uuid partition))
+        (options "lazy_itable_init=1,lazy_journal_init=1"))
+    (invoke "mke2fs" "-t" "ext4" "-d" root
+            "-L" label "-U" (uuid->string uuid)
+            "-E" (format #f "root_owner=~a:~a,~a"
+                         owner-uid owner-gid options)
+            target
+            (format #f "~ak"
+                    (size-in-kib
+                     (if (eq? size 'guess)
+                         (estimate-partition-size root)
+                         size))))))
+
+(define* (make-vfat-image partition target root)
+  "Handle the creation of VFAT partition images.  See 'make-partition-image'."
+  (let ((size (partition-size partition))
+        (label (partition-label partition)))
+    (invoke "mkdosfs" "-n" label "-C" target "-F" "16" "-S" "1024"
+            (size-in-kib
+             (if (eq? size 'guess)
+                 (estimate-partition-size root)
+                 size)))
+    (for-each (lambda (file)
+                (unless (member file '("." ".."))
+                  (invoke "mcopy" "-bsp" "-i" target
+                          (string-append root "/" file)
+                          (string-append "::" file))))
+              (scandir root))))
+
+(define* (make-partition-image partition-sexp target root)
+  "Create and return the image of PARTITION-SEXP as TARGET.  Use the given
+ROOT directory to populate the image."
+  (let* ((partition (sexp->partition partition-sexp))
+         (type (partition-file-system partition)))
+    (cond
+     ((string=? type "ext4")
+      (make-ext4-image partition target root))
+     ((string=? type "vfat")
+      (make-vfat-image partition target root))
+     (else
+      (format (current-error-port)
+              "Unsupported partition type~%.")))))
+
+(define* (genimage config target)
+  "Use genimage to generate in TARGET directory, the image described in the
+given CONFIG file."
+  ;; genimage needs a 'root' directory.
+  (mkdir "root")
+  (invoke "genimage" "--config" config
+          "--outputpath" target))
+
+(define* (register-closure prefix closure
+                           #:key
+                           (deduplicate? #t) (reset-timestamps? #t)
+                           (schema (sql-schema)))
+  "Register CLOSURE in PREFIX, where PREFIX is the directory name of the
+target store and CLOSURE is the name of a file containing a reference graph as
+produced by #:references-graphs..  As a side effect, if RESET-TIMESTAMPS? is
+true, reset timestamps on store files and, if DEDUPLICATE? is true,
+deduplicates files common to CLOSURE and the rest of PREFIX."
+  (let ((items (call-with-input-file closure read-reference-graph)))
+    (register-items items
+                    #:prefix prefix
+                    #:deduplicate? deduplicate?
+                    #:reset-timestamps? reset-timestamps?
+                    #:registration-time %epoch
+                    #:schema schema)))
+
+(define* (initialize-efi-partition root
+                                   #:key
+                                   bootloader-package
+                                   #:allow-other-keys)
+  "Install in ROOT directory, an EFI loader using BOOTLOADER-PACKAGE."
+  (install-efi-loader bootloader-package root))
+
+(define* (initialize-root-partition root
+                                    #:key
+                                    bootcfg
+                                    bootcfg-location
+                                    (deduplicate? #t)
+                                    references-graphs
+                                    (register-closures? #t)
+                                    system-directory
+                                    #:allow-other-keys)
+  "Initialize the given ROOT directory. Use BOOTCFG and BOOTCFG-LOCATION to
+install the bootloader configuration.
+
+If REGISTER-CLOSURES? is true, register REFERENCES-GRAPHS in the store.  If
+DEDUPLICATE? is true, then also deduplicate files common to CLOSURES and the
+rest of the store when registering the closures. SYSTEM-DIRECTORY is the name
+of the directory of the 'system' derivation."
+  (populate-root-file-system system-directory root)
+  (populate-store references-graphs root)
+
+  (when register-closures?
+    (for-each (lambda (closure)
+                (register-closure root
+                                  closure
+                                  #:reset-timestamps? #t
+                                  #:deduplicate? deduplicate?))
+              references-graphs))
+
+  (when bootcfg
+    (install-boot-config bootcfg bootcfg-location root)))
+
+(define* (make-iso9660-image xorriso grub-mkrescue-environment
+                             grub bootcfg system-directory root target
+                             #:key (volume-id "Guix_image") (volume-uuid #f)
+                             register-closures? (references-graphs '())
+                             (compression? #t))
+  "Given a GRUB package, creates an iso image as TARGET, using BOOTCFG as
+GRUB configuration and OS-DRV as the stuff in it."
+  (define grub-mkrescue
+    (string-append grub "/bin/grub-mkrescue"))
+
+  (define grub-mkrescue-sed.sh
+    (string-append (getcwd) "/" "grub-mkrescue-sed.sh"))
+
+  ;; Use a modified version of grub-mkrescue-sed.sh, see below.
+  (copy-file (string-append xorriso
+                            "/bin/grub-mkrescue-sed.sh")
+             grub-mkrescue-sed.sh)
+
+  ;; Force grub-mkrescue-sed.sh to use the build directory instead of /tmp
+  ;; that is read-only inside the build container.
+  (substitute* grub-mkrescue-sed.sh
+    (("/tmp/") (string-append (getcwd) "/"))
+    (("MKRESCUE_SED_XORRISO_ARGS \\$x")
+     (format #f "MKRESCUE_SED_XORRISO_ARGS $(echo $x | sed \"s|/tmp|~a|\")"
+             (getcwd))))
+
+  ;; 'grub-mkrescue' calls out to mtools programs to create 'efi.img', a FAT
+  ;; file system image, and mtools honors SOURCE_DATE_EPOCH for the mtime of
+  ;; those files.  The epoch for FAT is Jan. 1st 1980, not 1970, so choose
+  ;; that.
+  (setenv "SOURCE_DATE_EPOCH"
+          (number->string
+           (time-second
+            (date->time-utc (make-date 0 0 0 0 1 1 1980 0)))))
+
+  ;; Our patched 'grub-mkrescue' honors this environment variable and passes
+  ;; it to 'mformat', which makes it the serial number of 'efi.img'.  This
+  ;; allows for deterministic builds.
+  (setenv "GRUB_FAT_SERIAL_NUMBER"
+          (number->string (if volume-uuid
+
+                              ;; On 32-bit systems the 2nd argument must be
+                              ;; lower than 2^32.
+                              (string-hash (iso9660-uuid->string volume-uuid)
+                                           (- (expt 2 32) 1))
+
+                              #x77777777)
+                          16))
+
+  (setenv "MKRESCUE_SED_MODE" "original")
+  (setenv "MKRESCUE_SED_XORRISO" (string-append xorriso "/bin/xorriso"))
+  (setenv "MKRESCUE_SED_IN_EFI_NO_PT" "yes")
+
+  (for-each (match-lambda
+              ((name . value) (setenv name value)))
+            grub-mkrescue-environment)
+
+  (apply invoke grub-mkrescue
+         (string-append "--xorriso=" grub-mkrescue-sed.sh)
+         "-o" target
+         (string-append "boot/grub/grub.cfg=" bootcfg)
+         root
+         "--"
+         ;; Set all timestamps to 1.
+         "-volume_date" "all_file_dates" "=1"
+
+         `(,@(if compression?
+                 '(;; ‘zisofs’ compression reduces the total image size by
+                   ;; ~60%.
+                   "-zisofs" "level=9:block_size=128k" ; highest compression
+                   ;; It's transparent to our Linux-Libre kernel but not to
+                   ;; GRUB.  Don't compress the kernel, initrd, and other
+                   ;; files read by grub.cfg, as well as common
+                   ;; already-compressed file names.
+                   "-find" "/" "-type" "f"
+                   ;; XXX Even after "--" above, and despite documentation
+                   ;; claiming otherwise, "-or" is stolen by grub-mkrescue
+                   ;; which then chokes on it (as ‘-o …’) and dies.  Don't use
+                   ;; "-or".
+                   "-not" "-wholename" "/boot/*"
+                   "-not" "-wholename" "/System/*"
+                   "-not" "-name" "unicode.pf2"
+                   "-not" "-name" "bzImage"
+                   "-not" "-name" "*.gz"   ; initrd & all man pages
+                   "-not" "-name" "*.png"  ; includes grub-image.png
+                   "-exec" "set_filter" "--zisofs"
+                   "--")
+                 '())
+           "-volid" ,(string-upcase volume-id)
+           ,@(if volume-uuid
+             `("-volume_date" "uuid"
+               ,(string-filter (lambda (value)
+                                 (not (char=? #\- value)))
+                               (iso9660-uuid->string
+                                volume-uuid)))
+             '()))))
diff --git a/gnu/build/install.scm b/gnu/build/install.scm
index d46b588f53..87aa5d68da 100644
--- a/gnu/build/install.scm
+++ b/gnu/build/install.scm
@@ -25,7 +25,6 @@
   #:export (install-boot-config
             evaluate-populate-directive
             populate-root-file-system
-            register-closure
             install-database-and-gc-roots
             populate-single-profile-directory))
 
@@ -51,9 +50,14 @@ that the fonts, background images, etc. referred to by BOOTCFG are not GC'd."
     (copy-file bootcfg pivot)
     (rename-file pivot target)))
 
-(define (evaluate-populate-directive directive target)
+(define* (evaluate-populate-directive directive target
+                                      #:key
+                                      (default-gid 0)
+                                      (default-uid 0))
   "Evaluate DIRECTIVE, an sexp describing a file or directory to create under
-directory TARGET."
+directory TARGET.  DEFAULT-UID and DEFAULT-GID are the default UID and GID in
+the context of the caller.  If the directive matches those defaults then,
+'chown' won't be run."
   (let loop ((directive directive))
     (catch 'system-error
       (lambda ()
@@ -63,7 +67,12 @@ directory TARGET."
           (('directory name uid gid)
            (let ((dir (string-append target name)))
              (mkdir-p dir)
-             (chown dir uid gid)))
+             ;; If called from a context without "root" permissions, "chown"
+             ;; to root will fail.  In that case, do not try to run "chown"
+             ;; and assume that the file will be chowned elsewhere (when
+             ;; interned in the store for instance).
+             (or (and (= uid default-uid) (= gid default-gid))
+                 (chown dir uid gid))))
           (('directory name uid gid mode)
            (loop `(directory ,name ,uid ,gid))
            (chmod (string-append target name) mode))
@@ -98,9 +107,7 @@ directory TARGET."
 (define (directives store)
   "Return a list of directives to populate the root file system that will host
 STORE."
-  `(;; Note: the store's GID is fixed precisely so we can set it here rather
-    ;; than at activation time.
-    (directory ,store 0 30000 #o1775)
+  `((directory ,store 0 0 #o1775)
 
     (directory "/etc")
     (directory "/var/log")                          ; for shepherd
diff --git a/gnu/build/vm.scm b/gnu/build/vm.scm
index d2bf7418fd..433b5a7e8d 100644
--- a/gnu/build/vm.scm
+++ b/gnu/build/vm.scm
@@ -27,6 +27,7 @@
   #:use-module (guix build store-copy)
   #:use-module (guix build syscalls)
   #:use-module (guix store database)
+  #:use-module (gnu build bootloader)
   #:use-module (gnu build linux-boot)
   #:use-module (gnu build install)
   #:use-module (gnu system uuid)
@@ -57,8 +58,7 @@
             estimated-partition-size
             root-partition-initializer
             initialize-partition-table
-            initialize-hard-disk
-            make-iso9660-image))
+            initialize-hard-disk))
 
 ;;; Commentary:
 ;;;
@@ -439,159 +439,6 @@ system that is passed to 'populate-root-file-system'."
     (mkdir-p directory)
     (symlink bootcfg (string-append directory "/bootcfg"))))
 
-(define (install-efi grub esp config-file)
-  "Write a self-contained GRUB EFI loader to the mounted ESP using CONFIG-FILE."
-  (let* ((system %host-type)
-         ;; Hard code the output location to a well-known path recognized by
-         ;; compliant firmware. See "3.5.1.1 Removable Media Boot Behaviour":
-         ;; http://www.uefi.org/sites/default/files/resources/UEFI%20Spec%202_6.pdf
-         (grub-mkstandalone (string-append grub "/bin/grub-mkstandalone"))
-         (efi-directory (string-append esp "/EFI/BOOT"))
-         ;; Map grub target names to boot file names.
-         (efi-targets (cond ((string-prefix? "x86_64" system)
-                             '("x86_64-efi" . "BOOTX64.EFI"))
-                            ((string-prefix? "i686" system)
-                             '("i386-efi" . "BOOTIA32.EFI"))
-                            ((string-prefix? "armhf" system)
-                             '("arm-efi" . "BOOTARM.EFI"))
-                            ((string-prefix? "aarch64" system)
-                             '("arm64-efi" . "BOOTAA64.EFI")))))
-    ;; grub-mkstandalone requires a TMPDIR to prepare the firmware image.
-    (setenv "TMPDIR" esp)
-
-    (mkdir-p efi-directory)
-    (invoke grub-mkstandalone "-O" (car efi-targets)
-            "-o" (string-append efi-directory "/"
-                                (cdr efi-targets))
-            ;; Graft the configuration file onto the image.
-            (string-append "boot/grub/grub.cfg=" config-file))))
-
-(define* (make-iso9660-image xorriso grub-mkrescue-environment
-                             grub config-file os-drv target
-                             #:key (volume-id "Guix_image") (volume-uuid #f)
-                             register-closures? (closures '()))
-  "Given a GRUB package, creates an iso image as TARGET, using CONFIG-FILE as
-GRUB configuration and OS-DRV as the stuff in it."
-  (define grub-mkrescue
-    (string-append grub "/bin/grub-mkrescue"))
-
-  (define grub-mkrescue-sed.sh
-    (string-append xorriso "/bin/grub-mkrescue-sed.sh"))
-
-  (define target-store
-    (string-append "/tmp/root" (%store-directory)))
-
-  (define items
-    ;; The store items to add to the image.
-    (delete-duplicates
-     (append-map (lambda (closure)
-                   (map store-info-item
-                        (call-with-input-file (string-append "/xchg/" closure)
-                          read-reference-graph)))
-                 closures)))
-
-  (populate-root-file-system os-drv "/tmp/root")
-  (mount (%store-directory) target-store "" MS_BIND)
-
-  (when register-closures?
-    (display "registering closures...\n")
-    (for-each (lambda (closure)
-                (register-closure
-                 "/tmp/root"
-                 (string-append "/xchg/" closure)
-
-                 ;; TARGET-STORE is a read-only bind-mount so we shouldn't try
-                 ;; to modify it.
-                 #:deduplicate? #f
-                 #:reset-timestamps? #f))
-              closures)
-    (register-bootcfg-root "/tmp/root" config-file))
-
-  ;; 'grub-mkrescue' calls out to mtools programs to create 'efi.img', a FAT
-  ;; file system image, and mtools honors SOURCE_DATE_EPOCH for the mtime of
-  ;; those files.  The epoch for FAT is Jan. 1st 1980, not 1970, so choose
-  ;; that.
-  (setenv "SOURCE_DATE_EPOCH"
-          (number->string
-           (time-second
-            (date->time-utc (make-date 0 0 0 0 1 1 1980 0)))))
-
-  ;; Our patched 'grub-mkrescue' honors this environment variable and passes
-  ;; it to 'mformat', which makes it the serial number of 'efi.img'.  This
-  ;; allows for deterministic builds.
-  (setenv "GRUB_FAT_SERIAL_NUMBER"
-          (number->string (if volume-uuid
-
-                              ;; On 32-bit systems the 2nd argument must be
-                              ;; lower than 2^32.
-                              (string-hash (iso9660-uuid->string volume-uuid)
-                                           (- (expt 2 32) 1))
-
-                              #x77777777)
-                          16))
-
-  (setenv "MKRESCUE_SED_MODE" "original")
-  (setenv "MKRESCUE_SED_XORRISO" (string-append xorriso
-                                                "/bin/xorriso"))
-  (setenv "MKRESCUE_SED_IN_EFI_NO_PT" "yes")
-  (for-each (match-lambda
-             ((name . value) (setenv name value)))
-            grub-mkrescue-environment)
-
-  (let ((pipe
-         (apply open-pipe* OPEN_WRITE
-                grub-mkrescue
-                (string-append "--xorriso=" grub-mkrescue-sed.sh)
-                "-o" target
-                (string-append "boot/grub/grub.cfg=" config-file)
-                "etc=/tmp/root/etc"
-                "var=/tmp/root/var"
-                "run=/tmp/root/run"
-                ;; /mnt is used as part of the installation
-                ;; process, as the mount point for the target
-                ;; file system, so create it.
-                "mnt=/tmp/root/mnt"
-                "-path-list" "-"
-                "--"
-
-                ;; Set all timestamps to 1.
-                "-volume_date" "all_file_dates" "=1"
-
-                ;; ‘zisofs’ compression reduces the total image size by ~60%.
-                "-zisofs" "level=9:block_size=128k" ; highest compression
-                ;; It's transparent to our Linux-Libre kernel but not to GRUB.
-                ;; Don't compress the kernel, initrd, and other files read by
-                ;; grub.cfg, as well as common already-compressed file names.
-                "-find" "/" "-type" "f"
-                ;; XXX Even after "--" above, and despite documentation claiming
-                ;; otherwise, "-or" is stolen by grub-mkrescue which then chokes
-                ;; on it (as ‘-o …’) and dies.  Don't use "-or".
-                "-not" "-wholename" "/boot/*"
-                "-not" "-wholename" "/System/*"
-                "-not" "-name" "unicode.pf2"
-                "-not" "-name" "bzImage"
-                "-not" "-name" "*.gz"   ; initrd & all man pages
-                "-not" "-name" "*.png"  ; includes grub-image.png
-                "-exec" "set_filter" "--zisofs"
-                "--"
-
-                "-volid" (string-upcase volume-id)
-                (if volume-uuid
-                    `("-volume_date" "uuid"
-                      ,(string-filter (lambda (value)
-                                        (not (char=? #\- value)))
-                                      (iso9660-uuid->string
-                                       volume-uuid)))
-                    `()))))
-    ;; Pass lines like 'gnu/store/…-x=/gnu/store/…-x' corresponding to the
-    ;; '-path-list -' option.
-    (for-each (lambda (item)
-                (format pipe "~a=~a~%"
-                        (string-drop item 1) item))
-              items)
-    (unless (zero? (close-pipe pipe))
-      (error "oh, my! grub-mkrescue failed" grub-mkrescue))))
-
 (define* (initialize-hard-disk device
                                #:key
                                bootloader-package
@@ -633,30 +480,16 @@ passing it a directory name where it is mounted."
 
     (when esp
       ;; Mount the ESP somewhere and install GRUB UEFI image.
-      (let ((mount-point (string-append target "/boot/efi"))
-            (grub-config (string-append target "/tmp/grub-standalone.cfg")))
+      (let ((mount-point (string-append target "/boot/efi")))
         (display "mounting EFI system partition...\n")
         (mkdir-p mount-point)
         (mount (partition-device esp) mount-point
                (partition-file-system esp))
 
-        ;; Create a tiny configuration file telling the embedded grub
-        ;; where to load the real thing.
-        ;; XXX This is quite fragile, and can prevent the image from booting
-        ;; when there's more than one volume with this label present.
-        ;; Reproducible almost-UUIDs could reduce the risk (not eliminate it).
-        (call-with-output-file grub-config
-          (lambda (port)
-            (format port
-                    "insmod part_msdos~@
-                    search --set=root --label Guix_image~@
-                    configfile /boot/grub/grub.cfg~%")))
-
         (display "creating EFI firmware image...")
-        (install-efi grub-efi mount-point grub-config)
+        (install-efi-loader grub-efi mount-point)
         (display "done.\n")
 
-        (delete-file grub-config)
         (umount mount-point)))
 
     ;; Register BOOTCFG as a GC root.