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