;;; GNU Guix --- Functional package management for GNU
-;;; Copyright © 2013, 2014, 2015, 2016, 2017 Ludovic Courtès <ludo@gnu.org>
+;;; Copyright © 2013, 2014, 2015, 2016, 2017, 2018, 2019, 2020 Ludovic Courtès <ludo@gnu.org>
;;; Copyright © 2016 Mark H Weaver <mhw@netris.org>
;;; Copyright © 2016 Jan Nieuwenhuizen <janneke@gnu.org>
-;;; Copyright © 2017 Mathieu Othacehe <m.othacehe@gmail.com>
+;;; Copyright © 2017, 2019 Mathieu Othacehe <m.othacehe@gmail.com>
+;;; Copyright © 2019, 2020 Tobias Geerinckx-Rice <me@tobias.gr>
;;;
;;; This file is part of GNU Guix.
;;;
;;; along with GNU Guix. If not, see <http://www.gnu.org/licenses/>.
(define-module (gnu system linux-initrd)
- #:use-module (guix monads)
- #:use-module (guix store)
#:use-module (guix gexp)
#:use-module (guix utils)
#:use-module ((guix store)
#:use-module (gnu packages compression)
#:use-module (gnu packages disk)
#:use-module (gnu packages linux)
+ #:use-module (gnu packages file-systems)
#:use-module (gnu packages guile)
+ #:use-module ((gnu packages xorg)
+ #:select (console-setup xkeyboard-config))
#:use-module ((gnu packages make-bootstrap)
- #:select (%guile-static-stripped))
+ #:select (%guile-3.0-static-stripped))
#:use-module (gnu system file-systems)
#:use-module (gnu system mapped-devices)
+ #:use-module (gnu system keyboard)
#:use-module (ice-9 match)
#:use-module (ice-9 regex)
+ #:use-module (ice-9 vlist)
#:use-module (srfi srfi-1)
#:use-module (srfi srfi-26)
#:export (expression->initrd
+ %base-initrd-modules
raw-initrd
file-system-packages
base-initrd))
(define* (expression->initrd exp
#:key
- (guile %guile-static-stripped)
+ (guile %guile-3.0-static-stripped)
(gzip gzip)
(name "guile-initrd")
(system (%current-system)))
- "Return a derivation that builds a Linux initrd (a gzipped cpio archive)
+ "Return as a file-like object a Linux initrd (a gzipped cpio archive)
containing GUILE and that evaluates EXP, a G-expression, upon booting. All
the derivations referenced by EXP are automatically copied to the initrd."
;; General Linux overview in `Documentation/early-userspace/README' and
;; `Documentation/filesystems/ramfs-rootfs-initramfs.txt'.
- (mlet %store-monad ((init (gexp->script "init" exp
- #:guile guile)))
- (define builder
- (with-imported-modules (source-module-closure
- '((gnu build linux-initrd)))
- #~(begin
- (use-modules (gnu build linux-initrd))
+ (define init
+ (program-file "init" exp #:guile guile))
- (mkdir #$output)
- (build-initrd (string-append #$output "/initrd")
- #:guile #$guile
- #:init #$init
- ;; Copy everything INIT refers to into the initrd.
- #:references-graphs '("closure")
- #:gzip (string-append #$gzip "/bin/gzip")))))
+ (define (import-module? module)
+ ;; Since we don't use deduplication support in 'populate-store', don't
+ ;; import (guix store deduplication) and its dependencies, which includes
+ ;; Guile-Gcrypt. That way we can run tests with '--bootstrap'.
+ (and (guix-module-name? module)
+ (not (equal? module '(guix store deduplication)))))
+
+ (define builder
+ ;; Do not use "guile-zlib" extension here, otherwise it would drag the
+ ;; non-static "zlib" package to the initrd closure. It is not needed
+ ;; anyway because the modules are stored uncompressed within the initrd.
+ (with-imported-modules (source-module-closure
+ '((gnu build linux-initrd))
+ #:select? import-module?)
+ #~(begin
+ (use-modules (gnu build linux-initrd))
- (gexp->derivation name builder
- #:references-graphs `(("closure" ,init)))))
+ (mkdir #$output)
+
+ ;; The guile used in the initrd must be present in the store, so
+ ;; that module loading works once the root is switched.
+ ;;
+ ;; To ensure that is the case, add an explicit reference to the
+ ;; guile package used in the initrd to the output.
+ ;;
+ ;; This fixes guix-patches bug #28399, "Fix mysql activation, and
+ ;; add a basic test".
+ (call-with-output-file (string-append #$ output "/references")
+ (lambda (port)
+ (simple-format port "~A\n" #$guile)))
+
+ (build-initrd (string-append #$output "/initrd.cpio.gz")
+ #:guile #$guile
+ #:init #$init
+ ;; Copy everything INIT refers to into the initrd.
+ #:references-graphs '("closure")
+ #:gzip (string-append #+gzip "/bin/gzip")))))
+
+ (file-append (computed-file name builder
+ #:options
+ `(#:references-graphs (("closure" ,init))))
+ "/initrd.cpio.gz"))
(define (flat-linux-module-directory linux modules)
"Return a flat directory containing the Linux kernel modules listed in
MODULES and taken from LINUX."
+ (define imported-modules
+ (source-module-closure '((gnu build linux-modules)
+ (guix build utils))))
+
(define build-exp
- (with-imported-modules (source-module-closure
- '((guix build utils)
- (gnu build linux-modules)))
- #~(begin
- (use-modules (ice-9 match) (ice-9 regex)
- (srfi srfi-1)
- (guix build utils)
- (gnu build linux-modules))
-
- (define (string->regexp str)
- ;; Return a regexp that matches STR exactly.
- (string-append "^" (regexp-quote str) "$"))
-
- (define module-dir
- (string-append #$linux "/lib/modules"))
-
- (define (lookup module)
- (let ((name (ensure-dot-ko module)))
- (match (find-files module-dir (string->regexp name))
- ((file)
- file)
- (()
- (error "module not found" name module-dir))
- ((_ ...)
- (error "several modules by that name"
- name module-dir)))))
-
- (define modules
- (let ((modules (map lookup '#$modules)))
- (append modules
- (recursive-module-dependencies modules
- #:lookup-module lookup))))
+ (with-imported-modules imported-modules
+ (with-extensions (list guile-zlib)
+ #~(begin
+ (use-modules (gnu build linux-modules)
+ (guix build utils)
+ (srfi srfi-1)
+ (srfi srfi-26))
+
+ (define module-dir
+ (string-append #$linux "/lib/modules"))
+
+ (define modules
+ (let* ((lookup (cut find-module-file module-dir <>))
+ (modules (map lookup '#$modules)))
+ (append modules
+ (recursive-module-dependencies
+ modules
+ #:lookup-module lookup))))
+
+ (define (maybe-uncompress file)
+ ;; If FILE is a compressed module, uncompress it, as the initrd
+ ;; is already gzipped as a whole.
+ (cond
+ ((string-contains file ".ko.gz")
+ (invoke #+(file-append gzip "/bin/gunzip") file))))
- (mkdir #$output)
- (for-each (lambda (module)
- (format #t "copying '~a'...~%" module)
- (copy-file module
- (string-append #$output "/"
- (basename module))))
- (delete-duplicates modules)))))
+ (mkdir #$output)
+ (for-each (lambda (module)
+ (let ((out-module
+ (string-append #$output "/"
+ (basename module))))
+ (format #t "copying '~a'...~%" module)
+ (copy-file module out-module)
+ (maybe-uncompress out-module)))
+ (delete-duplicates modules))
+
+ ;; Hyphen or underscore? This database tells us.
+ (write-module-name-database #$output)))))
- (gexp->derivation "linux-modules" build-exp))
+ (computed-file "linux-modules" build-exp))
(define* (raw-initrd file-systems
#:key
(linux linux-libre)
(linux-modules '())
(mapped-devices '())
+ (keyboard-layout #f)
(helper-packages '())
qemu-networking?
- volatile-root?)
- "Return a monadic derivation that builds a raw initrd, with kernel
+ volatile-root?
+ (on-error 'debug))
+ "Return as a file-like object a raw initrd, with kernel
modules taken from LINUX. FILE-SYSTEMS is a list of file-systems to be
mounted by the initrd, possibly in addition to the root file system specified
on the kernel command line via '--root'. LINUX-MODULES is a list of kernel
HELPER-PACKAGES is a list of packages to be copied in the initrd. It may include
e2fsck/static or other packages needed by the initrd to check root partition.
+When true, KEYBOARD-LAYOUT is a <keyboard-layout> record denoting the desired
+console keyboard layout. This is done before MAPPED-DEVICES are set up and
+before FILE-SYSTEMS are mounted such that, should the user need to enter a
+passphrase or use the REPL, this happens using the intended keyboard layout.
+
When QEMU-NETWORKING? is true, set up networking with the standard QEMU
parameters.
+
When VOLATILE-ROOT? is true, the root file system is writable but any changes
-to it are lost."
+to it are lost.
+
+ON-ERROR is passed to 'call-with-error-handling'; it determines what happens
+upon error."
(define device-mapping-commands
;; List of gexps to open the mapped devices.
(map (lambda (md)
- (let* ((source (mapped-device-source md))
- (target (mapped-device-target md))
- (type (mapped-device-type md))
- (open (mapped-device-kind-open type)))
- (open source target)))
+ (let* ((source (mapped-device-source md))
+ (targets (mapped-device-targets md))
+ (type (mapped-device-type md))
+ (open (mapped-device-kind-open type)))
+ (open source targets)))
mapped-devices))
- (mlet %store-monad ((kodir (flat-linux-module-directory linux
- linux-modules)))
- (expression->initrd
- (with-imported-modules (source-module-closure
- '((gnu build linux-boot)
- (guix build utils)
- (guix build bournish)
- (gnu build file-systems)))
- #~(begin
- (use-modules (gnu build linux-boot)
- (guix build utils)
- (guix build bournish) ;add the 'bournish' meta-command
- (srfi srfi-26)
-
- ;; FIXME: The following modules are for
- ;; LUKS-DEVICE-MAPPING. We should instead propagate
- ;; this info via gexps.
- ((gnu build file-systems)
- #:select (find-partition-by-luks-uuid))
- (rnrs bytevectors))
-
- (with-output-to-port (%make-void-port "w")
- (lambda ()
- (set-path-environment-variable "PATH" '("bin" "sbin")
- '#$helper-packages)))
-
- (boot-system #:mounts '#$(map file-system->spec file-systems)
+ (define kodir
+ (flat-linux-module-directory linux linux-modules))
+
+ (expression->initrd
+ (with-imported-modules (source-module-closure
+ '((gnu build linux-boot)
+ (guix build utils)
+ (guix build bournish)
+ (gnu system file-systems)
+ (gnu build file-systems)))
+ #~(begin
+ (use-modules (gnu build linux-boot)
+ (gnu system file-systems)
+ ((guix build utils) #:hide (delete))
+ (guix build bournish) ;add the 'bournish' meta-command
+ (srfi srfi-1) ;for lvm-device-mapping
+ (srfi srfi-26)
+
+ ;; FIXME: The following modules are for
+ ;; LUKS-DEVICE-MAPPING. We should instead propagate
+ ;; this info via gexps.
+ ((gnu build file-systems)
+ #:select (find-partition-by-luks-uuid))
+ (rnrs bytevectors))
+
+ (with-output-to-port (%make-void-port "w")
+ (lambda ()
+ (set-path-environment-variable "PATH" '("bin" "sbin")
+ '#$helper-packages)))
+
+ (parameterize ((current-warning-port (%make-void-port "w")))
+ (boot-system #:mounts
+ (map spec->file-system
+ '#$(map file-system->spec file-systems))
#:pre-mount (lambda ()
(and #$@device-mapping-commands))
#:linux-modules '#$linux-modules
#:linux-module-directory '#$kodir
+ #:keymap-file #+(and=> keyboard-layout
+ keyboard-layout->console-keymap)
#:qemu-guest-networking? #$qemu-networking?
- #:volatile-root? '#$volatile-root?)))
- #:name "raw-initrd")))
+ #:volatile-root? '#$volatile-root?
+ #:on-error '#$on-error))))
+ #:name "raw-initrd"))
(define* (file-system-packages file-systems #:key (volatile-root? #f))
"Return the list of statically-linked, stripped packages to check
file-systems)
(list fatfsck/static)
'())
+ ,@(if (find (file-system-type-predicate "bcachefs") file-systems)
+ (list bcachefs-tools/static)
+ '())
,@(if (find (file-system-type-predicate "btrfs") file-systems)
(list btrfs-progs/static)
'())
- ,@(if volatile-root?
- (list unionfs-fuse/static)
+ ,@(if (find (file-system-type-predicate "jfs") file-systems)
+ (list jfs_fsck/static)
+ '())
+ ,@(if (find (file-system-type-predicate "f2fs") file-systems)
+ (list f2fs-fsck/static)
'())))
+(define-syntax vhash ;TODO: factorize
+ (syntax-rules (=>)
+ "Build a vhash with the given key/value mappings."
+ ((_)
+ vlist-null)
+ ((_ (key others ... => value) rest ...)
+ (vhash-cons key value
+ (vhash (others ... => value) rest ...)))
+ ((_ (=> value) rest ...)
+ (vhash rest ...))))
+
+(define-syntax lookup-procedure
+ (syntax-rules (else)
+ "Return a procedure that lookups keys in the given dictionary."
+ ((_ mapping ... (else default))
+ (let ((table (vhash mapping ...)))
+ (lambda (key)
+ (match (vhash-assoc key table)
+ (#f default)
+ ((key . value) value)))))))
+
+(define file-system-type-modules
+ ;; Given a file system type, return the list of modules it needs.
+ (lookup-procedure ("cifs" => '("md4" "ecb" "cifs"))
+ ("9p" => '("9p" "9pnet_virtio"))
+ ("bcachefs" => '("bcachefs"))
+ ("btrfs" => '("btrfs"))
+ ("iso9660" => '("isofs"))
+ ("jfs" => '("jfs"))
+ ("f2fs" => '("f2fs" "crc32_generic"))
+ (else '())))
+
+(define (file-system-modules file-systems)
+ "Return the list of Linux modules needed to mount FILE-SYSTEMS."
+ (append-map (compose file-system-type-modules file-system-type)
+ file-systems))
+
+(define* (default-initrd-modules
+ #:optional
+ (system (or (%current-target-system)
+ (%current-system))))
+ "Return the list of modules included in the initrd by default."
+ (define virtio-modules
+ ;; Modules for Linux para-virtualized devices, for use in QEMU guests.
+ '("virtio_pci" "virtio_balloon" "virtio_blk" "virtio_net"
+ "virtio_console" "virtio-rng"))
+
+ `("ahci" ;for SATA controllers
+ "usb-storage" "uas" ;for the installation image etc.
+ "usbhid" "hid-generic" "hid-apple" ;keyboards during early boot
+ "dm-crypt" "xts" "serpent_generic" "wp512" ;for encrypted root partitions
+ "nls_iso8859-1" ;for `mkfs.fat`, et.al
+ ,@(if (string-match "^(x86_64|i[3-6]86)-" system)
+ '("pata_acpi" "pata_atiixp" ;for ATA controllers
+ "isci") ;for SAS controllers like Intel C602
+ '())
+
+ ,@virtio-modules))
+
+(define-syntax %base-initrd-modules
+ ;; This more closely matches our naming convention.
+ (identifier-syntax (default-initrd-modules)))
+
(define* (base-initrd file-systems
#:key
(linux linux-libre)
+ (linux-modules '())
(mapped-devices '())
+ (keyboard-layout #f)
qemu-networking?
volatile-root?
- (virtio? #t)
- (extra-modules '()))
- "Return a monadic derivation that builds a generic initrd, with kernel
+ (extra-modules '()) ;deprecated
+ (on-error 'debug))
+ "Return as a file-like object a generic initrd, with kernel
modules taken from LINUX. FILE-SYSTEMS is a list of file-systems to be
mounted by the initrd, possibly in addition to the root file system specified
on the kernel command line via '--root'. MAPPED-DEVICES is a list of device
mappings to realize before FILE-SYSTEMS are mounted.
-QEMU-NETWORKING? and VOLATILE-ROOT? behaves as in raw-initrd.
+When true, KEYBOARD-LAYOUT is a <keyboard-layout> record denoting the desired
+console keyboard layout. This is done before MAPPED-DEVICES are set up and
+before FILE-SYSTEMS are mounted such that, should the user need to enter a
+passphrase or use the REPL, this happens using the intended keyboard layout.
-When VIRTIO? is true, load additional modules so the initrd can
-be used as a QEMU guest with the root file system on a para-virtualized block
-device.
+QEMU-NETWORKING? and VOLATILE-ROOT? behaves as in raw-initrd.
The initrd is automatically populated with all the kernel modules necessary
-for FILE-SYSTEMS and for the given options. However, additional kernel
-modules can be listed in EXTRA-MODULES. They will be added to the initrd, and
+for FILE-SYSTEMS and for the given options. Additional kernel
+modules can be listed in LINUX-MODULES. They will be added to the initrd, and
loaded at boot time in the order in which they appear."
- (define virtio-modules
- ;; Modules for Linux para-virtualized devices, for use in QEMU guests.
- '("virtio_pci" "virtio_balloon" "virtio_blk" "virtio_net"
- "virtio_console"))
-
- (define cifs-modules
- ;; Modules needed to mount CIFS file systems.
- '("md4" "ecb" "cifs"))
-
- (define virtio-9p-modules
- ;; Modules for the 9p paravirtualized file system.
- '("9p" "9pnet_virtio"))
-
- (define (file-system-type-predicate type)
- (lambda (fs)
- (string=? (file-system-type fs) type)))
-
- (define linux-modules
+ (define linux-modules*
;; Modules added to the initrd and loaded from the initrd.
- `("ahci" ;for SATA controllers
- "usb-storage" "uas" ;for the installation image etc.
- "usbhid" "hid-generic" "hid-apple" ;keyboards during early boot
- "dm-crypt" "xts" "serpent_generic" "wp512" ;for encrypted root partitions
- "nvme" ;for new SSD NVMe devices
- "nls_iso8859-1" ;for `mkfs.fat`, et.al
- ,@(if (string-match "^(x86_64|i[3-6]86)-" (%current-system))
- '("pata_acpi" "pata_atiixp" ;for ATA controllers
- "isci") ;for SAS controllers like Intel C602
- '())
- ,@(if (or virtio? qemu-networking?)
- virtio-modules
- '())
- ,@(if (find (file-system-type-predicate "cifs") file-systems)
- cifs-modules
- '())
- ,@(if (find (file-system-type-predicate "9p") file-systems)
- virtio-9p-modules
- '())
- ,@(if (find (file-system-type-predicate "btrfs") file-systems)
- '("btrfs")
- '())
+ `(,@linux-modules
+ ,@(file-system-modules file-systems)
,@(if volatile-root?
- '("fuse")
+ '("overlay")
'())
,@extra-modules))
(define helper-packages
- (file-system-packages file-systems #:volatile-root? volatile-root?))
+ (append (file-system-packages file-systems
+ #:volatile-root? volatile-root?)
+ (if keyboard-layout
+ (list loadkeys-static)
+ '())))
(raw-initrd file-systems
#:linux linux
- #:linux-modules linux-modules
+ #:linux-modules linux-modules*
#:mapped-devices mapped-devices
#:helper-packages helper-packages
+ #:keyboard-layout keyboard-layout
#:qemu-networking? qemu-networking?
- #:volatile-root? volatile-root?))
+ #:volatile-root? volatile-root?
+ #:on-error on-error))
;;; linux-initrd.scm ends here