github.hscsec.cn/u-root/u-root@v7.0.0+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  	"golang.org/x/sys/unix"
    23  )
    24  
    25  // Flags to finit_module(2) / FileInit.
    26  const (
    27  	// Ignore symbol version hashes.
    28  	MODULE_INIT_IGNORE_MODVERSIONS = 0x1
    29  
    30  	// Ignore kernel version magic.
    31  	MODULE_INIT_IGNORE_VERMAGIC = 0x2
    32  )
    33  
    34  // Init loads the kernel module given by image with the given options.
    35  func Init(image []byte, opts string) error {
    36  	return unix.InitModule(image, opts)
    37  }
    38  
    39  // FileInit loads the kernel module contained by `f` with the given opts and
    40  // flags.
    41  //
    42  // FileInit falls back to Init when the finit_module(2) syscall is not available.
    43  func FileInit(f *os.File, opts string, flags uintptr) error {
    44  	err := unix.FinitModule(int(f.Fd()), opts, int(flags))
    45  	if err == unix.ENOSYS {
    46  		if flags != 0 {
    47  			return err
    48  		}
    49  
    50  		// Fall back to regular init_module(2).
    51  		img, err := ioutil.ReadAll(f)
    52  		if err != nil {
    53  			return err
    54  		}
    55  		return Init(img, opts)
    56  	}
    57  
    58  	return err
    59  }
    60  
    61  // Delete removes a kernel module.
    62  func Delete(name string, flags uintptr) error {
    63  	return unix.DeleteModule(name, int(flags))
    64  }
    65  
    66  type modState uint8
    67  
    68  const (
    69  	unloaded modState = iota
    70  	loading
    71  	loaded
    72  	builtin
    73  )
    74  
    75  type dependency struct {
    76  	state modState
    77  	deps  []string
    78  }
    79  
    80  type depMap map[string]*dependency
    81  
    82  // ProbeOpts contains optional parameters to Probe.
    83  //
    84  // An empty ProbeOpts{} should lead to the default behavior.
    85  type ProbeOpts struct {
    86  	DryRunCB       func(string)
    87  	RootDir        string
    88  	KVer           string
    89  	IgnoreProcMods bool
    90  }
    91  
    92  // Probe loads the given kernel module and its dependencies.
    93  // It is calls ProbeOptions with the default ProbeOpts.
    94  func Probe(name string, modParams string) error {
    95  	return ProbeOptions(name, modParams, ProbeOpts{})
    96  }
    97  
    98  // ProbeOptions loads the given kernel module and its dependencies.
    99  // This functions takes ProbeOpts.
   100  func ProbeOptions(name, modParams string, opts ProbeOpts) error {
   101  	deps, err := genDeps(opts)
   102  	if err != nil {
   103  		return fmt.Errorf("could not generate dependency map %v", err)
   104  	}
   105  
   106  	modPath, err := findModPath(name, deps)
   107  	if err != nil {
   108  		return fmt.Errorf("could not find module path %q: %v", name, err)
   109  	}
   110  
   111  	dep := deps[modPath]
   112  
   113  	if dep.state == builtin || dep.state == loaded {
   114  		return nil
   115  	}
   116  
   117  	dep.state = loading
   118  	for _, d := range dep.deps {
   119  		if err := loadDeps(d, deps, opts); err != nil {
   120  			return err
   121  		}
   122  	}
   123  	if err := loadModule(modPath, modParams, opts); err != nil {
   124  		return err
   125  	}
   126  
   127  	return nil
   128  }
   129  
   130  func checkBuiltin(moduleDir string, deps depMap) error {
   131  	f, err := os.Open(filepath.Join(moduleDir, "modules.builtin"))
   132  	if os.IsNotExist(err) {
   133  		return nil
   134  	} else if err != nil {
   135  		return fmt.Errorf("could not open builtin file: %v", err)
   136  	}
   137  	defer f.Close()
   138  
   139  	scanner := bufio.NewScanner(f)
   140  	for scanner.Scan() {
   141  		txt := scanner.Text()
   142  		modPath := filepath.Join(moduleDir, strings.TrimSpace(txt))
   143  		if deps[modPath] == nil {
   144  			deps[modPath] = new(dependency)
   145  		}
   146  		deps[modPath].state = builtin
   147  	}
   148  
   149  	return scanner.Err()
   150  }
   151  
   152  func genDeps(opts ProbeOpts) (depMap, error) {
   153  	deps := make(depMap)
   154  	rel := opts.KVer
   155  
   156  	if rel == "" {
   157  		var u unix.Utsname
   158  		if err := unix.Uname(&u); err != nil {
   159  			return nil, fmt.Errorf("could not get release (uname -r): %v", err)
   160  		}
   161  		rel = string(u.Release[:bytes.IndexByte(u.Release[:], 0)])
   162  	}
   163  
   164  	var moduleDir string
   165  	for _, n := range []string{"/lib/modules", "/usr/lib/modules"} {
   166  		moduleDir = filepath.Join(opts.RootDir, n, strings.TrimSpace(rel))
   167  		if _, err := os.Stat(moduleDir); err == nil {
   168  			break
   169  		}
   170  	}
   171  
   172  	f, err := os.Open(filepath.Join(moduleDir, "modules.dep"))
   173  	if err != nil {
   174  		return nil, fmt.Errorf("could not open dependency file: %v", err)
   175  	}
   176  	defer f.Close()
   177  
   178  	scanner := bufio.NewScanner(f)
   179  	for scanner.Scan() {
   180  		txt := scanner.Text()
   181  		nameDeps := strings.Split(txt, ":")
   182  		modPath, modDeps := nameDeps[0], nameDeps[1]
   183  		modPath = filepath.Join(moduleDir, strings.TrimSpace(modPath))
   184  
   185  		var dependency dependency
   186  		if len(modDeps) > 0 {
   187  			for _, dep := range strings.Split(strings.TrimSpace(modDeps), " ") {
   188  				dependency.deps = append(dependency.deps, filepath.Join(moduleDir, dep))
   189  			}
   190  		}
   191  		deps[modPath] = &dependency
   192  	}
   193  
   194  	if err := scanner.Err(); err != nil {
   195  		return nil, err
   196  	}
   197  
   198  	if err = checkBuiltin(moduleDir, deps); err != nil {
   199  		return nil, err
   200  	}
   201  
   202  	if !opts.IgnoreProcMods {
   203  		fm, err := os.Open("/proc/modules")
   204  		if err == nil {
   205  			defer fm.Close()
   206  			genLoadedMods(fm, deps)
   207  		}
   208  	}
   209  
   210  	return deps, nil
   211  }
   212  
   213  func findModPath(name string, m depMap) (string, error) {
   214  	for mp := range m {
   215  		if path.Base(mp) == name+".ko" {
   216  			return mp, nil
   217  		}
   218  	}
   219  
   220  	return "", fmt.Errorf("could not find path for module %q", name)
   221  }
   222  
   223  func loadDeps(path string, m depMap, opts ProbeOpts) error {
   224  	dependency, ok := m[path]
   225  	if !ok {
   226  		return fmt.Errorf("could not find dependency %q", path)
   227  	}
   228  
   229  	if dependency.state == loading {
   230  		return fmt.Errorf("circular dependency! %q already LOADING", path)
   231  	} else if (dependency.state == loaded) || (dependency.state == builtin) {
   232  		return nil
   233  	}
   234  
   235  	m[path].state = loading
   236  
   237  	for _, dep := range dependency.deps {
   238  		if err := loadDeps(dep, m, opts); err != nil {
   239  			return err
   240  		}
   241  	}
   242  
   243  	// done with dependencies, load module
   244  	if err := loadModule(path, "", opts); err != nil {
   245  		return err
   246  	}
   247  	m[path].state = loaded
   248  
   249  	return nil
   250  }
   251  
   252  func loadModule(path, modParams string, opts ProbeOpts) error {
   253  	if opts.DryRunCB != nil {
   254  		opts.DryRunCB(path)
   255  		return nil
   256  	}
   257  
   258  	f, err := os.Open(path)
   259  	if err != nil {
   260  		return err
   261  	}
   262  	defer f.Close()
   263  
   264  	if err := FileInit(f, modParams, 0); err != nil && err != unix.EEXIST {
   265  		return err
   266  	}
   267  
   268  	return nil
   269  }
   270  
   271  func genLoadedMods(r io.Reader, deps depMap) error {
   272  	scanner := bufio.NewScanner(r)
   273  	for scanner.Scan() {
   274  		arr := strings.Split(scanner.Text(), " ")
   275  		name := strings.Replace(arr[0], "_", "-", -1)
   276  		modPath, err := findModPath(name, deps)
   277  		if err != nil {
   278  			return fmt.Errorf("could not find module path %q: %v", name, err)
   279  		}
   280  		if deps[modPath] == nil {
   281  			deps[modPath] = new(dependency)
   282  		}
   283  		deps[modPath].state = loaded
   284  	}
   285  	return scanner.Err()
   286  }