github.com/u-root/u-root@v7.0.1-0.20200915234505-ad7babab0a8e+incompatible/pkg/msr/msr_linux.go (about) 1 // Copyright 2012-2020 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 // This file contains support functions for msr access for Linux. 6 package msr 7 8 import ( 9 "encoding/binary" 10 "fmt" 11 "io/ioutil" 12 "os" 13 "path/filepath" 14 "sort" 15 "strconv" 16 "strings" 17 18 "github.com/intel-go/cpuid" 19 ) 20 21 // CPUs is a slice of the various cpus to read or write the MSR to. 22 type CPUs []uint64 23 24 func parseCPUs(s string) (CPUs, error) { 25 cpus := make(CPUs, 0) 26 // We expect the format to be "0-5,7-8..." or we could also get just one cpu. 27 // We're unlikely to get more than one range since we're looking at present cpus, 28 // but handle it just in case. 29 ranges := strings.Split(strings.TrimSpace(s), ",") 30 for _, r := range ranges { 31 if len(r) == 0 { 32 continue 33 } 34 // Split on a - if it exists. 35 cs := strings.Split(r, "-") 36 switch len(cs) { 37 case 1: 38 u, err := strconv.ParseUint(cs[0], 0, 64) 39 if err != nil { 40 return nil, fmt.Errorf("unknown cpu range: %v, failed to parse %v", r, err) 41 } 42 cpus = append(cpus, uint64(u)) 43 case 2: 44 ul, err := strconv.ParseUint(cs[0], 0, 64) 45 if err != nil { 46 return nil, fmt.Errorf("unknown cpu range: %v, failed to parse %v", r, err) 47 } 48 uh, err := strconv.ParseUint(cs[1], 0, 64) 49 if err != nil { 50 return nil, fmt.Errorf("unknown cpu range: %v, failed to parse %v", r, err) 51 } 52 if ul > uh { 53 return nil, fmt.Errorf("invalid cpu range, upper bound greater than lower: %v", r) 54 } 55 for i := ul; i <= uh; i++ { 56 cpus = append(cpus, uint64(i)) 57 } 58 default: 59 return nil, fmt.Errorf("unknown cpu range: %v", r) 60 } 61 } 62 if len(cpus) == 0 { 63 return nil, fmt.Errorf("no cpus found, input was %v", s) 64 } 65 sort.Slice(cpus, func(i, j int) bool { return cpus[i] < cpus[j] }) 66 // Remove duplicates 67 for i := 0; i < len(cpus)-1; i++ { 68 if cpus[i] == cpus[i+1] { 69 cpus = append(cpus[:i], cpus[i+1:]...) 70 i-- 71 } 72 } 73 return cpus, nil 74 75 } 76 77 // AllCPUs searches for actual present CPUs instead of relying on the glob. 78 // This is more accurate than what's presented in /dev/cpu/*/msr 79 func AllCPUs() (CPUs, error) { 80 v, err := ioutil.ReadFile("/sys/devices/system/cpu/present") 81 if err != nil { 82 return nil, err 83 } 84 return parseCPUs(string(v)) 85 } 86 87 // GlobCPUs allow the user to specify CPUs using a glob as one would in /dev/cpu 88 func GlobCPUs(g string) (CPUs, []error) { 89 var hadErr bool 90 91 f, err := filepath.Glob(filepath.Join("/dev/cpu", g, "msr")) 92 if err != nil { 93 return nil, []error{err} 94 } 95 96 c := make([]uint64, len(f)) 97 errs := make([]error, len(f)) 98 for i, v := range f { 99 c[i], errs[i] = strconv.ParseUint(filepath.Base(filepath.Dir(v)), 0, 64) 100 if errs[i] != nil { 101 hadErr = true 102 } 103 } 104 if hadErr { 105 return nil, errs 106 } 107 return c, nil 108 } 109 110 // String implements String() for MSR. 111 func (m MSR) String() string { 112 return fmt.Sprintf("%#x", uint32(m)) 113 } 114 115 // String pretty prints the list of CPUs. For example: 1-2,4 116 func (c CPUs) String() string { 117 if len(c) == 0 { 118 return "nil" 119 } 120 sort.Slice(c, func(i, j int) bool { return c[i] < c[j] }) 121 122 var s []string 123 for i := 0; i < len(c); i++ { 124 // Find the last CPU in this continuous range. 125 j := i 126 for j+1 < len(c) && c[j]+1 == c[j+1] { 127 j++ 128 } 129 130 if i == j { 131 // Continuous set of size 1. 132 s = append(s, fmt.Sprintf("%d", c[i])) 133 } else { 134 // Multiple CPUs in continous set. 135 s = append(s, fmt.Sprintf("%d-%d", c[i], c[j])) 136 } 137 138 i = j // Skip over set. 139 } 140 return strings.Join(s, ",") 141 } 142 143 func (c CPUs) paths() []string { 144 var p = make([]string, len(c)) 145 146 for i, v := range c { 147 p[i] = filepath.Join("/dev/cpu", strconv.Itoa(int(v)), "msr") 148 } 149 return p 150 } 151 152 // Read reads an MSR from a set of CPUs. 153 func (m MSR) Read(c CPUs) ([]uint64, []error) { 154 var hadErr bool 155 var regs = make([]uint64, len(c)) 156 157 paths := c.paths() 158 f, errs := openAll(paths, os.O_RDONLY) 159 if errs != nil { 160 return nil, errs 161 } 162 errs = make([]error, len(f)) 163 for i := range f { 164 defer f[i].Close() 165 errs[i] = doIO(f[i], m, func(port *os.File) error { 166 return binary.Read(port, binary.LittleEndian, ®s[i]) 167 }) 168 if errs[i] != nil { 169 hadErr = true 170 } 171 } 172 if hadErr { 173 return nil, errs 174 } 175 176 return regs, nil 177 } 178 179 // Write writes values to an MSR on a set of CPUs. 180 // The data must be passed as a scalar (single value) or a slice. 181 // If the data slice has more than one element, 182 // the length of the data slice and the CPU slice must be the same. 183 // If a single value is given, it will be written to all the CPUs. 184 // If multiple data values are given, each will be written to its corresponding 185 // CPU. 186 func (m MSR) Write(c CPUs, data ...uint64) []error { 187 var hadErr bool 188 189 if len(data) == 1 { 190 // Expand value to all cpus 191 for i := 1; i < len(c); i++ { 192 data = append(data, data[0]) 193 } 194 } 195 if len(data) != len(c) { 196 return []error{fmt.Errorf("mismatched lengths: cpus %v, data %v", c, data)} 197 } 198 199 paths := c.paths() 200 f, errs := openAll(paths, os.O_RDWR) 201 202 if errs != nil { 203 return errs 204 } 205 errs = make([]error, len(f)) 206 for i := range f { 207 defer f[i].Close() 208 errs[i] = doIO(f[i], m, func(port *os.File) error { 209 return binary.Write(port, binary.LittleEndian, data[i]) 210 }) 211 if errs[i] != nil { 212 hadErr = true 213 } 214 } 215 if hadErr { 216 return errs 217 } 218 return nil 219 } 220 221 // testAndSetMaybe takes a mask of bits to clear and to set, and applies them to the specified MSR in 222 // each of the CPUs. It will set the MSR only if the value is different and a set is requested. 223 // If the MSR is different for any reason that is an error. 224 func (m MSR) testAndSetMaybe(c CPUs, clearMask uint64, setMask uint64, set bool) []error { 225 var hadErr bool 226 paths := c.paths() 227 f, errs := openAll(paths, os.O_RDWR) 228 229 if errs != nil { 230 return errs 231 } 232 errs = make([]error, len(f)) 233 for i := range f { 234 defer f[i].Close() 235 errs[i] = doIO(f[i], m, func(port *os.File) error { 236 var v uint64 237 err := binary.Read(port, binary.LittleEndian, &v) 238 if err != nil { 239 return err 240 } 241 n := v & ^clearMask 242 n |= setMask 243 // We write only if there is a change. This is to avoid 244 // cases where we try to set a lock bit again, but the bit is 245 // already set 246 if n != v && set { 247 return binary.Write(port, binary.LittleEndian, n) 248 } 249 if n != v { 250 return fmt.Errorf("%#x", v) 251 } 252 return nil 253 }) 254 if errs[i] != nil { 255 hadErr = true 256 } 257 } 258 if hadErr { 259 return errs 260 } 261 return nil 262 } 263 264 // Test takes a mask of bits to clear and to set, and returns an error for those 265 // that do not match. 266 func (m MSR) Test(c CPUs, clearMask uint64, setMask uint64) []error { 267 return m.testAndSetMaybe(c, clearMask, setMask, false) 268 } 269 270 // TestAndSet takes a mask of bits to clear and to set, and applies them to the specified MSR in 271 // each of the CPUs. Note that TestAndSet does not write if the mask does not change the MSR. 272 func (m MSR) TestAndSet(c CPUs, clearMask uint64, setMask uint64) []error { 273 return m.testAndSetMaybe(c, clearMask, setMask, true) 274 } 275 276 // Locked verifies that for all MSRVal's for the CPU vendor, the MSRs are locked. 277 // TODO: this is another Intel-specific function at present. 278 func Locked() error { 279 vendor := cpuid.VendorIdentificatorString 280 // TODO: support more than Intel. Use the vendor id to look up msrs. 281 if vendor != "GenuineIntel" { 282 return fmt.Errorf("Sorry, this package only supports Intel at present") 283 } 284 285 cpus, err := AllCPUs() 286 if err != nil { 287 return err 288 } 289 290 var allerrors string 291 for _, m := range LockIntel { 292 Debug("MSR %v on cpus %v, clearmask 0x%8x, setmask 0x%8x", m.Addr, cpus, m.Clear, m.Set) 293 if m.WriteOnly { 294 continue 295 } 296 errs := m.Addr.Test(cpus, m.Clear, m.Set) 297 298 for i, e := range errs { 299 if e != nil { 300 allerrors += fmt.Sprintf("[cpu%d(%s)%v ", cpus[i], m.String(), e) 301 } 302 } 303 } 304 305 if allerrors != "" { 306 return fmt.Errorf("%s: %v", vendor, allerrors) 307 } 308 return nil 309 310 } 311 312 func openAll(m []string, o int) ([]*os.File, []error) { 313 var ( 314 f = make([]*os.File, len(m)) 315 errs = make([]error, len(m)) 316 hadErr bool 317 ) 318 for i := range m { 319 f[i], errs[i] = os.OpenFile(m[i], o, 0) 320 if errs[i] != nil { 321 hadErr = true 322 f[i] = nil // Not sure if I need to do this, it doesn't seem guaranteed. 323 } 324 } 325 if hadErr { 326 for i := range f { 327 if f[i] != nil { 328 f[i].Close() 329 } 330 } 331 return nil, errs 332 } 333 return f, nil 334 } 335 336 func doIO(msr *os.File, addr MSR, f func(*os.File) error) error { 337 if _, err := msr.Seek(int64(addr), 0); err != nil { 338 return fmt.Errorf("bad address %v: %v", addr, err) 339 } 340 return f(msr) 341 }