blob: d211fb55b4b9a2a7db7458e10fa050a8cb9eeec6 [file] [log] [blame]
Tim Windelschmidt6d33a432025-02-04 14:34:25 +01001// Copyright The Monogon Project Authors.
2// SPDX-License-Identifier: Apache-2.0
3
Lorenz Brunc7b036b2023-06-01 12:23:57 +02004package kmod
5
6import (
7 "bufio"
8 "bytes"
9 "debug/elf"
10 "errors"
11 "fmt"
12 "io"
13 "strings"
14)
15
16// ModuleInfo contains Linux kernel module metadata. It maps keys to a list
17// of values. For known keys accessor functions are provided.
18type ModuleInfo map[string][]string
19
20// Get returns the first value of the given key or an empty string if the key
21// does not exist.
22func (i ModuleInfo) Get(key string) string {
23 if len(i[key]) > 0 {
24 return i[key][0]
25 }
26 return ""
27}
28
29// Name returns the name of the module as defined in kbuild.
30func (i ModuleInfo) Name() string {
31 return i.Get("name")
32}
33
34// Authors returns the list of a authors of the module.
35func (i ModuleInfo) Authors() []string {
36 return i["author"]
37}
38
39// Description returns a human-readable description of the module or an empty
40// string if it is not available.
41func (i ModuleInfo) Description() string {
42 return i.Get("description")
43}
44
45// GetDependencies returns a list of module names which need to be loaded
46// before this one.
47func (i ModuleInfo) GetDependencies() []string {
48 if len(i["depends"]) == 1 && i["depends"][0] == "" {
49 return nil
50 }
Lorenz Brunba82bc62025-07-22 20:17:05 +020051 out := make([]string, 0, len(i["depends"]))
52 for _, dep := range i["depends"] {
53 parts := strings.Split(dep, ",")
54 for _, part := range parts {
55 out = append(out, strings.ReplaceAll(part, "-", "_"))
56 }
57 }
58 return out
Lorenz Brunc7b036b2023-06-01 12:23:57 +020059}
60
61type OptionalDependencies struct {
62 // Pre contains a list of module names to be optionally loaded before the
63 // module itself.
64 Pre []string
65 // Post contains a list of module names to be optionally loaded after the
66 // module itself.
67 Post []string
68}
69
70// GetOptionalDependencies returns a set of modules which are recommended to
71// be loaded before and after this module. These are optional, but enhance
72// the functionality of this module.
73func (i ModuleInfo) GetOptionalDependencies() OptionalDependencies {
74 var out OptionalDependencies
75 for _, s := range i["softdep"] {
76 tokens := strings.Fields(s)
77 const (
78 MODE_IDLE = 0
79 MODE_PRE = 1
80 MODE_POST = 2
81 )
Tim Windelschmidt5e460a92024-04-11 01:33:09 +020082 var state = MODE_IDLE
Lorenz Brunc7b036b2023-06-01 12:23:57 +020083 for _, token := range tokens {
84 switch token {
85 case "pre:":
86 state = MODE_PRE
87 case "post:":
88 state = MODE_POST
89 default:
90 switch state {
91 case MODE_PRE:
92 out.Pre = append(out.Pre, token)
93 case MODE_POST:
94 out.Post = append(out.Post, token)
95 default:
96 }
97 }
98 }
99 }
100 return out
101}
102
103// Aliases returns a list of match expressions for matching devices handled
104// by this module. Every returned string consists of a literal as well as '*'
105// wildcards matching one or more characters. These should be matched against
106// the kobject MODALIAS field or the modalias sysfs file.
107func (i ModuleInfo) Aliases() []string {
108 return i["alias"]
109}
110
111// Firmware returns a list of firmware file paths required by this module.
112// These paths are usually relative to the root of a linux-firmware install
113// unless the firmware is non-redistributable.
114func (i ModuleInfo) Firmware() []string {
115 return i["firmware"]
116}
117
Tim Windelschmidt51daf252024-04-18 23:18:43 +0200118// Licenses returns the licenses use of this module is governed by.
119// For mainline modules, the list of valid license strings is
120// documented in the kernel's Documentation/process/license-rules.rst file
121// under the `MODULE_LICENSE` section.
Lorenz Brunc7b036b2023-06-01 12:23:57 +0200122func (i ModuleInfo) Licenses() []string {
123 return i["license"]
124}
125
126// IsInTree returns true if the module was built in the Linux source tree and
127// not externally. This does not necessarily mean that the module is in the
128// mainline kernel.
129func (i ModuleInfo) IsInTree() bool {
130 return i.Get("intree") == "Y"
131}
132
133// vermagic and retpoline are intentionally not exposed here, if you need them
134// you should know how to get them out of the map yourself as AFAIK these
135// are not a stable interface and most programs should not process them.
136
137func nullByteSplit(data []byte, atEOF bool) (advance int, token []byte, err error) {
138 if atEOF && len(data) == 0 {
139 return 0, nil, nil
140 }
141 if i := bytes.IndexByte(data, 0x00); i >= 0 {
142 return i + 1, bytes.TrimLeft(data[0:i], "\x00"), nil
143 }
144 if atEOF {
145 return len(data), data, nil
146 }
147 return 0, nil, nil
148}
149
150// GetModuleInfo looks for a ".modinfo" section in the passed ELF Linux kernel
151// module and parses it into a ModuleInfo structure.
152func GetModuleInfo(e *elf.File) (ModuleInfo, error) {
153 for _, section := range e.Sections {
154 if section.Name == ".modinfo" {
155 out := make(ModuleInfo)
156 s := bufio.NewScanner(io.NewSectionReader(section, 0, int64(section.Size)))
157 s.Split(nullByteSplit)
158
159 for s.Scan() {
160 // Format is <key>=<value>
161 key, value, ok := bytes.Cut(s.Bytes(), []byte("="))
162 if !ok {
163 continue
164 }
165 keyStr := string(key)
166 out[keyStr] = append(out[keyStr], string(value))
167 }
168 return out, nil
169 }
170 }
171 return nil, errors.New("no .modinfo section found")
172}
173
174// GetBuiltinModulesInfo parses all modinfo structures for builtin modules from
175// a modinfo file (modules.builtin.modinfo).
176func GetBuiltinModulesInfo(f io.Reader) ([]ModuleInfo, error) {
177 var out []ModuleInfo
178 s := bufio.NewScanner(f)
179 s.Split(nullByteSplit)
180
181 currModule := make(ModuleInfo)
182 for s.Scan() {
183 if s.Err() != nil {
184 return nil, fmt.Errorf("failed scanning for next token: %w", s.Err())
185 }
186 // Format is <module>.<key>=<value>
187 modName, entry, ok := bytes.Cut(s.Bytes(), []byte{'.'})
188 if !ok {
189 continue
190 }
191 if string(modName) != currModule.Name() {
192 if currModule.Name() != "" {
193 out = append(out, currModule)
194 }
195 currModule = make(ModuleInfo)
196 currModule["name"] = []string{string(modName)}
197 }
198 key, value, ok := bytes.Cut(entry, []byte("="))
199 if !ok {
200 continue
201 }
202 keyStr := string(key)
203 currModule[keyStr] = append(currModule[keyStr], string(value))
204 }
205 if currModule.Name() != "" {
206 out = append(out, currModule)
207 }
Lorenz Brunc7b036b2023-06-01 12:23:57 +0200208 return out, nil
209}