github.com/u-root/u-root@v7.0.1-0.20200915234505-ad7babab0a8e+incompatible/pkg/kmodule/kmodule_linux.go (about)

     1  // Copyright 2017-2018 the u-root Authors. All rights reserved
     2  // Use of this source code is governed by a BSD-style
     3  // license that can be found in the LICENSE file.
     4  
     5  // Package kmodule interfaces with Linux kernel modules.
     6  //
     7  // kmodule allows loading and unloading kernel modules with dependencies, as
     8  // well as locating them through probing.
     9  package kmodule
    10  
    11  import (
    12  	"bufio"
    13  	"bytes"
    14  	"fmt"
    15  	"io"
    16  	"io/ioutil"
    17  	"os"
    18  	"path"
    19  	"path/filepath"
    20  	"strings"
    21  
    22  	"github.com/klauspost/pgzip"
    23  	"github.com/ulikunitz/xz"
    24  	"golang.org/x/sys/unix"
    25  )
    26  
    27  // Flags to finit_module(2) / FileInit.
    28  const (
    29  	// Ignore symbol version hashes.
    30  	MODULE_INIT_IGNORE_MODVERSIONS = 0x1
    31  
    32  	// Ignore kernel version magic.
    33  	MODULE_INIT_IGNORE_VERMAGIC = 0x2
    34  )
    35  
    36  // Init loads the kernel module given by image with the given options.
    37  func Init(image []byte, opts string) error {
    38  	return unix.InitModule(image, opts)
    39  }
    40  
    41  // FileInit loads the kernel module contained by `f` with the given opts and
    42  // flags. Uncompresses modules with a .xz and .gz suffix before loading.
    43  //
    44  // FileInit falls back to init_module(2) via Init when the finit_module(2)
    45  // syscall is not available and when loading compressed modules.
    46  func FileInit(f *os.File, opts string, flags uintptr) error {
    47  	var r io.Reader
    48  	if strings.HasSuffix(f.Name(), ".xz") {
    49  		var err error
    50  		if r, err = xz.NewReader(f); err != nil {
    51  			return err
    52  		}
    53  
    54  	} else if strings.HasSuffix(f.Name(), ".gz") {
    55  		var err error
    56  		if r, err = pgzip.NewReader(f); err != nil {
    57  			return err
    58  		}
    59  	}
    60  
    61  	if r == nil {
    62  		err := unix.FinitModule(int(f.Fd()), opts, int(flags))
    63  		if err == unix.ENOSYS {
    64  			if flags != 0 {
    65  				return err
    66  			}
    67  			// Fall back to init_module(2).
    68  			r = f
    69  		} else {
    70  			return err
    71  		}
    72  	}
    73  
    74  	img, err := ioutil.ReadAll(r)
    75  	if err != nil {
    76  		return err
    77  	}
    78  	return Init(img, opts)
    79  }
    80  
    81  // Delete removes a kernel module.
    82  func Delete(name string, flags uintptr) error {
    83  	return unix.DeleteModule(name, int(flags))
    84  }
    85  
    86  type modState uint8
    87  
    88  const (
    89  	unloaded modState = iota
    90  	loading
    91  	loaded
    92  	builtin
    93  )
    94  
    95  type dependency struct {
    96  	state modState
    97  	deps  []string
    98  }
    99  
   100  type depMap map[string]*dependency
   101  
   102  // ProbeOpts contains optional parameters to Probe.
   103  //
   104  // An empty ProbeOpts{} should lead to the default behavior.
   105  type ProbeOpts struct {
   106  	DryRunCB       func(string)
   107  	RootDir        string
   108  	KVer           string
   109  	IgnoreProcMods bool
   110  }
   111  
   112  // Probe loads the given kernel module and its dependencies.
   113  // It is calls ProbeOptions with the default ProbeOpts.
   114  func Probe(name string, modParams string) error {
   115  	return ProbeOptions(name, modParams, ProbeOpts{})
   116  }
   117  
   118  // ProbeOptions loads the given kernel module and its dependencies.
   119  // This functions takes ProbeOpts.
   120  func ProbeOptions(name, modParams string, opts ProbeOpts) error {
   121  	deps, err := genDeps(opts)
   122  	if err != nil {
   123  		return fmt.Errorf("could not generate dependency map %v", err)
   124  	}
   125  
   126  	modPath, err := findModPath(name, deps)
   127  	if err != nil {
   128  		return fmt.Errorf("could not find module path %q: %v", name, err)
   129  	}
   130  
   131  	dep := deps[modPath]
   132  
   133  	if dep.state == builtin || dep.state == loaded {
   134  		return nil
   135  	}
   136  
   137  	dep.state = loading
   138  	for _, d := range dep.deps {
   139  		if err := loadDeps(d, deps, opts); err != nil {
   140  			return err
   141  		}
   142  	}
   143  	return loadModule(modPath, modParams, opts)
   144  }
   145  
   146  func checkBuiltin(moduleDir string, deps depMap) error {
   147  	f, err := os.Open(filepath.Join(moduleDir, "modules.builtin"))
   148  	if os.IsNotExist(err) {
   149  		return nil
   150  	} else if err != nil {
   151  		return fmt.Errorf("could not open builtin file: %v", err)
   152  	}
   153  	defer f.Close()
   154  
   155  	scanner := bufio.NewScanner(f)
   156  	for scanner.Scan() {
   157  		txt := scanner.Text()
   158  		modPath := filepath.Join(moduleDir, strings.TrimSpace(txt))
   159  		if deps[modPath] == nil {
   160  			deps[modPath] = new(dependency)
   161  		}
   162  		deps[modPath].state = builtin
   163  	}
   164  
   165  	return scanner.Err()
   166  }
   167  
   168  func genDeps(opts ProbeOpts) (depMap, error) {
   169  	deps := make(depMap)
   170  	rel := opts.KVer
   171  
   172  	if rel == "" {
   173  		var u unix.Utsname
   174  		if err := unix.Uname(&u); err != nil {
   175  			return nil, fmt.Errorf("could not get release (uname -r): %v", err)
   176  		}
   177  		rel = string(u.Release[:bytes.IndexByte(u.Release[:], 0)])
   178  	}
   179  
   180  	var moduleDir string
   181  	for _, n := range []string{"/lib/modules", "/usr/lib/modules"} {
   182  		moduleDir = filepath.Join(opts.RootDir, n, strings.TrimSpace(rel))
   183  		if _, err := os.Stat(moduleDir); err == nil {
   184  			break
   185  		}
   186  	}
   187  
   188  	f, err := os.Open(filepath.Join(moduleDir, "modules.dep"))
   189  	if err != nil {
   190  		return nil, fmt.Errorf("could not open dependency file: %v", err)
   191  	}
   192  	defer f.Close()
   193  
   194  	scanner := bufio.NewScanner(f)
   195  	for scanner.Scan() {
   196  		txt := scanner.Text()
   197  		nameDeps := strings.Split(txt, ":")
   198  		modPath, modDeps := nameDeps[0], nameDeps[1]
   199  		modPath = filepath.Join(moduleDir, strings.TrimSpace(modPath))
   200  
   201  		var dependency dependency
   202  		if len(modDeps) > 0 {
   203  			for _, dep := range strings.Split(strings.TrimSpace(modDeps), " ") {
   204  				dependency.deps = append(dependency.deps, filepath.Join(moduleDir, dep))
   205  			}
   206  		}
   207  		deps[modPath] = &dependency
   208  	}
   209  
   210  	if err := scanner.Err(); err != nil {
   211  		return nil, err
   212  	}
   213  
   214  	if err = checkBuiltin(moduleDir, deps); err != nil {
   215  		return nil, err
   216  	}
   217  
   218  	if !opts.IgnoreProcMods {
   219  		fm, err := os.Open("/proc/modules")
   220  		if err == nil {
   221  			defer fm.Close()
   222  			genLoadedMods(fm, deps)
   223  		}
   224  	}
   225  
   226  	return deps, nil
   227  }
   228  
   229  func findModPath(name string, m depMap) (string, error) {
   230  	// Kernel modules do not have any consistency with use of hyphens and underscores
   231  	// matching from the module's name to the module's file path. Thus try matching
   232  	// the provided name using either.
   233  	nameH := strings.Replace(name, "_", "-", -1)
   234  	nameU := strings.Replace(name, "-", "_", -1)
   235  
   236  	for mp := range m {
   237  		switch path.Base(mp) {
   238  		case nameH + ".ko", nameH + ".ko.gz", nameH + ".ko.xz":
   239  			return mp, nil
   240  		case nameU + ".ko", nameU + ".ko.gz", nameU + ".ko.xz":
   241  			return mp, nil
   242  		}
   243  	}
   244  
   245  	return "", fmt.Errorf("could not find path for module %q", name)
   246  }
   247  
   248  func loadDeps(path string, m depMap, opts ProbeOpts) error {
   249  	dependency, ok := m[path]
   250  	if !ok {
   251  		return fmt.Errorf("could not find dependency %q", path)
   252  	}
   253  
   254  	if dependency.state == loading {
   255  		return fmt.Errorf("circular dependency! %q already LOADING", path)
   256  	} else if (dependency.state == loaded) || (dependency.state == builtin) {
   257  		return nil
   258  	}
   259  
   260  	m[path].state = loading
   261  
   262  	for _, dep := range dependency.deps {
   263  		if err := loadDeps(dep, m, opts); err != nil {
   264  			return err
   265  		}
   266  	}
   267  
   268  	// done with dependencies, load module
   269  	if err := loadModule(path, "", opts); err != nil {
   270  		return err
   271  	}
   272  	m[path].state = loaded
   273  
   274  	return nil
   275  }
   276  
   277  func loadModule(path, modParams string, opts ProbeOpts) error {
   278  	if opts.DryRunCB != nil {
   279  		opts.DryRunCB(path)
   280  		return nil
   281  	}
   282  
   283  	f, err := os.Open(path)
   284  	if err != nil {
   285  		return err
   286  	}
   287  	defer f.Close()
   288  
   289  	if err := FileInit(f, modParams, 0); err != nil && err != unix.EEXIST {
   290  		return err
   291  	}
   292  
   293  	return nil
   294  }
   295  
   296  func genLoadedMods(r io.Reader, deps depMap) error {
   297  	scanner := bufio.NewScanner(r)
   298  	for scanner.Scan() {
   299  		arr := strings.Split(scanner.Text(), " ")
   300  		name := arr[0]
   301  		modPath, err := findModPath(name, deps)
   302  		if err != nil {
   303  			return fmt.Errorf("could not find module path %q: %v", name, err)
   304  		}
   305  		if deps[modPath] == nil {
   306  			deps[modPath] = new(dependency)
   307  		}
   308  		deps[modPath].state = loaded
   309  	}
   310  	return scanner.Err()
   311  }