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 }