github.com/u-root/u-root@v7.0.1-0.20200915234505-ad7babab0a8e+incompatible/pkg/mei/mkhiclient_linux.go (about)

     1  // Copyright 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  package mei
     6  
     7  import (
     8  	"encoding/binary"
     9  	"errors"
    10  	"fmt"
    11  	"log"
    12  	"strconv"
    13  
    14  	"github.com/u-root/u-root/pkg/pci"
    15  )
    16  
    17  // Intel MEI PCI dev IDs,
    18  // from https://elixir.bootlin.com/linux/latest/source/drivers/misc/mei/hw-me-regs.h
    19  var meiDevIDs = map[string]uint{
    20  	// awk '/MEI_DEV_ID/ {print "\t\"" $2 "\": " $3 ","}' hw-me-regs.h
    21  	"MEI_DEV_ID_82946GZ":    0x2974,
    22  	"MEI_DEV_ID_82G35":      0x2984,
    23  	"MEI_DEV_ID_82Q965":     0x2994,
    24  	"MEI_DEV_ID_82G965":     0x29A4,
    25  	"MEI_DEV_ID_82GM965":    0x2A04,
    26  	"MEI_DEV_ID_82GME965":   0x2A14,
    27  	"MEI_DEV_ID_ICH9_82Q35": 0x29B4,
    28  	"MEI_DEV_ID_ICH9_82G33": 0x29C4,
    29  	"MEI_DEV_ID_ICH9_82Q33": 0x29D4,
    30  	"MEI_DEV_ID_ICH9_82X38": 0x29E4,
    31  	"MEI_DEV_ID_ICH9_3200":  0x29F4,
    32  	"MEI_DEV_ID_ICH9_6":     0x28B4,
    33  	"MEI_DEV_ID_ICH9_7":     0x28C4,
    34  	"MEI_DEV_ID_ICH9_8":     0x28D4,
    35  	"MEI_DEV_ID_ICH9_9":     0x28E4,
    36  	"MEI_DEV_ID_ICH9_10":    0x28F4,
    37  	"MEI_DEV_ID_ICH9M_1":    0x2A44,
    38  	"MEI_DEV_ID_ICH9M_2":    0x2A54,
    39  	"MEI_DEV_ID_ICH9M_3":    0x2A64,
    40  	"MEI_DEV_ID_ICH9M_4":    0x2A74,
    41  	"MEI_DEV_ID_ICH10_1":    0x2E04,
    42  	"MEI_DEV_ID_ICH10_2":    0x2E14,
    43  	"MEI_DEV_ID_ICH10_3":    0x2E24,
    44  	"MEI_DEV_ID_ICH10_4":    0x2E34,
    45  	"MEI_DEV_ID_IBXPK_1":    0x3B64,
    46  	"MEI_DEV_ID_IBXPK_2":    0x3B65,
    47  	"MEI_DEV_ID_CPT_1":      0x1C3A,
    48  	"MEI_DEV_ID_PBG_1":      0x1D3A,
    49  	"MEI_DEV_ID_PPT_1":      0x1E3A,
    50  	"MEI_DEV_ID_PPT_2":      0x1CBA,
    51  	"MEI_DEV_ID_PPT_3":      0x1DBA,
    52  	"MEI_DEV_ID_LPT_H":      0x8C3A,
    53  	"MEI_DEV_ID_LPT_W":      0x8D3A,
    54  	"MEI_DEV_ID_LPT_LP":     0x9C3A,
    55  	"MEI_DEV_ID_LPT_HR":     0x8CBA,
    56  	"MEI_DEV_ID_WPT_LP":     0x9CBA,
    57  	"MEI_DEV_ID_WPT_LP_2":   0x9CBB,
    58  	"MEI_DEV_ID_SPT":        0x9D3A,
    59  	"MEI_DEV_ID_SPT_2":      0x9D3B,
    60  	"MEI_DEV_ID_SPT_H":      0xA13A,
    61  	"MEI_DEV_ID_SPT_H_2":    0xA13B,
    62  	"MEI_DEV_ID_LBG":        0xA1BA,
    63  	"MEI_DEV_ID_BXT_M":      0x1A9A,
    64  	"MEI_DEV_ID_APL_I":      0x5A9A,
    65  	"MEI_DEV_ID_DNV_IE":     0x19E5,
    66  	"MEI_DEV_ID_GLK":        0x319A,
    67  	"MEI_DEV_ID_KBP":        0xA2BA,
    68  	"MEI_DEV_ID_KBP_2":      0xA2BB,
    69  	"MEI_DEV_ID_CNP_LP":     0x9DE0,
    70  	"MEI_DEV_ID_CNP_LP_4":   0x9DE4,
    71  	"MEI_DEV_ID_CNP_H":      0xA360,
    72  	"MEI_DEV_ID_CNP_H_4":    0xA364,
    73  	"MEI_DEV_ID_CMP_LP":     0x02e0,
    74  	"MEI_DEV_ID_CMP_LP_3":   0x02e4,
    75  	"MEI_DEV_ID_CMP_V":      0xA3BA,
    76  	"MEI_DEV_ID_CMP_H":      0x06e0,
    77  	"MEI_DEV_ID_CMP_H_3":    0x06e4,
    78  	"MEI_DEV_ID_CDF":        0x18D3,
    79  	"MEI_DEV_ID_ICP_LP":     0x34E0,
    80  	"MEI_DEV_ID_JSP_N":      0x4DE0,
    81  	"MEI_DEV_ID_TGP_LP":     0xA0E0,
    82  	"MEI_DEV_ID_MCC":        0x4B70,
    83  	"MEI_DEV_ID_MCC_4":      0x4B75,
    84  }
    85  
    86  // various ME-related constants
    87  const (
    88  	// ME - current working state set to normal
    89  	meHfs1CwsNormal = 0x5
    90  	// ME - current operation mode set to normal
    91  	meHfs1ComNormal = 0x0
    92  	// ME - CSE's firmware SKU is custom
    93  	meHfs3FwSkuCustom = 0x5
    94  
    95  	pciMEHfsts1 = 0x40
    96  	pciMEHfsts3 = 0x60
    97  )
    98  
    99  // MKHIClient is a client to send MKHI commands via MEI.
   100  type MKHIClient struct {
   101  	MEI *MEI
   102  }
   103  
   104  // MKHI command groups, see
   105  // https://github.com/coreboot/coreboot/blob/b8b8ec832360ada5a313f10938bb6cfc310a11eb/src/soc/intel/common/block/include/intelblocks/cse.h#L22
   106  const (
   107  	mkhiGroupIDCbm    = 0x0
   108  	mkhiGroupIDHMRFPO = 0x5
   109  	mkhiGroupIDGen    = 0xff
   110  )
   111  
   112  // MKHI HMRFPO command IDs, see
   113  // https://github.com/coreboot/coreboot/blob/b8b8ec832360ada5a313f10938bb6cfc310a11eb/src/soc/intel/common/block/include/intelblocks/cse.h#L33
   114  const (
   115  	mkhiHMRFPOEnable    = 0x1
   116  	mkhiHMRFPOGetStatus = 0x3
   117  )
   118  
   119  // OpenMKHI opens an MKHI client connection.
   120  func OpenMKHI(meiPath string) (*MKHIClient, error) {
   121  	m, err := OpenMEI(meiPath, MKHIGuid)
   122  	if err != nil {
   123  		return nil, fmt.Errorf("failed to open MKHI client: %v", err)
   124  	}
   125  	return &MKHIClient{MEI: m}, nil
   126  }
   127  
   128  // Close closes an MKHI client connection.
   129  func (m *MKHIClient) Close() error {
   130  	return m.MEI.Close()
   131  }
   132  
   133  // IsHMRFPOEnableAllowed queries whether the HMRFPO enable is allowed.
   134  func (m *MKHIClient) IsHMRFPOEnableAllowed() (bool, error) {
   135  	/* This is a reimplementation of cse_is_hmrfpo_enable_allowed from
   136  	 * coreboot/src/soc/intel/common/block/cse/cse.c . The below comment also
   137  	 * comes from that function.
   138  	 *
   139  	 * Allow sending HMRFPO ENABLE command only if:
   140  	 *  - CSE's current working state is Normal and current operation mode is Normal
   141  	 *  - (or) cse's current working state is normal and current operation mode is
   142  	 *    Soft Temp Disable if CSE's Firmware SKU is Custom
   143  	 *
   144  	 */
   145  	meiDev, err := GetMeiPciDevice()
   146  	if err != nil {
   147  		return false, fmt.Errorf("failed to get PCI MEI device: %v", err)
   148  	}
   149  	log.Printf("MEI Device found: %s", meiDev)
   150  
   151  	// check that CSE's current working state is normal
   152  	cs, err := meiDev.ReadConfigRegister(pciMEHfsts1, 32)
   153  	if err != nil {
   154  		return false, fmt.Errorf("PCI config read failed: %v", err)
   155  	}
   156  	// check that the current working state is ME_HFS1_CWS_NORMAL (0x05) and
   157  	// current operation mode is ME_HFS1_COM_NORMAL (0x0).
   158  	// `working_state` is bits 1-4 and `operation_state` is bits 7-9.
   159  	if (cs&0xf) != meHfs1CwsNormal || ((cs>>6)&0x7) != meHfs1ComNormal {
   160  		return false, nil
   161  	}
   162  	// check that CSE's firmware SKU is not custom, and if it is, that the
   163  	// current operation mode is Soft Temp Disable.
   164  	cs, err = meiDev.ReadConfigRegister(pciMEHfsts3, 32)
   165  	if err != nil {
   166  		return false, fmt.Errorf("PCI config read failed: %v", err)
   167  	}
   168  	// fw_sku is in bits 5-7 . ME_HFS3_FW_SKU_CUSTOM is 0x5
   169  	if (cs>>4)&0x7 == meHfs3FwSkuCustom {
   170  		// TODO implement the same as coreboot's cse_is_hfs1_com_soft_temp_disable()
   171  		return false, errors.New("IsHMRFPOEnableAllowed does not support checking for Soft Temp Disable yet")
   172  	}
   173  	return true, nil
   174  }
   175  
   176  type hmrfpoEnableMsg struct {
   177  	header mkhiHdr
   178  	nonce  uint32
   179  }
   180  
   181  func (hem hmrfpoEnableMsg) ToBytes() []byte {
   182  	var buf []byte
   183  	buf = append(buf, hem.header[:]...)
   184  	var nonce [4]byte
   185  	binary.LittleEndian.PutUint32(nonce[:], hem.nonce)
   186  	return append(buf, nonce[:]...)
   187  }
   188  
   189  type hmrfpoEnableResponse struct {
   190  	Header   mkhiHdr
   191  	FctBase  uint32
   192  	FctLimit uint32
   193  	Status   uint8
   194  	reserved [3]byte
   195  }
   196  
   197  func hmrfpoEnableResponseFromBytes(b []byte) (*hmrfpoEnableResponse, error) {
   198  	var resp hmrfpoEnableResponse
   199  	minlen := len(resp.Header)
   200  	maxlen := minlen +
   201  		4 /* FctBase */ +
   202  		4 /* FctLimit */ +
   203  		1 /* Status */ +
   204  		3 /* reserved bytes */
   205  	if len(b) != minlen && len(b) != maxlen {
   206  		return nil, fmt.Errorf("size mismatch, want %d/%d bytes, got %d", minlen, maxlen, len(b))
   207  	}
   208  	copy(resp.Header[:], b[:4])
   209  	if len(b) == minlen {
   210  		// don't parse the rest, we got a partial response
   211  		return &resp, nil
   212  	}
   213  	// TODO this could use u-root's pkg/uio
   214  	resp.FctBase = binary.LittleEndian.Uint32(b[4:8])
   215  	resp.FctLimit = binary.LittleEndian.Uint32(b[8:12])
   216  	resp.Status = b[12]
   217  	return &resp, nil
   218  }
   219  
   220  // EnableHMRFPO enables the HMRFPO (Host ME Region Flash Protection Override) via CSE,
   221  // see cse_hmrfpo_enable at
   222  // https://github.com/coreboot/coreboot/blob/b8b8ec832360ada5a313f10938bb6cfc310a11eb/src/soc/intel/common/block/include/intelblocks/cse.h#L64
   223  func (m *MKHIClient) EnableHMRFPO() error {
   224  
   225  	var hdr mkhiHdr
   226  	hdr.SetGroupID(mkhiGroupIDHMRFPO)
   227  	hdr.SetCommand(mkhiHMRFPOEnable)
   228  	canEnable, err := m.IsHMRFPOEnableAllowed()
   229  	if err != nil {
   230  		return fmt.Errorf("enabling HMRFPO failed: %v", err)
   231  	}
   232  	if !canEnable {
   233  		return fmt.Errorf("enabling HMRFPO is not allowed")
   234  	}
   235  	msg := hmrfpoEnableMsg{
   236  		header: hdr,
   237  		nonce:  0,
   238  	}
   239  	if _, err := m.MEI.Write(msg.ToBytes()); err != nil {
   240  		return fmt.Errorf("write to MEI failed: %v", err)
   241  	}
   242  	buf := make([]byte, m.MEI.ClientProperties.MaxMsgLength())
   243  	n, err := m.MEI.Read(buf)
   244  	if err != nil {
   245  		return fmt.Errorf("read from MEI failed: %v", err)
   246  	}
   247  	resp, err := hmrfpoEnableResponseFromBytes(buf[:n])
   248  	if err != nil {
   249  		return fmt.Errorf("failed to parse HMRFPOEnableResponse: %v", err)
   250  	}
   251  	if resp.Header.Result() != 0 {
   252  		return fmt.Errorf("failed to enable HMRFPO, request result is 0x%02x, want 0x0", resp.Header.Result())
   253  	}
   254  	if resp.Status != 0 {
   255  		return fmt.Errorf("failed to enable HMRFPO, request status is 0x%02x, want 0x0", resp.Status)
   256  	}
   257  	return nil
   258  }
   259  
   260  // GetMeiPciDevice will return the MEI PCI device object after scanning the PCI
   261  // bus.
   262  func GetMeiPciDevice() (*pci.PCI, error) {
   263  	br, err := pci.NewBusReader()
   264  	if err != nil {
   265  		return nil, fmt.Errorf("failed to create PCI bus reader: %v", err)
   266  	}
   267  	devices, err := br.Read()
   268  	if err != nil {
   269  		return nil, fmt.Errorf("failed to scan PCI bus: %v", err)
   270  	}
   271  	for _, device := range devices {
   272  		// look for vendor ID 8086 (Intel)
   273  		if device.Vendor != "8086" {
   274  			continue
   275  		}
   276  		productID, err := strconv.ParseInt(device.Device, 16, 64)
   277  		if err != nil {
   278  			return nil, fmt.Errorf("failed to parse device ID '%s' as hex: %v", device.Device, err)
   279  		}
   280  		// look for a known MEI product ID
   281  		for _, devID := range meiDevIDs {
   282  			if int64(devID) == productID {
   283  				device.SetVendorDeviceName()
   284  				// there is only one MEI device, right?
   285  				return device, nil
   286  			}
   287  		}
   288  	}
   289  	return nil, errors.New("no MEI device found")
   290  }