| 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 | 
 | } |