bootloader.scm 14 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353
  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 Ludovic Courtès <ludo@gnu.org>
  6. ;;; Copyright © 2020 Jan (janneke) Nieuwenhuizen <janneke@gnu.org>
  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. (define-module (gnu bootloader)
  23. #:use-module (guix discovery)
  24. #:use-module (guix gexp)
  25. #:use-module (guix profiles)
  26. #:use-module (guix records)
  27. #:use-module (guix ui)
  28. #:use-module (srfi srfi-1)
  29. #:use-module (ice-9 match)
  30. #:export (menu-entry
  31. menu-entry?
  32. menu-entry-label
  33. menu-entry-device
  34. menu-entry-linux
  35. menu-entry-linux-arguments
  36. menu-entry-initrd
  37. menu-entry-device-mount-point
  38. menu-entry-multiboot-kernel
  39. menu-entry-multiboot-arguments
  40. menu-entry-multiboot-modules
  41. menu-entry->sexp
  42. sexp->menu-entry
  43. bootloader
  44. bootloader?
  45. bootloader-name
  46. bootloader-package
  47. bootloader-installer
  48. bootloader-disk-image-installer
  49. bootloader-configuration-file
  50. bootloader-configuration-file-generator
  51. bootloader-configuration
  52. bootloader-configuration?
  53. bootloader-configuration-bootloader
  54. bootloader-configuration-target
  55. bootloader-configuration-menu-entries
  56. bootloader-configuration-default-entry
  57. bootloader-configuration-timeout
  58. bootloader-configuration-keyboard-layout
  59. bootloader-configuration-theme
  60. bootloader-configuration-terminal-outputs
  61. bootloader-configuration-terminal-inputs
  62. bootloader-configuration-serial-unit
  63. bootloader-configuration-serial-speed
  64. bootloader-configuration-additional-configuration
  65. %bootloaders
  66. lookup-bootloader-by-name
  67. efi-bootloader-chain))
  68. ;;;
  69. ;;; Menu-entry record.
  70. ;;;
  71. (define-record-type* <menu-entry>
  72. menu-entry make-menu-entry
  73. menu-entry?
  74. (label menu-entry-label)
  75. (device menu-entry-device ; file system uuid, label, or #f
  76. (default #f))
  77. (device-mount-point menu-entry-device-mount-point
  78. (default #f))
  79. (linux menu-entry-linux
  80. (default #f))
  81. (linux-arguments menu-entry-linux-arguments
  82. (default '())) ; list of string-valued gexps
  83. (initrd menu-entry-initrd ; file name of the initrd as a gexp
  84. (default #f))
  85. (multiboot-kernel menu-entry-multiboot-kernel
  86. (default #f))
  87. (multiboot-arguments menu-entry-multiboot-arguments
  88. (default '())) ; list of string-valued gexps
  89. (multiboot-modules menu-entry-multiboot-modules
  90. (default '()))) ; list of multiboot commands, where
  91. ; a command is a list of <string>
  92. (define (menu-entry->sexp entry)
  93. "Return ENTRY serialized as an sexp."
  94. (match entry
  95. (($ <menu-entry> label device mount-point linux linux-arguments initrd #f
  96. ())
  97. `(menu-entry (version 0)
  98. (label ,label)
  99. (device ,device)
  100. (device-mount-point ,mount-point)
  101. (linux ,linux)
  102. (linux-arguments ,linux-arguments)
  103. (initrd ,initrd)))
  104. (($ <menu-entry> label device mount-point #f () #f
  105. multiboot-kernel multiboot-arguments multiboot-modules)
  106. `(menu-entry (version 0)
  107. (label ,label)
  108. (device ,device)
  109. (device-mount-point ,mount-point)
  110. (multiboot-kernel ,multiboot-kernel)
  111. (multiboot-arguments ,multiboot-arguments)
  112. (multiboot-modules ,multiboot-modules)))))
  113. (define (sexp->menu-entry sexp)
  114. "Turn SEXP, an sexp as returned by 'menu-entry->sexp', into a <menu-entry>
  115. record."
  116. (match sexp
  117. (('menu-entry ('version 0)
  118. ('label label) ('device device)
  119. ('device-mount-point mount-point)
  120. ('linux linux) ('linux-arguments linux-arguments)
  121. ('initrd initrd) _ ...)
  122. (menu-entry
  123. (label label)
  124. (device device)
  125. (device-mount-point mount-point)
  126. (linux linux)
  127. (linux-arguments linux-arguments)
  128. (initrd initrd)))
  129. (('menu-entry ('version 0)
  130. ('label label) ('device device)
  131. ('device-mount-point mount-point)
  132. ('multiboot-kernel multiboot-kernel)
  133. ('multiboot-arguments multiboot-arguments)
  134. ('multiboot-modules multiboot-modules) _ ...)
  135. (menu-entry
  136. (label label)
  137. (device device)
  138. (device-mount-point mount-point)
  139. (multiboot-kernel multiboot-kernel)
  140. (multiboot-arguments multiboot-arguments)
  141. (multiboot-modules multiboot-modules)))))
  142. ;;;
  143. ;;; Bootloader record.
  144. ;;;
  145. ;; The <bootloader> record contains fields expressing how the bootloader
  146. ;; should be installed. Every bootloader in gnu/bootloader/ directory
  147. ;; has to be described by this record.
  148. (define-record-type* <bootloader>
  149. bootloader make-bootloader
  150. bootloader?
  151. (name bootloader-name)
  152. (package bootloader-package)
  153. (installer bootloader-installer)
  154. (disk-image-installer bootloader-disk-image-installer
  155. (default #f))
  156. (configuration-file bootloader-configuration-file)
  157. (configuration-file-generator bootloader-configuration-file-generator))
  158. ;;;
  159. ;;; Bootloader configuration record.
  160. ;;;
  161. ;; The <bootloader-configuration> record contains bootloader independant
  162. ;; configuration used to fill bootloader configuration file.
  163. (define-record-type* <bootloader-configuration>
  164. bootloader-configuration make-bootloader-configuration
  165. bootloader-configuration?
  166. (bootloader bootloader-configuration-bootloader) ;<bootloader>
  167. (target bootloader-configuration-target ;string
  168. (default #f))
  169. (menu-entries bootloader-configuration-menu-entries ;list of <menu-entry>
  170. (default '()))
  171. (default-entry bootloader-configuration-default-entry ;integer
  172. (default 0))
  173. (timeout bootloader-configuration-timeout ;seconds as integer
  174. (default 5))
  175. (keyboard-layout bootloader-configuration-keyboard-layout ;<keyboard-layout> | #f
  176. (default #f))
  177. (theme bootloader-configuration-theme ;bootloader-specific theme
  178. (default #f))
  179. (terminal-outputs bootloader-configuration-terminal-outputs ;list of symbols
  180. (default '(gfxterm)))
  181. (terminal-inputs bootloader-configuration-terminal-inputs ;list of symbols
  182. (default '()))
  183. (serial-unit bootloader-configuration-serial-unit ;integer | #f
  184. (default #f))
  185. (serial-speed bootloader-configuration-serial-speed ;integer | #f
  186. (default #f)))
  187. ;;;
  188. ;;; Bootloaders.
  189. ;;;
  190. (define (bootloader-modules)
  191. "Return the list of bootloader modules."
  192. (all-modules (map (lambda (entry)
  193. `(,entry . "gnu/bootloader"))
  194. %load-path)
  195. #:warn warn-about-load-error))
  196. (define %bootloaders
  197. ;; The list of publically-known bootloaders.
  198. (delay (fold-module-public-variables (lambda (obj result)
  199. (if (bootloader? obj)
  200. (cons obj result)
  201. result))
  202. '()
  203. (bootloader-modules))))
  204. (define (lookup-bootloader-by-name name)
  205. "Return the bootloader called NAME."
  206. (or (find (lambda (bootloader)
  207. (eq? name (bootloader-name bootloader)))
  208. (force %bootloaders))
  209. (leave (G_ "~a: no such bootloader~%") name)))
  210. (define (efi-bootloader-profile files bootloader-package hooks)
  211. "Creates a profile with BOOTLOADER-PACKAGE and a directory collection/ with
  212. links to additional FILES from the store. This collection is meant to be used
  213. by the bootloader installer.
  214. FILES is a list of file or directory names from the store, which will be
  215. symlinked into the collection/ directory. If a directory name ends with '/',
  216. then the directory content instead of the directory itself will be symlinked
  217. into the collection/ directory.
  218. FILES may contain file like objects produced by functions like plain-file,
  219. local-file, etc., or package contents produced with file-append.
  220. HOOKS lists additional hook functions to modify the profile."
  221. (define (bootloader-collection manifest)
  222. (define build
  223. (with-imported-modules '((guix build utils)
  224. (ice-9 ftw)
  225. (srfi srfi-1)
  226. (srfi srfi-26))
  227. #~(begin
  228. (use-modules ((guix build utils)
  229. #:select (mkdir-p strip-store-file-name))
  230. ((ice-9 ftw)
  231. #:select (scandir))
  232. ((srfi srfi-1)
  233. #:select (append-map every remove))
  234. ((srfi srfi-26)
  235. #:select (cut)))
  236. (define (symlink-to file directory transform)
  237. "Creates a symlink to FILE named (TRANSFORM FILE) in DIRECTORY."
  238. (symlink file (string-append directory "/" (transform file))))
  239. (define (directory-content directory)
  240. "Creates a list of absolute path names inside DIRECTORY."
  241. (map (lambda (name)
  242. (string-append directory name))
  243. (or (scandir directory (lambda (name)
  244. (not (member name '("." "..")))))
  245. '())))
  246. (define name-ends-with-/? (cut string-suffix? "/" <>))
  247. (define (name-is-store-entry? name)
  248. "Return #t if NAME is a direct store entry and nothing inside."
  249. (not (string-index (strip-store-file-name name) #\/)))
  250. (let* ((collection (string-append #$output "/collection"))
  251. (files '#$files)
  252. (directories (filter name-ends-with-/? files))
  253. (names-from-directories
  254. (append-map (lambda (directory)
  255. (directory-content directory))
  256. directories))
  257. (names (append names-from-directories
  258. (remove name-ends-with-/? files))))
  259. (mkdir-p collection)
  260. (if (every file-exists? names)
  261. (begin
  262. (for-each (lambda (name)
  263. (symlink-to name collection
  264. (if (name-is-store-entry? name)
  265. strip-store-file-name
  266. basename)))
  267. names)
  268. #t)
  269. #f)))))
  270. (gexp->derivation "bootloader-collection"
  271. build
  272. #:local-build? #t
  273. #:substitutable? #f
  274. #:properties
  275. `((type . profile-hook)
  276. (hook . bootloader-collection))))
  277. (profile (content (packages->manifest (list bootloader-package)))
  278. (name "bootloader-profile")
  279. (hooks (append (list bootloader-collection) hooks))
  280. (locales? #f)
  281. (allow-collisions? #f)
  282. (relative-symlinks? #f)))
  283. (define* (efi-bootloader-chain files
  284. final-bootloader
  285. #:key
  286. (hooks '())
  287. installer)
  288. "Define a bootloader chain with FINAL-BOOTLOADER as the final bootloader and
  289. certain directories and files from the store given in the list of FILES.
  290. FILES may contain file like objects produced by functions like plain-file,
  291. local-file, etc., or package contents produced with file-append. They will be
  292. collected inside a directory collection/ inside a generated bootloader profile,
  293. which will be passed to the INSTALLER.
  294. If a directory name in FILES ends with '/', then the directory content instead
  295. of the directory itself will be symlinked into the collection/ directory.
  296. The procedures in the HOOKS list can be used to further modify the bootloader
  297. profile. It is possible to pass a single function instead of a list.
  298. If the INSTALLER argument is used, then this function will be called to install
  299. the bootloader. Otherwise the installer of the FINAL-BOOTLOADER will be called."
  300. (let* ((final-installer (or installer
  301. (bootloader-installer final-bootloader)))
  302. (profile (efi-bootloader-profile files
  303. (bootloader-package final-bootloader)
  304. (if (list? hooks)
  305. hooks
  306. (list hooks)))))
  307. (bootloader
  308. (inherit final-bootloader)
  309. (package profile)
  310. (installer
  311. #~(lambda (bootloader target mount-point)
  312. (#$final-installer bootloader target mount-point)
  313. (copy-recursively
  314. (string-append bootloader "/collection")
  315. (string-append mount-point target)
  316. #:follow-symlinks? #t
  317. #:log (%make-void-port "w")))))))