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