github.com/xyproto/u-root@v6.0.1-0.20200302025726-5528e0c77a3c+incompatible/pkg/securelaunch/measurement/cpuid.go (about) 1 // Copyright 2019 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 measurement 6 7 import ( 8 "bytes" 9 "encoding/json" 10 "fmt" 11 "io" 12 "log" 13 "strings" 14 15 "github.com/intel-go/cpuid" 16 "github.com/u-root/u-root/pkg/mount" 17 slaunch "github.com/u-root/u-root/pkg/securelaunch" 18 "github.com/u-root/u-root/pkg/securelaunch/tpm" 19 ) 20 21 const ( 22 defaultCPUIDFile = "cpuid.txt" //only used if user doesn't provide any 23 ) 24 25 /* describes the "cpuid" portion of policy file */ 26 type CPUIDCollector struct { 27 Type string `json:"type"` 28 Location string `json:"location"` 29 } 30 31 /* 32 * NewCPUIDCollector extracts the "cpuid" portion from the policy file, 33 * initializes a new CPUIDCollector structure and returns error 34 * if unmarshalling of CPUIDCollector fails 35 */ 36 func NewCPUIDCollector(config []byte) (Collector, error) { 37 slaunch.Debug("New CPUID Collector initialized\n") 38 var fc = new(CPUIDCollector) 39 err := json.Unmarshal(config, &fc) 40 if err != nil { 41 return nil, err 42 } 43 return fc, nil 44 } 45 46 /* 47 * getCPUIDInfo used a string builder to store data obtained from intel-go/cpuid package. 48 * returns a byte slice of the string built via string builder. 49 */ 50 func getCPUIDInfo() []byte { 51 var w strings.Builder 52 fmt.Fprintf(&w, "VendorString: %s\n", cpuid.VendorIdentificatorString) 53 fmt.Fprintf(&w, "ProcessorBrandString: %s\n", cpuid.ProcessorBrandString) 54 fmt.Fprintf(&w, "SteppingId: %d\n", cpuid.SteppingId) 55 fmt.Fprintf(&w, "ProcessorType: %d\n", cpuid.ProcessorType) 56 fmt.Fprintf(&w, "DisplayFamily: %d\n", cpuid.DisplayFamily) 57 fmt.Fprintf(&w, "DisplayModel: %d\n", cpuid.DisplayModel) 58 fmt.Fprintf(&w, "CacheLineSize: %d\n", cpuid.CacheLineSize) 59 fmt.Fprintf(&w, "MaxLogocalCPUId:%d\n", cpuid.MaxLogocalCPUId) 60 fmt.Fprintf(&w, "InitialAPICId: %d\n", cpuid.InitialAPICId) 61 fmt.Fprintf(&w, "Smallest monitor-line size in bytes: %d\n", cpuid.MonLineSizeMin) 62 fmt.Fprintf(&w, "Largest monitor-line size in bytes: %d\n", cpuid.MonLineSizeMax) 63 fmt.Fprintf(&w, "Monitor Interrupt break-event is supported: %v\n", cpuid.MonitorIBE) 64 fmt.Fprintf(&w, "MONITOR/MWAIT extensions are supported: %v\n", cpuid.MonitorEMX) 65 fmt.Fprintf(&w, "AVX state: %v\n", cpuid.EnabledAVX) 66 fmt.Fprintf(&w, "AVX-512 state: %v\n", cpuid.EnabledAVX512) 67 fmt.Fprintf(&w, "Interrupt thresholds in digital thermal sensor: %v\n", cpuid.ThermalSensorInterruptThresholds) 68 69 fmt.Fprintf(&w, "Features: ") 70 for i := uint64(0); i < 64; i++ { 71 if cpuid.HasFeature(1 << i) { 72 fmt.Fprintf(&w, "%s ", cpuid.FeatureNames[1<<i]) 73 } 74 } 75 fmt.Fprintf(&w, "\n") 76 77 fmt.Fprintf(&w, "ExtendedFeatures: ") 78 for i := uint64(0); i < 64; i++ { 79 if cpuid.HasExtendedFeature(1 << i) { 80 fmt.Fprintf(&w, "%s ", cpuid.ExtendedFeatureNames[1<<i]) 81 } 82 } 83 fmt.Fprintf(&w, "\n") 84 85 fmt.Fprintf(&w, "ExtraFeatures: ") 86 for i := uint64(0); i < 64; i++ { 87 if cpuid.HasExtraFeature(1 << i) { 88 fmt.Fprintf(&w, "%s ", cpuid.ExtraFeatureNames[1<<i]) 89 } 90 } 91 fmt.Fprintf(&w, "\n") 92 93 fmt.Fprintf(&w, "ThermalAndPowerFeatures: ") 94 for i := uint32(0); i < 64; i++ { 95 if cpuid.HasThermalAndPowerFeature(1 << i) { 96 if name, found := cpuid.ThermalAndPowerFeatureNames[1<<i]; found { 97 fmt.Fprintf(&w, "%s ", name) 98 } 99 } 100 } 101 fmt.Fprintf(&w, "\n") 102 103 for _, cacheDescription := range cpuid.CacheDescriptors { 104 fmt.Fprintf(&w, "CacheDescriptor: %v\n", cacheDescription) 105 } 106 107 return []byte(w.String()) 108 } 109 110 /* 111 * measureCPUIDFile stores the CPUIDInfo obtained from cpuid package 112 * into the tpm device */ 113 func measureCPUIDFile(tpmHandle io.ReadWriteCloser) ([]byte, error) { 114 115 d := getCPUIDInfo() // return strings builder 116 if e := tpm.ExtendPCRDebug(tpmHandle, pcr, bytes.NewReader(d)); e != nil { 117 return nil, e 118 } 119 120 return d, nil 121 } 122 123 /* 124 * persist stores the cpuid info obtained from cpuid package into a file on disk. 125 * disk where target file is located is first mounted and unmounted shortly after 126 * write operation is completed. An error is returned if mount or unmount of disk, 127 * where target is located, fails _OR_ writing to disk fails. 128 * - data - byte slice of the cpuid data obtained from cpuid package. 129 * - cpuidTargetPath - target file path on disk where cpuid info should be copied. 130 */ 131 func persist(data []byte, cpuidTargetPath string) error { 132 133 // cpuidTargetPath is of form sda:/boot/cpuid.txt 134 filePath, mountPath, r := slaunch.GetMountedFilePath(cpuidTargetPath, 0) // 0 is flag for rw mount option 135 if r != nil { 136 return fmt.Errorf("EventLog: ERR: input %s could NOT be located, err=%v", cpuidTargetPath, r) 137 } 138 139 dst := filePath // /tmp/boot-733276578/cpuid 140 141 target, err := slaunch.WriteToFile(data, dst, defaultCPUIDFile) 142 if ret := mount.Unmount(mountPath, true, false); ret != nil { 143 log.Printf("Unmount failed. PANIC") 144 panic(ret) 145 } 146 147 if err != nil { 148 log.Printf("persist: err=%s", err) 149 return err 150 } 151 152 slaunch.Debug("CPUID Collector: Target File%s", target) 153 return nil 154 } 155 156 /* 157 * Collect satisfies collector interface. It calls various functions to 158 * 1. get the cpuid info from cpuid package 159 * 2. stores hash of the result in the tpm device. 160 * 3. also keeps a copy of the result on disk at location provided in policy file. 161 */ 162 func (s *CPUIDCollector) Collect(tpmHandle io.ReadWriteCloser) error { 163 164 d, err := measureCPUIDFile(tpmHandle) 165 if err != nil { 166 log.Printf("CPUID Collector: err = %v", err) 167 return err 168 } 169 170 if e := persist(d, s.Location); e != nil { 171 log.Printf("CPUID Collector: err= %s", e) 172 return e 173 } 174 return nil 175 }