Lorenz Brun | c7b036b | 2023-06-01 12:23:57 +0200 | [diff] [blame] | 1 | package kmod |
| 2 | |
| 3 | import ( |
| 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. |
| 15 | type 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. |
| 19 | func (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. |
| 27 | func (i ModuleInfo) Name() string { |
| 28 | return i.Get("name") |
| 29 | } |
| 30 | |
| 31 | // Authors returns the list of a authors of the module. |
| 32 | func (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. |
| 38 | func (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. |
| 44 | func (i ModuleInfo) GetDependencies() []string { |
| 45 | if len(i["depends"]) == 1 && i["depends"][0] == "" { |
| 46 | return nil |
| 47 | } |
| 48 | return i["depends"] |
| 49 | } |
| 50 | |
| 51 | type 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. |
| 63 | func (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. |
| 97 | func (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. |
| 104 | func (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. |
| 112 | func (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. |
| 119 | func (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 | |
| 127 | func 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. |
| 142 | func 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). |
| 166 | func 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 | } |