m/p/kmod: init

This adds a package for working with Linux kernel loadable modules in
Go. It contains syscall wrappers for loading and unloading modules, a
metadata format for fast lookup of modules handling devices using a
custom radix tree, parsers for module info metadata and various utility
functions and data structures.

A significant amount of the code in here has no formal spec and is
written against behavior and information extracted from the reference
kmod code as well as the Linux kernel itself.

Change-Id: I3d527f3631f4dd1832b9cfba2d50aeb03a2b88a8
Reviewed-on: https://review.monogon.dev/c/monogon/+/1789
Reviewed-by: Serge Bazanski <serge@monogon.tech>
Tested-by: Jenkins CI
diff --git a/metropolis/pkg/kmod/meta.go b/metropolis/pkg/kmod/meta.go
new file mode 100644
index 0000000..2554c33
--- /dev/null
+++ b/metropolis/pkg/kmod/meta.go
@@ -0,0 +1,50 @@
+package kmod
+
+import (
+	"fmt"
+	"log"
+
+	kmodpb "source.monogon.dev/metropolis/pkg/kmod/spec"
+)
+
+// MakeMetaFromModuleInfo is a more flexible alternative to MakeMeta. It only
+// relies on ModuleInfo structures, additional processing can be done outside of
+// this function. It does however require that for dynamically-loaded modules
+// the "path" key is set to the path of the .ko file relative to the module
+// root.
+func MakeMetaFromModuleInfo(modinfos []ModuleInfo) (*kmodpb.Meta, error) {
+	modIndices := make(map[string]uint32)
+	modInfoMap := make(map[string]ModuleInfo)
+	var meta kmodpb.Meta
+	meta.ModuleDeviceMatches = &kmodpb.RadixNode{
+		Type: kmodpb.RadixNode_ROOT,
+	}
+	var i uint32
+	for _, m := range modinfos {
+		meta.Modules = append(meta.Modules, &kmodpb.Module{
+			Name: m.Name(),
+			Path: m.Get("path"),
+		})
+		for _, p := range m.Aliases() {
+			if m.Get("path") == "" {
+				// Ignore built-in modaliases as they do not need to be loaded.
+				continue
+			}
+			if err := AddPattern(meta.ModuleDeviceMatches, p, i); err != nil {
+				return nil, fmt.Errorf("failed adding device match %q: %w", p, err)
+			}
+		}
+		modIndices[m.Name()] = i
+		modInfoMap[m.Name()] = m
+		i++
+	}
+	for _, m := range meta.Modules {
+		for _, dep := range modInfoMap[m.Name].GetDependencies() {
+			if _, ok := modIndices[dep]; !ok {
+				log.Printf("Unknown dependency %q for module %q", modInfoMap[m.Name].GetDependencies(), m.Name)
+			}
+			m.Depends = append(m.Depends, modIndices[dep])
+		}
+	}
+	return &meta, nil
+}