1 /*
2 * Copyright (c) 2026 Abdelkader Boudih <seuros@FreeBSD.org>
3 *
4 * SPDX-License-Identifier: BSD-2-Clause
5 */
6
7 /*
8 * hfsts(4) - Intel Management Engine Interface (MEI/HECI) debug probe.
9 *
10 * This is deliberately *not* a full MEI host-client driver: it only
11 * attaches to the PCI function and decodes the Host Firmware Status
12 * registers (HFSTS1-6, as supported by the device generation), which live
13 * entirely in PCI configuration space.
14 * BAR0 (the circular-buffer messaging ring) is never mapped.
15 *
16 * Register counts and layout follow Linux drivers/misc/mei/hw-me.c:
17 * ICH10 provides HFS1, PCH5 through PCH7 provide HFS1-2, and PCH8 and
18 * later provide HFS1-6 at PCI config offsets 0x40/0x48/0x60/0x64/0x68/0x6c.
19 */
20
21 #include <sys/param.h>
22 #include <sys/bus.h>
23 #include <sys/kernel.h>
24 #include <sys/module.h>
25 #include <sys/sysctl.h>
26
27 #include <dev/pci/pcivar.h>
28 #include <dev/pci/pcireg.h>
29 #include <dev/ichwd/ichwd.h>
30 #include <dev/intel/hfsts.h>
31
32 /* Host Firmware Status Registers - PCI configuration space offsets. */
33 #define HFSTS_CFG_HFS_1 0x40
34 #define HFSTS_HFS_1_CWS_MSK 0x0000000f
35 #define HFSTS_HFS_1_MFG_MODE_MSK 0x00000010
36 #define HFSTS_HFS_1_FPT_BAD_MSK 0x00000020
37 #define HFSTS_HFS_1_OPSTATE_MSK 0x000001c0
38 #define HFSTS_HFS_1_OPSTATE_SHIFT 6
39 #define HFSTS_HFS_1_FW_INIT_CMPL_MSK 0x00000200
40 #define HFSTS_HFS_1_BUP_FAIL_MSK 0x00000400
41 #define HFSTS_HFS_1_UPDATE_INPROG_MSK 0x00000800
42 #define HFSTS_HFS_1_ERROR_MSK 0x0000f000
43 #define HFSTS_HFS_1_ERROR_SHIFT 12
44 #define HFSTS_HFS_1_OPMODE_MSK 0x000f0000
45 #define HFSTS_HFS_1_OPMODE_SHIFT 16
46 #define HFSTS_HFS_1_BOOT_OPT_MSK 0x01000000
47 #define HFSTS_HFS_1_D0I3_MSK 0x80000000
48 #define HFSTS_CFG_HFS_2 0x48
49 #define HFSTS_HFS_2_PM_EVENT_MSK 0x0f000000
50 #define HFSTS_HFS_2_PM_EVENT_SHIFT 24
51 #define HFSTS_CFG_HFS_3 0x60
52 #define HFSTS_HFS_3_FW_SKU_MSK 0x00000070
53 #define HFSTS_HFS_3_FW_SKU_SHIFT 4
54 #define HFSTS_CFG_HFS_4 0x64
55 #define HFSTS_CFG_HFS_5 0x68
56 #define HFSTS_CFG_HFS_6 0x6c
57
58 /* HFS1 field values. Operation state and mode are sparse encodings. */
59 #define HFSTS_HFS_CWS_RESET 0
60 #define HFSTS_HFS_CWS_INIT 1
61 #define HFSTS_HFS_CWS_RECOVERY 2
62 #define HFSTS_HFS_CWS_TEST 3
63 #define HFSTS_HFS_CWS_DISABLED 4
64 #define HFSTS_HFS_CWS_NORMAL 5
65 #define HFSTS_HFS_CWS_WAIT 6
66 #define HFSTS_HFS_CWS_TRANS 7
67 #define HFSTS_HFS_CWS_INVALID 8
68
69 #define HFSTS_HFS_OPSTATE_PREBOOT 0
70 #define HFSTS_HFS_OPSTATE_M0_UMA 1
71 #define HFSTS_HFS_OPSTATE_M3 4
72 #define HFSTS_HFS_OPSTATE_M0 5
73 #define HFSTS_HFS_OPSTATE_BRINGUP 6
74 #define HFSTS_HFS_OPSTATE_IMAGE_ERROR 7
75
76 #define HFSTS_HFS_OPMODE_NORMAL 0
77 #define HFSTS_HFS_OPMODE_DEBUG 2
78 #define HFSTS_HFS_OPMODE_TEMP_DISABLED 3
79 #define HFSTS_HFS_OPMODE_JUMPER_OVERRIDE 4
80 #define HFSTS_HFS_OPMODE_MEI_OVERRIDE 5
81 #define HFSTS_HFS_OPMODE_SPS 15
82
83 #define HFSTS_HFS_ERROR_NONE 0
84 #define HFSTS_HFS_ERROR_UNCATEGORIZED 1
85 #define HFSTS_HFS_ERROR_DISABLED 2
86 #define HFSTS_HFS_ERROR_IMAGE 3
87 #define HFSTS_HFS_ERROR_DEBUG 4
88
89 static const char *const hfsts_cws_values[] = {
90 [HFSTS_HFS_CWS_RESET] = "Reset",
91 [HFSTS_HFS_CWS_INIT] = "Initializing",
92 [HFSTS_HFS_CWS_RECOVERY] = "Recovery",
93 [HFSTS_HFS_CWS_TEST] = "Test",
94 [HFSTS_HFS_CWS_DISABLED] = "Disabled",
95 [HFSTS_HFS_CWS_NORMAL] = "Normal",
96 [HFSTS_HFS_CWS_WAIT] = "Platform Disable Wait",
97 [HFSTS_HFS_CWS_TRANS] = "OP State Transition",
98 [HFSTS_HFS_CWS_INVALID] = "Invalid CPU Plugged In",
99 };
100
101 static const char *const hfsts_opstate_values[] = {
102 [HFSTS_HFS_OPSTATE_PREBOOT] = "Preboot",
103 [HFSTS_HFS_OPSTATE_M0_UMA] = "M0 (UMA)",
104 [HFSTS_HFS_OPSTATE_M3] = "M3 (no UMA)",
105 [HFSTS_HFS_OPSTATE_M0] = "M0 (no UMA)",
106 [HFSTS_HFS_OPSTATE_BRINGUP] = "Bring up",
107 [HFSTS_HFS_OPSTATE_IMAGE_ERROR] = "M0 (invalid firmware image)",
108 };
109
110 static const char *const hfsts_opmode_values[] = {
111 [HFSTS_HFS_OPMODE_NORMAL] = "Normal",
112 [HFSTS_HFS_OPMODE_DEBUG] = "Debug",
113 [HFSTS_HFS_OPMODE_TEMP_DISABLED] = "Temporarily disabled",
114 [HFSTS_HFS_OPMODE_JUMPER_OVERRIDE] = "Security override (jumper)",
115 [HFSTS_HFS_OPMODE_MEI_OVERRIDE] = "Security override (MEI)",
116 [HFSTS_HFS_OPMODE_SPS] = "Server Platform Services",
117 };
118
119 static const char *const hfsts_error_values[] = {
120 [HFSTS_HFS_ERROR_NONE] = "None",
121 [HFSTS_HFS_ERROR_UNCATEGORIZED] = "Uncategorized",
122 [HFSTS_HFS_ERROR_DISABLED] = "Disabled",
123 [HFSTS_HFS_ERROR_IMAGE] = "Firmware image",
124 [HFSTS_HFS_ERROR_DEBUG] = "Debug",
125 };
126
127 static const char *
hfsts_decode(const char * const * tbl,size_t tbl_sz,uint32_t idx)128 hfsts_decode(const char *const *tbl, size_t tbl_sz, uint32_t idx)
129 {
130
131 if (idx >= tbl_sz || tbl[idx] == NULL)
132 return ("Unknown");
133 return (tbl[idx]);
134 }
135 #define HFSTS_DECODE(tbl, idx) hfsts_decode((tbl), nitems(tbl), (idx))
136
137 /* HFS1-6 PCI configuration offsets, indexed 0-5. */
138 static const uint32_t hfsts_hfs_off[6] = {
139 HFSTS_CFG_HFS_1, HFSTS_CFG_HFS_2, HFSTS_CFG_HFS_3,
140 HFSTS_CFG_HFS_4, HFSTS_CFG_HFS_5, HFSTS_CFG_HFS_6,
141 };
142
143 /*
144 * All HFS fields are re-read from PCI configuration space on every sysctl
145 * access: the ME can transition CWS/opstate/error post-boot (watchdog
146 * resets, HAP toggles, S0i3/M0-M3 power events), so an attach-time snapshot
147 * would go stale under a running system.
148 */
149 static uint32_t
hfsts_read_hfs(device_t dev,u_int idx)150 hfsts_read_hfs(device_t dev, u_int idx)
151 {
152
153 return (pci_read_config(dev, hfsts_hfs_off[idx], 4));
154 }
155
156 /*
157 * One-word summary for the log/sysctl consumer who doesn't want to parse
158 * the raw HFS fields: Absent (register read didn't land on a real state),
159 * Disabled (ME explicitly turned off), Active (busy, transitional, or in an
160 * exceptional mode), or Normal (steady-state, with no reported error).
161 */
162 static const char *
hfsts_state_summary(uint32_t cws,uint32_t opmode,uint32_t error,uint32_t mfg_mode,uint32_t fpt_bad,uint32_t bup_fail,uint32_t fw_init_cmpl,uint32_t update_inprog)163 hfsts_state_summary(uint32_t cws, uint32_t opmode, uint32_t error,
164 uint32_t mfg_mode, uint32_t fpt_bad, uint32_t bup_fail,
165 uint32_t fw_init_cmpl, uint32_t update_inprog)
166 {
167
168 if (cws >= nitems(hfsts_cws_values) || hfsts_cws_values[cws] == NULL)
169 return ("Absent");
170 if (cws == HFSTS_HFS_CWS_DISABLED || cws == HFSTS_HFS_CWS_WAIT ||
171 opmode == HFSTS_HFS_OPMODE_TEMP_DISABLED ||
172 error == HFSTS_HFS_ERROR_DISABLED)
173 return ("Disabled");
174 if (cws != HFSTS_HFS_CWS_NORMAL || opmode != HFSTS_HFS_OPMODE_NORMAL ||
175 error != HFSTS_HFS_ERROR_NONE || mfg_mode || fpt_bad || bup_fail ||
176 !fw_init_cmpl || update_inprog)
177 return ("Active");
178 return ("Normal");
179 }
180
181 struct hfsts_device {
182 uint16_t device;
183 uint8_t hfs_count;
184 const char *name;
185 };
186
187 /*
188 * Intel MEI/HECI PCI functions with usable Host Firmware Status registers.
189 * ICH10 has HFS1, PCH5 through PCH7 have HFS1-2, and PCH8 and newer have
190 * HFS1-6. Legacy ICH devices, for which no HFS register is defined, are
191 * deliberately not matched.
192 */
193 #define HFSTS_DEV_ICH10(id, desc) { (id), 1, (desc) }
194 #define HFSTS_DEV_PCH(id, desc) { (id), 2, (desc) }
195 #define HFSTS_DEV_PCH8(id, desc) { (id), 6, (desc) }
196
197 static const struct hfsts_device hfsts_devices[] = {
198 HFSTS_DEV_ICH10(0x2e04, "Eaglelake MEI"),
199 HFSTS_DEV_ICH10(0x2e14, "Eaglelake MEI"),
200 HFSTS_DEV_ICH10(0x2e24, "Eaglelake MEI"),
201 HFSTS_DEV_ICH10(0x2e34, "Eaglelake MEI"),
202 HFSTS_DEV_PCH(0x3b64, "Calpella MEI"),
203 HFSTS_DEV_PCH(0x3b65, "Calpella MEI"),
204 HFSTS_DEV_PCH(0x1c3a, "Cougar Point MEI"),
205 HFSTS_DEV_PCH(0x1d3a, "C600/X79 Patsburg MEI"),
206 HFSTS_DEV_PCH(0x1e3a, "Panther Point MEI"),
207 HFSTS_DEV_PCH(0x1cba, "Panther Point MEI"),
208 HFSTS_DEV_PCH(0x1dba, "Panther Point MEI"),
209 HFSTS_DEV_PCH8(0x8c3a, "Lynx Point H MEI"),
210 HFSTS_DEV_PCH8(0x8d3a, "Lynx Point Wellsburg MEI"),
211 HFSTS_DEV_PCH8(0x9c3a, "Lynx Point LP MEI"),
212 HFSTS_DEV_PCH8(0x8cba, "Lynx Point H Refresh MEI"),
213 HFSTS_DEV_PCH8(0x9cba, "Wildcat Point LP MEI"),
214 HFSTS_DEV_PCH8(0x9cbb, "Wildcat Point LP 2 MEI"),
215 HFSTS_DEV_PCH8(0x9d3a, "Sunrise Point MEI"),
216 HFSTS_DEV_PCH8(0x9d3b, "Sunrise Point 2 MEI"),
217 HFSTS_DEV_PCH8(0x9d3e, "Sunrise Point 3 (iTouch) MEI"),
218 HFSTS_DEV_PCH8(0xa13a, "Sunrise Point H MEI"),
219 HFSTS_DEV_PCH8(0xa13b, "Sunrise Point H 2 MEI"),
220 HFSTS_DEV_PCH8(0xa1ba, "Lewisburg (SPT) MEI"),
221 HFSTS_DEV_PCH8(0x1a9a, "Broxton M MEI"),
222 HFSTS_DEV_PCH8(0x5a9a, "Apollo Lake I MEI"),
223 HFSTS_DEV_PCH8(0x19e5, "Denverton IE MEI"),
224 HFSTS_DEV_PCH8(0x319a, "Gemini Lake MEI"),
225 HFSTS_DEV_PCH8(0xa2ba, "Kaby Point MEI"),
226 HFSTS_DEV_PCH8(0xa2bb, "Kaby Point 2 MEI"),
227 HFSTS_DEV_PCH8(0xa2be, "Kaby Point 3 (iTouch) MEI"),
228 HFSTS_DEV_PCH8(0x9de0, "Cannon Point LP MEI"),
229 HFSTS_DEV_PCH8(0x9de4, "Cannon Point LP 3 (iTouch) MEI"),
230 HFSTS_DEV_PCH8(0xa360, "Cannon Point H MEI"),
231 HFSTS_DEV_PCH8(0xa364, "Cannon Point H 3 (iTouch) MEI"),
232 HFSTS_DEV_PCH8(0x02e0, "Comet Point LP MEI"),
233 HFSTS_DEV_PCH8(0x02e4, "Comet Point LP 3 (iTouch) MEI"),
234 HFSTS_DEV_PCH8(0xa3ba, "Comet Point Lake V MEI"),
235 HFSTS_DEV_PCH8(0x06e0, "Comet Lake H MEI"),
236 HFSTS_DEV_PCH8(0x06e4, "Comet Lake H 3 (iTouch) MEI"),
237 HFSTS_DEV_PCH8(0x18d3, "Cedar Fork MEI"),
238 HFSTS_DEV_PCH8(0x34e0, "Ice Lake Point LP MEI"),
239 HFSTS_DEV_PCH8(0x38e0, "Ice Lake Point N MEI"),
240 HFSTS_DEV_PCH8(0x4de0, "Jasper Lake Point N MEI"),
241 HFSTS_DEV_PCH8(0xa0e0, "Tiger Lake Point LP MEI"),
242 HFSTS_DEV_PCH8(0x43e0, "Tiger Lake Point H MEI"),
243 HFSTS_DEV_PCH8(0x4b70, "Mule Creek Canyon (EHL) MEI"),
244 HFSTS_DEV_PCH8(0x4b75, "Mule Creek Canyon 4 (EHL) MEI"),
245 HFSTS_DEV_PCH8(0x1be0, "Emmitsburg WS MEI"),
246 HFSTS_DEV_PCH8(0x7ae8, "Alder Lake Point S MEI"),
247 HFSTS_DEV_PCH8(0x7a60, "Alder Lake Point LP MEI"),
248 HFSTS_DEV_PCH8(0x51e0, "Alder Lake Point P MEI"),
249 HFSTS_DEV_PCH8(0x54e0, "Alder Lake Point N MEI"),
250 HFSTS_DEV_PCH8(0x7a68, "Raptor Lake Point S MEI"),
251 HFSTS_DEV_PCH8(0x7e70, "Meteor Lake Point M MEI"),
252 HFSTS_DEV_PCH8(0x7f68, "Arrow Lake Point S MEI"),
253 HFSTS_DEV_PCH8(0x7770, "Arrow Lake Point H MEI"),
254 HFSTS_DEV_PCH8(0xa870, "Lunar Lake Point M MEI"),
255 HFSTS_DEV_PCH8(0xe370, "Panther Lake H MEI"),
256 HFSTS_DEV_PCH8(0xe470, "Panther Lake P MEI"),
257 HFSTS_DEV_PCH8(0x4d70, "Wildcat Lake P MEI"),
258 HFSTS_DEV_PCH8(0x6e68, "Nova Lake Point S MEI"),
259 HFSTS_DEV_PCH8(0xd370, "Nova Lake Point H MEI"),
260 { 0, 0, NULL }
261 };
262
263 static const struct hfsts_device *
hfsts_find_device(device_t dev)264 hfsts_find_device(device_t dev)
265 {
266 const struct hfsts_device *id;
267
268 for (id = hfsts_devices; id->name != NULL; id++) {
269 if (pci_get_device(dev) == id->device)
270 return (id);
271 }
272 return (NULL);
273 }
274
275 /*
276 * The isa-side Function Disable check is only meaningful when no known
277 * MEI/HECI function enumerated on PCI.
278 */
279 bool
hfsts_pci_present(void)280 hfsts_pci_present(void)
281 {
282 const struct hfsts_device *id;
283
284 for (id = hfsts_devices; id->name != NULL; id++) {
285 if (pci_find_device(VENDORID_INTEL, id->device) != NULL)
286 return (true);
287 }
288 return (false);
289 }
290
291 struct hfsts_softc {
292 device_t sc_dev;
293 struct sysctl_ctx_list *sc_sysctlctx;
294 struct sysctl_oid *sc_sysctlnode;
295 uint8_t sc_hfs_count;
296 };
297
298 /* Simple masked/shifted HFS sub-fields, read live via a common handler. */
299 enum hfsts_field_id {
300 HFSTS_FLD_D0I3_SUPPORTED,
301 HFSTS_FLD_OPMODE,
302 HFSTS_FLD_PM_EVENT,
303 HFSTS_FLD_FW_SKU,
304 HFSTS_FLD_MFG_MODE,
305 HFSTS_FLD_UPDATE_INPROG,
306 };
307
308 struct hfsts_field {
309 uint8_t hfs_idx;
310 uint32_t mask;
311 uint8_t shift;
312 };
313
314 static const struct hfsts_field hfsts_fields[] = {
315 [HFSTS_FLD_D0I3_SUPPORTED] = { 0, HFSTS_HFS_1_D0I3_MSK, 31 },
316 [HFSTS_FLD_OPMODE] = { 0, HFSTS_HFS_1_OPMODE_MSK,
317 HFSTS_HFS_1_OPMODE_SHIFT },
318 [HFSTS_FLD_PM_EVENT] = { 1, HFSTS_HFS_2_PM_EVENT_MSK,
319 HFSTS_HFS_2_PM_EVENT_SHIFT },
320 [HFSTS_FLD_FW_SKU] = { 2, HFSTS_HFS_3_FW_SKU_MSK,
321 HFSTS_HFS_3_FW_SKU_SHIFT },
322 [HFSTS_FLD_MFG_MODE] = { 0, HFSTS_HFS_1_MFG_MODE_MSK, 4 },
323 [HFSTS_FLD_UPDATE_INPROG] = { 0, HFSTS_HFS_1_UPDATE_INPROG_MSK, 11 },
324 };
325
326 static int
hfsts_sysctl_raw(SYSCTL_HANDLER_ARGS)327 hfsts_sysctl_raw(SYSCTL_HANDLER_ARGS)
328 {
329 struct hfsts_softc *sc = arg1;
330 uint32_t val;
331
332 val = hfsts_read_hfs(sc->sc_dev, (u_int)arg2);
333 return (sysctl_handle_32(oidp, &val, 0, req));
334 }
335
336 static int
hfsts_sysctl_field(SYSCTL_HANDLER_ARGS)337 hfsts_sysctl_field(SYSCTL_HANDLER_ARGS)
338 {
339 struct hfsts_softc *sc = arg1;
340 const struct hfsts_field *f = &hfsts_fields[arg2];
341 uint32_t val;
342
343 val = (hfsts_read_hfs(sc->sc_dev, f->hfs_idx) & f->mask) >> f->shift;
344 return (sysctl_handle_32(oidp, &val, 0, req));
345 }
346
347 /* HFS1 sub-fields that decode to a string via a lookup table. */
348 enum hfsts_decode_id {
349 HFSTS_DEC_CWS,
350 HFSTS_DEC_OPSTATE,
351 HFSTS_DEC_OPMODE,
352 HFSTS_DEC_ERROR,
353 };
354
355 struct hfsts_decode {
356 uint32_t mask;
357 uint8_t shift;
358 const char *const *tbl;
359 size_t tbl_sz;
360 };
361
362 static const struct hfsts_decode hfsts_decodes[] = {
363 [HFSTS_DEC_CWS] = { HFSTS_HFS_1_CWS_MSK, 0,
364 hfsts_cws_values, nitems(hfsts_cws_values) },
365 [HFSTS_DEC_OPSTATE] = { HFSTS_HFS_1_OPSTATE_MSK,
366 HFSTS_HFS_1_OPSTATE_SHIFT,
367 hfsts_opstate_values,
368 nitems(hfsts_opstate_values) },
369 [HFSTS_DEC_OPMODE] = { HFSTS_HFS_1_OPMODE_MSK,
370 HFSTS_HFS_1_OPMODE_SHIFT,
371 hfsts_opmode_values,
372 nitems(hfsts_opmode_values) },
373 [HFSTS_DEC_ERROR] = { HFSTS_HFS_1_ERROR_MSK, HFSTS_HFS_1_ERROR_SHIFT,
374 hfsts_error_values, nitems(hfsts_error_values) },
375 };
376
377 static int
hfsts_sysctl_decode(SYSCTL_HANDLER_ARGS)378 hfsts_sysctl_decode(SYSCTL_HANDLER_ARGS)
379 {
380 struct hfsts_softc *sc = arg1;
381 const struct hfsts_decode *d = &hfsts_decodes[arg2];
382 uint32_t val;
383
384 val = (hfsts_read_hfs(sc->sc_dev, 0) & d->mask) >> d->shift;
385 return (SYSCTL_OUT_STR(req, hfsts_decode(d->tbl, d->tbl_sz, val)));
386 }
387
388 static int
hfsts_sysctl_summary(SYSCTL_HANDLER_ARGS)389 hfsts_sysctl_summary(SYSCTL_HANDLER_ARGS)
390 {
391 struct hfsts_softc *sc = arg1;
392 uint32_t hfs1, cws, opmode, error;
393 uint32_t mfg_mode, fpt_bad, bup_fail, fw_init_cmpl, update_inprog;
394
395 hfs1 = hfsts_read_hfs(sc->sc_dev, 0);
396 cws = hfs1 & HFSTS_HFS_1_CWS_MSK;
397 opmode = (hfs1 & HFSTS_HFS_1_OPMODE_MSK) >> HFSTS_HFS_1_OPMODE_SHIFT;
398 error = (hfs1 & HFSTS_HFS_1_ERROR_MSK) >> HFSTS_HFS_1_ERROR_SHIFT;
399 mfg_mode = (hfs1 & HFSTS_HFS_1_MFG_MODE_MSK) != 0;
400 fpt_bad = (hfs1 & HFSTS_HFS_1_FPT_BAD_MSK) != 0;
401 bup_fail = (hfs1 & HFSTS_HFS_1_BUP_FAIL_MSK) != 0;
402 fw_init_cmpl = (hfs1 & HFSTS_HFS_1_FW_INIT_CMPL_MSK) != 0;
403 update_inprog = (hfs1 & HFSTS_HFS_1_UPDATE_INPROG_MSK) != 0;
404
405 return (SYSCTL_OUT_STR(req, hfsts_state_summary(cws, opmode, error,
406 mfg_mode, fpt_bad, bup_fail, fw_init_cmpl, update_inprog)));
407 }
408
409 /* Table-driven sysctl registration; skipped when sc_hfs_count < min_hfs. */
410 typedef int hfsts_sysctl_handler_t(SYSCTL_HANDLER_ARGS);
411
412 struct hfsts_sysctl_def {
413 const char *name;
414 int ctltype;
415 intmax_t arg2;
416 hfsts_sysctl_handler_t *handler;
417 const char *fmt;
418 const char *descr;
419 uint8_t min_hfs;
420 };
421
422 static const struct hfsts_sysctl_def hfsts_sysctls[] = {
423 { "hfs1", CTLTYPE_U32, 0, hfsts_sysctl_raw, "IU",
424 "Host Firmware Status 1 (raw, live)", 1 },
425 { "hfs2", CTLTYPE_U32, 1, hfsts_sysctl_raw, "IU",
426 "Host Firmware Status 2 (raw, live)", 2 },
427 { "hfs3", CTLTYPE_U32, 2, hfsts_sysctl_raw, "IU",
428 "Host Firmware Status 3 (raw, live)", 6 },
429 { "hfs4", CTLTYPE_U32, 3, hfsts_sysctl_raw, "IU",
430 "Host Firmware Status 4 (raw, live)", 6 },
431 { "hfs5", CTLTYPE_U32, 4, hfsts_sysctl_raw, "IU",
432 "Host Firmware Status 5 (raw, live)", 6 },
433 { "hfs6", CTLTYPE_U32, 5, hfsts_sysctl_raw, "IU",
434 "Host Firmware Status 6 (raw, live)", 6 },
435 { "d0i3_supported", CTLTYPE_U32, HFSTS_FLD_D0I3_SUPPORTED,
436 hfsts_sysctl_field, "IU",
437 "ME D0i3 capability (from HFS1, live)", 6 },
438 { "opmode", CTLTYPE_U32, HFSTS_FLD_OPMODE, hfsts_sysctl_field, "IU",
439 "ME operation mode (from HFS1, live)", 1 },
440 { "pm_event", CTLTYPE_U32, HFSTS_FLD_PM_EVENT, hfsts_sysctl_field,
441 "IU", "ME power management event (from HFS2, live)", 2 },
442 { "fw_sku", CTLTYPE_U32, HFSTS_FLD_FW_SKU, hfsts_sysctl_field, "IU",
443 "ME firmware SKU (from HFS3, live)", 6 },
444 { "mfg_mode", CTLTYPE_U32, HFSTS_FLD_MFG_MODE, hfsts_sysctl_field,
445 "IU", "ME running in Manufacturing Mode (from HFS1, live)", 1 },
446 { "update_in_progress", CTLTYPE_U32, HFSTS_FLD_UPDATE_INPROG,
447 hfsts_sysctl_field, "IU",
448 "ME firmware update in progress (from HFS1, live)", 1 },
449 { "state", CTLTYPE_STRING, HFSTS_DEC_CWS, hfsts_sysctl_decode, "A",
450 "ME Current Working State (from HFS1, live)", 1 },
451 { "opstate", CTLTYPE_STRING, HFSTS_DEC_OPSTATE, hfsts_sysctl_decode,
452 "A", "ME Current Operation State (from HFS1, live)", 1 },
453 { "opmode_str", CTLTYPE_STRING, HFSTS_DEC_OPMODE, hfsts_sysctl_decode,
454 "A", "ME Current Operation Mode (from HFS1, live)", 1 },
455 { "error", CTLTYPE_STRING, HFSTS_DEC_ERROR, hfsts_sysctl_decode, "A",
456 "ME Error Code (from HFS1, live)", 1 },
457 { "summary", CTLTYPE_STRING, 0, hfsts_sysctl_summary, "A",
458 "One-word ME state summary (live)", 1 },
459 };
460
461 static int
hfsts_probe(device_t dev)462 hfsts_probe(device_t dev)
463 {
464 const struct hfsts_device *id;
465
466 if (pci_get_vendor(dev) != VENDORID_INTEL)
467 return (ENXIO);
468
469 id = hfsts_find_device(dev);
470 if (id == NULL)
471 return (ENXIO);
472 device_set_desc(dev, id->name);
473 return (BUS_PROBE_GENERIC);
474 }
475
476 static int
hfsts_attach(device_t dev)477 hfsts_attach(device_t dev)
478 {
479 const struct hfsts_device *id;
480 struct hfsts_softc *sc;
481 struct sysctl_oid_list *children;
482 const char *summary;
483 uint32_t hfs1;
484 uint32_t opmode;
485 uint32_t cws, opstate, error, mfg_mode, fpt_bad, fw_init_cmpl;
486 uint32_t bup_fail, update_inprog, boot_opt;
487 u_int i;
488
489 sc = device_get_softc(dev);
490 sc->sc_dev = dev;
491 id = hfsts_find_device(dev);
492 if (id == NULL)
493 return (ENXIO);
494 sc->sc_hfs_count = id->hfs_count;
495
496 /* One-shot read for the attach-time log line only; sysctls re-read. */
497 hfs1 = hfsts_read_hfs(dev, 0);
498 opmode = (hfs1 & HFSTS_HFS_1_OPMODE_MSK) >> HFSTS_HFS_1_OPMODE_SHIFT;
499 cws = hfs1 & HFSTS_HFS_1_CWS_MSK;
500 opstate = (hfs1 & HFSTS_HFS_1_OPSTATE_MSK) >> HFSTS_HFS_1_OPSTATE_SHIFT;
501 error = (hfs1 & HFSTS_HFS_1_ERROR_MSK) >> HFSTS_HFS_1_ERROR_SHIFT;
502 mfg_mode = (hfs1 & HFSTS_HFS_1_MFG_MODE_MSK) != 0;
503 fpt_bad = (hfs1 & HFSTS_HFS_1_FPT_BAD_MSK) != 0;
504 fw_init_cmpl = (hfs1 & HFSTS_HFS_1_FW_INIT_CMPL_MSK) != 0;
505 bup_fail = (hfs1 & HFSTS_HFS_1_BUP_FAIL_MSK) != 0;
506 update_inprog = (hfs1 & HFSTS_HFS_1_UPDATE_INPROG_MSK) != 0;
507 boot_opt = (hfs1 & HFSTS_HFS_1_BOOT_OPT_MSK) != 0;
508 summary = hfsts_state_summary(cws, opmode, error, mfg_mode, fpt_bad,
509 bup_fail, fw_init_cmpl, update_inprog);
510
511 if (bootverbose) {
512 device_printf(dev,
513 "state=%s opstate=\"%s\" opmode=\"%s\" error=\"%s\" "
514 "mfg_mode=%u fpt_bad=%u fw_init_complete=%u "
515 "update_in_progress=%u boot_options_present=%u\n",
516 HFSTS_DECODE(hfsts_cws_values, cws),
517 HFSTS_DECODE(hfsts_opstate_values, opstate),
518 HFSTS_DECODE(hfsts_opmode_values, opmode),
519 HFSTS_DECODE(hfsts_error_values, error),
520 mfg_mode, fpt_bad, fw_init_cmpl, update_inprog, boot_opt);
521 }
522
523 device_printf(dev, "ME state: %s\n", summary);
524
525 sc->sc_sysctlctx = device_get_sysctl_ctx(dev);
526 sc->sc_sysctlnode = device_get_sysctl_tree(dev);
527 children = SYSCTL_CHILDREN(sc->sc_sysctlnode);
528
529 for (i = 0; i < nitems(hfsts_sysctls); i++) {
530 const struct hfsts_sysctl_def *d = &hfsts_sysctls[i];
531
532 if (sc->sc_hfs_count < d->min_hfs)
533 continue;
534 sysctl_add_oid(sc->sc_sysctlctx, children, OID_AUTO, d->name,
535 d->ctltype | CTLFLAG_RD | CTLFLAG_MPSAFE, sc, d->arg2,
536 d->handler, d->fmt, __DESCR(d->descr), NULL);
537 }
538
539 return (0);
540 }
541
542 static int
hfsts_detach(device_t dev)543 hfsts_detach(device_t dev)
544 {
545
546 return (0);
547 }
548
549 static device_method_t hfsts_methods[] = {
550 DEVMETHOD(device_probe, hfsts_probe),
551 DEVMETHOD(device_attach, hfsts_attach),
552 DEVMETHOD(device_detach, hfsts_detach),
553 DEVMETHOD_END
554 };
555
556 static driver_t hfsts_driver = {
557 "hfsts",
558 hfsts_methods,
559 sizeof(struct hfsts_softc)
560 };
561
562 DRIVER_MODULE(hfsts, pci, hfsts_driver, 0, 0);
563 MODULE_VERSION(hfsts, 1);
564 MODULE_DEPEND(hfsts, pci, 1, 1, 1);
565