1 ;;; GNU Guix --- Functional package management for GNU
2 ;;; Copyright © 2013 Ludovic Courtès <ludo@gnu.org>
4 ;;; This file is part of GNU Guix.
6 ;;; GNU Guix is free software; you can redistribute it and/or modify it
7 ;;; under the terms of the GNU General Public License as published by
8 ;;; the Free Software Foundation; either version 3 of the License, or (at
9 ;;; your option) any later version.
11 ;;; GNU Guix is distributed in the hope that it will be useful, but
12 ;;; WITHOUT ANY WARRANTY; without even the implied warranty of
13 ;;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14 ;;; GNU General Public License for more details.
16 ;;; You should have received a copy of the GNU General Public License
17 ;;; along with GNU Guix. If not, see <http://www.gnu.org/licenses/>.
19 (define-module (gnu packages linux-initrd)
20 #:use-module (guix utils)
21 #:use-module (guix licenses)
22 #:use-module (gnu packages)
23 #:use-module (gnu packages cpio)
24 #:use-module (gnu packages compression)
25 #:use-module (gnu packages linux)
26 #:use-module ((gnu packages make-bootstrap)
27 #:select (%guile-static-stripped))
28 #:use-module (guix packages)
29 #:use-module (guix download)
30 #:use-module (guix build-system trivial))
35 ;;; Tools to build initial RAM disks (initrd's) for Linux-Libre, and in
36 ;;; particular initrd's that run Guile.
41 (define* (expression->initrd exp
43 (guile %guile-static-stripped)
47 (system (%current-system))
50 "Return a package that contains a Linux initrd (a gzipped cpio archive)
51 containing GUILE and that evaluates EXP upon booting. LINUX-MODULES is a list
52 of `.ko' file names to be copied from LINUX into the initrd."
53 ;; TODO: Add a `modules' parameter.
55 ;; General Linux overview in `Documentation/early-userspace/README' and
56 ;; `Documentation/filesystems/ramfs-rootfs-initramfs.txt'.
60 (use-modules (guix build utils)
68 ((system foreign) #:select (sizeof)))
70 (let ((guile (assoc-ref %build-inputs "guile"))
71 (cpio (string-append (assoc-ref %build-inputs "cpio")
73 (gzip (string-append (assoc-ref %build-inputs "gzip")
75 (out (assoc-ref %outputs "out")))
78 (with-directory-excursion "contents"
79 (copy-recursively guile ".")
80 (call-with-output-file "init"
82 (format p "#!/bin/guile -ds~%!#~%" guile)
83 (pretty-print ',exp p)))
85 (chmod "bin/guile" #o555)
88 (let ((go-dir (format #f ".cache/guile/ccache/~a-~a-~a-~a"
90 (if (eq? (native-endianness) (endianness little))
94 (effective-version))))
97 #:opts %auto-compilation-options
98 #:output-file (string-append go-dir "/init.go")))
100 (let* ((linux (assoc-ref %build-inputs "linux"))
101 (module-dir (and linux
102 (string-append linux "/lib/modules"))))
104 ,@(map (lambda (module)
105 `(match (find-files module-dir ,module)
107 (format #t "copying '~a'...~%" file)
108 (copy-file file (string-append "modules/"
111 (error "module not found" ,module module-dir))
113 (error "several modules by that name"
114 ,module module-dir))))
117 ;; Reset the timestamps of all the files that will make it in the
119 (for-each (cut utime <> 0 0 0 0)
120 (find-files "." ".*"))
122 (system* cpio "--version")
123 (let ((pipe (open-pipe* OPEN_WRITE cpio "-o"
124 "-O" (string-append out "/initrd")
125 "-H" "newc" "--null")))
127 (let ((len (string-length "./")))
129 (format pipe "~a\0" (string-drop file len)))))
131 ;; Note: as per `ramfs-rootfs-initramfs.txt', always add
132 ;; directory entries before the files that are inside of it: "The
133 ;; Linux kernel cpio extractor won't create files in a directory
134 ;; that doesn't exist, so the directory entries must go before
135 ;; the files that go in those directories."
136 (file-system-fold (const #t)
137 (lambda (file stat result) ; leaf
139 (lambda (dir stat result) ; down
140 (unless (string=? dir ".")
148 (and (zero? (close-pipe pipe))
149 (with-directory-excursion out
150 (and (zero? (system* gzip "--best" "initrd"))
151 (rename-file "initrd.gz" "initrd")))))))))
158 (build-system trivial-build-system)
159 (arguments `(#:modules ((guix build utils))
161 (inputs `(("guile" ,guile)
167 (synopsis "An initial RAM disk (initrd) for the Linux kernel")
169 "An initial RAM disk (initrd), really a gzipped cpio archive, for use by
172 (home-page "http://www.gnu.org/software/guix/"))))
174 (define-public qemu-initrd
177 (use-modules (rnrs io ports)
181 ((system foreign) #:select (string->pointer))
182 ((system base compile) #:select (compile-file)))
184 (display "Welcome, this is GNU/Guile!\n")
185 (display "Use '--repl' for an initrd REPL.\n\n")
188 (mount "none" "/proc" "proc")
191 (mount "none" "/sys" "sysfs")
193 (let* ((command (string-trim-both
194 (call-with-input-file "/proc/cmdline"
196 (args (string-split command char-set:blank))
197 (option (lambda (opt)
198 (let ((opt (string-append opt "=")))
199 (and=> (find (cut string-prefix? opt <>)
202 (substring arg (+ 1 (string-index arg #\=))))))))
203 (to-load (option "--load"))
204 (root (option "--root")))
206 (when (member "--repl" args)
207 ((@ (system repl repl) start-repl)))
209 (let ((slurp (lambda (module)
210 (call-with-input-file
211 (string-append "/modules/" module)
212 get-bytevector-all))))
213 (display "loading CIFS and companion modules...\n")
214 (for-each (compose load-linux-module slurp)
215 (list "md4.ko" "ecb.ko" "cifs.ko")))
217 ;; See net/slirp.c for default QEMU networking values.
218 (display "configuring network...\n")
219 (let* ((sock (socket AF_INET SOCK_STREAM 0))
220 (address (make-socket-address AF_INET
224 (flags (network-interface-flags sock "eth0")))
225 (set-network-interface-address sock "eth0" address)
226 (set-network-interface-flags sock "eth0"
227 (logior flags IFF_UP))
228 (if (logand (network-interface-flags sock "eth0") IFF_UP)
229 (display "network interface is up\n")
230 (display "network interface is DOWN\n"))
233 (call-with-output-file "/etc/resolv.conf"
235 (display "nameserver 10.0.2.3\n" p)))
238 ;; Prepare the real root file system under /root.
239 (unless (file-exists? "/root")
242 (mount root "/root" "ext3")
243 (mount "none" "/root" "tmpfs"))
245 (mount "none" "/root/proc" "proc")
247 (mount "none" "/root/sys" "sysfs")
250 (mkdir "/root/nix/store")
253 (let ((makedev (lambda (major minor)
254 (+ (* major 256) minor))))
255 (mknod "/root/dev/null" 'char-special #o666 (makedev 1 3))
256 (mknod "/root/dev/zero" 'char-special #o666 (makedev 1 5)))
258 ;; Mount the host's store and exchange directory.
259 (display "mounting QEMU's SMB shares...\n")
260 (let ((server "10.0.2.4"))
261 (mount (string-append "//" server "/store") "/root/nix/store" "cifs" 0
262 (string->pointer "guest,sec=none"))
263 (mount (string-append "//" server "/xchg") "/root/xchg" "cifs" 0
264 (string->pointer "guest,sec=none")))
268 (format #t "loading boot file '~a'...\n" to-load)
269 (compile-file (string-append "/root/" to-load)
270 #:output-file "/root/loader.go"
271 #:opts %auto-compilation-options)
272 (match (primitive-fork)
275 (load-compiled "/loader.go"))
277 (format #t "boot file loaded under PID ~a~%" pid)
278 (let ((status (waitpid pid)))
281 (display "no boot file passed via '--load'\n")
282 (display "entering a warm and cozy REPL\n")
283 ((@ (system repl repl) start-repl))))))
286 #:linux-modules '("cifs.ko" "md4.ko" "ecb.ko")))
288 ;;; linux-initrd.scm ends here