| package kmod |
| |
| import ( |
| "bufio" |
| "bytes" |
| "debug/elf" |
| "errors" |
| "fmt" |
| "io" |
| "strings" |
| ) |
| |
| // ModuleInfo contains Linux kernel module metadata. It maps keys to a list |
| // of values. For known keys accessor functions are provided. |
| type ModuleInfo map[string][]string |
| |
| // Get returns the first value of the given key or an empty string if the key |
| // does not exist. |
| func (i ModuleInfo) Get(key string) string { |
| if len(i[key]) > 0 { |
| return i[key][0] |
| } |
| return "" |
| } |
| |
| // Name returns the name of the module as defined in kbuild. |
| func (i ModuleInfo) Name() string { |
| return i.Get("name") |
| } |
| |
| // Authors returns the list of a authors of the module. |
| func (i ModuleInfo) Authors() []string { |
| return i["author"] |
| } |
| |
| // Description returns a human-readable description of the module or an empty |
| // string if it is not available. |
| func (i ModuleInfo) Description() string { |
| return i.Get("description") |
| } |
| |
| // GetDependencies returns a list of module names which need to be loaded |
| // before this one. |
| func (i ModuleInfo) GetDependencies() []string { |
| if len(i["depends"]) == 1 && i["depends"][0] == "" { |
| return nil |
| } |
| return i["depends"] |
| } |
| |
| type OptionalDependencies struct { |
| // Pre contains a list of module names to be optionally loaded before the |
| // module itself. |
| Pre []string |
| // Post contains a list of module names to be optionally loaded after the |
| // module itself. |
| Post []string |
| } |
| |
| // GetOptionalDependencies returns a set of modules which are recommended to |
| // be loaded before and after this module. These are optional, but enhance |
| // the functionality of this module. |
| func (i ModuleInfo) GetOptionalDependencies() OptionalDependencies { |
| var out OptionalDependencies |
| for _, s := range i["softdep"] { |
| tokens := strings.Fields(s) |
| const ( |
| MODE_IDLE = 0 |
| MODE_PRE = 1 |
| MODE_POST = 2 |
| ) |
| var state int = MODE_IDLE |
| for _, token := range tokens { |
| switch token { |
| case "pre:": |
| state = MODE_PRE |
| case "post:": |
| state = MODE_POST |
| default: |
| switch state { |
| case MODE_PRE: |
| out.Pre = append(out.Pre, token) |
| case MODE_POST: |
| out.Post = append(out.Post, token) |
| default: |
| } |
| } |
| } |
| } |
| return out |
| } |
| |
| // Aliases returns a list of match expressions for matching devices handled |
| // by this module. Every returned string consists of a literal as well as '*' |
| // wildcards matching one or more characters. These should be matched against |
| // the kobject MODALIAS field or the modalias sysfs file. |
| func (i ModuleInfo) Aliases() []string { |
| return i["alias"] |
| } |
| |
| // Firmware returns a list of firmware file paths required by this module. |
| // These paths are usually relative to the root of a linux-firmware install |
| // unless the firmware is non-redistributable. |
| func (i ModuleInfo) Firmware() []string { |
| return i["firmware"] |
| } |
| |
| // License returns the licenses use of this module is governed by. For mainline |
| // modules, the list of valid license strings is documented in the kernel's |
| // Documentation/process/license-rules.rst file under the `MODULE_LICENSE` |
| // section. |
| func (i ModuleInfo) Licenses() []string { |
| return i["license"] |
| } |
| |
| // IsInTree returns true if the module was built in the Linux source tree and |
| // not externally. This does not necessarily mean that the module is in the |
| // mainline kernel. |
| func (i ModuleInfo) IsInTree() bool { |
| return i.Get("intree") == "Y" |
| } |
| |
| // vermagic and retpoline are intentionally not exposed here, if you need them |
| // you should know how to get them out of the map yourself as AFAIK these |
| // are not a stable interface and most programs should not process them. |
| |
| func nullByteSplit(data []byte, atEOF bool) (advance int, token []byte, err error) { |
| if atEOF && len(data) == 0 { |
| return 0, nil, nil |
| } |
| if i := bytes.IndexByte(data, 0x00); i >= 0 { |
| return i + 1, bytes.TrimLeft(data[0:i], "\x00"), nil |
| } |
| if atEOF { |
| return len(data), data, nil |
| } |
| return 0, nil, nil |
| } |
| |
| // GetModuleInfo looks for a ".modinfo" section in the passed ELF Linux kernel |
| // module and parses it into a ModuleInfo structure. |
| func GetModuleInfo(e *elf.File) (ModuleInfo, error) { |
| for _, section := range e.Sections { |
| if section.Name == ".modinfo" { |
| out := make(ModuleInfo) |
| s := bufio.NewScanner(io.NewSectionReader(section, 0, int64(section.Size))) |
| s.Split(nullByteSplit) |
| |
| for s.Scan() { |
| // Format is <key>=<value> |
| key, value, ok := bytes.Cut(s.Bytes(), []byte("=")) |
| if !ok { |
| continue |
| } |
| keyStr := string(key) |
| out[keyStr] = append(out[keyStr], string(value)) |
| } |
| return out, nil |
| } |
| } |
| return nil, errors.New("no .modinfo section found") |
| } |
| |
| // GetBuiltinModulesInfo parses all modinfo structures for builtin modules from |
| // a modinfo file (modules.builtin.modinfo). |
| func GetBuiltinModulesInfo(f io.Reader) ([]ModuleInfo, error) { |
| var out []ModuleInfo |
| s := bufio.NewScanner(f) |
| s.Split(nullByteSplit) |
| |
| currModule := make(ModuleInfo) |
| for s.Scan() { |
| if s.Err() != nil { |
| return nil, fmt.Errorf("failed scanning for next token: %w", s.Err()) |
| } |
| // Format is <module>.<key>=<value> |
| modName, entry, ok := bytes.Cut(s.Bytes(), []byte{'.'}) |
| if !ok { |
| continue |
| } |
| if string(modName) != currModule.Name() { |
| if currModule.Name() != "" { |
| out = append(out, currModule) |
| } |
| currModule = make(ModuleInfo) |
| currModule["name"] = []string{string(modName)} |
| } |
| key, value, ok := bytes.Cut(entry, []byte("=")) |
| if !ok { |
| continue |
| } |
| keyStr := string(key) |
| currModule[keyStr] = append(currModule[keyStr], string(value)) |
| } |
| if currModule.Name() != "" { |
| out = append(out, currModule) |
| } |
| seenModNames := make(map[string]bool) |
| for _, m := range out { |
| if seenModNames[m.Name()] { |
| return nil, fmt.Errorf("duplicate/out-of-order module metadata for module %q", m) |
| } |
| seenModNames[m.Name()] = true |
| } |
| return out, nil |
| } |