diff mbox series

[bug#55343,2/2] image: Add new efi32-raw format for 32bit UEFI on 64bit systems

Message ID 20220509232451.30605-2-GNUtoo@cyberdimension.org
State New
Headers show
Series [bug#55343,1/2] utils: Define 'target-x86?' predicate. | expand

Checks

Context Check Description
cbaines/comparison success View comparision
cbaines/git branch success View Git branch
cbaines/applying patch success View Laminar job
cbaines/issue success View issue

Commit Message

Denis 'GNUtoo' Carikli May 9, 2022, 11:24 p.m. UTC
* gnu/bootloader/grub.scm (grub-efi32-bootloader): New variable.
  (install-grub-efi32): New variable
* gnu/build/bootloader.scm (install-efi32-loader): New variable.
  (install-efi32-loader): New variable.
* gnu/build/image.scm (initialize-efi32-partition): New variable
* 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.
  (partition-image): Add partition-image keyword
---
 gnu/bootloader/grub.scm      | 32 +++++++++++++++++++++++++
 gnu/build/bootloader.scm     | 46 +++++++++++++++++++++++++++++++++++-
 gnu/build/image.scm          |  9 +++++++
 gnu/packages/bootloaders.scm | 13 ++++++++++
 gnu/system/image.scm         | 19 +++++++++++++++
 5 files changed, 118 insertions(+), 1 deletion(-)

Comments

Maxime Devos May 10, 2022, 9:32 a.m. UTC | #1
Denis 'GNUtoo' Carikli schreef op di 10-05-2022 om 01:24 [+0200]:
> +           ((#:configure-flags flags
> +             ''()) `(cons* ,(cond ((target-x86?) "--target=i386")
> +                                  ((target-arm?) "--target=arm"))
> +                           ,flags)))))))

Isn't this just grub but compiled for the 32-bit variant of the system?
If so, can we reuse Guix built-in cross-compilation and native
compilation mechanisms here?  Maybe

  (list #:target "i386")

or

  (list #:target "i686-linux-gnu") ; if i386 is invalid

or

  (list #:system "i686-linux)

or

  (list #:system "i586-gnu")


Or do you mean to use 'grub' as a cross-compiler here (instead of
something that is being cross-compiled), like 'gcc'?

Greetings,
Maxime
Denis 'GNUtoo' Carikli May 10, 2022, 9:39 p.m. UTC | #2
On Tue, 10 May 2022 11:32:52 +0200
Maxime Devos <maximedevos@telenet.be> wrote:

> Denis 'GNUtoo' Carikli schreef op di 10-05-2022 om 01:24 [+0200]:
> > +           ((#:configure-flags flags
> > +             ''()) `(cons* ,(cond ((target-x86?) "--target=i386")
> > +                                  ((target-arm?) "--target=arm"))
> > +                           ,flags)))))))
> 
> Isn't this just grub but compiled for the 32-bit variant of the
> system?

> Or do you mean to use 'grub' as a cross-compiler here (instead of
> something that is being cross-compiled), like 'gcc'?
Apparently the modification I did (passing --target=i386) results in a
64bit grub-mkimage and 32bit modules in lib/grub/i386-efi so I think
it's more like a cross-compiler. I verified that by installing
grub-efi32 and using file on the mentioned files.

It's also possible to have something like multiple target/architectures
with GRUB by compiling it for each target/architecture and then
shipping the modules in lib/grub/* but that would require way more
work, so it would make more sense to do that as a separate patch set if
needed.

Though for now I'm just interested in adding 32bit UEFI support because
I did the work for testing/learning, and I would prefer if that work
results in Guix supporting 32bit UEFI somehow.

Denis.
Maxime Devos May 11, 2022, 9:07 a.m. UTC | #3
Denis 'GNUtoo' Carikli schreef op di 10-05-2022 om 23:39 [+0200]:
> On Tue, 10 May 2022 11:32:52 +0200
> Maxime Devos <maximedevos@telenet.be> wrote:
> 
> > Denis 'GNUtoo' Carikli schreef op di 10-05-2022 om 01:24 [+0200]:
> > > +           ((#:configure-flags flags
> > > +             ''()) `(cons* ,(cond ((target-x86?) "--
> target=i386")
> > > +                                  ((target-arm?) "--
> target=arm"))
> > > +                           ,flags)))))))
> > 
> > Isn't this just grub but compiled for the 32-bit variant of the
> > system?
> 
> > Or do you mean to use 'grub' as a cross-compiler here (instead of
> > something that is being cross-compiled), like 'gcc'?
> Apparently the modification I did (passing --target=i386) results in
> a
> 64bit grub-mkimage and 32bit modules in lib/grub/i386-efi so I think
> it's more like a cross-compiler. I verified that by installing
> grub-efi32 and using file on the mentioned files.
> [...]

Ok, --target looks like a better fit in this case.  Though now I'm
wondering if on i686-linux we might need a grub-efi64 variant on some
setups ...

Greetings,
Maxime
Denis 'GNUtoo' Carikli May 11, 2022, 5 p.m. UTC | #4
Hi,

On Tue, 10 May 2022 11:32:52 +0200
Maxime Devos <maximedevos@telenet.be> wrote:
>   (list #:target "i386")

I've tried that I get the following error:
> ice-9/boot-9.scm:1685:16: In procedure raise-exception:
> dynamic linker name not known for this system "i386"

This means that I have to use "i686-linux" because only "i686-linux" is
in the list of allowed values.

I've tried and this adds a cross toolchain to the list of
dependencies[1] and this doesn't work because efibootmgr which is a
dependency doesn't compile anymore as it doesn't find some of the
headers like efivar.h and efiboot.h. This could probably be fixed
somehow though.

In addition I've found the following issues with that approach:
- It hardcode the kernel (Linux or HURD) so we'd probably
  need extra logic to handle it well. With --target=i386 that is
  transparent. Note that I didn't test grub-efi32 with HURD in either
  cases.

- It doesn't support "canadian" cross compilation where the builder
  (for instance x86) builds a cross compiler to run on another
  architecture (for instance ARM) to then compile programs for another
  architecture (for instance riscv). With --target=i386 we can in
  theory do that (though I've not tested it).

And with it, I see the following advantage: since it's wrapped somehow
Guix can probably do things automatically (like checks or change
things) on all the packages that are cross compiled.

What approach do you think is best (I don't know the implementation of
(list #:target "i686-linux") so you probably know way better than me
which one is the best) ?.

If (list #:target "i686-linux") is the way to go, do you have any
pointers to make it find the headers it needs? I've attached its build
log in case it is useful.

References:
-----------
[1] Here's the output when trying to build grub-efi32 with
    (list #:target "i686-linux"):
> $ ./pre-inst-env guix package -i grub-efi32
> The following package will be upgraded:
>    grub-efi32 (dependencies or package changed)
> 
> The following derivations will be built:
>   /gnu/store/j4divh7pf1bxr11ivzddqhsihan6ij4p-util-linux-2.37.2.drv
>   /gnu/store/h86glf6qlyfmf214qj6xsqbj8vmrfss9-zlib-1.2.11.drv
>   /gnu/store/hy4x62rrx3gqdnn476dja6im74pxzkpk-glibc-cross-i686-linux-2.33.drv
>   /gnu/store/1qynvffnfnf7dlzmrkrkx7nzxq6mkz1k-gcc-cross-sans-libc-i686-linux-10.3.0.drv
>   /gnu/store/kyr74wyvikyldkx9a0zd55fmxjs6862c-binutils-cross-i686-linux-2.37.drv
>   /gnu/store/wc7zhlgnzrg9z5w5wqyvkwlg0ninavs1-ld-wrapper-i686-linux-0.drv
>   /gnu/store/i4h82qxwyaj3i97rgcvh6khfibbj4x3p-linux-libre-headers-cross-i686-linux-5.10.35.drv
>   /gnu/store/j4w5c2iqvm9ylfnc2gsadipngl00labp-gcc-cross-i686-linux-10.3.0.drv
>   /gnu/store/y15y0pici7yhgycjv13a4hm9jjshdzzm-ncurses-6.2.20210619.drv
>   /gnu/store/l5jdw7k9mdm0af4gyklhixw447fgsnxn-pkg-config-i686-linux-0.29.2.drv
>   /gnu/store/zg2fyiwc4i4vwarbk8imi9wsgzp8z90h-file-5.39.drv

Denis.
Maxime Devos May 11, 2022, 5:19 p.m. UTC | #5
Denis 'GNUtoo' Carikli schreef op wo 11-05-2022 om 19:00 [+0200]:
> What approach do you think is best (I don't know the implementation
> of
> (list #:target "i686-linux") so you probably know way better than me
> which one is the best) ?.

#:target determines for which architecture the grub binaries
('bin/install-grub' or something like that?  I forgot the name ...),
whereas (IIUC) what we need is to compile the _bootloader_ itself (and
its modules) for "i[36]86-SOMETHING" (*).  So since #:target isn't
appropriate here, and what you originally sent presumably works, so
let's go for the original "--target=..."?

(*) in practice, I don't think the SOMETHING or 386/686 matters here,
I'd expect the grub makefile/configure to set the -nostdlib and -
march=... bits appropriately?

Greetings,
Maxime.
Maxime Devos May 11, 2022, 5:25 p.m. UTC | #6
Denis 'GNUtoo' Carikli schreef op wo 11-05-2022 om 19:00 [+0200]:
> [1] Here's the output when trying to build grub-efi32 with
>     (list #:target "i686-linux"):

I don't think it will be relevant to grub-efi32, at least for now, but
I have opened an issue for this at <https://issues.guix.gnu.org/55373>.

Greetings,
Maxime.
Denis 'GNUtoo' Carikli May 11, 2022, 5:35 p.m. UTC | #7
On Wed, 11 May 2022 11:07:22 +0200
Maxime Devos <maximedevos@telenet.be> wrote:

> Ok, --target looks like a better fit in this case.
Sorry for the previous mail, I saw this one after finishing sending the
one with a report on tests with (list #:target "i686-linux").

> Though now I'm wondering if on i686-linux we might need a grub-efi64
> variant on some setups ...
Good question.

It would require to have a computer where the UEFI implementation is
only able to launch 64bit PE files on computers with a CPU that also
support 32bit OS.

For ARM I'm not sure if we can boot 32bit kernels on 64bit CPUs: In
theory many ARM computers hardware is capable of that. However in
practice we'd need to make that kernel configurations works for that,
and platform drivers that only work for aarch64 are probably required
anyway. Maybe in case of emulation that doesn't apply though.

For x86 I didn't ear of 64bit-only UEFI with CPUs that still
support 32bit OS, but it could exist.

And according to the official GRUB documentation, it is possible to
boot 32bit OS when the UEFI is 64bit and vice versa[1]:
|                                | ia32 EFI | amd64 EFI |
| BIOS chainloading              | no (1)   | no (1)    |
| [...]                          | [...]    | [...]     |
| FreeDOS                        | no (1)   | no (1)    |
| Multiboot                      | yes      | yes       |
| Multiboot2                     | yes      | yes       |
| 32-bit Linux (legacy protocol) | no (1)   | no (1)    |
| 64-bit Linux (legacy protocol) | no (1)   | no (1)    |
| 32-bit Linux (modern protocol) | yes      | yes       |
| 64-bit Linux (modern protocol) | yes      | yes       |
| [...]                          | [...]    | [...]     |
| 32-bit EFI chainloader         | yes      | no (3)    |
| 64-bit EFI chainloader         | no (3)   | yes       |
| [...]                          | [...]    | [...]     |
1. Requires BIOS 

So there might be a use case here if such machines exist. I'll try to
look for infos on that.

References:
[1]https://www.gnu.org/software/grub/manual/grub/grub.html#Supported-boot-targets

Denis.
Denis 'GNUtoo' Carikli May 18, 2022, 1:50 p.m. UTC | #8
On Wed, 11 May 2022 19:25:10 +0200
Maxime Devos <maximedevos@telenet.be> wrote:

> Denis 'GNUtoo' Carikli schreef op wo 11-05-2022 om 19:00 [+0200]:
> > [1] Here's the output when trying to build grub-efi32 with
> >     (list #:target "i686-linux"):  
> 
> I don't think it will be relevant to grub-efi32, at least for now, but
> I have opened an issue for this at
> <https://issues.guix.gnu.org/55373>.
Thanks.

Do I still need to do something with the patch serie adding support for
32bit UEFI or is everything OK?

Denis.
diff mbox series

Patch

diff --git a/gnu/bootloader/grub.scm b/gnu/bootloader/grub.scm
index 120cd55012..1ea356030f 100644
--- a/gnu/bootloader/grub.scm
+++ b/gnu/bootloader/grub.scm
@@ -7,6 +7,7 @@ 
 ;;; Copyright © 2019, 2020 Miguel Ángel Arruga Vivas <rosen644835@gmail.com>
 ;;; Copyright © 2020 Maxim Cournoyer <maxim.cournoyer@gmail.com>
 ;;; Copyright © 2020 Stefan <stefan-guix@vodafonemail.de>
+;;; Copyright © 2022 Denis 'GNUtoo' Carikli <GNUtoo@cyberdimension.org>
 ;;;
 ;;; This file is part of GNU Guix.
 ;;;
@@ -54,6 +55,7 @@  (define-module (gnu bootloader grub)
 
             grub-bootloader
             grub-efi-bootloader
+            grub-efi32-bootloader
             grub-efi-netboot-bootloader
             grub-mkrescue-bootloader
             grub-minimal-bootloader
@@ -608,6 +610,29 @@  (define install-grub-efi
                         "--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."
@@ -734,6 +759,13 @@  (define grub-efi-bootloader
    (name 'grub-efi)
    (package grub-efi)))
 
+(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..d41143d98e 100644
--- a/gnu/build/bootloader.scm
+++ b/gnu/build/bootloader.scm
@@ -1,6 +1,7 @@ 
 ;;; GNU Guix --- Functional package management for GNU
 ;;; Copyright © 2017 Mathieu Othacehe <m.othacehe@gmail.com>
 ;;; Copyright © 2019 Ludovic Courtès <ludo@gnu.org>
+;;; Copyright © 2022 Denis 'GNUtoo' Carikli <GNUtoo@cyberdimension.org>
 ;;;
 ;;; This file is part of GNU Guix.
 ;;;
@@ -25,7 +26,8 @@  (define-module (gnu build bootloader)
   #:use-module (rnrs io ports)
   #:use-module (rnrs io simple)
   #:export (write-file-on-device
-            install-efi-loader))
+            install-efi-loader
+            install-efi32-loader))
 
 
 ;;;
@@ -81,6 +83,29 @@  (define (install-efi grub grub-config esp)
             ;; Graft the configuration file onto the image.
             (string-append "boot/grub/grub.cfg=" grub-config))))
 
+(define (install-efi32 grub grub-config esp)
+  "Write a self-contained GRUB EFI 32bit 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 ((target-x86?)
+                             '("i386-efi" . "BOOTIA32.EFI"))
+                            ((target-arm?)
+                             '("arm-efi" . "BOOTARM.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."
@@ -99,3 +124,22 @@  (define (install-efi-loader grub-efi esp)
                configfile /boot/grub/grub.cfg~%")))
     (install-efi grub-efi grub-config esp)
     (delete-file grub-config)))
+
+(define (install-efi32-loader grub-efi32 esp)
+  "Install in ESP directory the given GRUB-EFI 32bit 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~@
+               insmod part_gpt~@
+               search --set=root --label Guix_image~@
+               configfile /boot/grub/grub.cfg~%")))
+    (install-efi32 grub-efi32 grub-config esp)
+    (delete-file grub-config)))
diff --git a/gnu/build/image.scm b/gnu/build/image.scm
index 81caa424f8..bae747494f 100644
--- a/gnu/build/image.scm
+++ b/gnu/build/image.scm
@@ -5,6 +5,7 @@ 
 ;;; Copyright © 2017 Marius Bakke <mbakke@fastmail.com>
 ;;; Copyright © 2020, 2022 Tobias Geerinckx-Rice <me@tobias.gr>
 ;;; Copyright © 2020 Mathieu Othacehe <m.othacehe@gmail.com>
+;;; Copyright © 2022 Denis 'GNUtoo' Carikli <GNUtoo@cyberdimension.org>
 ;;;
 ;;; This file is part of GNU Guix.
 ;;;
@@ -40,6 +41,7 @@  (define-module (gnu build image)
             convert-disk-image
             genimage
             initialize-efi-partition
+            initialize-efi32-partition
             initialize-root-partition
 
             make-iso9660-image))
@@ -162,6 +164,13 @@  (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-efi32-loader grub-efi32 root))
+
 (define* (initialize-root-partition root
                                     #:key
                                     bootcfg
diff --git a/gnu/packages/bootloaders.scm b/gnu/packages/bootloaders.scm
index 7ea6f5a647..f133d9f604 100644
--- a/gnu/packages/bootloaders.scm
+++ b/gnu/packages/bootloaders.scm
@@ -15,6 +15,7 @@ 
 ;;; Copyright © 2020, 2021 Pierre Langlois <pierre.langlois@gmx.com>
 ;;; Copyright © 2021 Vincent Legoll <vincent.legoll@gmail.com>
 ;;; Copyright © 2021 Brice Waegeneire <brice@waegenei.re>
+;;; Copyright © 2022 Denis 'GNUtoo' Carikli <GNUtoo@cyberdimension.org>
 ;;;
 ;;; 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 42e215f614..e35c54ca50 100644
--- a/gnu/system/image.scm
+++ b/gnu/system/image.scm
@@ -1,6 +1,7 @@ 
 ;;; GNU Guix --- Functional package management for GNU
 ;;; Copyright © 2020, 2021 Mathieu Othacehe <m.othacehe@gmail.com>
 ;;; Copyright © 2020 Jan (janneke) Nieuwenhuizen <janneke@gnu.org>
+;;; Copyright © 2022 Denis 'GNUtoo' Carikli <GNUtoo@cyberdimension.org>
 ;;;
 ;;; This file is part of GNU Guix.
 ;;;
@@ -65,6 +66,7 @@  (define-module (gnu system image)
             root-label
 
             esp-partition
+            esp32-partition
             root-partition
 
             efi-disk-image
@@ -74,6 +76,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
@@ -109,6 +112,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)
@@ -122,6 +130,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)
@@ -163,6 +176,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)
@@ -352,6 +370,7 @@  (define (partition-image partition)
                                                 #$(image-shared-store? image))
                               #:system-directory #$os
                               #:grub-efi #+grub-efi
+                              #:grub-efi32 #+grub-efi32
                               #:bootloader-package
                               #+(bootloader-package bootloader)
                               #:bootloader-installer