Subject: [bug#55343] Add support for 32bit UEFI

From: Timothy Sample <[email-masked]>
Date: Fri, 17 Jun 2022 23:18:35 -0600

Hi all,

Ludovic Courtès <[email-masked]> writes:

> Denis 'GNUtoo' Carikli <[email-masked]> skribis:
>
>> As I understood that serie was ready to be merged or is there something
>> I still need to do on my side?
>
> Sorry for the delay; Mathieu, this is more your area of expertise: WDYT?

I’m not Mathieu, but I wanted to chime in anyway.  :)

I have an old Mac Mini with the same setup: it’s a 64bit system with a
32bit UEFI.  I’ve been using the same GRUB package and bootloader
modifications from this patch for years and it works great.  I also just
tested the image generation code.  I built an image, wrote it to a USB
storage device, and booted the machine from it.  That’s pretty handy!

I’ve attached a slightly modified version of the second patch.  It
removes some duplication.  Essentially, I added a keyword to the
‘install-efi’ procedure so that callers can override the GRUB image type
and output file.  There’s lots of duplication in the patch, but that
seems to be largely due to the structure of the existing code, so I
wouldn’t worry about it.

So, barring any comments from Mathieu, these patches LGTM.

-- Tim

>From 4be147b1c8fdef014ba06fd92e701753148d58c6 Mon Sep 17 00:00:00 2001
From: Denis 'GNUtoo' Carikli <[email-masked]>
Date: Tue, 10 May 2022 01:24:51 +0200
Subject: [PATCH] image: Support 32bit UEFI on 64bit systems.

* gnu/bootloader/grub.scm (grub-efi32-bootloader): New variable.
(install-grub-efi32): New variable.
* gnu/build/bootloader.scm (install-efi): Add a 'targets' keyword
argument.
(install-efi-loader): Likewise.
* gnu/build/image.scm (initialize-efi32-partition): New procedure.
* gnu/packages/bootloaders.scm (grub-efi32): New variable.
* gnu/system/image.scm (esp32-partition): New variable
(efi32-disk-image): New variable.
(efi32-raw-image-type): New variable.
(system-disk-image)[partition-image]: Set '#:grub-efi32' when
calling the partition initializer.
---
 gnu/bootloader/grub.scm      | 32 +++++++++++++++++++++++++++++++
 gnu/build/bootloader.scm     | 37 +++++++++++++++++++++++-------------
 gnu/build/image.scm          | 14 ++++++++++++++
 gnu/packages/bootloaders.scm | 13 +++++++++++++
 gnu/system/image.scm         | 19 ++++++++++++++++++
 5 files changed, 102 insertions(+), 13 deletions(-)

diff --git a/gnu/bootloader/grub.scm b/gnu/bootloader/grub.scm
index 65d7171432..4f18c9b518 100644
--- a/gnu/bootloader/grub.scm
+++ b/gnu/bootloader/grub.scm
@@ -8,6 +8,7 @@
 ;;; Copyright © 2020 Maxim Cournoyer <[email-masked]>
 ;;; Copyright © 2020 Stefan <[email-masked]>
 ;;; Copyright © 2022 Karl Hallsby <[email-masked]>
+;;; Copyright © 2022 Denis 'GNUtoo' Carikli <[email-masked]>
 ;;;
 ;;; This file is part of GNU Guix.
 ;;;
@@ -57,6 +58,7 @@ (define-module (gnu bootloader grub)
             grub-bootloader
             grub-efi-bootloader
             grub-efi-removable-bootloader
+            grub-efi32-bootloader
             grub-efi-netboot-bootloader
             grub-mkrescue-bootloader
             grub-minimal-bootloader
@@ -636,6 +638,29 @@ (define install-grub-efi-removable
                         "--bootloader-id=Guix"
                         "--efi-directory" target-esp)))))
 
+(define install-grub-efi32
+  #~(lambda (bootloader efi-dir mount-point)
+      ;; There is nothing useful to do when called in the context of a disk
+      ;; image generation.
+      (when efi-dir
+        ;; Install GRUB onto the EFI partition mounted at EFI-DIR, for the
+        ;; system whose root is mounted at MOUNT-POINT.
+        (let ((grub-install (string-append bootloader "/sbin/grub-install"))
+              (install-dir (string-append mount-point "/boot"))
+              ;; When installing Guix, it's common to mount EFI-DIR below
+              ;; MOUNT-POINT rather than /boot/efi on the live image.
+              (target-esp (if (file-exists? (string-append mount-point efi-dir))
+                              (string-append mount-point efi-dir)
+                              efi-dir)))
+          ;; Tell 'grub-install' that there might be a LUKS-encrypted /boot or
+          ;; root partition.
+          (setenv "GRUB_ENABLE_CRYPTODISK" "y")
+          (invoke/quiet grub-install "--boot-directory" install-dir
+                        "--bootloader-id=Guix"
+			(cond ((target-x86?) "--target=i386-efi")
+                              ((target-arm?) "--target=arm-efi"))
+                        "--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."
@@ -768,6 +793,13 @@ (define grub-efi-removable-bootloader
    (name 'grub-efi-removable-bootloader)
    (installer install-grub-efi-removable)))
 
+(define grub-efi32-bootloader
+  (bootloader
+   (inherit grub-efi-bootloader)
+   (installer install-grub-efi32)
+   (name 'grub-efi32)
+   (package grub-efi32)))
+
 (define grub-efi-netboot-bootloader
   (bootloader
    (inherit grub-efi-bootloader)
diff --git a/gnu/build/bootloader.scm b/gnu/build/bootloader.scm
index 9a89fe55cb..af6063a884 100644
--- a/gnu/build/bootloader.scm
+++ b/gnu/build/bootloader.scm
@@ -1,6 +1,8 @@
 ;;; GNU Guix --- Functional package management for GNU
 ;;; Copyright © 2017 Mathieu Othacehe <[email-masked]>
 ;;; Copyright © 2019 Ludovic Courtès <[email-masked]>
+;;; Copyright © 2022 Denis 'GNUtoo' Carikli <[email-masked]>
+;;; Copyright © 2022 Timothy Sample <[email-masked]>
 ;;;
 ;;; This file is part of GNU Guix.
 ;;;
@@ -54,8 +56,12 @@ (define (write-file-on-device file size device offset)
 ;;; EFI bootloader.
 ;;;
 
-(define (install-efi grub grub-config esp)
-  "Write a self-contained GRUB EFI loader to the mounted ESP using GRUB-CONFIG."
+(define* (install-efi grub grub-config esp #:key targets)
+  "Write a self-contained GRUB EFI loader to the mounted ESP using
+GRUB-CONFIG.
+
+If TARGETS is set, use its car as the GRUB image format and its cdr as
+the output filename.  Otherwise, use defaults for the host platform."
   (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":
@@ -63,14 +69,15 @@ (define (install-efi grub grub-config esp)
          (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")))))
+         (efi-targets (or 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)
 
@@ -81,9 +88,12 @@ (define (install-efi grub grub-config esp)
             ;; Graft the configuration file onto the image.
             (string-append "boot/grub/grub.cfg=" grub-config))))
 
-(define (install-efi-loader grub-efi esp)
+(define* (install-efi-loader grub-efi esp #:key targets)
   "Install in ESP directory the given GRUB-EFI bootloader.  Configure it to
-load the Grub bootloader located in the 'Guix_image' root partition."
+load the Grub bootloader located in the 'Guix_image' root partition.
+
+If TARGETS is set, use its car as the GRUB image format and its cdr as
+the output filename.  Otherwise, use defaults for the host platform."
   (let ((grub-config "grub.cfg"))
     (call-with-output-file grub-config
       (lambda (port)
@@ -97,5 +107,6 @@ (define (install-efi-loader grub-efi esp)
                insmod part_gpt~@
                search --set=root --label Guix_image~@
                configfile /boot/grub/grub.cfg~%")))
-    (install-efi grub-efi grub-config esp)
+    (install-efi grub-efi grub-config esp #:targets targets)
     (delete-file grub-config)))
+
diff --git a/gnu/build/image.scm b/gnu/build/image.scm
index ddfd34c111..321be8e4b1 100644
--- a/gnu/build/image.scm
+++ b/gnu/build/image.scm
@@ -6,6 +6,7 @@
 ;;; Copyright © 2020, 2022 Tobias Geerinckx-Rice <[email-masked]>
 ;;; Copyright © 2020 Mathieu Othacehe <[email-masked]>
 ;;; Copyright © 2022 Pavel Shlyak <[email-masked]>
+;;; Copyright © 2022 Denis 'GNUtoo' Carikli <[email-masked]>
 ;;;
 ;;; This file is part of GNU Guix.
 ;;;
@@ -27,6 +28,7 @@ (define-module (gnu build image)
   #:use-module (guix build syscalls)
   #:use-module (guix build utils)
   #:use-module (guix store database)
+  #:use-module (guix utils)
   #:use-module (gnu build bootloader)
   #:use-module (gnu build install)
   #:use-module (gnu build linux-boot)
@@ -41,6 +43,7 @@ (define-module (gnu build image)
             convert-disk-image
             genimage
             initialize-efi-partition
+            initialize-efi32-partition
             initialize-root-partition
 
             make-iso9660-image))
@@ -169,6 +172,17 @@ (define* (initialize-efi-partition root
   "Install in ROOT directory, an EFI loader using GRUB-EFI."
   (install-efi-loader grub-efi root))
 
+(define* (initialize-efi32-partition root
+                                     #:key
+                                     grub-efi32
+                                     #:allow-other-keys)
+  "Install in ROOT directory, an EFI 32bit loader using GRUB-EFI32."
+  (install-efi-loader grub-efi32 root
+                      #:targets (cond ((target-x86?)
+                                       '("i386-efi" . "BOOTIA32.EFI"))
+                                      ((target-arm?)
+                                       '("arm-efi" . "BOOTARM.EFI")))))
+
 (define* (initialize-root-partition root
                                     #:key
                                     bootcfg
diff --git a/gnu/packages/bootloaders.scm b/gnu/packages/bootloaders.scm
index 91d259475a..71a10f54d5 100644
--- a/gnu/packages/bootloaders.scm
+++ b/gnu/packages/bootloaders.scm
@@ -15,6 +15,7 @@
 ;;; Copyright © 2020, 2021 Pierre Langlois <[email-masked]>
 ;;; Copyright © 2021 Vincent Legoll <[email-masked]>
 ;;; Copyright © 2021 Brice Waegeneire <[email-masked]>
+;;; Copyright © 2022 Denis 'GNUtoo' Carikli <[email-masked]>
 ;;;
 ;;; This file is part of GNU Guix.
 ;;;
@@ -332,6 +333,18 @@ (define-public grub-efi
                                        "/bin/mcopy\"")))
                      #t))))))))))
 
+(define-public grub-efi32
+  (package
+    (inherit grub-efi)
+    (name "grub-efi32")
+    (synopsis "GRand Unified Boot loader (UEFI 32bit version)")
+    (arguments
+     `(,@(substitute-keyword-arguments (package-arguments grub-efi)
+           ((#:configure-flags flags
+             ''()) `(cons* ,(cond ((target-x86?) "--target=i386")
+                                  ((target-arm?) "--target=arm"))
+                           ,flags)))))))
+
 ;; Because grub searches hardcoded paths it's easiest to just build grub
 ;; again to make it find both grub-pc and grub-efi.  There is a command
 ;; line argument which allows you to specify ONE platform - but
diff --git a/gnu/system/image.scm b/gnu/system/image.scm
index f02f6e0b8c..5972a944d7 100644
--- a/gnu/system/image.scm
+++ b/gnu/system/image.scm
@@ -2,6 +2,7 @@
 ;;; Copyright © 2020, 2021 Mathieu Othacehe <[email-masked]>
 ;;; Copyright © 2020 Jan (janneke) Nieuwenhuizen <[email-masked]>
 ;;; Copyright © 2022 Pavel Shlyak <[email-masked]>
+;;; Copyright © 2022 Denis 'GNUtoo' Carikli <[email-masked]>
 ;;;
 ;;; This file is part of GNU Guix.
 ;;;
@@ -66,6 +67,7 @@ (define-module (gnu system image)
             root-label
 
             esp-partition
+            esp32-partition
             root-partition
 
             efi-disk-image
@@ -75,6 +77,7 @@ (define-module (gnu system image)
 
             image-with-os
             efi-raw-image-type
+            efi32-raw-image-type
             qcow2-image-type
             iso-image-type
             uncompressed-iso-image-type
@@ -110,6 +113,11 @@ (define esp-partition
    (flags '(esp))
    (initializer (gexp initialize-efi-partition))))
 
+(define esp32-partition
+  (partition
+   (inherit esp-partition)
+   (initializer (gexp initialize-efi32-partition))))
+
 (define root-partition
   (partition
    (size 'guess)
@@ -123,6 +131,11 @@ (define efi-disk-image
    (format 'disk-image)
    (partitions (list esp-partition root-partition))))
 
+(define efi32-disk-image
+  (image
+   (format 'disk-image)
+   (partitions (list esp32-partition root-partition))))
+
 (define iso9660-image
   (image
    (format 'iso9660)
@@ -164,6 +177,11 @@ (define efi-raw-image-type
    (name 'efi-raw)
    (constructor (cut image-with-os efi-disk-image <>))))
 
+(define efi32-raw-image-type
+  (image-type
+   (name 'efi32-raw)
+   (constructor (cut image-with-os efi32-disk-image <>))))
+
 (define qcow2-image-type
   (image-type
    (name 'qcow2)
@@ -376,6 +394,7 @@ (define* (system-disk-image image
                                                 #$(image-shared-store? image))
                               #:system-directory #$os
                               #:grub-efi #+grub-efi
+                              #:grub-efi32 #+grub-efi32
                               #:bootloader-package
                               #+(bootloader-package bootloader)
                               #:bootloader-installer
-- 
2.36.1