Commit | Line | Data |
---|---|---|
b09a8da4 MO |
1 | ;;; GNU Guix --- Functional package management for GNU |
2 | ;;; Copyright © 2017 David Craven <david@craven.ch> | |
7feefb3b | 3 | ;;; Copyright © 2017, 2020 Mathieu Othacehe <m.othacehe@gmail.com> |
b09a8da4 | 4 | ;;; Copyright © 2017 Leo Famulari <leo@famulari.name> |
baf4272d | 5 | ;;; Copyright © 2019, 2021 Ludovic Courtès <ludo@gnu.org> |
21acd8d6 | 6 | ;;; Copyright © 2020 Jan (janneke) Nieuwenhuizen <janneke@gnu.org> |
b09a8da4 MO |
7 | ;;; |
8 | ;;; This file is part of GNU Guix. | |
9 | ;;; | |
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. | |
14 | ;;; | |
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. | |
19 | ;;; | |
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/>. | |
22 | ||
23 | (define-module (gnu bootloader) | |
24 | #:use-module (guix discovery) | |
74eeb11d S |
25 | #:use-module (guix gexp) |
26 | #:use-module (guix profiles) | |
b09a8da4 | 27 | #:use-module (guix records) |
baf4272d LC |
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) | |
b09a8da4 | 32 | #:use-module (srfi srfi-1) |
a28cfee8 | 33 | #:use-module (ice-9 match) |
8b22107e MO |
34 | #:export (menu-entry |
35 | menu-entry? | |
36 | menu-entry-label | |
37 | menu-entry-device | |
38 | menu-entry-linux | |
39 | menu-entry-linux-arguments | |
40 | menu-entry-initrd | |
1975c754 | 41 | menu-entry-device-mount-point |
21acd8d6 JN |
42 | menu-entry-multiboot-kernel |
43 | menu-entry-multiboot-arguments | |
44 | menu-entry-multiboot-modules | |
8b22107e | 45 | |
a28cfee8 LC |
46 | menu-entry->sexp |
47 | sexp->menu-entry | |
48 | ||
8b22107e | 49 | bootloader |
b09a8da4 MO |
50 | bootloader? |
51 | bootloader-name | |
52 | bootloader-package | |
53 | bootloader-installer | |
7feefb3b | 54 | bootloader-disk-image-installer |
b09a8da4 MO |
55 | bootloader-configuration-file |
56 | bootloader-configuration-file-generator | |
57 | ||
58 | bootloader-configuration | |
59 | bootloader-configuration? | |
60 | bootloader-configuration-bootloader | |
2ca982ff MC |
61 | bootloader-configuration-target ;deprecated |
62 | bootloader-configuration-targets | |
b09a8da4 MO |
63 | bootloader-configuration-menu-entries |
64 | bootloader-configuration-default-entry | |
65 | bootloader-configuration-timeout | |
8d058e7b | 66 | bootloader-configuration-keyboard-layout |
b09a8da4 MO |
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 | |
73 | ||
74 | %bootloaders | |
74eeb11d S |
75 | lookup-bootloader-by-name |
76 | ||
77 | efi-bootloader-chain)) | |
b09a8da4 MO |
78 | |
79 | \f | |
8b22107e MO |
80 | ;;; |
81 | ;;; Menu-entry record. | |
82 | ;;; | |
83 | ||
84 | (define-record-type* <menu-entry> | |
85 | menu-entry make-menu-entry | |
86 | menu-entry? | |
87 | (label menu-entry-label) | |
88 | (device menu-entry-device ; file system uuid, label, or #f | |
89 | (default #f)) | |
1975c754 DM |
90 | (device-mount-point menu-entry-device-mount-point |
91 | (default #f)) | |
21acd8d6 JN |
92 | (linux menu-entry-linux |
93 | (default #f)) | |
8b22107e MO |
94 | (linux-arguments menu-entry-linux-arguments |
95 | (default '())) ; list of string-valued gexps | |
21acd8d6 JN |
96 | (initrd menu-entry-initrd ; file name of the initrd as a gexp |
97 | (default #f)) | |
98 | (multiboot-kernel menu-entry-multiboot-kernel | |
99 | (default #f)) | |
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> | |
8b22107e | 105 | |
a28cfee8 LC |
106 | (define (menu-entry->sexp entry) |
107 | "Return ENTRY serialized as an sexp." | |
108 | (match entry | |
21acd8d6 JN |
109 | (($ <menu-entry> label device mount-point linux linux-arguments initrd #f |
110 | ()) | |
a28cfee8 LC |
111 | `(menu-entry (version 0) |
112 | (label ,label) | |
113 | (device ,device) | |
114 | (device-mount-point ,mount-point) | |
115 | (linux ,linux) | |
116 | (linux-arguments ,linux-arguments) | |
21acd8d6 JN |
117 | (initrd ,initrd))) |
118 | (($ <menu-entry> label device mount-point #f () #f | |
119 | multiboot-kernel multiboot-arguments multiboot-modules) | |
120 | `(menu-entry (version 0) | |
121 | (label ,label) | |
122 | (device ,device) | |
123 | (device-mount-point ,mount-point) | |
124 | (multiboot-kernel ,multiboot-kernel) | |
125 | (multiboot-arguments ,multiboot-arguments) | |
126 | (multiboot-modules ,multiboot-modules))))) | |
a28cfee8 LC |
127 | |
128 | (define (sexp->menu-entry sexp) | |
129 | "Turn SEXP, an sexp as returned by 'menu-entry->sexp', into a <menu-entry> | |
130 | record." | |
131 | (match sexp | |
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) _ ...) | |
137 | (menu-entry | |
138 | (label label) | |
139 | (device device) | |
140 | (device-mount-point mount-point) | |
141 | (linux linux) | |
142 | (linux-arguments linux-arguments) | |
21acd8d6 JN |
143 | (initrd initrd))) |
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) _ ...) | |
150 | (menu-entry | |
151 | (label label) | |
152 | (device device) | |
153 | (device-mount-point mount-point) | |
154 | (multiboot-kernel multiboot-kernel) | |
155 | (multiboot-arguments multiboot-arguments) | |
156 | (multiboot-modules multiboot-modules))))) | |
a28cfee8 | 157 | |
8b22107e | 158 | \f |
b09a8da4 MO |
159 | ;;; |
160 | ;;; Bootloader record. | |
161 | ;;; | |
162 | ||
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. | |
166 | ||
167 | (define-record-type* <bootloader> | |
168 | bootloader make-bootloader | |
169 | bootloader? | |
170 | (name bootloader-name) | |
171 | (package bootloader-package) | |
172 | (installer bootloader-installer) | |
7feefb3b MO |
173 | (disk-image-installer bootloader-disk-image-installer |
174 | (default #f)) | |
b09a8da4 MO |
175 | (configuration-file bootloader-configuration-file) |
176 | (configuration-file-generator bootloader-configuration-file-generator)) | |
177 | ||
178 | \f | |
179 | ;;; | |
180 | ;;; Bootloader configuration record. | |
181 | ;;; | |
182 | ||
183 | ;; The <bootloader-configuration> record contains bootloader independant | |
184 | ;; configuration used to fill bootloader configuration file. | |
185 | ||
7c414262 JP |
186 | (define-with-syntax-properties (warn-target-field-deprecation |
187 | (value properties)) | |
188 | (when value | |
189 | (warning (source-properties->location properties) | |
190 | (G_ "the 'target' field is deprecated, please use 'targets' \ | |
191 | instead~%"))) | |
192 | value) | |
baf4272d | 193 | |
b09a8da4 MO |
194 | (define-record-type* <bootloader-configuration> |
195 | bootloader-configuration make-bootloader-configuration | |
196 | bootloader-configuration? | |
91b6873b | 197 | (bootloader bootloader-configuration-bootloader) ;<bootloader> |
2ca982ff MC |
198 | (targets %bootloader-configuration-targets ;list of strings |
199 | (default #f)) | |
200 | (target %bootloader-configuration-target ;deprecated | |
baf4272d | 201 | (default #f) (sanitize warn-target-field-deprecation)) |
ac9cd78e | 202 | (menu-entries bootloader-configuration-menu-entries ;list of <menu-entry> |
91b6873b LC |
203 | (default '())) |
204 | (default-entry bootloader-configuration-default-entry ;integer | |
205 | (default 0)) | |
206 | (timeout bootloader-configuration-timeout ;seconds as integer | |
207 | (default 5)) | |
8d058e7b LC |
208 | (keyboard-layout bootloader-configuration-keyboard-layout ;<keyboard-layout> | #f |
209 | (default #f)) | |
91b6873b LC |
210 | (theme bootloader-configuration-theme ;bootloader-specific theme |
211 | (default #f)) | |
212 | (terminal-outputs bootloader-configuration-terminal-outputs ;list of symbols | |
213 | (default '(gfxterm))) | |
214 | (terminal-inputs bootloader-configuration-terminal-inputs ;list of symbols | |
215 | (default '())) | |
216 | (serial-unit bootloader-configuration-serial-unit ;integer | #f | |
217 | (default #f)) | |
218 | (serial-speed bootloader-configuration-serial-speed ;integer | #f | |
219 | (default #f))) | |
b09a8da4 | 220 | |
baf4272d LC |
221 | (define-deprecated (bootloader-configuration-target config) |
222 | bootloader-configuration-targets | |
2ca982ff MC |
223 | (%bootloader-configuration-target config)) |
224 | ||
225 | (define (bootloader-configuration-targets config) | |
226 | (or (%bootloader-configuration-targets config) | |
227 | ;; TODO: Remove after the deprecated 'target' field is removed. | |
baf4272d | 228 | (list (%bootloader-configuration-target config)) |
2ca982ff MC |
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 '(). | |
232 | (list #f))) | |
233 | ||
b09a8da4 MO |
234 | \f |
235 | ;;; | |
236 | ;;; Bootloaders. | |
237 | ;;; | |
238 | ||
239 | (define (bootloader-modules) | |
240 | "Return the list of bootloader modules." | |
241 | (all-modules (map (lambda (entry) | |
242 | `(,entry . "gnu/bootloader")) | |
3c0128b0 LC |
243 | %load-path) |
244 | #:warn warn-about-load-error)) | |
b09a8da4 MO |
245 | |
246 | (define %bootloaders | |
247 | ;; The list of publically-known bootloaders. | |
248 | (delay (fold-module-public-variables (lambda (obj result) | |
249 | (if (bootloader? obj) | |
250 | (cons obj result) | |
251 | result)) | |
252 | '() | |
253 | (bootloader-modules)))) | |
254 | ||
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))) | |
74eeb11d | 261 | |
ede4117f | 262 | (define (efi-bootloader-profile files bootloader-package hooks) |
74eeb11d S |
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. | |
266 | ||
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. | |
271 | ||
272 | FILES may contain file like objects produced by functions like plain-file, | |
ede4117f S |
273 | local-file, etc., or package contents produced with file-append. |
274 | ||
275 | HOOKS lists additional hook functions to modify the profile." | |
74eeb11d S |
276 | (define (bootloader-collection manifest) |
277 | (define build | |
278 | (with-imported-modules '((guix build utils) | |
279 | (ice-9 ftw) | |
280 | (srfi srfi-1) | |
281 | (srfi srfi-26)) | |
282 | #~(begin | |
283 | (use-modules ((guix build utils) | |
284 | #:select (mkdir-p strip-store-file-name)) | |
285 | ((ice-9 ftw) | |
286 | #:select (scandir)) | |
287 | ((srfi srfi-1) | |
288 | #:select (append-map every remove)) | |
289 | ((srfi srfi-26) | |
290 | #:select (cut))) | |
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." | |
296 | (map (lambda (name) | |
297 | (string-append directory name)) | |
298 | (or (scandir directory (lambda (name) | |
299 | (not (member name '("." ".."))))) | |
300 | '()))) | |
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")) | |
306 | (files '#$files) | |
307 | (directories (filter name-ends-with-/? files)) | |
308 | (names-from-directories | |
309 | (append-map (lambda (directory) | |
310 | (directory-content directory)) | |
311 | directories)) | |
312 | (names (append names-from-directories | |
313 | (remove name-ends-with-/? files)))) | |
314 | (mkdir-p collection) | |
315 | (if (every file-exists? names) | |
316 | (begin | |
317 | (for-each (lambda (name) | |
318 | (symlink-to name collection | |
319 | (if (name-is-store-entry? name) | |
320 | strip-store-file-name | |
321 | basename))) | |
322 | names) | |
323 | #t) | |
324 | #f))))) | |
325 | ||
326 | (gexp->derivation "bootloader-collection" | |
327 | build | |
328 | #:local-build? #t | |
329 | #:substitutable? #f | |
330 | #:properties | |
331 | `((type . profile-hook) | |
332 | (hook . bootloader-collection)))) | |
333 | ||
334 | (profile (content (packages->manifest (list bootloader-package))) | |
ede4117f S |
335 | (name "bootloader-profile") |
336 | (hooks (append (list bootloader-collection) hooks)) | |
74eeb11d S |
337 | (locales? #f) |
338 | (allow-collisions? #f) | |
339 | (relative-symlinks? #f))) | |
340 | ||
341 | (define* (efi-bootloader-chain files | |
342 | final-bootloader | |
343 | #:key | |
ede4117f | 344 | (hooks '()) |
74eeb11d S |
345 | installer) |
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. | |
348 | ||
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. | |
353 | ||
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. | |
356 | ||
ede4117f S |
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. | |
74eeb11d S |
359 | |
360 | If the INSTALLER argument is used, then this function will be called to install | |
ede4117f | 361 | the bootloader. Otherwise the installer of the FINAL-BOOTLOADER will be called." |
74eeb11d S |
362 | (let* ((final-installer (or installer |
363 | (bootloader-installer final-bootloader))) | |
364 | (profile (efi-bootloader-profile files | |
365 | (bootloader-package final-bootloader) | |
ede4117f S |
366 | (if (list? hooks) |
367 | hooks | |
368 | (list hooks))))) | |
74eeb11d S |
369 | (bootloader |
370 | (inherit final-bootloader) | |
371 | (package profile) | |
372 | (installer | |
373 | #~(lambda (bootloader target mount-point) | |
374 | (#$final-installer bootloader target mount-point) | |
375 | (copy-recursively | |
376 | (string-append bootloader "/collection") | |
377 | (string-append mount-point target) | |
378 | #:follow-symlinks? #t | |
379 | #:log (%make-void-port "w"))))))) |