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