github.com/u-root/u-root@v7.0.1-0.20200915234505-ad7babab0a8e+incompatible/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 interfaces with Linux kernel modules. 6 // 7 // kmodule allows loading and unloading kernel modules with dependencies, as 8 // well as locating them through probing. 9 package kmodule 10 11 import ( 12 "bufio" 13 "bytes" 14 "fmt" 15 "io" 16 "io/ioutil" 17 "os" 18 "path" 19 "path/filepath" 20 "strings" 21 22 "github.com/klauspost/pgzip" 23 "github.com/ulikunitz/xz" 24 "golang.org/x/sys/unix" 25 ) 26 27 // Flags to finit_module(2) / FileInit. 28 const ( 29 // Ignore symbol version hashes. 30 MODULE_INIT_IGNORE_MODVERSIONS = 0x1 31 32 // Ignore kernel version magic. 33 MODULE_INIT_IGNORE_VERMAGIC = 0x2 34 ) 35 36 // Init loads the kernel module given by image with the given options. 37 func Init(image []byte, opts string) error { 38 return unix.InitModule(image, opts) 39 } 40 41 // FileInit loads the kernel module contained by `f` with the given opts and 42 // flags. Uncompresses modules with a .xz and .gz suffix before loading. 43 // 44 // FileInit falls back to init_module(2) via Init when the finit_module(2) 45 // syscall is not available and when loading compressed modules. 46 func FileInit(f *os.File, opts string, flags uintptr) error { 47 var r io.Reader 48 if strings.HasSuffix(f.Name(), ".xz") { 49 var err error 50 if r, err = xz.NewReader(f); err != nil { 51 return err 52 } 53 54 } else if strings.HasSuffix(f.Name(), ".gz") { 55 var err error 56 if r, err = pgzip.NewReader(f); err != nil { 57 return err 58 } 59 } 60 61 if r == nil { 62 err := unix.FinitModule(int(f.Fd()), opts, int(flags)) 63 if err == unix.ENOSYS { 64 if flags != 0 { 65 return err 66 } 67 // Fall back to init_module(2). 68 r = f 69 } else { 70 return err 71 } 72 } 73 74 img, err := ioutil.ReadAll(r) 75 if err != nil { 76 return err 77 } 78 return Init(img, opts) 79 } 80 81 // Delete removes a kernel module. 82 func Delete(name string, flags uintptr) error { 83 return unix.DeleteModule(name, int(flags)) 84 } 85 86 type modState uint8 87 88 const ( 89 unloaded modState = iota 90 loading 91 loaded 92 builtin 93 ) 94 95 type dependency struct { 96 state modState 97 deps []string 98 } 99 100 type depMap map[string]*dependency 101 102 // ProbeOpts contains optional parameters to Probe. 103 // 104 // An empty ProbeOpts{} should lead to the default behavior. 105 type ProbeOpts struct { 106 DryRunCB func(string) 107 RootDir string 108 KVer string 109 IgnoreProcMods bool 110 } 111 112 // Probe loads the given kernel module and its dependencies. 113 // It is calls ProbeOptions with the default ProbeOpts. 114 func Probe(name string, modParams string) error { 115 return ProbeOptions(name, modParams, ProbeOpts{}) 116 } 117 118 // ProbeOptions loads the given kernel module and its dependencies. 119 // This functions takes ProbeOpts. 120 func ProbeOptions(name, modParams string, opts ProbeOpts) error { 121 deps, err := genDeps(opts) 122 if err != nil { 123 return fmt.Errorf("could not generate dependency map %v", err) 124 } 125 126 modPath, err := findModPath(name, deps) 127 if err != nil { 128 return fmt.Errorf("could not find module path %q: %v", name, err) 129 } 130 131 dep := deps[modPath] 132 133 if dep.state == builtin || dep.state == loaded { 134 return nil 135 } 136 137 dep.state = loading 138 for _, d := range dep.deps { 139 if err := loadDeps(d, deps, opts); err != nil { 140 return err 141 } 142 } 143 return loadModule(modPath, modParams, opts) 144 } 145 146 func checkBuiltin(moduleDir string, deps depMap) error { 147 f, err := os.Open(filepath.Join(moduleDir, "modules.builtin")) 148 if os.IsNotExist(err) { 149 return nil 150 } else if err != nil { 151 return fmt.Errorf("could not open builtin file: %v", err) 152 } 153 defer f.Close() 154 155 scanner := bufio.NewScanner(f) 156 for scanner.Scan() { 157 txt := scanner.Text() 158 modPath := filepath.Join(moduleDir, strings.TrimSpace(txt)) 159 if deps[modPath] == nil { 160 deps[modPath] = new(dependency) 161 } 162 deps[modPath].state = builtin 163 } 164 165 return scanner.Err() 166 } 167 168 func genDeps(opts ProbeOpts) (depMap, error) { 169 deps := make(depMap) 170 rel := opts.KVer 171 172 if rel == "" { 173 var u unix.Utsname 174 if err := unix.Uname(&u); err != nil { 175 return nil, fmt.Errorf("could not get release (uname -r): %v", err) 176 } 177 rel = string(u.Release[:bytes.IndexByte(u.Release[:], 0)]) 178 } 179 180 var moduleDir string 181 for _, n := range []string{"/lib/modules", "/usr/lib/modules"} { 182 moduleDir = filepath.Join(opts.RootDir, n, strings.TrimSpace(rel)) 183 if _, err := os.Stat(moduleDir); err == nil { 184 break 185 } 186 } 187 188 f, err := os.Open(filepath.Join(moduleDir, "modules.dep")) 189 if err != nil { 190 return nil, fmt.Errorf("could not open dependency file: %v", err) 191 } 192 defer f.Close() 193 194 scanner := bufio.NewScanner(f) 195 for scanner.Scan() { 196 txt := scanner.Text() 197 nameDeps := strings.Split(txt, ":") 198 modPath, modDeps := nameDeps[0], nameDeps[1] 199 modPath = filepath.Join(moduleDir, strings.TrimSpace(modPath)) 200 201 var dependency dependency 202 if len(modDeps) > 0 { 203 for _, dep := range strings.Split(strings.TrimSpace(modDeps), " ") { 204 dependency.deps = append(dependency.deps, filepath.Join(moduleDir, dep)) 205 } 206 } 207 deps[modPath] = &dependency 208 } 209 210 if err := scanner.Err(); err != nil { 211 return nil, err 212 } 213 214 if err = checkBuiltin(moduleDir, deps); err != nil { 215 return nil, err 216 } 217 218 if !opts.IgnoreProcMods { 219 fm, err := os.Open("/proc/modules") 220 if err == nil { 221 defer fm.Close() 222 genLoadedMods(fm, deps) 223 } 224 } 225 226 return deps, nil 227 } 228 229 func findModPath(name string, m depMap) (string, error) { 230 // Kernel modules do not have any consistency with use of hyphens and underscores 231 // matching from the module's name to the module's file path. Thus try matching 232 // the provided name using either. 233 nameH := strings.Replace(name, "_", "-", -1) 234 nameU := strings.Replace(name, "-", "_", -1) 235 236 for mp := range m { 237 switch path.Base(mp) { 238 case nameH + ".ko", nameH + ".ko.gz", nameH + ".ko.xz": 239 return mp, nil 240 case nameU + ".ko", nameU + ".ko.gz", nameU + ".ko.xz": 241 return mp, nil 242 } 243 } 244 245 return "", fmt.Errorf("could not find path for module %q", name) 246 } 247 248 func loadDeps(path string, m depMap, opts ProbeOpts) error { 249 dependency, ok := m[path] 250 if !ok { 251 return fmt.Errorf("could not find dependency %q", path) 252 } 253 254 if dependency.state == loading { 255 return fmt.Errorf("circular dependency! %q already LOADING", path) 256 } else if (dependency.state == loaded) || (dependency.state == builtin) { 257 return nil 258 } 259 260 m[path].state = loading 261 262 for _, dep := range dependency.deps { 263 if err := loadDeps(dep, m, opts); err != nil { 264 return err 265 } 266 } 267 268 // done with dependencies, load module 269 if err := loadModule(path, "", opts); err != nil { 270 return err 271 } 272 m[path].state = loaded 273 274 return nil 275 } 276 277 func loadModule(path, modParams string, opts ProbeOpts) error { 278 if opts.DryRunCB != nil { 279 opts.DryRunCB(path) 280 return nil 281 } 282 283 f, err := os.Open(path) 284 if err != nil { 285 return err 286 } 287 defer f.Close() 288 289 if err := FileInit(f, modParams, 0); err != nil && err != unix.EEXIST { 290 return err 291 } 292 293 return nil 294 } 295 296 func genLoadedMods(r io.Reader, deps depMap) error { 297 scanner := bufio.NewScanner(r) 298 for scanner.Scan() { 299 arr := strings.Split(scanner.Text(), " ") 300 name := arr[0] 301 modPath, err := findModPath(name, deps) 302 if err != nil { 303 return fmt.Errorf("could not find module path %q: %v", name, err) 304 } 305 if deps[modPath] == nil { 306 deps[modPath] = new(dependency) 307 } 308 deps[modPath].state = loaded 309 } 310 return scanner.Err() 311 }