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  }