Merge branch 'master' into staging
[jackhill/guix/guix.git] / gnu / build / linux-boot.scm
CommitLineData
88840f02 1;;; GNU Guix --- Functional package management for GNU
683cba75 2;;; Copyright © 2013, 2014, 2015, 2016, 2017, 2018, 2019, 2020 Ludovic Courtès <ludo@gnu.org>
a5e13c3b 3;;; Copyright © 2017 Mathieu Othacehe <m.othacehe@gmail.com>
900ef20b 4;;; Copyright © 2019 Guillaume Le Vaillant <glv@posteo.net>
88840f02
LC
5;;;
6;;; This file is part of GNU Guix.
7;;;
8;;; GNU Guix is free software; you can redistribute it and/or modify it
9;;; under the terms of the GNU General Public License as published by
10;;; the Free Software Foundation; either version 3 of the License, or (at
11;;; your option) any later version.
12;;;
13;;; GNU Guix is distributed in the hope that it will be useful, but
14;;; WITHOUT ANY WARRANTY; without even the implied warranty of
15;;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
16;;; GNU General Public License for more details.
17;;;
18;;; You should have received a copy of the GNU General Public License
19;;; along with GNU Guix. If not, see <http://www.gnu.org/licenses/>.
20
8a9e21d1 21(define-module (gnu build linux-boot)
88840f02 22 #:use-module (rnrs io ports)
e3ced65a 23 #:use-module (system repl error-handling)
d4254711 24 #:autoload (system repl repl) (start-repl)
d4254711 25 #:use-module (srfi srfi-1)
97817e7f 26 #:use-module (srfi srfi-9)
d4254711
LC
27 #:use-module (srfi srfi-26)
28 #:use-module (ice-9 match)
97817e7f
DM
29 #:use-module (ice-9 rdelim)
30 #:use-module (ice-9 regex)
44ddf33e 31 #:use-module (ice-9 ftw)
d4254711 32 #:use-module (guix build utils)
1c65cca5
LC
33 #:use-module ((guix build syscalls)
34 #:hide (file-system-type))
0e704a2d 35 #:use-module (gnu build linux-modules)
e2f4b305 36 #:use-module (gnu build file-systems)
1c65cca5 37 #:use-module (gnu system file-systems)
88840f02
LC
38 #:export (mount-essential-file-systems
39 linux-command-line
87a52da7 40 find-long-option
015d0a84 41 find-long-options
d91712ee 42 make-essential-device-nodes
97817e7f 43 make-static-device-nodes
88840f02 44 configure-qemu-networking
85a83edb 45
d4254711
LC
46 device-number
47 boot-system))
88840f02
LC
48
49;;; Commentary:
50;;;
51;;; Utility procedures useful in a Linux initial RAM disk (initrd). Note that
52;;; many of these use procedures not yet available in vanilla Guile (`mount',
53;;; `load-linux-module', etc.); these are provided by a Guile patch used in
54;;; the GNU distribution.
55;;;
56;;; Code:
57
58(define* (mount-essential-file-systems #:key (root "/"))
cc0e575a 59 "Mount /dev, /proc, and /sys under ROOT."
88840f02
LC
60 (define (scope dir)
61 (string-append root
62 (if (string-suffix? "/" root)
63 ""
64 "/")
65 dir))
66
67 (unless (file-exists? (scope "proc"))
68 (mkdir (scope "proc")))
69 (mount "none" (scope "proc") "proc")
70
cc0e575a
LC
71 (unless (file-exists? (scope "dev"))
72 (mkdir (scope "dev")))
73 (mount "none" (scope "dev") "devtmpfs")
74
88840f02
LC
75 (unless (file-exists? (scope "sys"))
76 (mkdir (scope "sys")))
77 (mount "none" (scope "sys") "sysfs"))
78
1d462832
LC
79(define (move-essential-file-systems root)
80 "Move currently mounted essential file systems to ROOT."
81 (for-each (lambda (dir)
82 (let ((target (string-append root dir)))
83 (unless (file-exists? target)
84 (mkdir target))
85 (mount dir target "" MS_MOVE)))
cc0e575a 86 '("/dev" "/proc" "/sys")))
1d462832 87
88840f02
LC
88(define (linux-command-line)
89 "Return the Linux kernel command line as a list of strings."
90 (string-tokenize
91 (call-with-input-file "/proc/cmdline"
92 get-string-all)))
93
87a52da7
LC
94(define (find-long-option option arguments)
95 "Find OPTION among ARGUMENTS, where OPTION is something like \"--load\".
96Return the value associated with OPTION, or #f on failure."
97 (let ((opt (string-append option "=")))
98 (and=> (find (cut string-prefix? opt <>)
99 arguments)
100 (lambda (arg)
101 (substring arg (+ 1 (string-index arg #\=)))))))
102
015d0a84
DM
103(define (find-long-options option arguments)
104 "Find OPTIONs among ARGUMENTS, where OPTION is something like \"console\".
105Return the values associated with OPTIONs as a list, or the empty list if
106OPTION doesn't appear in ARGUMENTS."
107 (let ((opt (string-append option "=")))
108 (filter-map (lambda (arg)
109 (and (string-prefix? opt arg)
110 (substring arg (+ 1 (string-index arg #\=)))))
111 arguments)))
112
ac52e80b
LC
113(define* (make-disk-device-nodes base major #:optional (minor 0))
114 "Make the block device nodes around BASE (something like \"/root/dev/sda\")
115with the given MAJOR number, starting with MINOR."
116 (mknod base 'block-special #o644 (device-number major minor))
117 (let loop ((i 1))
ced0106a 118 (when (< i 16)
ac52e80b
LC
119 (mknod (string-append base (number->string i))
120 'block-special #o644 (device-number major (+ minor i)))
121 (loop (+ i 1)))))
122
97817e7f
DM
123;; Representation of a /dev node.
124(define-record-type <device-node>
125 (device-node name type major minor module)
126 device-node?
127 (name device-node-name)
128 (type device-node-type)
129 (major device-node-major)
130 (minor device-node-minor)
131 (module device-node-module))
132
133(define (read-static-device-nodes port)
134 "Read from PORT a list of <device-node> written in the format used by
135/lib/modules/*/*.devname files."
136 (let loop ((line (read-line port)))
137 (if (eof-object? line)
138 '()
139 (match (string-split line #\space)
140 (((? (cut string-prefix? "#" <>)) _ ...)
141 (loop (read-line port)))
142 ((module-name device-name device-spec)
143 (let* ((device-parts
144 (string-match "([bc])([0-9][0-9]*):([0-9][0-9]*)"
145 device-spec))
146 (type-string (match:substring device-parts 1))
147 (type (match type-string
148 ("c" 'char-special)
149 ("b" 'block-special)))
150 (major-string (match:substring device-parts 2))
151 (major (string->number major-string 10))
152 (minor-string (match:substring device-parts 3))
153 (minor (string->number minor-string 10)))
154 (cons (device-node device-name type major minor module-name)
155 (loop (read-line port)))))
156 (_
157 (begin
158 (format (current-error-port)
159 "read-static-device-nodes: ignored devname line '~a'~%" line)
160 (loop (read-line port))))))))
161
162(define* (mkdir-p* dir #:optional (mode #o755))
163 "This is a variant of 'mkdir-p' that works around
164<http://bugs.gnu.org/24659> by passing MODE explicitly in each 'mkdir' call."
165 (define absolute?
166 (string-prefix? "/" dir))
167
168 (define not-slash
169 (char-set-complement (char-set #\/)))
170
171 (let loop ((components (string-tokenize dir not-slash))
172 (root (if absolute?
173 ""
174 ".")))
175 (match components
176 ((head tail ...)
177 (let ((path (string-append root "/" head)))
178 (catch 'system-error
179 (lambda ()
180 (mkdir path mode)
181 (loop tail path))
182 (lambda args
183 (if (= EEXIST (system-error-errno args))
184 (loop tail path)
185 (apply throw args))))))
186 (() #t))))
187
188(define (report-system-error name . args)
189 "Report a system error for the file NAME."
190 (let ((errno (system-error-errno args)))
191 (format (current-error-port) "could not create '~a': ~a~%" name
192 (strerror errno))))
193
194;; Catch a system-error, log it and don't die from it.
195(define-syntax-rule (catch-system-error name exp)
196 (catch 'system-error
197 (lambda ()
198 exp)
199 (lambda args
200 (apply report-system-error name args))))
201
162a1374 202;; Create a device node like the <device-node> passed here on the file system.
97817e7f
DM
203(define create-device-node
204 (match-lambda
205 (($ <device-node> xname type major minor module)
206 (let ((name (string-append "/dev/" xname)))
207 (mkdir-p* (dirname name))
208 (catch-system-error name
209 (mknod name type #o600 (device-number major minor)))))))
210
211(define* (make-static-device-nodes linux-release-module-directory)
212 "Create static device nodes required by the given Linux release.
213This is required in order to solve a chicken-or-egg problem:
214The Linux kernel has a feature to autoload modules when a device is first
215accessed.
216And udev has a feature to set the permissions of static nodes correctly
217when it is starting up and also to automatically create nodes when hardware
218is hotplugged. That leaves universal device files which are not linked to
219one specific hardware device. These we have to create."
220 (let ((devname-name (string-append linux-release-module-directory "/"
221 "modules.devname")))
222 (for-each create-device-node
223 (call-with-input-file devname-name
224 read-static-device-nodes))))
225
683cba75 226(define* (make-essential-device-nodes #:optional (root "/"))
d91712ee 227 "Make essential device nodes under ROOT/dev."
cc0e575a 228 ;; The hand-made devtmpfs/udev!
d91712ee
LC
229
230 (define (scope dir)
231 (string-append root
232 (if (string-suffix? "/" root)
233 ""
234 "/")
235 dir))
236
237 (unless (file-exists? (scope "dev"))
238 (mkdir (scope "dev")))
239
fc4bc4b6 240 ;; Make the device nodes for SCSI disks.
ac52e80b
LC
241 (make-disk-device-nodes (scope "dev/sda") 8)
242 (make-disk-device-nodes (scope "dev/sdb") 8 16)
243 (make-disk-device-nodes (scope "dev/sdc") 8 32)
244 (make-disk-device-nodes (scope "dev/sdd") 8 48)
245
246 ;; SCSI CD-ROM devices (aka. "/dev/sr0" etc.).
247 (mknod (scope "dev/scd0") 'block-special #o644 (device-number 11 0))
248 (mknod (scope "dev/scd1") 'block-special #o644 (device-number 11 1))
fc4bc4b6
LC
249
250 ;; The virtio (para-virtualized) block devices, as supported by QEMU/KVM.
ac52e80b 251 (make-disk-device-nodes (scope "dev/vda") 252)
d91712ee 252
c04c6ff6
LC
253 ;; Memory (used by Xorg's VESA driver.)
254 (mknod (scope "dev/mem") 'char-special #o640 (device-number 1 1))
255 (mknod (scope "dev/kmem") 'char-special #o640 (device-number 1 2))
256
1c221510
LC
257 ;; Inputs (used by Xorg.)
258 (unless (file-exists? (scope "dev/input"))
259 (mkdir (scope "dev/input")))
260 (mknod (scope "dev/input/mice") 'char-special #o640 (device-number 13 63))
261 (mknod (scope "dev/input/mouse0") 'char-special #o640 (device-number 13 32))
262 (mknod (scope "dev/input/event0") 'char-special #o640 (device-number 13 64))
263
9b4a163a
LC
264 ;; System console. This node is magically created by the kernel on the
265 ;; initrd's root, so don't try to create it in that case.
266 (unless (string=? root "/")
267 (mknod (scope "dev/console") 'char-special #o600
268 (device-number 5 1)))
269
d91712ee 270 ;; TTYs.
29804e6e
LC
271 (mknod (scope "dev/tty") 'char-special #o600
272 (device-number 5 0))
289773c1 273 (chmod (scope "dev/tty") #o666)
d91712ee
LC
274 (let loop ((n 0))
275 (and (< n 50)
276 (let ((name (format #f "dev/tty~a" n)))
29804e6e 277 (mknod (scope name) 'char-special #o600
d91712ee
LC
278 (device-number 4 n))
279 (loop (+ 1 n)))))
280
7f17ff78
LC
281 ;; Serial line.
282 (mknod (scope "dev/ttyS0") 'char-special #o660
283 (device-number 4 64))
284
c9c88118
LC
285 ;; Pseudo ttys.
286 (mknod (scope "dev/ptmx") 'char-special #o666
287 (device-number 5 2))
289773c1 288 (chmod (scope "dev/ptmx") #o666)
c9c88118 289
c865a878 290 ;; Create /dev/pts; it will be mounted later, at boot time.
c9c88118
LC
291 (unless (file-exists? (scope "dev/pts"))
292 (mkdir (scope "dev/pts")))
c9c88118 293
37c825eb
LC
294 ;; Rendez-vous point for syslogd.
295 (mknod (scope "dev/log") 'socket #o666 0)
296 (mknod (scope "dev/kmsg") 'char-special #o600 (device-number 1 11))
297
289773c1
LC
298 ;; Other useful nodes, notably relied on by guix-daemon.
299 (for-each (match-lambda
300 ((file major minor)
301 (mknod (scope file) 'char-special #o666
302 (device-number major minor))
303 (chmod (scope file) #o666)))
304 '(("dev/null" 1 3)
305 ("dev/zero" 1 5)
306 ("dev/full" 1 7)
307 ("dev/random" 1 8)
308 ("dev/urandom" 1 9)))
309
310 (symlink "/proc/self/fd" (scope "dev/fd"))
311 (symlink "/proc/self/fd/0" (scope "dev/stdin"))
312 (symlink "/proc/self/fd/1" (scope "dev/stdout"))
1c96c1bb
LC
313 (symlink "/proc/self/fd/2" (scope "dev/stderr"))
314
3035b50f
LC
315 ;; Loopback devices.
316 (let loop ((i 0))
317 (when (< i 8)
318 (mknod (scope (string-append "dev/loop" (number->string i)))
319 'block-special #o660
320 (device-number 7 i))
321 (loop (+ 1 i))))
322
1c96c1bb
LC
323 ;; File systems in user space (FUSE).
324 (mknod (scope "dev/fuse") 'char-special #o666 (device-number 10 229)))
d91712ee 325
88840f02
LC
326(define %host-qemu-ipv4-address
327 (inet-pton AF_INET "10.0.2.10"))
328
329(define* (configure-qemu-networking #:optional (interface "eth0"))
330 "Setup the INTERFACE network interface and /etc/resolv.conf according to
331QEMU's default networking settings (see net/slirp.c in QEMU for default
332networking values.) Return #t if INTERFACE is up, #f otherwise."
333 (display "configuring QEMU networking...\n")
334 (let* ((sock (socket AF_INET SOCK_STREAM 0))
335 (address (make-socket-address AF_INET %host-qemu-ipv4-address 0))
336 (flags (network-interface-flags sock interface)))
337 (set-network-interface-address sock interface address)
338 (set-network-interface-flags sock interface (logior flags IFF_UP))
339
c0b9213d
LC
340 ;; Hello! We used to create /etc/resolv.conf here, with "nameserver
341 ;; 10.0.2.3\n". However, with Linux-libre 3.16, we're getting ENOSPC.
342 ;; And since it's actually unnecessary, it's gone.
88840f02
LC
343
344 (logand (network-interface-flags sock interface) IFF_UP)))
345
88840f02
LC
346(define (device-number major minor)
347 "Return the device number for the device with MAJOR and MINOR, for use as
348the last argument of `mknod'."
349 (+ (* major 256) minor))
350
7d57cfd3
LC
351(define (pidof program)
352 "Return the PID of the first presumed instance of PROGRAM."
353 (let ((program (basename program)))
354 (find (lambda (pid)
355 (let ((exe (format #f "/proc/~a/exe" pid)))
356 (and=> (false-if-exception (readlink exe))
357 (compose (cut string=? program <>) basename))))
358 (filter-map string->number (scandir "/proc")))))
359
83bcd0b8 360(define* (mount-root-file-system root type
55e21617
GLV
361 #:key volatile-root? (flags 0) options)
362 "Mount the root file system of type TYPE at device ROOT. If VOLATILE-ROOT? is
363true, mount ROOT read-only and make it an overlay with a writable tmpfs using
364the kernel built-in overlayfs. FLAGS and OPTIONS indicates the options to use
cc9b889e 365to mount ROOT, and behave the same as for the `mount' procedure."
41e03c4b 366
4dfbdcbc
LC
367 (if volatile-root?
368 (begin
369 (mkdir-p "/real-root")
cc9b889e 370 (mount root "/real-root" type (logior MS_RDONLY flags) options)
4dfbdcbc
LC
371 (mkdir-p "/rw-root")
372 (mount "none" "/rw-root" "tmpfs")
373
c8289690
HG
374 ;; Create the upperdir and the workdir of the overlayfs
375 (mkdir-p "/rw-root/upper")
376 (mkdir-p "/rw-root/work")
377
4dfbdcbc 378 ;; We want read-write /dev nodes.
c8289690
HG
379 (mkdir-p "/rw-root/upper/dev")
380 (mount "none" "/rw-root/upper/dev" "devtmpfs")
381
382 ;; Make /root an overlay of the tmpfs and the actual root.
383 (mount "none" "/root" "overlay" 0
384 "lowerdir=/real-root,upperdir=/rw-root/upper,workdir=/rw-root/work"))
4dfbdcbc
LC
385 (begin
386 (check-file-system root type)
55e21617 387 (mount root "/root" type flags options)))
b1995341 388
9331ba5d 389 ;; Make sure /root/etc/mtab is a symlink to /proc/self/mounts.
01ed3c4f 390 (false-if-exception
9331ba5d 391 (delete-file "/root/etc/mtab"))
748d4a84 392 (mkdir-p "/root/etc")
9331ba5d 393 (symlink "/proc/self/mounts" "/root/etc/mtab"))
83bcd0b8 394
1d462832
LC
395(define (switch-root root)
396 "Switch to ROOT as the root file system, in a way similar to what
397util-linux' switch_root(8) does."
398 (move-essential-file-systems root)
399 (chdir root)
26a728eb
LC
400
401 ;; Since we're about to 'rm -rf /', try to make sure we're on an initrd.
402 ;; TODO: Use 'statfs' to check the fs type, like klibc does.
403 (when (or (not (file-exists? "/init")) (directory-exists? "/home"))
404 (format (current-error-port)
405 "The root file system is probably not an initrd; \
406bailing out.~%root contents: ~s~%" (scandir "/"))
407 (force-output (current-error-port))
408 (exit 1))
409
410 ;; Delete files from the old root, without crossing mount points (assuming
411 ;; there are no mount points in sub-directories.) That means we're leaving
412 ;; the empty ROOT directory behind us, but that's OK.
413 (let ((root-device (stat:dev (stat "/"))))
414 (for-each (lambda (file)
415 (unless (member file '("." ".."))
416 (let* ((file (string-append "/" file))
417 (device (stat:dev (lstat file))))
418 (when (= device root-device)
419 (delete-file-recursively file)))))
420 (scandir "/")))
421
422 ;; Make ROOT the new root.
1d462832 423 (mount root "/" "" MS_MOVE)
26a728eb
LC
424 (chroot ".")
425 (chdir "/")
426
427 (when (file-exists? "/dev/console")
428 ;; Close the standard file descriptors since they refer to the old
474b832d
LC
429 ;; /dev/console, and reopen them.
430 (let ((console (open-file "/dev/console" "r+b0")))
431 (for-each close-fdes '(0 1 2))
432
433 (dup2 (fileno console) 0)
434 (dup2 (fileno console) 1)
435 (dup2 (fileno console) 2)
436
437 (close-port console))))
1d462832 438
85a83edb 439\f
d4254711
LC
440(define* (boot-system #:key
441 (linux-modules '())
0e704a2d 442 linux-module-directory
ae7a316b 443 keymap-file
d4254711 444 qemu-guest-networking?
3c05b4bc 445 volatile-root?
de1c158f 446 pre-mount
aeed74f3
LC
447 (mounts '())
448 (on-error 'debug))
d4254711 449 "This procedure is meant to be called from an initrd. Boot a system by
0e704a2d 450first loading LINUX-MODULES (a list of module names) from
ae7a316b
LC
451LINUX-MODULE-DIRECTORY, then installing KEYMAP-FILE with 'loadkeys' (if
452KEYMAP-FILE is true), then setting up QEMU guest networking if
0e704a2d
LC
453QEMU-GUEST-NETWORKING? is true, calling PRE-MOUNT, mounting the file systems
454specified in MOUNTS, and finally booting into the new root if any. The initrd
455supports kernel command-line options '--load', '--root', and '--repl'.
d4254711 456
3c05b4bc
LC
457Mount the root file system, specified by the '--root' command-line argument,
458if any.
03ddfaf5 459
1c65cca5 460MOUNTS must be a list of <file-system> objects.
d4254711 461
44ddf33e 462When VOLATILE-ROOT? is true, the root file system is writable but any changes
aeed74f3
LC
463to it are lost.
464
465ON-ERROR is passed to 'call-with-error-handling'; it determines what happens
466upon error."
1c65cca5
LC
467 (define (root-mount-point? fs)
468 (string=? (file-system-mount-point fs) "/"))
3c05b4bc 469
281d80d8
MC
470 (define (device-string->file-system-device device-string)
471 ;; The "--root=SPEC" kernel command-line option always provides a
1c3b709e
S
472 ;; string, but the string can represent a device, an nfs-root, a UUID, or a
473 ;; label. So check for all four.
281d80d8 474 (cond ((string-prefix? "/" device-string) device-string)
1c3b709e 475 ((string-contains device-string ":/") device-string) ; nfs-root
281d80d8
MC
476 ((uuid device-string) => identity)
477 (else (file-system-label device-string))))
900ef20b 478
d4254711
LC
479 (display "Welcome, this is GNU's early boot Guile.\n")
480 (display "Use '--repl' for an initrd REPL.\n\n")
481
e3ced65a 482 (call-with-error-handling
c09903ac
MC
483 (lambda ()
484 (mount-essential-file-systems)
485 (let* ((args (linux-command-line))
486 (to-load (find-long-option "--load" args))
281d80d8
MC
487 (root-fs (find root-mount-point? mounts))
488 (root-fs-type (or (and=> root-fs file-system-type)
489 "ext4"))
490 (root-fs-device (and=> root-fs file-system-device))
491 (root-fs-flags (mount-flags->bit-mask
492 (or (and=> root-fs file-system-flags)
493 '())))
494 (root-options (if root-fs
495 (file-system-options root-fs)
496 #f))
497 ;; --root takes precedence over the 'device' field of the root
498 ;; <file-system> record.
499 (root-device (or (and=> (find-long-option "--root" args)
500 device-string->file-system-device)
501 root-fs-device)))
c09903ac
MC
502
503 (when (member "--repl" args)
504 (start-repl))
505
506 (display "loading kernel modules...\n")
507 (load-linux-modules-from-directory linux-modules
508 linux-module-directory)
509
510 (when keymap-file
511 (let ((status (system* "loadkeys" keymap-file)))
512 (unless (zero? status)
513 ;; Emit a warning rather than abort when we cannot load
514 ;; KEYMAP-FILE.
515 (format (current-error-port)
516 "warning: 'loadkeys' exited with status ~a~%"
517 status))))
518
519 (when qemu-guest-networking?
520 (unless (configure-qemu-networking)
521 (display "network interface is DOWN\n")))
522
523 ;; Prepare the real root file system under /root.
524 (unless (file-exists? "/root")
525 (mkdir "/root"))
526
527 (when (procedure? pre-mount)
528 ;; Do whatever actions are needed before mounting the root file
529 ;; system--e.g., installing device mappings. Error out when the
530 ;; return value is false.
531 (unless (pre-mount)
532 (error "pre-mount actions failed")))
533
534 (setenv "EXT2FS_NO_MTAB_OK" "1")
535
281d80d8
MC
536 (if root-device
537 (mount-root-file-system (canonicalize-device-spec root-device)
538 root-fs-type
539 #:volatile-root? volatile-root?
540 #:flags root-fs-flags
541 #:options root-options)
c09903ac
MC
542 (mount "none" "/root" "tmpfs"))
543
544 ;; Mount the specified file systems.
545 (for-each mount-file-system
546 (remove root-mount-point? mounts))
547
548 (setenv "EXT2FS_NO_MTAB_OK" #f)
549
550 (if to-load
551 (begin
552 (switch-root "/root")
553 (format #t "loading '~a'...\n" to-load)
554
555 (primitive-load to-load)
556
557 (format (current-error-port)
558 "boot program '~a' terminated, rebooting~%"
559 to-load)
560 (sleep 2)
561 (reboot))
562 (begin
563 (display "no boot file passed via '--load'\n")
564 (display "entering a warm and cozy REPL\n")
565 (start-repl)))))
566 #:on-error on-error))
d4254711 567
b37c5441 568;;; linux-boot.scm ends here