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 }