github.com/rck/u-root@v0.0.0-20180106144920-7eb602e381bb/pkg/kmodule/kmodule_linux.go (about)

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