blob: b7783171b6f2f5b46dc90dcd084c31badb255554 [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 }
51 return i["depends"]
52}
53
54type OptionalDependencies struct {
55 // Pre contains a list of module names to be optionally loaded before the
56 // module itself.
57 Pre []string
58 // Post contains a list of module names to be optionally loaded after the
59 // module itself.
60 Post []string
61}
62
63// GetOptionalDependencies returns a set of modules which are recommended to
64// be loaded before and after this module. These are optional, but enhance
65// the functionality of this module.
66func (i ModuleInfo) GetOptionalDependencies() OptionalDependencies {
67 var out OptionalDependencies
68 for _, s := range i["softdep"] {
69 tokens := strings.Fields(s)
70 const (
71 MODE_IDLE = 0
72 MODE_PRE = 1
73 MODE_POST = 2
74 )
Tim Windelschmidt5e460a92024-04-11 01:33:09 +020075 var state = MODE_IDLE
Lorenz Brunc7b036b2023-06-01 12:23:57 +020076 for _, token := range tokens {
77 switch token {
78 case "pre:":
79 state = MODE_PRE
80 case "post:":
81 state = MODE_POST
82 default:
83 switch state {
84 case MODE_PRE:
85 out.Pre = append(out.Pre, token)
86 case MODE_POST:
87 out.Post = append(out.Post, token)
88 default:
89 }
90 }
91 }
92 }
93 return out
94}
95
96// Aliases returns a list of match expressions for matching devices handled
97// by this module. Every returned string consists of a literal as well as '*'
98// wildcards matching one or more characters. These should be matched against
99// the kobject MODALIAS field or the modalias sysfs file.
100func (i ModuleInfo) Aliases() []string {
101 return i["alias"]
102}
103
104// Firmware returns a list of firmware file paths required by this module.
105// These paths are usually relative to the root of a linux-firmware install
106// unless the firmware is non-redistributable.
107func (i ModuleInfo) Firmware() []string {
108 return i["firmware"]
109}
110
Tim Windelschmidt51daf252024-04-18 23:18:43 +0200111// Licenses returns the licenses use of this module is governed by.
112// For mainline modules, the list of valid license strings is
113// documented in the kernel's Documentation/process/license-rules.rst file
114// under the `MODULE_LICENSE` section.
Lorenz Brunc7b036b2023-06-01 12:23:57 +0200115func (i ModuleInfo) Licenses() []string {
116 return i["license"]
117}
118
119// IsInTree returns true if the module was built in the Linux source tree and
120// not externally. This does not necessarily mean that the module is in the
121// mainline kernel.
122func (i ModuleInfo) IsInTree() bool {
123 return i.Get("intree") == "Y"
124}
125
126// vermagic and retpoline are intentionally not exposed here, if you need them
127// you should know how to get them out of the map yourself as AFAIK these
128// are not a stable interface and most programs should not process them.
129
130func nullByteSplit(data []byte, atEOF bool) (advance int, token []byte, err error) {
131 if atEOF && len(data) == 0 {
132 return 0, nil, nil
133 }
134 if i := bytes.IndexByte(data, 0x00); i >= 0 {
135 return i + 1, bytes.TrimLeft(data[0:i], "\x00"), nil
136 }
137 if atEOF {
138 return len(data), data, nil
139 }
140 return 0, nil, nil
141}
142
143// GetModuleInfo looks for a ".modinfo" section in the passed ELF Linux kernel
144// module and parses it into a ModuleInfo structure.
145func GetModuleInfo(e *elf.File) (ModuleInfo, error) {
146 for _, section := range e.Sections {
147 if section.Name == ".modinfo" {
148 out := make(ModuleInfo)
149 s := bufio.NewScanner(io.NewSectionReader(section, 0, int64(section.Size)))
150 s.Split(nullByteSplit)
151
152 for s.Scan() {
153 // Format is <key>=<value>
154 key, value, ok := bytes.Cut(s.Bytes(), []byte("="))
155 if !ok {
156 continue
157 }
158 keyStr := string(key)
159 out[keyStr] = append(out[keyStr], string(value))
160 }
161 return out, nil
162 }
163 }
164 return nil, errors.New("no .modinfo section found")
165}
166
167// GetBuiltinModulesInfo parses all modinfo structures for builtin modules from
168// a modinfo file (modules.builtin.modinfo).
169func GetBuiltinModulesInfo(f io.Reader) ([]ModuleInfo, error) {
170 var out []ModuleInfo
171 s := bufio.NewScanner(f)
172 s.Split(nullByteSplit)
173
174 currModule := make(ModuleInfo)
175 for s.Scan() {
176 if s.Err() != nil {
177 return nil, fmt.Errorf("failed scanning for next token: %w", s.Err())
178 }
179 // Format is <module>.<key>=<value>
180 modName, entry, ok := bytes.Cut(s.Bytes(), []byte{'.'})
181 if !ok {
182 continue
183 }
184 if string(modName) != currModule.Name() {
185 if currModule.Name() != "" {
186 out = append(out, currModule)
187 }
188 currModule = make(ModuleInfo)
189 currModule["name"] = []string{string(modName)}
190 }
191 key, value, ok := bytes.Cut(entry, []byte("="))
192 if !ok {
193 continue
194 }
195 keyStr := string(key)
196 currModule[keyStr] = append(currModule[keyStr], string(value))
197 }
198 if currModule.Name() != "" {
199 out = append(out, currModule)
200 }
201 seenModNames := make(map[string]bool)
202 for _, m := range out {
203 if seenModNames[m.Name()] {
204 return nil, fmt.Errorf("duplicate/out-of-order module metadata for module %q", m)
205 }
206 seenModNames[m.Name()] = true
207 }
208 return out, nil
209}