gnu: svt-av1: Update to 0.8.3.
[jackhill/guix/guix.git] / gnu / bootloader / grub.scm
CommitLineData
0ded70f3 1;;; GNU Guix --- Functional package management for GNU
9512ba6b 2;;; Copyright © 2013, 2014, 2015, 2016, 2017, 2018, 2019, 2020 Ludovic Courtès <ludo@gnu.org>
1ef8b72a 3;;; Copyright © 2016 Chris Marusich <cmmarusich@gmail.com>
e0b2e930 4;;; Copyright © 2017 Leo Famulari <leo@famulari.name>
b09a8da4 5;;; Copyright © 2017 Mathieu Othacehe <m.othacehe@gmail.com>
6a790fe3 6;;; Copyright © 2019, 2020 Jan (janneke) Nieuwenhuizen <janneke@gnu.org>
aaffde38 7;;; Copyright © 2020 Maxim Cournoyer <maxim.cournoyer@gmail.com>
0ded70f3
LC
8;;;
9;;; This file is part of GNU Guix.
10;;;
11;;; GNU Guix is free software; you can redistribute it and/or modify it
12;;; under the terms of the GNU General Public License as published by
13;;; the Free Software Foundation; either version 3 of the License, or (at
14;;; your option) any later version.
15;;;
16;;; GNU Guix is distributed in the hope that it will be useful, but
17;;; WITHOUT ANY WARRANTY; without even the implied warranty of
18;;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
19;;; GNU General Public License for more details.
20;;;
21;;; You should have received a copy of the GNU General Public License
22;;; along with GNU Guix. If not, see <http://www.gnu.org/licenses/>.
23
b09a8da4 24(define-module (gnu bootloader grub)
0ded70f3 25 #:use-module (guix records)
46c296dc 26 #:use-module ((guix utils) #:select (%current-system))
f6a7b21d 27 #:use-module (guix gexp)
84dfb458 28 #:use-module (gnu artwork)
b09a8da4 29 #:use-module (gnu bootloader)
9b336338 30 #:use-module (gnu system uuid)
a5acc17a 31 #:use-module (gnu system file-systems)
8d058e7b 32 #:use-module (gnu system keyboard)
6a7c4636 33 #:use-module (gnu packages bootloaders)
ffde82c9 34 #:autoload (gnu packages gtk) (guile-cairo guile-rsvg)
8d058e7b 35 #:autoload (gnu packages xorg) (xkeyboard-config)
0ded70f3 36 #:use-module (ice-9 match)
6b173ac0 37 #:use-module (ice-9 regex)
0ded70f3 38 #:use-module (srfi srfi-1)
6794653e 39 #:use-module (srfi srfi-2)
99ae9ceb
LC
40 #:export (grub-image
41 grub-image?
42 grub-image-aspect-ratio
43 grub-image-file
44
45 grub-theme
46 grub-theme?
47 grub-theme-images
48 grub-theme-color-normal
49 grub-theme-color-highlight
50
51 %background-image
52 %default-theme
53
b09a8da4
MO
54 grub-bootloader
55 grub-efi-bootloader
cf189709 56 grub-mkrescue-bootloader
6a790fe3 57 grub-minimal-bootloader
d5b429ab 58
b09a8da4 59 grub-configuration))
0ded70f3
LC
60
61;;; Commentary:
62;;;
63;;; Configuration of GNU GRUB.
64;;;
65;;; Code:
66
1ef8b72a
CM
67(define (strip-mount-point mount-point file)
68 "Strip MOUNT-POINT from FILE, which is a gexp or other lowerable object
69denoting a file name."
8b22107e
MO
70 (match mount-point
71 ((? string? mount-point)
72 (if (string=? mount-point "/")
73 file
74 #~(let ((file #$file))
75 (if (string-prefix? #$mount-point file)
76 (substring #$file #$(string-length mount-point))
77 file))))
78 (#f file)))
0f65f54e 79
99ae9ceb
LC
80(define-record-type* <grub-image>
81 grub-image make-grub-image
82 grub-image?
83 (aspect-ratio grub-image-aspect-ratio ;rational number
84 (default 4/3))
85 (file grub-image-file)) ;file-valued gexp (SVG)
86
87(define-record-type* <grub-theme>
88 grub-theme make-grub-theme
89 grub-theme?
90 (images grub-theme-images
91 (default '())) ;list of <grub-image>
92 (color-normal grub-theme-color-normal
93 (default '((fg . cyan) (bg . blue))))
94 (color-highlight grub-theme-color-highlight
f52fe7c3
JN
95 (default '((fg . white) (bg . blue))))
96 (gfxmode grub-gfxmode
97 (default '("auto")))) ;list of string
99ae9ceb 98
99ae9ceb
LC
99(define %background-image
100 (grub-image
101 (aspect-ratio 4/3)
357db1f9
LC
102 (file (file-append %artwork-repository
103 "/grub/GuixSD-fully-black-4-3.svg"))))
99ae9ceb
LC
104
105(define %default-theme
106 ;; Default theme contributed by Felipe López.
107 (grub-theme
108 (images (list %background-image))
9c09760a 109 (color-highlight '((fg . yellow) (bg . black)))
99ae9ceb
LC
110 (color-normal '((fg . light-gray) (bg . black))))) ;XXX: #x303030
111
99ae9ceb
LC
112\f
113;;;
114;;; Background image & themes.
115;;;
116
b09a8da4
MO
117(define (bootloader-theme config)
118 "Return user defined theme in CONFIG if defined or %default-theme
119otherwise."
120 (or (bootloader-configuration-theme config) %default-theme))
121
ffde82c9
LC
122(define* (svg->png svg #:key width height)
123 "Build a PNG of HEIGHT x WIDTH from SVG."
46c296dc
LC
124 (computed-file "grub-image.png"
125 (with-imported-modules '((gnu build svg))
126 (with-extensions (list guile-rsvg guile-cairo)
127 #~(begin
128 (use-modules (gnu build svg))
129 (svg->png #+svg #$output
130 #:width #$width
131 #:height #$height))))))
99ae9ceb 132
6394fe65 133(define* (grub-background-image config #:key (width 1024) (height 768))
99ae9ceb
LC
134 "Return the GRUB background image defined in CONFIG with a ratio of
135WIDTH/HEIGHT, or #f if none was found."
136 (let* ((ratio (/ width height))
137 (image (find (lambda (image)
138 (= (grub-image-aspect-ratio image) ratio))
b09a8da4
MO
139 (grub-theme-images
140 (bootloader-theme config)))))
46c296dc
LC
141 (and image
142 (svg->png (grub-image-file image)
143 #:width width #:height height))))
99ae9ceb 144
1ef8b72a
CM
145(define* (eye-candy config store-device store-mount-point
146 #:key system port)
46c296dc 147 "Return a gexp that writes to PORT (a port-valued gexp) the
99ae9ceb 148'grub.cfg' part concerned with graphics mode, background images, colors, and
1ef8b72a
CM
149all that. STORE-DEVICE designates the device holding the store, and
150STORE-MOUNT-POINT is its mount point; these are used to determine where the
151background image and fonts must be searched for. SYSTEM must be the target
152system string---e.g., \"x86_64-linux\"."
6b173ac0 153 (define setup-gfxterm-body
6794653e
MC
154 (let ((gfxmode
155 (or (and-let* ((theme (bootloader-configuration-theme config))
156 (gfxmode (grub-gfxmode theme)))
157 (string-join gfxmode ";"))
158 "auto")))
159
160 ;; Intel and EFI systems need to be switched into graphics mode, whereas
161 ;; most other modern architectures have no other mode and therefore
162 ;; don't need to be switched.
163
164 ;; XXX: Do we really need to restrict to x86 systems? We could imitate
165 ;; what the GRUB default configuration does and decide based on whether
166 ;; a user provided 'gfxterm' in the terminal-outputs field of their
167 ;; bootloader-configuration record.
168 (if (string-match "^(x86_64|i[3-6]86)-" system)
169 (format #f "
170 set gfxmode=~a
aaffde38 171 insmod all_video
6794653e
MC
172 insmod gfxterm~%" gfxmode)
173 "")))
6b173ac0 174
e0b2e930 175 (define (setup-gfxterm config font-file)
b09a8da4
MO
176 (if (memq 'gfxterm (bootloader-configuration-terminal-outputs config))
177 #~(format #f "if loadfont ~a; then
e0b2e930 178 setup_gfxterm
8cf7dd24 179fi~%" #+font-file)
b09a8da4 180 ""))
e0b2e930 181
99ae9ceb 182 (define (theme-colors type)
b09a8da4 183 (let* ((theme (bootloader-theme config))
99ae9ceb
LC
184 (colors (type theme)))
185 (string-append (symbol->string (assoc-ref colors 'fg)) "/"
186 (symbol->string (assoc-ref colors 'bg)))))
187
6b779207 188 (define font-file
1ef8b72a 189 (strip-mount-point store-mount-point
0f65f54e 190 (file-append grub "/share/grub/unicode.pf2")))
6b779207 191
46c296dc
LC
192 (define image
193 (grub-background-image config))
194
195 (and image
196 #~(format #$port "
6b173ac0 197function setup_gfxterm {~a}
99ae9ceb 198
ccc2678b 199# Set 'root' to the partition that contains /gnu/store.
6b779207 200~a
ccc2678b 201
e0b2e930
LF
202~a
203~a
99ae9ceb
LC
204
205insmod png
206if background_image ~a; then
207 set color_normal=~a
208 set color_highlight=~a
209else
210 set menu_color_normal=cyan/blue
211 set menu_color_highlight=white/blue
212fi~%"
46c296dc
LC
213 #$setup-gfxterm-body
214 #$(grub-root-search store-device font-file)
215 #$(setup-gfxterm config font-file)
216 #$(grub-setup-io config)
6b779207 217
46c296dc
LC
218 #$(strip-mount-point store-mount-point image)
219 #$(theme-colors grub-theme-color-normal)
220 #$(theme-colors grub-theme-color-highlight))))
99ae9ceb
LC
221
222\f
223;;;
224;;; Configuration file.
225;;;
226
8d058e7b
LC
227(define* (keyboard-layout-file layout
228 #:key
229 (grub grub))
230 "Process the X keyboard layout description LAYOUT, a <keyboard-layout> record,
231and return a file in the format for GRUB keymaps. LAYOUT must be present in
232the 'share/X11/xkb/symbols/' directory of 'xkeyboard-config'."
233 (define builder
234 (with-imported-modules '((guix build utils))
235 #~(begin
236 (use-modules (guix build utils))
237
238 ;; 'grub-kbdcomp' passes all its arguments but '-o' to 'ckbcomp'
239 ;; (from the 'console-setup' package).
8cf7dd24 240 (invoke #+(file-append grub "/bin/grub-mklayout")
8d058e7b
LC
241 "-i" #+(keyboard-layout->console-keymap layout)
242 "-o" #$output))))
243
2729cb40
LC
244 (computed-file (string-append "grub-keymap."
245 (string-map (match-lambda
246 (#\, #\-)
247 (chr chr))
248 (keyboard-layout-name layout)))
8d058e7b
LC
249 builder))
250
e0b2e930
LF
251(define (grub-setup-io config)
252 "Return GRUB commands to configure the input / output interfaces. The result
253is a string that can be inserted in grub.cfg."
254 (let* ((symbols->string (lambda (list)
255 (string-join (map symbol->string list) " ")))
b09a8da4
MO
256 (outputs (bootloader-configuration-terminal-outputs config))
257 (inputs (bootloader-configuration-terminal-inputs config))
258 (unit (bootloader-configuration-serial-unit config))
259 (speed (bootloader-configuration-serial-speed config))
e0b2e930
LF
260
261 ;; Respectively, GRUB_TERMINAL_OUTPUT and GRUB_TERMINAL_INPUT,
262 ;; as documented in GRUB manual section "Simple Configuration
263 ;; Handling".
264 (valid-outputs '(console serial serial_0 serial_1 serial_2 serial_3
265 gfxterm vga_text mda_text morse spkmodem))
266 (valid-inputs '(console serial serial_0 serial_1 serial_2 serial_3
267 at_keyboard usb_keyboard))
268
269 (io (string-append
270 "terminal_output "
271 (symbols->string
272 (map
273 (lambda (output)
274 (if (memq output valid-outputs) output #f)) outputs)) "\n"
275 (if (null? inputs)
276 ""
277 (string-append
278 "terminal_input "
279 (symbols->string
280 (map
281 (lambda (input)
282 (if (memq input valid-inputs) input #f)) inputs)) "\n"))
283 ;; UNIT and SPEED are arguments to the same GRUB command
284 ;; ("serial"), so we process them together.
285 (if (or unit speed)
286 (string-append
287 "serial"
288 (if unit
289 ;; COM ports 1 through 4
290 (if (and (exact-integer? unit) (<= unit 3) (>= unit 0))
291 (string-append " --unit=" (number->string unit))
292 #f)
293 "")
294 (if speed
295 (if (exact-integer? speed)
296 (string-append " --speed=" (number->string speed))
297 #f)
298 ""))
299 ""))))
300 (format #f "~a" io)))
301
1ef8b72a
CM
302(define (grub-root-search device file)
303 "Return the GRUB 'search' command to look for DEVICE, which contains FILE,
6b779207
LC
304a gexp. The result is a gexp that can be inserted in the grub.cfg-generation
305code."
5babe521
LC
306 ;; Usually FILE is a file name gexp like "/gnu/store/…-linux/vmlinuz", but
307 ;; it can also be something like "(hd0,msdos1)/vmlinuz" in the case of
308 ;; custom menu entries. In the latter case, don't emit a 'search' command.
309 (if (and (string? file) (not (string-prefix? "/" file)))
310 ""
1ef8b72a
CM
311 (match device
312 ;; Preferably refer to DEVICE by its UUID or label. This is more
ecc4324f 313 ;; efficient and less ambiguous, see <http://bugs.gnu.org/22281>.
9b336338 314 ((? uuid? uuid)
5babe521 315 (format #f "search --fs-uuid --set ~a"
1ef8b72a 316 (uuid->string device)))
a5acc17a
LC
317 ((? file-system-label? label)
318 (format #f "search --label --set ~a"
319 (file-system-label->string label)))
320 ((or #f (? string?))
5babe521 321 #~(format #f "search --file --set ~a" #$file)))))
6b779207 322
1ef8b72a 323(define* (grub-configuration-file config entries
fe6e3fe2
LC
324 #:key
325 (system (%current-system))
326 (old-entries '()))
d5b429ab 327 "Return the GRUB configuration file corresponding to CONFIG, a
b09a8da4
MO
328<bootloader-configuration> object, and where the store is available at
329STORE-FS, a <file-system> object. OLD-ENTRIES is taken to be a list of menu
330entries corresponding to old generations of the system."
d5b429ab 331 (define all-entries
1975c754
DM
332 (append entries (bootloader-configuration-menu-entries config)))
333 (define (menu-entry->gexp entry)
334 (let ((device (menu-entry-device entry))
335 (device-mount-point (menu-entry-device-mount-point entry))
336 (label (menu-entry-label entry))
337 (kernel (menu-entry-linux entry))
338 (arguments (menu-entry-linux-arguments entry))
339 (initrd (menu-entry-initrd entry)))
1ef8b72a 340 ;; Here DEVICE is the store and DEVICE-MOUNT-POINT is its mount point.
8b22107e 341 ;; Use the right file names for KERNEL and INITRD in case
1ef8b72a
CM
342 ;; DEVICE-MOUNT-POINT is not "/", meaning that the store is on a
343 ;; separate partition.
8b22107e
MO
344 (let ((kernel (strip-mount-point device-mount-point kernel))
345 (initrd (strip-mount-point device-mount-point initrd)))
0f65f54e 346 #~(format port "menuentry ~s {
6b779207 347 ~a
44d5f54e 348 linux ~a ~a
d9f0a237 349 initrd ~a
0ded70f3 350}~%"
0f65f54e 351 #$label
8b22107e
MO
352 #$(grub-root-search device kernel)
353 #$kernel (string-join (list #$@arguments))
354 #$initrd))))
46c296dc
LC
355 (define sugar
356 (eye-candy config
357 (menu-entry-device (first all-entries))
358 (menu-entry-device-mount-point (first all-entries))
359 #:system system
360 #:port #~port))
361
8d058e7b
LC
362 (define keyboard-layout-config
363 (let ((layout (bootloader-configuration-keyboard-layout config))
364 (grub (bootloader-package
365 (bootloader-configuration-bootloader config))))
366 #~(let ((keymap #$(and layout
367 (keyboard-layout-file layout #:grub grub))))
368 (when keymap
369 (format port "\
8d058e7b
LC
370insmod keylayouts
371keymap ~a~%" keymap)))))
372
46c296dc
LC
373 (define builder
374 #~(call-with-output-file #$output
375 (lambda (port)
376 (format port
59e80445 377 "# This file was generated from your Guix configuration. Any changes
fdf14c64
JD
378# will be lost upon reconfiguration.
379")
46c296dc 380 #$sugar
8d058e7b 381 #$keyboard-layout-config
46c296dc 382 (format port "
f6a7b21d 383set default=~a
6c777cf8 384set timeout=~a~%"
46c296dc
LC
385 #$(bootloader-configuration-default-entry config)
386 #$(bootloader-configuration-timeout config))
387 #$@(map menu-entry->gexp all-entries)
99ae9ceb 388
46c296dc
LC
389 #$@(if (pair? old-entries)
390 #~((format port "
fe6e3fe2 391submenu \"GNU system, old configurations...\" {~%")
46c296dc
LC
392 #$@(map menu-entry->gexp old-entries)
393 (format port "}~%"))
b0d09586
BW
394 #~())
395 (format port "
396if [ \"${grub_platform}\" == efi ]; then
397 menuentry \"Firmware setup\" {
398 fwsetup
399 }
400fi~%"))))
0ded70f3 401
9512ba6b
LC
402 ;; Since this file is rather unique, there's no point in trying to
403 ;; substitute it.
404 (computed-file "grub.cfg" builder
405 #:options '(#:local-build? #t
406 #:substitutable? #f)))
0ded70f3 407
b09a8da4
MO
408\f
409
410;;;
411;;; Install procedures.
412;;;
413
414(define install-grub
415 #~(lambda (bootloader device mount-point)
416 ;; Install GRUB on DEVICE which is mounted at MOUNT-POINT.
417 (let ((grub (string-append bootloader "/sbin/grub-install"))
418 (install-dir (string-append mount-point "/boot")))
419 ;; Tell 'grub-install' that there might be a LUKS-encrypted /boot or
420 ;; root partition.
421 (setenv "GRUB_ENABLE_CRYPTODISK" "y")
422
21fcfe1e
LC
423 ;; Hide potentially confusing messages from the user, such as
424 ;; "Installing for i386-pc platform."
425 (invoke/quiet grub "--no-floppy" "--target=i386-pc"
426 "--boot-directory" install-dir
427 device))))
2941b347
AW
428
429(define install-grub-efi
430 #~(lambda (bootloader efi-dir mount-point)
431 ;; Install GRUB onto the EFI partition mounted at EFI-DIR, for the
432 ;; system whose root is mounted at MOUNT-POINT.
433 (let ((grub-install (string-append bootloader "/sbin/grub-install"))
aa5a549c 434 (install-dir (string-append mount-point "/boot"))
59e80445 435 ;; When installing Guix, it's common to mount EFI-DIR below
aa5a549c
MB
436 ;; MOUNT-POINT rather than /boot/efi on the live image.
437 (target-esp (if (file-exists? (string-append mount-point efi-dir))
438 (string-append mount-point efi-dir)
439 efi-dir)))
2941b347
AW
440 ;; Tell 'grub-install' that there might be a LUKS-encrypted /boot or
441 ;; root partition.
442 (setenv "GRUB_ENABLE_CRYPTODISK" "y")
21fcfe1e
LC
443 (invoke/quiet grub-install "--boot-directory" install-dir
444 "--bootloader-id=Guix"
445 "--efi-directory" target-esp))))
b09a8da4
MO
446
447\f
448
449;;;
450;;; Bootloader definitions.
451;;;
452
453(define grub-bootloader
454 (bootloader
455 (name 'grub)
456 (package grub)
457 (installer install-grub)
458 (configuration-file "/boot/grub/grub.cfg")
459 (configuration-file-generator grub-configuration-file)))
460
6a790fe3
JN
461(define grub-minimal-bootloader
462 (bootloader
463 (name 'grub)
464 (package grub-minimal)
465 (installer install-grub)
466 (configuration-file "/boot/grub/grub.cfg")
467 (configuration-file-generator grub-configuration-file)))
468
b09a8da4
MO
469(define* grub-efi-bootloader
470 (bootloader
471 (inherit grub-bootloader)
2941b347 472 (installer install-grub-efi)
b09a8da4
MO
473 (name 'grub-efi)
474 (package grub-efi)))
475
cf189709
DM
476(define* grub-mkrescue-bootloader
477 (bootloader
478 (inherit grub-efi-bootloader)
479 (package grub-hybrid)))
480
b09a8da4
MO
481\f
482;;;
483;;; Compatibility macros.
484;;;
485
486(define-syntax grub-configuration
487 (syntax-rules (grub)
488 ((_ (grub package) fields ...)
489 (if (eq? package grub)
490 (bootloader-configuration
491 (bootloader grub-bootloader)
492 fields ...)
493 (bootloader-configuration
494 (bootloader grub-efi-bootloader)
495 fields ...)))
496 ((_ fields ...)
497 (bootloader-configuration
498 (bootloader grub-bootloader)
499 fields ...))))
500
0ded70f3 501;;; grub.scm ends here