1 ;;; GNU Guix --- Functional package management for GNU
2 ;;; Copyright © 2017 David Craven <david@craven.ch>
3 ;;; Copyright © 2017, 2020 Mathieu Othacehe <m.othacehe@gmail.com>
4 ;;; Copyright © 2017 Leo Famulari <leo@famulari.name>
5 ;;; Copyright © 2019, 2021 Ludovic Courtès <ludo@gnu.org>
6 ;;; Copyright © 2020 Jan (janneke) Nieuwenhuizen <janneke@gnu.org>
8 ;;; This file is part of GNU Guix.
10 ;;; GNU Guix is free software; you can redistribute it and/or modify it
11 ;;; under the terms of the GNU General Public License as published by
12 ;;; the Free Software Foundation; either version 3 of the License, or (at
13 ;;; your option) any later version.
15 ;;; GNU Guix is distributed in the hope that it will be useful, but
16 ;;; WITHOUT ANY WARRANTY; without even the implied warranty of
17 ;;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
18 ;;; GNU General Public License for more details.
20 ;;; You should have received a copy of the GNU General Public License
21 ;;; along with GNU Guix. If not, see <http://www.gnu.org/licenses/>.
23 (define-module (gnu bootloader)
24 #:use-module (guix discovery)
25 #:use-module (guix gexp)
26 #:use-module (guix profiles)
27 #:use-module (guix records)
28 #:use-module (guix deprecation)
29 #:use-module ((guix ui) #:select (warn-about-load-error))
30 #:use-module (guix diagnostics)
31 #:use-module (guix i18n)
32 #:use-module (srfi srfi-1)
33 #:use-module (ice-9 match)
39 menu-entry-linux-arguments
41 menu-entry-device-mount-point
42 menu-entry-multiboot-kernel
43 menu-entry-multiboot-arguments
44 menu-entry-multiboot-modules
54 bootloader-disk-image-installer
55 bootloader-configuration-file
56 bootloader-configuration-file-generator
58 bootloader-configuration
59 bootloader-configuration?
60 bootloader-configuration-bootloader
61 bootloader-configuration-target ;deprecated
62 bootloader-configuration-targets
63 bootloader-configuration-menu-entries
64 bootloader-configuration-default-entry
65 bootloader-configuration-timeout
66 bootloader-configuration-keyboard-layout
67 bootloader-configuration-theme
68 bootloader-configuration-terminal-outputs
69 bootloader-configuration-terminal-inputs
70 bootloader-configuration-serial-unit
71 bootloader-configuration-serial-speed
72 bootloader-configuration-additional-configuration
75 lookup-bootloader-by-name
77 efi-bootloader-chain))
81 ;;; Menu-entry record.
84 (define-record-type* <menu-entry>
85 menu-entry make-menu-entry
87 (label menu-entry-label)
88 (device menu-entry-device ; file system uuid, label, or #f
90 (device-mount-point menu-entry-device-mount-point
92 (linux menu-entry-linux
94 (linux-arguments menu-entry-linux-arguments
95 (default '())) ; list of string-valued gexps
96 (initrd menu-entry-initrd ; file name of the initrd as a gexp
98 (multiboot-kernel menu-entry-multiboot-kernel
100 (multiboot-arguments menu-entry-multiboot-arguments
101 (default '())) ; list of string-valued gexps
102 (multiboot-modules menu-entry-multiboot-modules
103 (default '()))) ; list of multiboot commands, where
104 ; a command is a list of <string>
106 (define (menu-entry->sexp entry)
107 "Return ENTRY serialized as an sexp."
109 (($ <menu-entry> label device mount-point linux linux-arguments initrd #f
111 `(menu-entry (version 0)
114 (device-mount-point ,mount-point)
116 (linux-arguments ,linux-arguments)
118 (($ <menu-entry> label device mount-point #f () #f
119 multiboot-kernel multiboot-arguments multiboot-modules)
120 `(menu-entry (version 0)
123 (device-mount-point ,mount-point)
124 (multiboot-kernel ,multiboot-kernel)
125 (multiboot-arguments ,multiboot-arguments)
126 (multiboot-modules ,multiboot-modules)))))
128 (define (sexp->menu-entry sexp)
129 "Turn SEXP, an sexp as returned by 'menu-entry->sexp', into a <menu-entry>
132 (('menu-entry ('version 0)
133 ('label label) ('device device)
134 ('device-mount-point mount-point)
135 ('linux linux) ('linux-arguments linux-arguments)
136 ('initrd initrd) _ ...)
140 (device-mount-point mount-point)
142 (linux-arguments linux-arguments)
144 (('menu-entry ('version 0)
145 ('label label) ('device device)
146 ('device-mount-point mount-point)
147 ('multiboot-kernel multiboot-kernel)
148 ('multiboot-arguments multiboot-arguments)
149 ('multiboot-modules multiboot-modules) _ ...)
153 (device-mount-point mount-point)
154 (multiboot-kernel multiboot-kernel)
155 (multiboot-arguments multiboot-arguments)
156 (multiboot-modules multiboot-modules)))))
160 ;;; Bootloader record.
163 ;; The <bootloader> record contains fields expressing how the bootloader
164 ;; should be installed. Every bootloader in gnu/bootloader/ directory
165 ;; has to be described by this record.
167 (define-record-type* <bootloader>
168 bootloader make-bootloader
170 (name bootloader-name)
171 (package bootloader-package)
172 (installer bootloader-installer)
173 (disk-image-installer bootloader-disk-image-installer
175 (configuration-file bootloader-configuration-file)
176 (configuration-file-generator bootloader-configuration-file-generator))
180 ;;; Bootloader configuration record.
183 ;; The <bootloader-configuration> record contains bootloader independant
184 ;; configuration used to fill bootloader configuration file.
186 (define-with-syntax-properties (warn-target-field-deprecation
189 (warning (source-properties->location properties)
190 (G_ "the 'target' field is deprecated, please use 'targets' \
194 (define-record-type* <bootloader-configuration>
195 bootloader-configuration make-bootloader-configuration
196 bootloader-configuration?
197 (bootloader bootloader-configuration-bootloader) ;<bootloader>
198 (targets %bootloader-configuration-targets ;list of strings
200 (target %bootloader-configuration-target ;deprecated
201 (default #f) (sanitize warn-target-field-deprecation))
202 (menu-entries bootloader-configuration-menu-entries ;list of <menu-entry>
204 (default-entry bootloader-configuration-default-entry ;integer
206 (timeout bootloader-configuration-timeout ;seconds as integer
208 (keyboard-layout bootloader-configuration-keyboard-layout ;<keyboard-layout> | #f
210 (theme bootloader-configuration-theme ;bootloader-specific theme
212 (terminal-outputs bootloader-configuration-terminal-outputs ;list of symbols
213 (default '(gfxterm)))
214 (terminal-inputs bootloader-configuration-terminal-inputs ;list of symbols
216 (serial-unit bootloader-configuration-serial-unit ;integer | #f
218 (serial-speed bootloader-configuration-serial-speed ;integer | #f
221 (define-deprecated (bootloader-configuration-target config)
222 bootloader-configuration-targets
223 (%bootloader-configuration-target config))
225 (define (bootloader-configuration-targets config)
226 (or (%bootloader-configuration-targets config)
227 ;; TODO: Remove after the deprecated 'target' field is removed.
228 (list (%bootloader-configuration-target config))
229 ;; XXX: At least the GRUB installer (see (gnu bootloader grub)) has this
230 ;; peculiar behavior of installing fonts and GRUB modules when DEVICE is #f,
231 ;; hence the default value of '(#f) rather than '().
239 (define (bootloader-modules)
240 "Return the list of bootloader modules."
241 (all-modules (map (lambda (entry)
242 `(,entry . "gnu/bootloader"))
244 #:warn warn-about-load-error))
247 ;; The list of publically-known bootloaders.
248 (delay (fold-module-public-variables (lambda (obj result)
249 (if (bootloader? obj)
253 (bootloader-modules))))
255 (define (lookup-bootloader-by-name name)
256 "Return the bootloader called NAME."
257 (or (find (lambda (bootloader)
258 (eq? name (bootloader-name bootloader)))
259 (force %bootloaders))
260 (leave (G_ "~a: no such bootloader~%") name)))
262 (define (efi-bootloader-profile files bootloader-package hooks)
263 "Creates a profile with BOOTLOADER-PACKAGE and a directory collection/ with
264 links to additional FILES from the store. This collection is meant to be used
265 by the bootloader installer.
267 FILES is a list of file or directory names from the store, which will be
268 symlinked into the collection/ directory. If a directory name ends with '/',
269 then the directory content instead of the directory itself will be symlinked
270 into the collection/ directory.
272 FILES may contain file like objects produced by functions like plain-file,
273 local-file, etc., or package contents produced with file-append.
275 HOOKS lists additional hook functions to modify the profile."
276 (define (bootloader-collection manifest)
278 (with-imported-modules '((guix build utils)
283 (use-modules ((guix build utils)
284 #:select (mkdir-p strip-store-file-name))
288 #:select (append-map every remove))
291 (define (symlink-to file directory transform)
292 "Creates a symlink to FILE named (TRANSFORM FILE) in DIRECTORY."
293 (symlink file (string-append directory "/" (transform file))))
294 (define (directory-content directory)
295 "Creates a list of absolute path names inside DIRECTORY."
297 (string-append directory name))
298 (or (scandir directory (lambda (name)
299 (not (member name '("." "..")))))
301 (define name-ends-with-/? (cut string-suffix? "/" <>))
302 (define (name-is-store-entry? name)
303 "Return #t if NAME is a direct store entry and nothing inside."
304 (not (string-index (strip-store-file-name name) #\/)))
305 (let* ((collection (string-append #$output "/collection"))
307 (directories (filter name-ends-with-/? files))
308 (names-from-directories
309 (append-map (lambda (directory)
310 (directory-content directory))
312 (names (append names-from-directories
313 (remove name-ends-with-/? files))))
315 (if (every file-exists? names)
317 (for-each (lambda (name)
318 (symlink-to name collection
319 (if (name-is-store-entry? name)
320 strip-store-file-name
326 (gexp->derivation "bootloader-collection"
331 `((type . profile-hook)
332 (hook . bootloader-collection))))
334 (profile (content (packages->manifest (list bootloader-package)))
335 (name "bootloader-profile")
336 (hooks (append (list bootloader-collection) hooks))
338 (allow-collisions? #f)
339 (relative-symlinks? #f)))
341 (define* (efi-bootloader-chain files
346 "Define a bootloader chain with FINAL-BOOTLOADER as the final bootloader and
347 certain directories and files from the store given in the list of FILES.
349 FILES may contain file like objects produced by functions like plain-file,
350 local-file, etc., or package contents produced with file-append. They will be
351 collected inside a directory collection/ inside a generated bootloader profile,
352 which will be passed to the INSTALLER.
354 If a directory name in FILES ends with '/', then the directory content instead
355 of the directory itself will be symlinked into the collection/ directory.
357 The procedures in the HOOKS list can be used to further modify the bootloader
358 profile. It is possible to pass a single function instead of a list.
360 If the INSTALLER argument is used, then this function will be called to install
361 the bootloader. Otherwise the installer of the FINAL-BOOTLOADER will be called."
362 (let* ((final-installer (or installer
363 (bootloader-installer final-bootloader)))
364 (profile (efi-bootloader-profile files
365 (bootloader-package final-bootloader)
370 (inherit final-bootloader)
373 #~(lambda (bootloader target mount-point)
374 (#$final-installer bootloader target mount-point)
376 (string-append bootloader "/collection")
377 (string-append mount-point target)
378 #:follow-symlinks? #t
379 #:log (%make-void-port "w")))))))