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 }