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 }