Subject: [bug#40579] [PATCH v2] gnu: Add iPXE.

From: Brice Waegeneire <[email-masked]>
Date: Tue, 09 Jun 2020 21:31:43 +0200

Hello Vincent, Tobias, Danny,

Danny Milosavljevic <[email-masked]> writes:

> ipxe.iso is not reproducible it seems.
>
> [...]

The attached patch should be reproducible, it is based on v3 from
Vincent.

It adds the following:
- generate a BUILD_ID based on the package's output hash
- use 'let' to set BUILD_TIMESTAMP
- set some useful options
- add a patch that reset the timestamps of ipxe.iso

- Brice

>From 5d6acaa49844ce1ad3f8ca4cba74be695901fb24 Mon Sep 17 00:00:00 2001
From: Vincent Legoll <[email-masked]>
Date: Mon, 13 Apr 2020 01:28:31 +0200
Subject: [PATCH v4] gnu: Add iPXE.
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit

* gnu/packages/bootloaders.scm (ipxe): New variable.
* gnu/packages/patches/ipxe-reproducible-geniso.patch: New file …
* gnu/local.mk (dist_patch_DATA): … add it.

Co-authored-by: Tobias Geerinckx-Rice <[email-masked]>
Co-authored-by: Brice Waegeneire <[email-masked]>
---
 gnu/local.mk                                  |   1 +
 gnu/packages/bootloaders.scm                  | 122 ++++++++++++++++++
 .../patches/ipxe-reproducible-geniso.patch    |  78 +++++++++++
 3 files changed, 201 insertions(+)
 create mode 100644 gnu/packages/patches/ipxe-reproducible-geniso.patch

diff --git a/gnu/local.mk b/gnu/local.mk
index 33b344d41b..92ad64135a 100644
--- a/gnu/local.mk
+++ b/gnu/local.mk
@@ -1105,6 +1105,7 @@ dist_patch_DATA =						\
   %D%/packages/patches/inetutils-hurd.patch			\
   %D%/packages/patches/inkscape-poppler-0.76.patch		\
   %D%/packages/patches/intltool-perl-compatibility.patch	\
+  %D%/packages/patches/ipxe-reproducible-geniso.patch	        \
   %D%/packages/patches/irrlicht-use-system-libs.patch		\
   %D%/packages/patches/isl-0.11.1-aarch64-support.patch	\
   %D%/packages/patches/jacal-fix-texinfo.patch			\
diff --git a/gnu/packages/bootloaders.scm b/gnu/packages/bootloaders.scm
index ea80cf020e..c819c903e8 100644
--- a/gnu/packages/bootloaders.scm
+++ b/gnu/packages/bootloaders.scm
@@ -14,6 +14,8 @@
 ;;; Copyright © 2020 Jan (janneke) Nieuwenhuizen <[email-masked]>
 ;;; Copyright © 2018, 2019, 2020 Vagrant Cascadian <[email-masked]>
 ;;; Copyright © 2020 Pierre Langlois <[email-masked]>
+;;; Copyright © 2020 Vincent Legoll <[email-masked]>
+;;; Copyright © 2020 Brice Waegeneire <[email-masked]>
 ;;;
 ;;; This file is part of GNU Guix.
 ;;;
@@ -1068,3 +1070,123 @@ systems so that they can be added to the bootloader.  It also works out how to
 boot existing GNU/Linux systems and detects what distribution is installed in
 order to add a suitable bootloader menu entry.")
     (license license:gpl2+)))
+
+(define-public ipxe
+  ;; XXX: 'BUILD_TIMESTAMP' is used to automatically select the newest version
+  ;; of iPXE if multiple iPXE drivers are loaded concurrently in a UEFI system.
+  ;;
+  ;; TODO: Bump this timestamp at each modifications of the package (not only
+  ;; for updates) by running: date +%s.
+  (let ((timestamp "1591706427"))
+    (package
+      (name "ipxe")
+      (version "1.20.1")
+      (source (origin
+                (method git-fetch)
+                (uri (git-reference
+                      (url "https://github.com/ipxe/ipxe")
+                      (commit (string-append "v" version))))
+                (file-name (git-file-name name version))
+                (patches (search-patches "ipxe-reproducible-geniso.patch"))
+                (sha256
+                 (base32
+                  "0w7h7y97gj9nqvbmsg1zp6zj5mpbbpckqbbx7bpp6k3ahy5fk8zp"))))
+      (build-system gnu-build-system)
+      (arguments
+       `(#:modules ((guix build utils)
+                    (guix build gnu-build-system)
+                    (guix base32)
+                    (ice-9 string-fun)
+                    (ice-9 regex)
+                    (rnrs bytevectors))
+         #:imported-modules ((guix base32)
+                             ,@%gnu-build-system-modules)
+         #:make-flags
+         ;; XXX: 'BUILD_ID' is used to determine when another ROM in the
+         ;; system contains identical code in order to save space within the
+         ;; legacy BIOS option ROM area, which is extremely limited in size.
+         ;; It is supposed to be collision-free across all ROMs, to do so we
+         ;; use the truncated output hash of the package.
+         (let ((build-id
+                (lambda (out)
+                  (let* ((nix-store (string-append
+                                     (or (getenv "NIX_STORE") "/gnu/store")
+                                     "/"))
+                         (filename
+                          (string-replace-substring out nix-store ""))
+                         (hash (match:substring (string-match "[0-9a-z]{32}"
+                                                              filename)))
+                         (bv (nix-base32-string->bytevector hash)))
+                    (format #f "0x~x"
+                            (bytevector-u32-ref bv 0 (endianness big))))))
+               (out (assoc-ref %outputs "out"))
+               (syslinux (assoc-ref %build-inputs "syslinux")))
+           (list "ECHO_E_BIN_ECHO=echo"
+                 "ECHO_E_BIN_ECHO_E=echo -e"
+
+                 ;; cdrtools' mkisofs will silently ignore a missing isolinux.bin!
+                 ;; Luckily xorriso is more strict.
+                 (string-append "ISOLINUX_BIN=" syslinux
+                                "/share/syslinux/isolinux.bin")
+                 (string-append "SYSLINUX_MBR_DISK_PATH=" syslinux
+                                "/share/syslinux/isohdpfx.bin")
+
+                 ;; Build reproducibly.
+                 (string-append "BUILD_ID_CMD=echo -n " (build-id out))
+                 (string-append "BUILD_TIMESTAMP=" ,timestamp)
+                 "everything"))
+         #:phases
+         (modify-phases %standard-phases
+           (add-after 'unpack 'enter-source-directory
+             (lambda _ (chdir "src") #t))
+           (add-after 'enter-source-directory 'set-options
+             (lambda _
+               (substitute* "config/general.h"
+                 (("^//(#define PING_CMD.*)" _ uncommented) uncommented)
+                 (("^//(#define IMAGE_TRUST_CMD.*)" _ uncommented)
+                  uncommented)
+                 (("^#undef.*(DOWNLOAD_PROTO_HTTPS.*)" _ option)
+                  (string-append "#define " option))
+                 (("^#undef.*(DOWNLOAD_PROTO_NFS.*)" _ option)
+                  (string-append "#define " option)))
+               #t))
+           (delete 'configure)          ; no configure script
+           (replace 'install
+             (lambda* (#:key outputs #:allow-other-keys)
+               (let* ((out (assoc-ref outputs "out"))
+                      (ipxe (string-append out "/lib/ipxe"))
+                      (exts-re
+                       "\\.(efi|efirom|iso|kkpxe|kpxe|lkrn|mrom|pxe|rom|usb)$")
+                      (dirs '("bin" "bin-i386-linux" "bin-x86_64-pcbios"
+                              "bin-x86_64-efi" "bin-x86_64-linux" "bin-i386-efi"))
+                      (files (apply append
+                                    (map (lambda (dir)
+                                           (find-files dir exts-re)) dirs))))
+                 (for-each (lambda (file)
+                             (let* ((subdir (dirname file))
+                                    (fn (basename file))
+                                    (tgtsubdir (cond
+                                                ((string=? "bin" subdir) "")
+                                                ((string-prefix? "bin-" subdir)
+                                                 (string-drop subdir 4)))))
+                               (install-file file
+                                             (string-append ipxe "/" tgtsubdir))))
+                           files))
+               #t))
+           (add-after 'install 'leave-source-directory
+             (lambda _ (chdir "..") #t)))
+         #:tests? #f))                  ; no test suite
+      (native-inputs
+       `(("perl" ,perl)
+         ("syslinux" ,syslinux)
+         ("xorriso" ,xorriso)))
+      (home-page "https://ipxe.org")
+      (synopsis "PXE-compliant network boot firmware")
+      (description "iPXE is a network boot firmware.  It provides a full PXE
+implementation enhanced with additional features such as booting from: a web
+server via HTTP, an iSCSI SAN, a Fibre Channel SAN via FCoE, an AoE SAN, a
+wireless network, a wide-area network, an Infiniband network.  It allows to
+control the boot process with a script.  You can use iPXE to replace the
+existing PXE ROM on your network card, or you can chainload into iPXE to obtain
+the features of iPXE without the hassle of reflashing.")
+      (license license:gpl2+))))
diff --git a/gnu/packages/patches/ipxe-reproducible-geniso.patch b/gnu/packages/patches/ipxe-reproducible-geniso.patch
new file mode 100644
index 0000000000..d3c97c3085
--- /dev/null
+++ b/gnu/packages/patches/ipxe-reproducible-geniso.patch
@@ -0,0 +1,78 @@
+From 052d24d8217c51c572c2f6cbb4a687be2e8ba52d Mon Sep 17 00:00:00 2001
+From: Brice Waegeneire <[email-masked]>
+Date: Fri, 5 Jun 2020 14:38:43 +0200
+Subject: [PATCH] [geniso] Make it reproducible
+
+Some timestamps get embedded in the generated ISO, making it
+unreproducible so we overwrite those timestamps to be at the UNIX epoch.
+---
+ src/util/geniso | 24 +++++++++++++++++++++---
+ 1 file changed, 21 insertions(+), 3 deletions(-)
+
+diff --git a/src/util/geniso b/src/util/geniso
+index ff090d4a..e032ffb0 100755
+--- a/src/util/geniso
++++ b/src/util/geniso
+@@ -11,6 +11,13 @@ function help() {
+ 	echo " -o FILE  save iso image to file"
+ }
+ 
++function reset_timestamp() {
++	for f in "$1"/*; do
++		touch -t 197001010100 "$f"
++	done
++	touch -t 197001010100 "$1"
++}
++
+ LEGACY=0
+ FIRST=""
+ 
+@@ -37,8 +44,9 @@ if [ -z "${OUT}" ]; then
+ 	exit 1
+ fi
+ 
+-# There should either be mkisofs or the compatible genisoimage program
+-for command in genisoimage mkisofs; do
++# There should either be mkisofs, xorriso or the compatible genisoimage
++# program
++for command in xorriso genisoimage mkisofs; do
+ 	if ${command} --version >/dev/null 2>/dev/null; then
+ 		mkisofs=(${command})
+ 		break
+@@ -46,8 +54,10 @@ for command in genisoimage mkisofs; do
+ done
+ 
+ if [ -z "${mkisofs}" ]; then
+-	echo "${0}: mkisofs or genisoimage not found, please install or set PATH" >&2
++	echo "${0}: mkisofs, xorriso or genisoimage not found, please install or set PATH" >&2
+ 	exit 1
++elif [ "$mkisofs" = "xorriso" ]; then
++	 mkisofs+=(-as mkisofs)
+ fi
+ 
+ dir=$(mktemp -d bin/iso.dir.XXXXXX)
+@@ -115,6 +125,8 @@ case "${LEGACY}" in
+ 			exit 1
+ 		fi
+ 
++		reset_timestamp "$dir"
++
+ 		# generate the iso image
+ 		"${mkisofs[@]}" -b boot.img -output ${OUT} ${dir}
+ 		;;
+@@ -127,6 +139,12 @@ case "${LEGACY}" in
+ 			cp ${LDLINUX_C32} ${dir}
+ 		fi
+ 
++		reset_timestamp "$dir"
++
++		if [ "${mkisofs[0]}" = "xorriso" ]; then
++			mkisofs+=(-isohybrid-mbr "$SYSLINUX_MBR_DISK_PATH")
++		fi
++
+ 		# generate the iso image
+ 		"${mkisofs[@]}" -b isolinux.bin -no-emul-boot -boot-load-size 4 -boot-info-table -output ${OUT} ${dir}
+ 
+-- 
+2.26.2
+
-- 
2.26.2