linux-modules.scm 7.7 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217
  1. ;;; GNU Guix --- Functional package management for GNU
  2. ;;; Copyright © 2014, 2016 Ludovic Courtès <ludo@gnu.org>
  3. ;;; Copyright © 2017 Mathieu Othacehe <m.othacehe@gmail.com>
  4. ;;;
  5. ;;; This file is part of GNU Guix.
  6. ;;;
  7. ;;; GNU Guix is free software; you can redistribute it and/or modify it
  8. ;;; under the terms of the GNU General Public License as published by
  9. ;;; the Free Software Foundation; either version 3 of the License, or (at
  10. ;;; your option) any later version.
  11. ;;;
  12. ;;; GNU Guix is distributed in the hope that it will be useful, but
  13. ;;; WITHOUT ANY WARRANTY; without even the implied warranty of
  14. ;;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
  15. ;;; GNU General Public License for more details.
  16. ;;;
  17. ;;; You should have received a copy of the GNU General Public License
  18. ;;; along with GNU Guix. If not, see <http://www.gnu.org/licenses/>.
  19. (define-module (gnu build linux-modules)
  20. #:use-module (guix elf)
  21. #:use-module (guix build syscalls)
  22. #:use-module (rnrs io ports)
  23. #:use-module (rnrs bytevectors)
  24. #:use-module (srfi srfi-1)
  25. #:use-module (srfi srfi-26)
  26. #:use-module (ice-9 vlist)
  27. #:use-module (ice-9 match)
  28. #:export (dot-ko
  29. ensure-dot-ko
  30. module-dependencies
  31. recursive-module-dependencies
  32. modules-loaded
  33. module-loaded?
  34. load-linux-module*
  35. current-module-debugging-port))
  36. ;;; Commentary:
  37. ;;;
  38. ;;; Tools to deal with Linux kernel modules.
  39. ;;;
  40. ;;; Code:
  41. (define current-module-debugging-port
  42. (make-parameter (%make-void-port "w")))
  43. (define (section-contents elf section)
  44. "Return the contents of SECTION in ELF as a bytevector."
  45. (let* ((modinfo (elf-section-by-name elf ".modinfo"))
  46. (contents (make-bytevector (elf-section-size modinfo))))
  47. (bytevector-copy! (elf-bytes elf) (elf-section-offset modinfo)
  48. contents 0
  49. (elf-section-size modinfo))
  50. contents))
  51. (define %not-nul
  52. (char-set-complement (char-set #\nul)))
  53. (define (nul-separated-string->list str)
  54. "Split STR at occurrences of the NUL character and return the resulting
  55. string list."
  56. (string-tokenize str %not-nul))
  57. (define (key=value->pair str)
  58. "Assuming STR has the form \"KEY=VALUE\", return a pair like (KEY
  59. . \"VALUE\")."
  60. (let ((= (string-index str #\=)))
  61. (cons (string->symbol (string-take str =))
  62. (string-drop str (+ 1 =)))))
  63. (define (modinfo-section-contents file)
  64. "Return the contents of the '.modinfo' section of FILE as a list of
  65. key/value pairs.."
  66. (let* ((bv (call-with-input-file file get-bytevector-all))
  67. (elf (parse-elf bv))
  68. (modinfo (section-contents elf ".modinfo")))
  69. (map key=value->pair
  70. (nul-separated-string->list (utf8->string modinfo)))))
  71. (define %not-comma
  72. (char-set-complement (char-set #\,)))
  73. (define (module-dependencies file)
  74. "Return the list of modules that FILE depends on. The returned list
  75. contains module names, not actual file names."
  76. (let ((info (modinfo-section-contents file)))
  77. (match (assq 'depends info)
  78. (('depends . what)
  79. (string-tokenize what %not-comma)))))
  80. (define dot-ko
  81. (cut string-append <> ".ko"))
  82. (define (ensure-dot-ko name)
  83. "Return NAME with a '.ko' prefix appended, unless it already has it."
  84. (if (string-suffix? ".ko" name)
  85. name
  86. (dot-ko name)))
  87. (define (normalize-module-name module)
  88. "Return the \"canonical\" name for MODULE, replacing hyphens with
  89. underscores."
  90. ;; See 'modname_normalize' in libkmod.
  91. (string-map (lambda (chr)
  92. (case chr
  93. ((#\-) #\_)
  94. (else chr)))
  95. module))
  96. (define (file-name->module-name file)
  97. "Return the module name corresponding to FILE, stripping the trailing '.ko'
  98. and normalizing it."
  99. (normalize-module-name (basename file ".ko")))
  100. (define* (recursive-module-dependencies files
  101. #:key (lookup-module dot-ko))
  102. "Return the topologically-sorted list of file names of the modules depended
  103. on by FILES, recursively. File names of modules are determined by applying
  104. LOOKUP-MODULE to the module name."
  105. (let loop ((files files)
  106. (result '())
  107. (visited vlist-null))
  108. (match files
  109. (()
  110. (delete-duplicates (reverse result)))
  111. ((head . tail)
  112. (let* ((visited? (vhash-assoc head visited))
  113. (deps (if visited?
  114. '()
  115. (map lookup-module (module-dependencies head))))
  116. (visited (if visited?
  117. visited
  118. (vhash-cons head #t visited))))
  119. (loop (append deps tail)
  120. (append result deps) visited))))))
  121. (define %not-newline
  122. (char-set-complement (char-set #\newline)))
  123. (define (modules-loaded)
  124. "Return the list of names of currently loaded Linux modules."
  125. (let* ((contents (call-with-input-file "/proc/modules"
  126. get-string-all))
  127. (lines (string-tokenize contents %not-newline)))
  128. (match (map string-tokenize lines)
  129. (((modules . _) ...)
  130. modules))))
  131. (define (module-black-list)
  132. "Return the black list of modules that must not be loaded. This black list
  133. is specified using 'modprobe.blacklist=MODULE1,MODULE2,...' on the kernel
  134. command line; it is honored by libkmod for users that pass
  135. 'KMOD_PROBE_APPLY_BLACKLIST', which includes 'modprobe --use-blacklist' and
  136. udev."
  137. (define parameter
  138. "modprobe.blacklist=")
  139. (let ((command (call-with-input-file "/proc/cmdline"
  140. get-string-all)))
  141. (append-map (lambda (arg)
  142. (if (string-prefix? parameter arg)
  143. (string-tokenize (string-drop arg (string-length parameter))
  144. %not-comma)
  145. '()))
  146. (string-tokenize command))))
  147. (define (module-loaded? module)
  148. "Return #t if MODULE is already loaded. MODULE must be a Linux module name,
  149. not a file name."
  150. (member module (modules-loaded)))
  151. (define* (load-linux-module* file
  152. #:key
  153. (recursive? #t)
  154. (lookup-module dot-ko)
  155. (black-list (module-black-list)))
  156. "Load Linux module from FILE, the name of a '.ko' file; return true on
  157. success, false otherwise. When RECURSIVE? is true, load its dependencies
  158. first (à la 'modprobe'.) The actual files containing modules depended on are
  159. obtained by calling LOOKUP-MODULE with the module name. Modules whose name
  160. appears in BLACK-LIST are not loaded."
  161. (define (slurp module)
  162. ;; TODO: Use 'finit_module' to reduce memory usage.
  163. (call-with-input-file file get-bytevector-all))
  164. (define (black-listed? module)
  165. (let ((result (member module black-list)))
  166. (when result
  167. (format (current-module-debugging-port)
  168. "not loading module '~a' because it's black-listed~%"
  169. module))
  170. result))
  171. (define (load-dependencies file)
  172. (let ((dependencies (module-dependencies file)))
  173. (every (cut load-linux-module* <> #:lookup-module lookup-module)
  174. (map lookup-module dependencies))))
  175. (and (not (black-listed? (file-name->module-name file)))
  176. (or (not recursive?)
  177. (load-dependencies file))
  178. (begin
  179. (format (current-module-debugging-port)
  180. "loading Linux module from '~a'...~%" file)
  181. (catch 'system-error
  182. (lambda ()
  183. (load-linux-module (slurp file)))
  184. (lambda args
  185. ;; If this module was already loaded and we're in modprobe style, ignore
  186. ;; the error.
  187. (or (and recursive? (= EEXIST (system-error-errno args)))
  188. (apply throw args)))))))
  189. ;;; linux-modules.scm ends here