summary refs log tree commit diff
path: root/gnu/build
diff options
context:
space:
mode:
Diffstat (limited to 'gnu/build')
-rw-r--r--gnu/build/bootloader.scm37
-rw-r--r--gnu/build/linux-boot.scm107
-rw-r--r--gnu/build/vm.scm44
3 files changed, 178 insertions, 10 deletions
diff --git a/gnu/build/bootloader.scm b/gnu/build/bootloader.scm
new file mode 100644
index 0000000000..d00674dd40
--- /dev/null
+++ b/gnu/build/bootloader.scm
@@ -0,0 +1,37 @@
+;;; GNU Guix --- Functional package management for GNU
+;;; Copyright © 2017 Mathieu Othacehe <m.othacehe@gmail.com>
+;;;
+;;; This file is part of GNU Guix.
+;;;
+;;; GNU Guix is free software; you can redistribute it and/or modify it
+;;; under the terms of the GNU General Public License as published by
+;;; the Free Software Foundation; either version 3 of the License, or (at
+;;; your option) any later version.
+;;;
+;;; GNU Guix is distributed in the hope that it will be useful, but
+;;; WITHOUT ANY WARRANTY; without even the implied warranty of
+;;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+;;; GNU General Public License for more details.
+;;;
+;;; You should have received a copy of the GNU General Public License
+;;; along with GNU Guix.  If not, see <http://www.gnu.org/licenses/>.
+
+(define-module (gnu build bootloader)
+  #:use-module (ice-9 binary-ports)
+  #:export (write-file-on-device))
+
+
+;;;
+;;; Writing utils.
+;;;
+
+(define (write-file-on-device file size device offset)
+  "Write SIZE bytes from FILE to DEVICE starting at OFFSET."
+  (call-with-input-file file
+    (lambda (input)
+      (let ((bv (get-bytevector-n input size)))
+        (call-with-output-file device
+          (lambda (output)
+            (seek output offset SEEK_SET)
+            (put-bytevector output bv))
+          #:binary #t)))))
diff --git a/gnu/build/linux-boot.scm b/gnu/build/linux-boot.scm
index 2547f1e0af..4dd740174e 100644
--- a/gnu/build/linux-boot.scm
+++ b/gnu/build/linux-boot.scm
@@ -22,8 +22,11 @@
   #:use-module (system repl error-handling)
   #:autoload   (system repl repl) (start-repl)
   #:use-module (srfi srfi-1)
+  #:use-module (srfi srfi-9)
   #:use-module (srfi srfi-26)
   #:use-module (ice-9 match)
+  #:use-module (ice-9 rdelim)
+  #:use-module (ice-9 regex)
   #:use-module (ice-9 ftw)
   #:use-module (guix build utils)
   #:use-module ((guix build syscalls)
@@ -35,6 +38,7 @@
             linux-command-line
             find-long-option
             make-essential-device-nodes
+            make-static-device-nodes
             configure-qemu-networking
 
             bind-mount
@@ -105,6 +109,109 @@ with the given MAJOR number, starting with MINOR."
              'block-special #o644 (device-number major (+ minor i)))
       (loop (+ i 1)))))
 
+;; Representation of a /dev node.
+(define-record-type <device-node>
+  (device-node name type major minor module)
+  device-node?
+  (name device-node-name)
+  (type device-node-type)
+  (major device-node-major)
+  (minor device-node-minor)
+  (module device-node-module))
+
+(define (read-static-device-nodes port)
+  "Read from PORT a list of <device-node> written in the format used by
+/lib/modules/*/*.devname files."
+  (let loop ((line (read-line port)))
+    (if (eof-object? line)
+      '()
+      (match (string-split line #\space)
+       (((? (cut string-prefix? "#" <>)) _ ...)
+        (loop (read-line port)))
+       ((module-name device-name device-spec)
+        (let* ((device-parts
+               (string-match "([bc])([0-9][0-9]*):([0-9][0-9]*)"
+                             device-spec))
+               (type-string (match:substring device-parts 1))
+               (type (match type-string
+                      ("c" 'char-special)
+                      ("b" 'block-special)))
+               (major-string (match:substring device-parts 2))
+               (major (string->number major-string 10))
+               (minor-string (match:substring device-parts 3))
+               (minor (string->number minor-string 10)))
+          (cons (device-node device-name type major minor module-name)
+                (loop (read-line port)))))
+       (_
+        (begin
+          (format (current-error-port)
+                  "read-static-device-nodes: ignored devname line '~a'~%" line)
+          (loop (read-line port))))))))
+
+(define* (mkdir-p* dir #:optional (mode #o755))
+  "This is a variant of 'mkdir-p' that works around
+<http://bugs.gnu.org/24659> by passing MODE explicitly in each 'mkdir' call."
+  (define absolute?
+    (string-prefix? "/" dir))
+
+  (define not-slash
+    (char-set-complement (char-set #\/)))
+
+  (let loop ((components (string-tokenize dir not-slash))
+             (root       (if absolute?
+                             ""
+                             ".")))
+    (match components
+      ((head tail ...)
+       (let ((path (string-append root "/" head)))
+         (catch 'system-error
+           (lambda ()
+             (mkdir path mode)
+             (loop tail path))
+           (lambda args
+             (if (= EEXIST (system-error-errno args))
+                 (loop tail path)
+                 (apply throw args))))))
+      (() #t))))
+
+(define (report-system-error name . args)
+  "Report a system error for the file NAME."
+  (let ((errno (system-error-errno args)))
+    (format (current-error-port) "could not create '~a': ~a~%" name
+            (strerror errno))))
+
+;; Catch a system-error, log it and don't die from it.
+(define-syntax-rule (catch-system-error name exp)
+  (catch 'system-error
+    (lambda ()
+      exp)
+    (lambda args
+      (apply report-system-error name args))))
+
+;; Create a device node like the <device-node> passed here on the filesystem.
+(define create-device-node
+  (match-lambda
+    (($ <device-node> xname type major minor module)
+     (let ((name (string-append "/dev/" xname)))
+       (mkdir-p* (dirname name))
+       (catch-system-error name
+         (mknod name type #o600 (device-number major minor)))))))
+
+(define* (make-static-device-nodes linux-release-module-directory)
+  "Create static device nodes required by the given Linux release.
+This is required in order to solve a chicken-or-egg problem:
+The Linux kernel has a feature to autoload modules when a device is first
+accessed.
+And udev has a feature to set the permissions of static nodes correctly
+when it is starting up and also to automatically create nodes when hardware
+is hotplugged. That leaves universal device files which are not linked to
+one specific hardware device. These we have to create."
+  (let ((devname-name (string-append linux-release-module-directory "/"
+                                     "modules.devname")))
+    (for-each create-device-node
+              (call-with-input-file devname-name
+                                    read-static-device-nodes))))
+
 (define* (make-essential-device-nodes #:key (root "/"))
   "Make essential device nodes under ROOT/dev."
   ;; The hand-made devtmpfs/udev!
diff --git a/gnu/build/vm.scm b/gnu/build/vm.scm
index 20ee12709b..404f324045 100644
--- a/gnu/build/vm.scm
+++ b/gnu/build/vm.scm
@@ -1,7 +1,7 @@
 ;;; GNU Guix --- Functional package management for GNU
 ;;; Copyright © 2013, 2014, 2015, 2016, 2017 Ludovic Courtès <ludo@gnu.org>
 ;;; Copyright © 2016 Christopher Allan Webber <cwebber@dustycloud.org>
-;;; Copyright © 2016 Leo Famulari <leo@famulari.name>
+;;; Copyright © 2016, 2017 Leo Famulari <leo@famulari.name>
 ;;; Copyright © 2017 Mathieu Othacehe <m.othacehe@gmail.com>
 ;;; Copyright © 2017 Marius Bakke <mbakke@fastmail.com>
 ;;;
@@ -77,6 +77,7 @@
                            linux initrd
                            make-disk-image?
                            single-file-output?
+                           target-arm32?
                            (disk-image-size (* 100 (expt 2 20)))
                            (disk-image-format "qcow2")
                            (references-graphs '()))
@@ -91,6 +92,31 @@ access it via /dev/hda.
 
 REFERENCES-GRAPHS can specify a list of reference-graph files as produced by
 the #:references-graphs parameter of 'derivation'."
+
+  (define arch-specific-flags
+    `(;; On ARM, a machine has to be specified. Use "virt" machine to avoid
+      ;; hardware limits imposed by other machines.
+      ,@(if target-arm32? '("-M" "virt") '())
+
+      ;; Only enable kvm if we see /dev/kvm exists.  This allows users without
+      ;; hardware virtualization to still use these commands.  KVM support is
+      ;; still buggy on some ARM32 boards. Do not use it even if available.
+      ,@(if (and (file-exists? "/dev/kvm")
+                 (not target-arm32?))
+            '("-enable-kvm")
+            '())
+      "-append"
+      ;; The serial port name differs between emulated architectures/machines.
+      ,@(if target-arm32?
+            `(,(string-append "console=ttyAMA0 --load=" builder))
+            `(,(string-append "console=ttyS0 --load=" builder)))
+      ;; NIC is not supported on ARM "virt" machine, so use a user mode
+      ;; network stack instead.
+      ,@(if target-arm32?
+            '("-device" "virtio-net-pci,netdev=mynet"
+              "-netdev" "user,id=mynet")
+            '("-net" "nic,model=virtio"))))
+
   (when make-disk-image?
     (format #t "creating ~a image of ~,2f MiB...~%"
             disk-image-format (/ disk-image-size (expt 2 20)))
@@ -113,7 +139,8 @@ the #:references-graphs parameter of 'derivation'."
   (unless (zero?
            (apply system* qemu "-nographic" "-no-reboot"
                   "-m" (number->string memory-size)
-                  "-net" "nic,model=virtio"
+                  "-object" "rng-random,filename=/dev/urandom,id=guixsd-vm-rng"
+                  "-device" "virtio-rng-pci,rng=guixsd-vm-rng"
                   "-virtfs"
                   (string-append "local,id=store_dev,path="
                                  (%store-directory)
@@ -127,15 +154,12 @@ the #:references-graphs parameter of 'derivation'."
                                            builder)
                   (append
                    (if make-disk-image?
-                       `("-drive" ,(string-append "file=" output
-                                                  ",if=virtio"))
+                       `("-device" "virtio-blk,drive=myhd"
+                         "-drive" ,(string-append "if=none,file=" output
+                                                  ",format=" disk-image-format
+                                                  ",id=myhd"))
                        '())
-                   ;; Only enable kvm if we see /dev/kvm exists.
-                   ;; This allows users without hardware virtualization to still
-                   ;; use these commands.
-                   (if (file-exists? "/dev/kvm")
-                       '("-enable-kvm")
-                       '()))))
+                   arch-specific-flags)))
     (error "qemu failed" qemu))
 
   ;; When MAKE-DISK-IMAGE? is true, the image is in OUTPUT already.