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