Page Menu
Home
FreeBSD
Search
Configure Global Search
Log In
Files
F166713107
D58863.diff
No One
Temporary
Actions
View File
Edit File
Delete File
View Transforms
Subscribe
Mute Notifications
Flag For Later
Award Token
Size
31 KB
Referenced Files
None
Subscribers
None
D58863.diff
View Options
diff --git a/share/man/man4/Makefile b/share/man/man4/Makefile
--- a/share/man/man4/Makefile
+++ b/share/man/man4/Makefile
@@ -344,6 +344,7 @@
md.4 \
mdio.4 \
me.4 \
+ mei.4 \
mem.4 \
mfi.4 \
${_mgb.4} \
@@ -784,6 +785,7 @@
MLINKS+=lp.4 plip.4
MLINKS+=malo.4 if_malo.4
MLINKS+=me.4 if_me.4
+MLINKS+=mei.4 meifd.4
MLINKS+=mem.4 kmem.4
MLINKS+=mfi.4 mfi_linux.4 \
mfi.4 mfip.4
diff --git a/share/man/man4/mei.4 b/share/man/man4/mei.4
new file mode 100644
--- /dev/null
+++ b/share/man/man4/mei.4
@@ -0,0 +1,223 @@
+.\" SPDX-License-Identifier: BSD-2-Clause
+.\"
+.\" Copyright (c) 2026 Abdelkader Boudih <freebsd@seuros.com>
+.\"
+.\" Redistribution and use in source and binary forms, with or without
+.\" modification, are permitted provided that the following conditions
+.\" are met:
+.\" 1. Redistributions of source code must retain the above copyright
+.\" notice, this list of conditions and the following disclaimer.
+.\" 2. Redistributions in binary form must reproduce the above copyright
+.\" notice, this list of conditions and the following disclaimer in the
+.\" documentation and/or other materials provided with the distribution.
+.\"
+.\" THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
+.\" ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+.\" IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+.\" ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
+.\" FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+.\" DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+.\" OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+.\" HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+.\" LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+.\" OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+.\" SUCH DAMAGE.
+.\"
+.Dd August 15, 2026
+.Dt MEI 4
+.Os
+.Sh NAME
+.Nm mei ,
+.Nm meifd
+.Nd Intel Management Engine Interface (MEI/HECI) debug probe
+.Sh SYNOPSIS
+To compile this driver into the kernel,
+place the following lines in your
+kernel configuration file:
+.Bd -ragged -offset indent
+.Cd "device pci"
+.Cd "device isa"
+.Cd "device mei"
+.Ed
+.Pp
+Alternatively, to load the driver as a
+module at boot time, place the following line in
+.Xr loader.conf 5 :
+.Bd -literal -offset indent
+mei_load="YES"
+.Ed
+.Sh DESCRIPTION
+The
+.Nm mei
+driver is a debug probe for the Intel Management Engine / HECI
+interface.
+It is supported on
+amd64
+systems only.
+It is deliberately
+.Em not
+a full MEI host-client driver: it attaches only to the PCI function
+and decodes the Host Firmware Status registers available on that generation
+.Pq Dv HFSTS1 Ns \(en Ns Dv HFSTS6 ,
+which live entirely in PCI configuration space.
+BAR0, the circular-buffer messaging ring used for actual host-ME
+communication, is never mapped.
+.Pp
+The register counts and layout are confirmed against the Linux
+.Pa drivers/misc/mei/hw-me-regs.h
+and
+.Pa hw-me.c
+sources.
+ICH10 provides HFSTS1, PCH5 through PCH7 provide HFSTS1 and HFSTS2,
+and PCH8 and later provide all six registers at PCI configuration
+offsets 0x40/0x48/0x60/0x64/0x68/0x6c.
+Older ICH devices without a defined Host Firmware Status register are
+not matched.
+.Pp
+On attach, the driver decodes the Current Working State, Operation
+State, Operation Mode, and Error Code fields into named strings and
+derives a one-word summary
+.Pq Dq Normal , Dq Active , Dq Disabled , or Dq Absent
+for consumers that do not want to parse the raw fields.
+.Ss meifd
+The MEI PCI function never enumerates when firmware has disabled it
+via the PCH Function Disable mechanism, so there is no PCI device
+instance for
+.Nm mei
+to attach to.
+.Nm meifd
+is an isa-attached identify driver, modeled on
+.Xr ichwd 4 Ns 's
+.Fn ichwd_identify ,
+that reads the LPC bridge's RCBA
+.Pq Root Complex Base Address
+and the FD2
+.Pq Function Disable 2
+register at RCBA+0x3428 to determine whether HECI1/HECI2 are present
+in silicon but hidden.
+.Pp
+The RCBA and FD2 layout is supported on Ibex Peak, Cougar Point,
+Panther Point, Lynx Point, and Wildcat Point.
+Other chipsets report one of:
+.Bl -tag -width "unknown-generation"
+.It Cm unknown-generation
+The LPC bridge is an Intel device but is not one of the supported PCH
+generations.
+No assumption is made about its ME capability or disable mechanism.
+.It Cm not-applicable
+The LPC bridge is not a PCI device, or is not an Intel device.
+.It Cm rcba-disabled
+RCBA is present but not enabled, so the Function Disable register
+cannot be read.
+.It Cm checked
+FD2 was read successfully; the
+.Va mei1_disabled
+and
+.Va mei2_disabled
+sysctls reflect its bits.
+.El
+.Sh SYSCTL VARIABLES
+The following
+.Xr sysctl 8
+variables are available under
+.Va dev.mei.N
+when the driver is attached to a PCI MEI/HECI function:
+.Bl -tag -width "update_in_progress"
+.It Va hfs1
+Raw Host Firmware Status 1 register value.
+.It Va hfs2
+Raw Host Firmware Status 2 register value, on PCH5 and later.
+.It Va hfs3 , hfs4 , hfs5 , hfs6
+Raw Host Firmware Status 3 through 6 register values, on PCH8 and later.
+.It Va d0i3
+D0i3 power state, from HFS1, on PCH8 and later.
+.It Va opmode
+ME operation mode, from HFS1, as a numeric value.
+.It Va pm_event
+ME power management event, from HFS2, on PCH5 and later.
+.It Va fw_sku
+ME firmware SKU, from HFS3, on PCH8 and later.
+.It Va mfg_mode
+Non-zero if the ME is running in Manufacturing Mode, from HFS1.
+.It Va update_in_progress
+Non-zero if an ME firmware update is in progress, from HFS1.
+.It Va state
+ME Current Working State, decoded to a string
+.Pq e.g., Dq Normal , Dq Disabled .
+.It Va opstate
+ME Current Operation State, decoded to a string.
+.It Va opmode_str
+ME Current Operation Mode, decoded to a string.
+.It Va error
+ME Error Code, decoded to a string.
+.It Va summary
+One-word ME state summary; one of
+.Dq Normal ,
+.Dq Active ,
+.Dq Disabled ,
+or
+.Dq Absent .
+.El
+.Pp
+The
+.Va status
+.Xr sysctl 8
+variable is available under
+.Va dev.meifd.N
+when the
+.Nm meifd
+identify driver has attached.
+It contains the
+function-disable check result; one of the states listed in
+.Sx meifd
+above.
+.Pp
+When
+.Va status
+is
+.Cm checked ,
+the following additional variables are available:
+.Bl -tag -width "mei1_disabled"
+.It Va fd2
+Raw Function Disable 2 register value.
+.It Va mei1_disabled
+Non-zero if HECI1 is disabled via the Function Disable register.
+.It Va mei2_disabled
+Non-zero if HECI2 is disabled via the Function Disable register.
+.El
+.Sh EXAMPLES
+Show the decoded ME state on a system where MEI enumerates normally:
+.Pp
+.Dl "sysctl dev.mei.0"
+.Pp
+Check whether HECI has been hidden via PCH Function Disable on a
+system where no MEI PCI function is present:
+.Pp
+.Dl "sysctl dev.meifd.0"
+.Sh SEE ALSO
+.Xr ichwd 4 ,
+.Xr pci 4 ,
+.Xr sysctl 8
+.Sh HISTORY
+The
+.Nm mei
+and
+.Nm meifd
+drivers first appeared in
+.Fx 16.0 .
+.Sh AUTHORS
+.An Abdelkader Boudih Aq Mt freebsd@seuros.com .
+.Sh CAVEATS
+.Nm mei
+only decodes PCI-configuration-space firmware status registers; it
+provides no access to the MEI messaging ring and cannot be used to
+communicate with the ME.
+.Pp
+.Nm meifd
+identifies HECI Function Disable state on a known set of PCH
+generations only; chipsets that moved ME-disable to a P2SB-sideband
+mechanism
+.Pq Skylake/Sunrise Point and later
+are reported as
+.Cm unknown-generation
+rather than decoded.
diff --git a/sys/conf/files.amd64 b/sys/conf/files.amd64
--- a/sys/conf/files.amd64
+++ b/sys/conf/files.amd64
@@ -248,6 +248,7 @@
compile-with "${NORMAL_C} -I$S/dev/ixl"
dev/ixl/i40e_dcb.c optional ixl pci \
compile-with "${NORMAL_C} -I$S/dev/ixl"
+dev/mei/mei_pci.c optional mei pci isa
dev/ncthwm/ncthwm.c optional ncthwm superio
dev/qlxge/qls_dbg.c optional qlxge pci
dev/qlxge/qls_dump.c optional qlxge pci
diff --git a/sys/dev/mei/mei_pci.c b/sys/dev/mei/mei_pci.c
new file mode 100644
--- /dev/null
+++ b/sys/dev/mei/mei_pci.c
@@ -0,0 +1,659 @@
+/*
+ * Copyright (c) 2026 Abdelkader Boudih <freebsd@seuros.com>
+ *
+ * SPDX-License-Identifier: BSD-2-Clause
+ */
+
+/*
+ * mei(4) - Intel Management Engine Interface (MEI/HECI) debug probe.
+ *
+ * This is deliberately *not* a full MEI host-client driver: it only
+ * attaches to the PCI function and decodes the Host Firmware Status
+ * registers (HFSTS1-6, as supported by the device generation), which live
+ * entirely in PCI configuration space.
+ * BAR0 (the circular-buffer messaging ring) is never mapped.
+ *
+ * Register counts and layout follow Linux drivers/misc/mei/hw-me.c:
+ * ICH10 provides HFS1, PCH5 through PCH7 provide HFS1-2, and PCH8 and
+ * later provide HFS1-6 at PCI config offsets 0x40/0x48/0x60/0x64/0x68/0x6c.
+ */
+
+#include <sys/param.h>
+#include <sys/bus.h>
+#include <sys/kernel.h>
+#include <sys/module.h>
+#include <sys/sysctl.h>
+
+#include <machine/bus.h>
+#include <machine/pmap.h>
+
+#include <dev/pci/pcivar.h>
+#include <dev/pci/pcireg.h>
+#include <dev/ichwd/ichwd.h>
+
+#define MEI_PCI_VENDOR_INTEL 0x8086
+
+/* Host Firmware Status Registers - PCI configuration space offsets. */
+#define MEI_PCI_CFG_HFS_1 0x40
+#define MEI_HFS_1_CWS_MSK 0x0000000f
+#define MEI_HFS_1_MFG_MODE_MSK 0x00000010
+#define MEI_HFS_1_FPT_BAD_MSK 0x00000020
+#define MEI_HFS_1_OPSTATE_MSK 0x000001c0
+#define MEI_HFS_1_OPSTATE_SHIFT 6
+#define MEI_HFS_1_FW_INIT_CMPL_MSK 0x00000200
+#define MEI_HFS_1_BUP_FAIL_MSK 0x00000400
+#define MEI_HFS_1_UPDATE_INPROG_MSK 0x00000800
+#define MEI_HFS_1_ERROR_MSK 0x0000f000
+#define MEI_HFS_1_ERROR_SHIFT 12
+#define MEI_HFS_1_OPMODE_MSK 0x000f0000
+#define MEI_HFS_1_OPMODE_SHIFT 16
+#define MEI_HFS_1_BOOT_OPT_MSK 0x01000000
+#define MEI_HFS_1_D0I3_MSK 0x80000000
+#define MEI_PCI_CFG_HFS_2 0x48
+#define MEI_HFS_2_PM_EVENT_MSK 0x0f000000
+#define MEI_HFS_2_PM_EVENT_SHIFT 24
+#define MEI_PCI_CFG_HFS_3 0x60
+#define MEI_HFS_3_FW_SKU_MSK 0x00000070
+#define MEI_HFS_3_FW_SKU_SHIFT 4
+#define MEI_PCI_CFG_HFS_4 0x64
+#define MEI_PCI_CFG_HFS_5 0x68
+#define MEI_PCI_CFG_HFS_6 0x6c
+
+/* HFS1 field values. Operation state and mode are sparse encodings. */
+#define MEI_HFS_CWS_RESET 0
+#define MEI_HFS_CWS_INIT 1
+#define MEI_HFS_CWS_RECOVERY 2
+#define MEI_HFS_CWS_TEST 3
+#define MEI_HFS_CWS_DISABLED 4
+#define MEI_HFS_CWS_NORMAL 5
+
+#define MEI_HFS_OPSTATE_PREBOOT 0
+#define MEI_HFS_OPSTATE_M0_UMA 1
+#define MEI_HFS_OPSTATE_M3 4
+#define MEI_HFS_OPSTATE_M0 5
+#define MEI_HFS_OPSTATE_IMAGE_ERROR 7
+
+#define MEI_HFS_OPMODE_NORMAL 0
+#define MEI_HFS_OPMODE_DEBUG 2
+#define MEI_HFS_OPMODE_TEMP_DISABLED 3
+#define MEI_HFS_OPMODE_JUMPER_OVERRIDE 4
+#define MEI_HFS_OPMODE_MEI_OVERRIDE 5
+#define MEI_HFS_OPMODE_SPS 15
+
+#define MEI_HFS_ERROR_NONE 0
+#define MEI_HFS_ERROR_UNCATEGORIZED 1
+#define MEI_HFS_ERROR_DISABLED 2
+#define MEI_HFS_ERROR_IMAGE 3
+#define MEI_HFS_ERROR_DEBUG 4
+
+static const char *const mei_cws_values[] = {
+ [MEI_HFS_CWS_RESET] = "Reset",
+ [MEI_HFS_CWS_INIT] = "Initializing",
+ [MEI_HFS_CWS_RECOVERY] = "Recovery",
+ [MEI_HFS_CWS_TEST] = "Test",
+ [MEI_HFS_CWS_DISABLED] = "Disabled",
+ [MEI_HFS_CWS_NORMAL] = "Normal",
+};
+
+static const char *const mei_opstate_values[] = {
+ [MEI_HFS_OPSTATE_PREBOOT] = "Preboot",
+ [MEI_HFS_OPSTATE_M0_UMA] = "M0 (UMA)",
+ [MEI_HFS_OPSTATE_M3] = "M3 (no UMA)",
+ [MEI_HFS_OPSTATE_M0] = "M0 (no UMA)",
+ [MEI_HFS_OPSTATE_IMAGE_ERROR] = "M0 (invalid firmware image)",
+};
+
+static const char *const mei_opmode_values[] = {
+ [MEI_HFS_OPMODE_NORMAL] = "Normal",
+ [MEI_HFS_OPMODE_DEBUG] = "Debug",
+ [MEI_HFS_OPMODE_TEMP_DISABLED] = "Temporarily disabled",
+ [MEI_HFS_OPMODE_JUMPER_OVERRIDE] = "Security override (jumper)",
+ [MEI_HFS_OPMODE_MEI_OVERRIDE] = "Security override (MEI)",
+ [MEI_HFS_OPMODE_SPS] = "Server Platform Services",
+};
+
+static const char *const mei_error_values[] = {
+ [MEI_HFS_ERROR_NONE] = "None",
+ [MEI_HFS_ERROR_UNCATEGORIZED] = "Uncategorized",
+ [MEI_HFS_ERROR_DISABLED] = "Disabled",
+ [MEI_HFS_ERROR_IMAGE] = "Firmware image",
+ [MEI_HFS_ERROR_DEBUG] = "Debug",
+};
+
+static const char *
+mei_decode(const char *const *tbl, size_t tbl_sz, uint32_t idx)
+{
+
+ if (idx >= tbl_sz || tbl[idx] == NULL)
+ return ("Unknown");
+ return (tbl[idx]);
+}
+#define MEI_DECODE(tbl, idx) mei_decode((tbl), nitems(tbl), (idx))
+
+/*
+ * One-word summary for the log/sysctl consumer who doesn't want to parse
+ * the raw HFS fields: Absent (register read didn't land on a real state),
+ * Disabled (ME explicitly turned off), Active (busy, transitional, or in an
+ * exceptional mode), or Normal (steady-state, with no reported error).
+ */
+static const char *
+mei_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)
+{
+
+ if (cws >= nitems(mei_cws_values) || mei_cws_values[cws] == NULL)
+ return ("Absent");
+ if (cws == MEI_HFS_CWS_DISABLED ||
+ opmode == MEI_HFS_OPMODE_TEMP_DISABLED ||
+ error == MEI_HFS_ERROR_DISABLED)
+ return ("Disabled");
+ if (cws != MEI_HFS_CWS_NORMAL || opmode != MEI_HFS_OPMODE_NORMAL ||
+ error != MEI_HFS_ERROR_NONE || mfg_mode || fpt_bad || bup_fail ||
+ !fw_init_cmpl || update_inprog)
+ return ("Active");
+ return ("Normal");
+}
+
+struct mei_pci_device {
+ uint16_t device;
+ uint8_t hfs_count;
+ const char *name;
+};
+
+/*
+ * Intel MEI/HECI PCI functions with usable Host Firmware Status registers.
+ * ICH10 has HFS1, PCH5 through PCH7 have HFS1-2, and PCH8 and newer have
+ * HFS1-6. Legacy ICH devices, for which no HFS register is defined, are
+ * deliberately not matched.
+ */
+#define MEI_DEVICE_ICH10(id, desc) { (id), 1, (desc) }
+#define MEI_DEVICE_PCH(id, desc) { (id), 2, (desc) }
+#define MEI_DEVICE_PCH8(id, desc) { (id), 6, (desc) }
+
+static const struct mei_pci_device mei_pci_devices[] = {
+ MEI_DEVICE_ICH10(0x2e04, "Eaglelake MEI"),
+ MEI_DEVICE_ICH10(0x2e14, "Eaglelake MEI"),
+ MEI_DEVICE_ICH10(0x2e24, "Eaglelake MEI"),
+ MEI_DEVICE_ICH10(0x2e34, "Eaglelake MEI"),
+ MEI_DEVICE_PCH(0x3b64, "Calpella MEI"),
+ MEI_DEVICE_PCH(0x3b65, "Calpella MEI"),
+ MEI_DEVICE_PCH(0x1c3a, "Cougar Point MEI"),
+ MEI_DEVICE_PCH(0x1d3a, "C600/X79 Patsburg MEI"),
+ MEI_DEVICE_PCH(0x1e3a, "Panther Point MEI"),
+ MEI_DEVICE_PCH(0x1cba, "Panther Point MEI"),
+ MEI_DEVICE_PCH(0x1dba, "Panther Point MEI"),
+ MEI_DEVICE_PCH8(0x8c3a, "Lynx Point H MEI"),
+ MEI_DEVICE_PCH8(0x8d3a, "Lynx Point Wellsburg MEI"),
+ MEI_DEVICE_PCH8(0x9c3a, "Lynx Point LP MEI"),
+ MEI_DEVICE_PCH8(0x8cba, "Lynx Point H Refresh MEI"),
+ MEI_DEVICE_PCH8(0x9cba, "Wildcat Point LP MEI"),
+ MEI_DEVICE_PCH8(0x9cbb, "Wildcat Point LP 2 MEI"),
+ MEI_DEVICE_PCH8(0x9d3a, "Sunrise Point MEI"),
+ MEI_DEVICE_PCH8(0x9d3b, "Sunrise Point 2 MEI"),
+ MEI_DEVICE_PCH8(0x9d3e, "Sunrise Point 3 (iTouch) MEI"),
+ MEI_DEVICE_PCH8(0xa13a, "Sunrise Point H MEI"),
+ MEI_DEVICE_PCH8(0xa13b, "Sunrise Point H 2 MEI"),
+ MEI_DEVICE_PCH8(0xa1ba, "Lewisburg (SPT) MEI"),
+ MEI_DEVICE_PCH8(0x1a9a, "Broxton M MEI"),
+ MEI_DEVICE_PCH8(0x5a9a, "Apollo Lake I MEI"),
+ MEI_DEVICE_PCH8(0x19e5, "Denverton IE MEI"),
+ MEI_DEVICE_PCH8(0x319a, "Gemini Lake MEI"),
+ MEI_DEVICE_PCH8(0xa2ba, "Kaby Point MEI"),
+ MEI_DEVICE_PCH8(0xa2bb, "Kaby Point 2 MEI"),
+ MEI_DEVICE_PCH8(0xa2be, "Kaby Point 3 (iTouch) MEI"),
+ MEI_DEVICE_PCH8(0x9de0, "Cannon Point LP MEI"),
+ MEI_DEVICE_PCH8(0x9de4, "Cannon Point LP 3 (iTouch) MEI"),
+ MEI_DEVICE_PCH8(0xa360, "Cannon Point H MEI"),
+ MEI_DEVICE_PCH8(0xa364, "Cannon Point H 3 (iTouch) MEI"),
+ MEI_DEVICE_PCH8(0x02e0, "Comet Point LP MEI"),
+ MEI_DEVICE_PCH8(0x02e4, "Comet Point LP 3 (iTouch) MEI"),
+ MEI_DEVICE_PCH8(0xa3ba, "Comet Point Lake V MEI"),
+ MEI_DEVICE_PCH8(0x06e0, "Comet Lake H MEI"),
+ MEI_DEVICE_PCH8(0x06e4, "Comet Lake H 3 (iTouch) MEI"),
+ MEI_DEVICE_PCH8(0x18d3, "Cedar Fork MEI"),
+ MEI_DEVICE_PCH8(0x34e0, "Ice Lake Point LP MEI"),
+ MEI_DEVICE_PCH8(0x38e0, "Ice Lake Point N MEI"),
+ MEI_DEVICE_PCH8(0x4de0, "Jasper Lake Point N MEI"),
+ MEI_DEVICE_PCH8(0xa0e0, "Tiger Lake Point LP MEI"),
+ MEI_DEVICE_PCH8(0x43e0, "Tiger Lake Point H MEI"),
+ MEI_DEVICE_PCH8(0x4b70, "Mule Creek Canyon (EHL) MEI"),
+ MEI_DEVICE_PCH8(0x4b75, "Mule Creek Canyon 4 (EHL) MEI"),
+ MEI_DEVICE_PCH8(0x1be0, "Emmitsburg WS MEI"),
+ MEI_DEVICE_PCH8(0x7ae8, "Alder Lake Point S MEI"),
+ MEI_DEVICE_PCH8(0x7a60, "Alder Lake Point LP MEI"),
+ MEI_DEVICE_PCH8(0x51e0, "Alder Lake Point P MEI"),
+ MEI_DEVICE_PCH8(0x54e0, "Alder Lake Point N MEI"),
+ MEI_DEVICE_PCH8(0x7a68, "Raptor Lake Point S MEI"),
+ MEI_DEVICE_PCH8(0x7e70, "Meteor Lake Point M MEI"),
+ MEI_DEVICE_PCH8(0x7f68, "Arrow Lake Point S MEI"),
+ MEI_DEVICE_PCH8(0x7770, "Arrow Lake Point H MEI"),
+ MEI_DEVICE_PCH8(0xa870, "Lunar Lake Point M MEI"),
+ MEI_DEVICE_PCH8(0xe370, "Panther Lake H MEI"),
+ MEI_DEVICE_PCH8(0xe470, "Panther Lake P MEI"),
+ MEI_DEVICE_PCH8(0x4d70, "Wildcat Lake P MEI"),
+ MEI_DEVICE_PCH8(0x6e68, "Nova Lake Point S MEI"),
+ MEI_DEVICE_PCH8(0xd370, "Nova Lake Point H MEI"),
+ { 0, 0, NULL }
+};
+
+static const struct mei_pci_device *
+mei_pci_find_device(device_t dev)
+{
+ const struct mei_pci_device *id;
+
+ for (id = mei_pci_devices; id->name != NULL; id++) {
+ if (pci_get_device(dev) == id->device)
+ return (id);
+ }
+ return (NULL);
+}
+
+struct mei_softc {
+ device_t sc_dev;
+ struct sysctl_ctx_list *sc_sysctlctx;
+ struct sysctl_oid *sc_sysctlnode;
+ uint32_t sc_hfs[6];
+ uint8_t sc_hfs_count;
+};
+
+static int
+mei_pci_probe(device_t dev)
+{
+ const struct mei_pci_device *id;
+
+ if (pci_get_vendor(dev) != MEI_PCI_VENDOR_INTEL)
+ return (ENXIO);
+
+ id = mei_pci_find_device(dev);
+ if (id == NULL)
+ return (ENXIO);
+ device_set_desc(dev, id->name);
+ return (BUS_PROBE_DEFAULT);
+}
+
+static int
+mei_pci_attach(device_t dev)
+{
+ const struct mei_pci_device *id;
+ struct mei_softc *sc;
+ const char *summary;
+ static const uint32_t hfs_off[6] = {
+ MEI_PCI_CFG_HFS_1, MEI_PCI_CFG_HFS_2, MEI_PCI_CFG_HFS_3,
+ MEI_PCI_CFG_HFS_4, MEI_PCI_CFG_HFS_5, MEI_PCI_CFG_HFS_6,
+ };
+ uint32_t d0i3, opmode, pm_event, fw_sku;
+ uint32_t cws, opstate, error, mfg_mode, fpt_bad, fw_init_cmpl;
+ uint32_t bup_fail, update_inprog, boot_opt;
+ int i;
+
+ sc = device_get_softc(dev);
+ sc->sc_dev = dev;
+ id = mei_pci_find_device(dev);
+ if (id == NULL)
+ return (ENXIO);
+ sc->sc_hfs_count = id->hfs_count;
+
+ for (i = 0; i < sc->sc_hfs_count; i++)
+ sc->sc_hfs[i] = pci_read_config(dev, hfs_off[i], 4);
+
+ d0i3 = 0;
+ pm_event = 0;
+ fw_sku = 0;
+ if (sc->sc_hfs_count >= 6) {
+ d0i3 = (sc->sc_hfs[0] & MEI_HFS_1_D0I3_MSK) != 0;
+ fw_sku = (sc->sc_hfs[2] & MEI_HFS_3_FW_SKU_MSK) >>
+ MEI_HFS_3_FW_SKU_SHIFT;
+ }
+ if (sc->sc_hfs_count >= 2) {
+ pm_event = (sc->sc_hfs[1] & MEI_HFS_2_PM_EVENT_MSK) >>
+ MEI_HFS_2_PM_EVENT_SHIFT;
+ }
+ opmode = (sc->sc_hfs[0] & MEI_HFS_1_OPMODE_MSK) >>
+ MEI_HFS_1_OPMODE_SHIFT;
+
+ cws = sc->sc_hfs[0] & MEI_HFS_1_CWS_MSK;
+ opstate = (sc->sc_hfs[0] & MEI_HFS_1_OPSTATE_MSK) >>
+ MEI_HFS_1_OPSTATE_SHIFT;
+ error = (sc->sc_hfs[0] & MEI_HFS_1_ERROR_MSK) >> MEI_HFS_1_ERROR_SHIFT;
+ mfg_mode = (sc->sc_hfs[0] & MEI_HFS_1_MFG_MODE_MSK) != 0;
+ fpt_bad = (sc->sc_hfs[0] & MEI_HFS_1_FPT_BAD_MSK) != 0;
+ fw_init_cmpl = (sc->sc_hfs[0] & MEI_HFS_1_FW_INIT_CMPL_MSK) != 0;
+ bup_fail = (sc->sc_hfs[0] & MEI_HFS_1_BUP_FAIL_MSK) != 0;
+ update_inprog = (sc->sc_hfs[0] & MEI_HFS_1_UPDATE_INPROG_MSK) != 0;
+ boot_opt = (sc->sc_hfs[0] & MEI_HFS_1_BOOT_OPT_MSK) != 0;
+ summary = mei_state_summary(cws, opmode, error, mfg_mode, fpt_bad,
+ bup_fail, fw_init_cmpl, update_inprog);
+
+ device_printf(dev,
+ "state=%s opstate=\"%s\" opmode=\"%s\" error=\"%s\" "
+ "mfg_mode=%u fpt_bad=%u fw_init_complete=%u update_in_progress=%u "
+ "boot_options_present=%u\n",
+ MEI_DECODE(mei_cws_values, cws),
+ MEI_DECODE(mei_opstate_values, opstate),
+ MEI_DECODE(mei_opmode_values, opmode),
+ MEI_DECODE(mei_error_values, error),
+ mfg_mode, fpt_bad, fw_init_cmpl, update_inprog, boot_opt);
+
+ device_printf(dev, "ME state: %s\n", summary);
+
+ sc->sc_sysctlctx = device_get_sysctl_ctx(dev);
+ sc->sc_sysctlnode = device_get_sysctl_tree(dev);
+
+ SYSCTL_ADD_U32(sc->sc_sysctlctx, SYSCTL_CHILDREN(sc->sc_sysctlnode),
+ OID_AUTO, "hfs1", CTLFLAG_RD, &sc->sc_hfs[0], 0,
+ "Host Firmware Status 1 (raw)");
+ if (sc->sc_hfs_count >= 2) {
+ SYSCTL_ADD_U32(sc->sc_sysctlctx,
+ SYSCTL_CHILDREN(sc->sc_sysctlnode), OID_AUTO, "hfs2",
+ CTLFLAG_RD, &sc->sc_hfs[1], 0,
+ "Host Firmware Status 2 (raw)");
+ }
+ if (sc->sc_hfs_count >= 6) {
+ SYSCTL_ADD_U32(sc->sc_sysctlctx,
+ SYSCTL_CHILDREN(sc->sc_sysctlnode), OID_AUTO, "hfs3",
+ CTLFLAG_RD, &sc->sc_hfs[2], 0,
+ "Host Firmware Status 3 (raw)");
+ SYSCTL_ADD_U32(sc->sc_sysctlctx,
+ SYSCTL_CHILDREN(sc->sc_sysctlnode), OID_AUTO, "hfs4",
+ CTLFLAG_RD, &sc->sc_hfs[3], 0,
+ "Host Firmware Status 4 (raw)");
+ SYSCTL_ADD_U32(sc->sc_sysctlctx,
+ SYSCTL_CHILDREN(sc->sc_sysctlnode), OID_AUTO, "hfs5",
+ CTLFLAG_RD, &sc->sc_hfs[4], 0,
+ "Host Firmware Status 5 (raw)");
+ SYSCTL_ADD_U32(sc->sc_sysctlctx,
+ SYSCTL_CHILDREN(sc->sc_sysctlnode), OID_AUTO, "hfs6",
+ CTLFLAG_RD, &sc->sc_hfs[5], 0,
+ "Host Firmware Status 6 (raw)");
+ }
+
+ if (sc->sc_hfs_count >= 6) {
+ SYSCTL_ADD_U32(sc->sc_sysctlctx,
+ SYSCTL_CHILDREN(sc->sc_sysctlnode), OID_AUTO, "d0i3",
+ CTLFLAG_RD, NULL, d0i3, "D0i3 power state (from HFS1)");
+ }
+ SYSCTL_ADD_U32(sc->sc_sysctlctx, SYSCTL_CHILDREN(sc->sc_sysctlnode),
+ OID_AUTO, "opmode", CTLFLAG_RD, NULL, opmode,
+ "ME operation mode (from HFS1)");
+ if (sc->sc_hfs_count >= 2) {
+ SYSCTL_ADD_U32(sc->sc_sysctlctx,
+ SYSCTL_CHILDREN(sc->sc_sysctlnode), OID_AUTO, "pm_event",
+ CTLFLAG_RD, NULL, pm_event,
+ "ME power management event (from HFS2)");
+ }
+ if (sc->sc_hfs_count >= 6) {
+ SYSCTL_ADD_U32(sc->sc_sysctlctx,
+ SYSCTL_CHILDREN(sc->sc_sysctlnode), OID_AUTO, "fw_sku",
+ CTLFLAG_RD, NULL, fw_sku,
+ "ME firmware SKU (from HFS3)");
+ }
+ SYSCTL_ADD_U32(sc->sc_sysctlctx, SYSCTL_CHILDREN(sc->sc_sysctlnode),
+ OID_AUTO, "mfg_mode", CTLFLAG_RD, NULL, mfg_mode,
+ "ME running in Manufacturing Mode (from HFS1)");
+ SYSCTL_ADD_U32(sc->sc_sysctlctx, SYSCTL_CHILDREN(sc->sc_sysctlnode),
+ OID_AUTO, "update_in_progress", CTLFLAG_RD, NULL, update_inprog,
+ "ME firmware update in progress (from HFS1)");
+ SYSCTL_ADD_STRING(sc->sc_sysctlctx, SYSCTL_CHILDREN(sc->sc_sysctlnode),
+ OID_AUTO, "state", CTLFLAG_RD,
+ __DECONST(char *, MEI_DECODE(mei_cws_values, cws)), 0,
+ "ME Current Working State (from HFS1)");
+ SYSCTL_ADD_STRING(sc->sc_sysctlctx, SYSCTL_CHILDREN(sc->sc_sysctlnode),
+ OID_AUTO, "opstate", CTLFLAG_RD,
+ __DECONST(char *, MEI_DECODE(mei_opstate_values, opstate)), 0,
+ "ME Current Operation State (from HFS1)");
+ SYSCTL_ADD_STRING(sc->sc_sysctlctx, SYSCTL_CHILDREN(sc->sc_sysctlnode),
+ OID_AUTO, "opmode_str", CTLFLAG_RD,
+ __DECONST(char *, MEI_DECODE(mei_opmode_values, opmode)), 0,
+ "ME Current Operation Mode (from HFS1)");
+ SYSCTL_ADD_STRING(sc->sc_sysctlctx, SYSCTL_CHILDREN(sc->sc_sysctlnode),
+ OID_AUTO, "error", CTLFLAG_RD,
+ __DECONST(char *, MEI_DECODE(mei_error_values, error)), 0,
+ "ME Error Code (from HFS1)");
+ SYSCTL_ADD_STRING(sc->sc_sysctlctx, SYSCTL_CHILDREN(sc->sc_sysctlnode),
+ OID_AUTO, "summary", CTLFLAG_RD, __DECONST(char *, summary), 0,
+ "One-word ME state summary");
+
+ return (0);
+}
+
+static int
+mei_pci_detach(device_t dev)
+{
+
+ return (0);
+}
+
+static device_method_t mei_pci_methods[] = {
+ DEVMETHOD(device_probe, mei_pci_probe),
+ DEVMETHOD(device_attach, mei_pci_attach),
+ DEVMETHOD(device_detach, mei_pci_detach),
+ DEVMETHOD_END
+};
+
+static driver_t mei_pci_driver = {
+ "mei",
+ mei_pci_methods,
+ sizeof(struct mei_softc)
+};
+
+DRIVER_MODULE(mei, pci, mei_pci_driver, 0, 0);
+MODULE_VERSION(mei, 1);
+MODULE_DEPEND(mei, pci, 1, 1, 1);
+
+/*
+ * meifd(4) - detects HECI hidden via the PCH Function Disable register.
+ *
+ * The MEI PCI function never enumerates when firmware (coreboot's
+ * PCH_DISABLE_MEI1/2) has disabled it, so there is no PCI device instance
+ * for mei_pci_attach() to run against. This is an isa-attached identify
+ * driver instead, modeled on ichwd_identify() in sys/dev/ichwd/ichwd.c,
+ * which faces the same problem for the ICH watchdog.
+ *
+ * RCBA (LPC bridge config offset 0xF0) and FD2 (RCBA+0x3428, bit 1 =
+ * MEI1 disabled, bit 2 = MEI2 disabled) use this layout on Ibex Peak,
+ * Cougar Point, Panther Point, Lynx Point, and Wildcat Point. Other
+ * generations report "unknown-generation" instead of assuming that their
+ * disable mechanism or ME capability is known.
+ */
+
+#define MEI_FD_RCBA_OFFSET 0xf0
+#define MEI_FD_RCBA_EN 0x00000001
+#define MEI_FD_RCBA_MASK 0xffffc000
+#define MEI_FD_RCBA_SIZE 0x4000
+#define MEI_FD_FD2_OFFSET 0x3428
+#define MEI_FD_FD2_MEI1_DIS 0x00000002
+#define MEI_FD_FD2_MEI2_DIS 0x00000004
+
+static const uint16_t mei_fd_lpc_ids[] = {
+ DEVICEID_LPT0, DEVICEID_LPT1, DEVICEID_LPT2, DEVICEID_LPT3,
+ DEVICEID_LPT4, DEVICEID_LPT5, DEVICEID_LPT6, DEVICEID_LPT7,
+ DEVICEID_LPT8, DEVICEID_LPT9, DEVICEID_LPT10, DEVICEID_LPT11,
+ DEVICEID_LPT12, DEVICEID_LPT13, DEVICEID_LPT14, DEVICEID_LPT15,
+ DEVICEID_LPT16, DEVICEID_LPT17, DEVICEID_LPT18, DEVICEID_LPT19,
+ DEVICEID_LPT20, DEVICEID_LPT21, DEVICEID_LPT22, DEVICEID_LPT23,
+ DEVICEID_LPT24, DEVICEID_LPT25, DEVICEID_LPT26, DEVICEID_LPT27,
+ DEVICEID_LPT28, DEVICEID_LPT29, DEVICEID_LPT30, DEVICEID_LPT31,
+ DEVICEID_LPT_LP0, DEVICEID_LPT_LP1, DEVICEID_LPT_LP2,
+ DEVICEID_LPT_LP3, DEVICEID_LPT_LP4, DEVICEID_LPT_LP5,
+ DEVICEID_LPT_LP6, DEVICEID_LPT_LP7,
+ DEVICEID_WCPT1, DEVICEID_WCPT2, DEVICEID_WCPT3, DEVICEID_WCPT4,
+ DEVICEID_WCPT6,
+ DEVICEID_WCPT_LP1, DEVICEID_WCPT_LP2, DEVICEID_WCPT_LP3,
+ DEVICEID_WCPT_LP5, DEVICEID_WCPT_LP6, DEVICEID_WCPT_LP7,
+ DEVICEID_WCPT_LP9,
+ DEVICEID_CPT0, DEVICEID_CPT1, DEVICEID_CPT2, DEVICEID_CPT3,
+ DEVICEID_CPT4, DEVICEID_CPT5, DEVICEID_CPT6, DEVICEID_CPT7,
+ DEVICEID_CPT8, DEVICEID_CPT9, DEVICEID_CPT10, DEVICEID_CPT11,
+ DEVICEID_CPT12, DEVICEID_CPT13, DEVICEID_CPT14, DEVICEID_CPT15,
+ DEVICEID_CPT16, DEVICEID_CPT17, DEVICEID_CPT18, DEVICEID_CPT19,
+ DEVICEID_CPT20, DEVICEID_CPT21, DEVICEID_CPT22, DEVICEID_CPT23,
+ DEVICEID_CPT24, DEVICEID_CPT25, DEVICEID_CPT26, DEVICEID_CPT27,
+ DEVICEID_CPT28, DEVICEID_CPT29, DEVICEID_CPT30, DEVICEID_CPT31,
+ DEVICEID_PPT0, DEVICEID_PPT1, DEVICEID_PPT2, DEVICEID_PPT3,
+ DEVICEID_PPT4, DEVICEID_PPT5, DEVICEID_PPT6, DEVICEID_PPT7,
+ DEVICEID_PPT8, DEVICEID_PPT9, DEVICEID_PPT10, DEVICEID_PPT11,
+ DEVICEID_PPT12, DEVICEID_PPT13, DEVICEID_PPT14, DEVICEID_PPT15,
+ DEVICEID_PCH, DEVICEID_PCHM, DEVICEID_P55, DEVICEID_PM55,
+ DEVICEID_H55, DEVICEID_QM57, DEVICEID_H57, DEVICEID_HM55,
+ DEVICEID_Q57, DEVICEID_HM57, DEVICEID_PCHMSFF, DEVICEID_QS57,
+};
+
+struct mei_fd_softc {
+ device_t sc_dev;
+ char sc_status[24];
+ uint32_t sc_fd2;
+ uint32_t sc_mei1_disabled;
+ uint32_t sc_mei2_disabled;
+ bool sc_checked;
+};
+
+static bool
+mei_fd_lpc_known(uint16_t devid)
+{
+ u_int i;
+
+ for (i = 0; i < nitems(mei_fd_lpc_ids); i++) {
+ if (mei_fd_lpc_ids[i] == devid)
+ return (true);
+ }
+ return (false);
+}
+
+static void
+mei_fd_identify(driver_t *driver, device_t parent)
+{
+
+ if (device_find_child(parent, driver->name, 0) == NULL)
+ BUS_ADD_CHILD(parent, 0, driver->name, 0);
+}
+
+static int
+mei_fd_probe(device_t dev)
+{
+
+ device_set_desc(dev, "Intel ME Function Disable check");
+ return (BUS_PROBE_NOWILDCARD);
+}
+
+static void
+mei_fd_set_status(struct mei_fd_softc *sc, const char *status)
+{
+
+ strlcpy(sc->sc_status, status, sizeof(sc->sc_status));
+}
+
+static int
+mei_fd_attach(device_t dev)
+{
+ struct mei_fd_softc *sc;
+ struct sysctl_ctx_list *ctx;
+ struct sysctl_oid *tree;
+ device_t isab;
+ vm_paddr_t rcba;
+ bus_space_handle_t bsh;
+ uint32_t fd2;
+
+ sc = device_get_softc(dev);
+ sc->sc_dev = dev;
+
+ isab = device_get_parent(device_get_parent(dev));
+
+ if (!is_pci_device(isab)) {
+ device_printf(dev, "isa0's parent is not a PCI device - "
+ "cannot locate the LPC bridge\n");
+ mei_fd_set_status(sc, "not-applicable");
+ goto done;
+ }
+
+ if (pci_get_vendor(isab) != MEI_PCI_VENDOR_INTEL) {
+ device_printf(dev, "non-Intel LPC bridge (vendor 0x%04x) - "
+ "Intel ME does not apply\n", pci_get_vendor(isab));
+ mei_fd_set_status(sc, "not-applicable");
+ goto done;
+ }
+
+ if (!mei_fd_lpc_known(pci_get_device(isab))) {
+ device_printf(dev, "LPC bridge device 0x%04x is not a known "
+ "Ibex Peak/Cougar Point/Panther Point/Lynx Point/Wildcat "
+ "Point PCH - cannot determine ME Function Disable "
+ "state\n", pci_get_device(isab));
+ mei_fd_set_status(sc, "unknown-generation");
+ goto done;
+ }
+
+ rcba = pci_read_config(isab, MEI_FD_RCBA_OFFSET, 4);
+ if ((rcba & MEI_FD_RCBA_EN) == 0 || (rcba & MEI_FD_RCBA_MASK) == 0) {
+ device_printf(dev,
+ "RCBA not enabled, cannot read Function Disable register\n");
+ mei_fd_set_status(sc, "rcba-disabled");
+ goto done;
+ }
+
+ bsh = (bus_space_handle_t)pmap_mapdev(rcba & MEI_FD_RCBA_MASK,
+ MEI_FD_RCBA_SIZE);
+ fd2 = bus_space_read_4(X86_BUS_SPACE_MEM, bsh, MEI_FD_FD2_OFFSET);
+ pmap_unmapdev((void *)bsh, MEI_FD_RCBA_SIZE);
+
+ sc->sc_fd2 = fd2;
+ sc->sc_mei1_disabled = (fd2 & MEI_FD_FD2_MEI1_DIS) != 0;
+ sc->sc_mei2_disabled = (fd2 & MEI_FD_FD2_MEI2_DIS) != 0;
+ sc->sc_checked = true;
+ mei_fd_set_status(sc, "checked");
+
+ if (sc->sc_mei1_disabled) {
+ device_printf(dev, "HECI1 disabled via PCH Function Disable "
+ "register (silicon present, hidden by firmware)\n");
+ } else {
+ device_printf(dev, "HECI1 not disabled - MEI should be "
+ "enumerable as a normal PCI function\n");
+ }
+ if (sc->sc_mei2_disabled)
+ device_printf(dev, "HECI2 disabled via PCH Function Disable "
+ "register\n");
+
+done:
+ ctx = device_get_sysctl_ctx(dev);
+ tree = device_get_sysctl_tree(dev);
+
+ SYSCTL_ADD_STRING(ctx, SYSCTL_CHILDREN(tree), OID_AUTO, "status",
+ CTLFLAG_RD, sc->sc_status, 0,
+ "Function Disable check result");
+ if (!sc->sc_checked)
+ return (0);
+ SYSCTL_ADD_U32(ctx, SYSCTL_CHILDREN(tree), OID_AUTO, "fd2",
+ CTLFLAG_RD, &sc->sc_fd2, 0,
+ "Function Disable 2 register (raw)");
+ SYSCTL_ADD_U32(ctx, SYSCTL_CHILDREN(tree), OID_AUTO, "mei1_disabled",
+ CTLFLAG_RD, &sc->sc_mei1_disabled, 0,
+ "HECI1 disabled via Function Disable register");
+ SYSCTL_ADD_U32(ctx, SYSCTL_CHILDREN(tree), OID_AUTO, "mei2_disabled",
+ CTLFLAG_RD, &sc->sc_mei2_disabled, 0,
+ "HECI2 disabled via Function Disable register");
+
+ return (0);
+}
+
+static int
+mei_fd_detach(device_t dev)
+{
+
+ return (0);
+}
+
+static device_method_t mei_fd_methods[] = {
+ DEVMETHOD(device_identify, mei_fd_identify),
+ DEVMETHOD(device_probe, mei_fd_probe),
+ DEVMETHOD(device_attach, mei_fd_attach),
+ DEVMETHOD(device_detach, mei_fd_detach),
+ DEVMETHOD_END
+};
+
+static driver_t mei_fd_driver = {
+ "meifd",
+ mei_fd_methods,
+ sizeof(struct mei_fd_softc)
+};
+
+DRIVER_MODULE(meifd, isa, mei_fd_driver, 0, 0);
+MODULE_VERSION(meifd, 1);
diff --git a/sys/modules/Makefile b/sys/modules/Makefile
--- a/sys/modules/Makefile
+++ b/sys/modules/Makefile
@@ -255,6 +255,7 @@
${_mana} \
md \
mdio \
+ ${_mei} \
${_mgb} \
mem \
mfi \
@@ -879,6 +880,7 @@
_ioat= ioat
_iwx= iwx
_ixl= ixl
+_mei= mei
_nvdimm= nvdimm
_pms= pms
_pt= pt
diff --git a/sys/modules/mei/Makefile b/sys/modules/mei/Makefile
new file mode 100644
--- /dev/null
+++ b/sys/modules/mei/Makefile
@@ -0,0 +1,11 @@
+# SPDX-License-Identifier: BSD-2-Clause
+#
+# Copyright (c) 2026 Abdelkader Boudih <freebsd@seuros.com>
+
+.PATH: ${SRCTOP}/sys/dev/mei
+
+KMOD= mei
+SRCS= mei_pci.c
+SRCS+= bus_if.h device_if.h pci_if.h isa_if.h
+
+.include <bsd.kmod.mk>
File Metadata
Details
Attached
Mime Type
text/plain
Expires
Sun, Aug 16, 8:16 PM (5 h, 3 m)
Storage Engine
blob
Storage Format
Raw Data
Storage Handle
36834123
Default Alt Text
D58863.diff (31 KB)
Attached To
Mode
D58863: mei: add debug-probe driver for Intel ME/HECI firmware status
Attached
Detach File
Event Timeline
Log In to Comment