Page Menu
Home
FreeBSD
Search
Configure Global Search
Log In
Files
F170864432
D58863.id185127.diff
No One
Temporary
Actions
View File
Edit File
Delete File
View Transforms
Subscribe
Mute Notifications
Flag For Later
Award Token
Size
36 KB
Referenced Files
None
Subscribers
None
D58863.id185127.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
@@ -210,6 +210,7 @@
h_ertt.4 \
hconf.4 \
hcons.4 \
+ hfsts.4 \
hgame.4 \
hidbus.4 \
hidquirk.4 \
@@ -755,6 +756,7 @@
MLINKS+=gpioths.4 dht11.4
MLINKS+=gpioths.4 dht22.4
MLINKS+=gre.4 if_gre.4
+MLINKS+=hfsts.4 hfstsfd.4
MLINKS+=hpet.4 acpi_hpet.4
MLINKS+=${_hv_netvsc.4} ${_hn.4} \
${_hv_netvsc.4} ${_if_hn.4}
diff --git a/share/man/man4/hfsts.4 b/share/man/man4/hfsts.4
new file mode 100644
--- /dev/null
+++ b/share/man/man4/hfsts.4
@@ -0,0 +1,232 @@
+.\" SPDX-License-Identifier: BSD-2-Clause
+.\"
+.\" Copyright (c) 2026 Abdelkader Boudih <seuros@FreeBSD.org>
+.\"
+.\" 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 26, 2026
+.Dt HFSTS 4
+.Os
+.Sh NAME
+.Nm hfsts ,
+.Nm hfstsfd
+.Nd Intel Management Engine Host Firmware Status (HFSTS) 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 hfsts"
+.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
+hfsts_load="YES"
+.Ed
+.Sh DESCRIPTION
+The
+.Nm hfsts
+driver is a debug probe that decodes the Intel Management Engine's
+Host Firmware Status (HFSTS) registers.
+It is supported on
+amd64
+systems only, and only on Intel platforms.
+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 number of Host Firmware Status registers available depends on the
+PCH generation: ICH10 provides HFSTS1 only, PCH5 through PCH7 provide
+HFSTS1 and HFSTS2, and PCH8 and later provide all six.
+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 hfstsfd
+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 hfsts
+to attach to.
+.Nm hfstsfd
+is an isa-attached identify driver, modeled on
+.Xr ichwd 4 Ns 's
+.Fn ichwd_identify ,
+that checks whether HECI1/HECI2 are present in silicon but hidden by
+the LPC bridge's Function Disable register.
+It only attaches on the following PCH generations:
+.Pp
+.Bl -bullet -compact
+.It
+Ibex Peak
+.It
+Cougar Point
+.It
+Panther Point
+.It
+Lynx Point
+.It
+Wildcat Point
+.El
+.Pp
+On any other chipset
+.Nm hfstsfd
+does not attach at all.
+When it does attach, its
+.Va status
+sysctl is one of:
+.Bl -tag -width "rcba-disabled"
+.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.hfsts.N
+when the driver is attached to a PCI MEI/HECI function.
+Each is read live from PCI configuration space on every access, not
+cached at attach time, since the ME's firmware status can change while
+the system is running
+.Pq CWS resets, HAP toggles, M0/M3 power transitions :
+.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_supported
+ME D0i3 capability, 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.hfstsfd.N
+when the
+.Nm hfstsfd
+identify driver has attached.
+It contains the
+function-disable check result; one of the states listed in
+.Sx hfstsfd
+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.hfsts.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.hfstsfd.0"
+.Sh SEE ALSO
+.Xr ichwd 4 ,
+.Xr pci 4 ,
+.Xr sysctl 8
+.Sh HISTORY
+The
+.Nm hfsts
+and
+.Nm hfstsfd
+drivers first appeared in
+.Fx 16.0 .
+.Sh AUTHORS
+.An Abdelkader Boudih Aq Mt seuros@FreeBSD.org .
+.Sh CAVEATS
+.Nm hfsts
+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.
+It is Intel-specific and does not attach on other platforms;
+AMD's Platform Security Processor uses an unrelated register interface
+and has no driver here.
+.Pp
+.Nm hfstsfd
+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 not probed, so
+.Nm hfstsfd
+never attaches on them.
diff --git a/sys/conf/files.amd64 b/sys/conf/files.amd64
--- a/sys/conf/files.amd64
+++ b/sys/conf/files.amd64
@@ -218,6 +218,8 @@
compile-with "${CP} $S/contrib/dev/ice/ice-1.3.41.0.pkg ice_ddp.fw" \
no-obj no-implicit-rule \
clean "ice_ddp.fw"
+dev/intel/hfsts.c optional hfsts pci
+dev/intel/hfsts_isa.c optional hfsts isa pci
dev/ioat/ioat.c optional ioat pci
dev/ioat/ioat_test.c optional ioat pci
dev/ixl/if_ixl.c optional ixl pci \
diff --git a/sys/dev/intel/hfsts.h b/sys/dev/intel/hfsts.h
new file mode 100644
--- /dev/null
+++ b/sys/dev/intel/hfsts.h
@@ -0,0 +1,12 @@
+/*
+ * Copyright (c) 2026 Abdelkader Boudih <seuros@FreeBSD.org>
+ *
+ * SPDX-License-Identifier: BSD-2-Clause
+ */
+
+#ifndef _DEV_INTEL_HFSTS_H_
+#define _DEV_INTEL_HFSTS_H_
+
+bool hfsts_pci_present(void);
+
+#endif /* _DEV_INTEL_HFSTS_H_ */
diff --git a/sys/dev/intel/hfsts.c b/sys/dev/intel/hfsts.c
new file mode 100644
--- /dev/null
+++ b/sys/dev/intel/hfsts.c
@@ -0,0 +1,564 @@
+/*
+ * Copyright (c) 2026 Abdelkader Boudih <seuros@FreeBSD.org>
+ *
+ * SPDX-License-Identifier: BSD-2-Clause
+ */
+
+/*
+ * hfsts(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 <dev/pci/pcivar.h>
+#include <dev/pci/pcireg.h>
+#include <dev/ichwd/ichwd.h>
+#include <dev/intel/hfsts.h>
+
+/* Host Firmware Status Registers - PCI configuration space offsets. */
+#define HFSTS_CFG_HFS_1 0x40
+#define HFSTS_HFS_1_CWS_MSK 0x0000000f
+#define HFSTS_HFS_1_MFG_MODE_MSK 0x00000010
+#define HFSTS_HFS_1_FPT_BAD_MSK 0x00000020
+#define HFSTS_HFS_1_OPSTATE_MSK 0x000001c0
+#define HFSTS_HFS_1_OPSTATE_SHIFT 6
+#define HFSTS_HFS_1_FW_INIT_CMPL_MSK 0x00000200
+#define HFSTS_HFS_1_BUP_FAIL_MSK 0x00000400
+#define HFSTS_HFS_1_UPDATE_INPROG_MSK 0x00000800
+#define HFSTS_HFS_1_ERROR_MSK 0x0000f000
+#define HFSTS_HFS_1_ERROR_SHIFT 12
+#define HFSTS_HFS_1_OPMODE_MSK 0x000f0000
+#define HFSTS_HFS_1_OPMODE_SHIFT 16
+#define HFSTS_HFS_1_BOOT_OPT_MSK 0x01000000
+#define HFSTS_HFS_1_D0I3_MSK 0x80000000
+#define HFSTS_CFG_HFS_2 0x48
+#define HFSTS_HFS_2_PM_EVENT_MSK 0x0f000000
+#define HFSTS_HFS_2_PM_EVENT_SHIFT 24
+#define HFSTS_CFG_HFS_3 0x60
+#define HFSTS_HFS_3_FW_SKU_MSK 0x00000070
+#define HFSTS_HFS_3_FW_SKU_SHIFT 4
+#define HFSTS_CFG_HFS_4 0x64
+#define HFSTS_CFG_HFS_5 0x68
+#define HFSTS_CFG_HFS_6 0x6c
+
+/* HFS1 field values. Operation state and mode are sparse encodings. */
+#define HFSTS_HFS_CWS_RESET 0
+#define HFSTS_HFS_CWS_INIT 1
+#define HFSTS_HFS_CWS_RECOVERY 2
+#define HFSTS_HFS_CWS_TEST 3
+#define HFSTS_HFS_CWS_DISABLED 4
+#define HFSTS_HFS_CWS_NORMAL 5
+#define HFSTS_HFS_CWS_WAIT 6
+#define HFSTS_HFS_CWS_TRANS 7
+#define HFSTS_HFS_CWS_INVALID 8
+
+#define HFSTS_HFS_OPSTATE_PREBOOT 0
+#define HFSTS_HFS_OPSTATE_M0_UMA 1
+#define HFSTS_HFS_OPSTATE_M3 4
+#define HFSTS_HFS_OPSTATE_M0 5
+#define HFSTS_HFS_OPSTATE_BRINGUP 6
+#define HFSTS_HFS_OPSTATE_IMAGE_ERROR 7
+
+#define HFSTS_HFS_OPMODE_NORMAL 0
+#define HFSTS_HFS_OPMODE_DEBUG 2
+#define HFSTS_HFS_OPMODE_TEMP_DISABLED 3
+#define HFSTS_HFS_OPMODE_JUMPER_OVERRIDE 4
+#define HFSTS_HFS_OPMODE_MEI_OVERRIDE 5
+#define HFSTS_HFS_OPMODE_SPS 15
+
+#define HFSTS_HFS_ERROR_NONE 0
+#define HFSTS_HFS_ERROR_UNCATEGORIZED 1
+#define HFSTS_HFS_ERROR_DISABLED 2
+#define HFSTS_HFS_ERROR_IMAGE 3
+#define HFSTS_HFS_ERROR_DEBUG 4
+
+static const char *const hfsts_cws_values[] = {
+ [HFSTS_HFS_CWS_RESET] = "Reset",
+ [HFSTS_HFS_CWS_INIT] = "Initializing",
+ [HFSTS_HFS_CWS_RECOVERY] = "Recovery",
+ [HFSTS_HFS_CWS_TEST] = "Test",
+ [HFSTS_HFS_CWS_DISABLED] = "Disabled",
+ [HFSTS_HFS_CWS_NORMAL] = "Normal",
+ [HFSTS_HFS_CWS_WAIT] = "Platform Disable Wait",
+ [HFSTS_HFS_CWS_TRANS] = "OP State Transition",
+ [HFSTS_HFS_CWS_INVALID] = "Invalid CPU Plugged In",
+};
+
+static const char *const hfsts_opstate_values[] = {
+ [HFSTS_HFS_OPSTATE_PREBOOT] = "Preboot",
+ [HFSTS_HFS_OPSTATE_M0_UMA] = "M0 (UMA)",
+ [HFSTS_HFS_OPSTATE_M3] = "M3 (no UMA)",
+ [HFSTS_HFS_OPSTATE_M0] = "M0 (no UMA)",
+ [HFSTS_HFS_OPSTATE_BRINGUP] = "Bring up",
+ [HFSTS_HFS_OPSTATE_IMAGE_ERROR] = "M0 (invalid firmware image)",
+};
+
+static const char *const hfsts_opmode_values[] = {
+ [HFSTS_HFS_OPMODE_NORMAL] = "Normal",
+ [HFSTS_HFS_OPMODE_DEBUG] = "Debug",
+ [HFSTS_HFS_OPMODE_TEMP_DISABLED] = "Temporarily disabled",
+ [HFSTS_HFS_OPMODE_JUMPER_OVERRIDE] = "Security override (jumper)",
+ [HFSTS_HFS_OPMODE_MEI_OVERRIDE] = "Security override (MEI)",
+ [HFSTS_HFS_OPMODE_SPS] = "Server Platform Services",
+};
+
+static const char *const hfsts_error_values[] = {
+ [HFSTS_HFS_ERROR_NONE] = "None",
+ [HFSTS_HFS_ERROR_UNCATEGORIZED] = "Uncategorized",
+ [HFSTS_HFS_ERROR_DISABLED] = "Disabled",
+ [HFSTS_HFS_ERROR_IMAGE] = "Firmware image",
+ [HFSTS_HFS_ERROR_DEBUG] = "Debug",
+};
+
+static const char *
+hfsts_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 HFSTS_DECODE(tbl, idx) hfsts_decode((tbl), nitems(tbl), (idx))
+
+/* HFS1-6 PCI configuration offsets, indexed 0-5. */
+static const uint32_t hfsts_hfs_off[6] = {
+ HFSTS_CFG_HFS_1, HFSTS_CFG_HFS_2, HFSTS_CFG_HFS_3,
+ HFSTS_CFG_HFS_4, HFSTS_CFG_HFS_5, HFSTS_CFG_HFS_6,
+};
+
+/*
+ * All HFS fields are re-read from PCI configuration space on every sysctl
+ * access: the ME can transition CWS/opstate/error post-boot (watchdog
+ * resets, HAP toggles, S0i3/M0-M3 power events), so an attach-time snapshot
+ * would go stale under a running system.
+ */
+static uint32_t
+hfsts_read_hfs(device_t dev, u_int idx)
+{
+
+ return (pci_read_config(dev, hfsts_hfs_off[idx], 4));
+}
+
+/*
+ * 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 *
+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)
+{
+
+ if (cws >= nitems(hfsts_cws_values) || hfsts_cws_values[cws] == NULL)
+ return ("Absent");
+ if (cws == HFSTS_HFS_CWS_DISABLED || cws == HFSTS_HFS_CWS_WAIT ||
+ opmode == HFSTS_HFS_OPMODE_TEMP_DISABLED ||
+ error == HFSTS_HFS_ERROR_DISABLED)
+ return ("Disabled");
+ if (cws != HFSTS_HFS_CWS_NORMAL || opmode != HFSTS_HFS_OPMODE_NORMAL ||
+ error != HFSTS_HFS_ERROR_NONE || mfg_mode || fpt_bad || bup_fail ||
+ !fw_init_cmpl || update_inprog)
+ return ("Active");
+ return ("Normal");
+}
+
+struct hfsts_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 HFSTS_DEV_ICH10(id, desc) { (id), 1, (desc) }
+#define HFSTS_DEV_PCH(id, desc) { (id), 2, (desc) }
+#define HFSTS_DEV_PCH8(id, desc) { (id), 6, (desc) }
+
+static const struct hfsts_device hfsts_devices[] = {
+ HFSTS_DEV_ICH10(0x2e04, "Eaglelake MEI"),
+ HFSTS_DEV_ICH10(0x2e14, "Eaglelake MEI"),
+ HFSTS_DEV_ICH10(0x2e24, "Eaglelake MEI"),
+ HFSTS_DEV_ICH10(0x2e34, "Eaglelake MEI"),
+ HFSTS_DEV_PCH(0x3b64, "Calpella MEI"),
+ HFSTS_DEV_PCH(0x3b65, "Calpella MEI"),
+ HFSTS_DEV_PCH(0x1c3a, "Cougar Point MEI"),
+ HFSTS_DEV_PCH(0x1d3a, "C600/X79 Patsburg MEI"),
+ HFSTS_DEV_PCH(0x1e3a, "Panther Point MEI"),
+ HFSTS_DEV_PCH(0x1cba, "Panther Point MEI"),
+ HFSTS_DEV_PCH(0x1dba, "Panther Point MEI"),
+ HFSTS_DEV_PCH8(0x8c3a, "Lynx Point H MEI"),
+ HFSTS_DEV_PCH8(0x8d3a, "Lynx Point Wellsburg MEI"),
+ HFSTS_DEV_PCH8(0x9c3a, "Lynx Point LP MEI"),
+ HFSTS_DEV_PCH8(0x8cba, "Lynx Point H Refresh MEI"),
+ HFSTS_DEV_PCH8(0x9cba, "Wildcat Point LP MEI"),
+ HFSTS_DEV_PCH8(0x9cbb, "Wildcat Point LP 2 MEI"),
+ HFSTS_DEV_PCH8(0x9d3a, "Sunrise Point MEI"),
+ HFSTS_DEV_PCH8(0x9d3b, "Sunrise Point 2 MEI"),
+ HFSTS_DEV_PCH8(0x9d3e, "Sunrise Point 3 (iTouch) MEI"),
+ HFSTS_DEV_PCH8(0xa13a, "Sunrise Point H MEI"),
+ HFSTS_DEV_PCH8(0xa13b, "Sunrise Point H 2 MEI"),
+ HFSTS_DEV_PCH8(0xa1ba, "Lewisburg (SPT) MEI"),
+ HFSTS_DEV_PCH8(0x1a9a, "Broxton M MEI"),
+ HFSTS_DEV_PCH8(0x5a9a, "Apollo Lake I MEI"),
+ HFSTS_DEV_PCH8(0x19e5, "Denverton IE MEI"),
+ HFSTS_DEV_PCH8(0x319a, "Gemini Lake MEI"),
+ HFSTS_DEV_PCH8(0xa2ba, "Kaby Point MEI"),
+ HFSTS_DEV_PCH8(0xa2bb, "Kaby Point 2 MEI"),
+ HFSTS_DEV_PCH8(0xa2be, "Kaby Point 3 (iTouch) MEI"),
+ HFSTS_DEV_PCH8(0x9de0, "Cannon Point LP MEI"),
+ HFSTS_DEV_PCH8(0x9de4, "Cannon Point LP 3 (iTouch) MEI"),
+ HFSTS_DEV_PCH8(0xa360, "Cannon Point H MEI"),
+ HFSTS_DEV_PCH8(0xa364, "Cannon Point H 3 (iTouch) MEI"),
+ HFSTS_DEV_PCH8(0x02e0, "Comet Point LP MEI"),
+ HFSTS_DEV_PCH8(0x02e4, "Comet Point LP 3 (iTouch) MEI"),
+ HFSTS_DEV_PCH8(0xa3ba, "Comet Point Lake V MEI"),
+ HFSTS_DEV_PCH8(0x06e0, "Comet Lake H MEI"),
+ HFSTS_DEV_PCH8(0x06e4, "Comet Lake H 3 (iTouch) MEI"),
+ HFSTS_DEV_PCH8(0x18d3, "Cedar Fork MEI"),
+ HFSTS_DEV_PCH8(0x34e0, "Ice Lake Point LP MEI"),
+ HFSTS_DEV_PCH8(0x38e0, "Ice Lake Point N MEI"),
+ HFSTS_DEV_PCH8(0x4de0, "Jasper Lake Point N MEI"),
+ HFSTS_DEV_PCH8(0xa0e0, "Tiger Lake Point LP MEI"),
+ HFSTS_DEV_PCH8(0x43e0, "Tiger Lake Point H MEI"),
+ HFSTS_DEV_PCH8(0x4b70, "Mule Creek Canyon (EHL) MEI"),
+ HFSTS_DEV_PCH8(0x4b75, "Mule Creek Canyon 4 (EHL) MEI"),
+ HFSTS_DEV_PCH8(0x1be0, "Emmitsburg WS MEI"),
+ HFSTS_DEV_PCH8(0x7ae8, "Alder Lake Point S MEI"),
+ HFSTS_DEV_PCH8(0x7a60, "Alder Lake Point LP MEI"),
+ HFSTS_DEV_PCH8(0x51e0, "Alder Lake Point P MEI"),
+ HFSTS_DEV_PCH8(0x54e0, "Alder Lake Point N MEI"),
+ HFSTS_DEV_PCH8(0x7a68, "Raptor Lake Point S MEI"),
+ HFSTS_DEV_PCH8(0x7e70, "Meteor Lake Point M MEI"),
+ HFSTS_DEV_PCH8(0x7f68, "Arrow Lake Point S MEI"),
+ HFSTS_DEV_PCH8(0x7770, "Arrow Lake Point H MEI"),
+ HFSTS_DEV_PCH8(0xa870, "Lunar Lake Point M MEI"),
+ HFSTS_DEV_PCH8(0xe370, "Panther Lake H MEI"),
+ HFSTS_DEV_PCH8(0xe470, "Panther Lake P MEI"),
+ HFSTS_DEV_PCH8(0x4d70, "Wildcat Lake P MEI"),
+ HFSTS_DEV_PCH8(0x6e68, "Nova Lake Point S MEI"),
+ HFSTS_DEV_PCH8(0xd370, "Nova Lake Point H MEI"),
+ { 0, 0, NULL }
+};
+
+static const struct hfsts_device *
+hfsts_find_device(device_t dev)
+{
+ const struct hfsts_device *id;
+
+ for (id = hfsts_devices; id->name != NULL; id++) {
+ if (pci_get_device(dev) == id->device)
+ return (id);
+ }
+ return (NULL);
+}
+
+/*
+ * The isa-side Function Disable check is only meaningful when no known
+ * MEI/HECI function enumerated on PCI.
+ */
+bool
+hfsts_pci_present(void)
+{
+ const struct hfsts_device *id;
+
+ for (id = hfsts_devices; id->name != NULL; id++) {
+ if (pci_find_device(VENDORID_INTEL, id->device) != NULL)
+ return (true);
+ }
+ return (false);
+}
+
+struct hfsts_softc {
+ device_t sc_dev;
+ struct sysctl_ctx_list *sc_sysctlctx;
+ struct sysctl_oid *sc_sysctlnode;
+ uint8_t sc_hfs_count;
+};
+
+/* Simple masked/shifted HFS sub-fields, read live via a common handler. */
+enum hfsts_field_id {
+ HFSTS_FLD_D0I3_SUPPORTED,
+ HFSTS_FLD_OPMODE,
+ HFSTS_FLD_PM_EVENT,
+ HFSTS_FLD_FW_SKU,
+ HFSTS_FLD_MFG_MODE,
+ HFSTS_FLD_UPDATE_INPROG,
+};
+
+struct hfsts_field {
+ uint8_t hfs_idx;
+ uint32_t mask;
+ uint8_t shift;
+};
+
+static const struct hfsts_field hfsts_fields[] = {
+ [HFSTS_FLD_D0I3_SUPPORTED] = { 0, HFSTS_HFS_1_D0I3_MSK, 31 },
+ [HFSTS_FLD_OPMODE] = { 0, HFSTS_HFS_1_OPMODE_MSK,
+ HFSTS_HFS_1_OPMODE_SHIFT },
+ [HFSTS_FLD_PM_EVENT] = { 1, HFSTS_HFS_2_PM_EVENT_MSK,
+ HFSTS_HFS_2_PM_EVENT_SHIFT },
+ [HFSTS_FLD_FW_SKU] = { 2, HFSTS_HFS_3_FW_SKU_MSK,
+ HFSTS_HFS_3_FW_SKU_SHIFT },
+ [HFSTS_FLD_MFG_MODE] = { 0, HFSTS_HFS_1_MFG_MODE_MSK, 4 },
+ [HFSTS_FLD_UPDATE_INPROG] = { 0, HFSTS_HFS_1_UPDATE_INPROG_MSK, 11 },
+};
+
+static int
+hfsts_sysctl_raw(SYSCTL_HANDLER_ARGS)
+{
+ struct hfsts_softc *sc = arg1;
+ uint32_t val;
+
+ val = hfsts_read_hfs(sc->sc_dev, (u_int)arg2);
+ return (sysctl_handle_32(oidp, &val, 0, req));
+}
+
+static int
+hfsts_sysctl_field(SYSCTL_HANDLER_ARGS)
+{
+ struct hfsts_softc *sc = arg1;
+ const struct hfsts_field *f = &hfsts_fields[arg2];
+ uint32_t val;
+
+ val = (hfsts_read_hfs(sc->sc_dev, f->hfs_idx) & f->mask) >> f->shift;
+ return (sysctl_handle_32(oidp, &val, 0, req));
+}
+
+/* HFS1 sub-fields that decode to a string via a lookup table. */
+enum hfsts_decode_id {
+ HFSTS_DEC_CWS,
+ HFSTS_DEC_OPSTATE,
+ HFSTS_DEC_OPMODE,
+ HFSTS_DEC_ERROR,
+};
+
+struct hfsts_decode {
+ uint32_t mask;
+ uint8_t shift;
+ const char *const *tbl;
+ size_t tbl_sz;
+};
+
+static const struct hfsts_decode hfsts_decodes[] = {
+ [HFSTS_DEC_CWS] = { HFSTS_HFS_1_CWS_MSK, 0,
+ hfsts_cws_values, nitems(hfsts_cws_values) },
+ [HFSTS_DEC_OPSTATE] = { HFSTS_HFS_1_OPSTATE_MSK,
+ HFSTS_HFS_1_OPSTATE_SHIFT,
+ hfsts_opstate_values,
+ nitems(hfsts_opstate_values) },
+ [HFSTS_DEC_OPMODE] = { HFSTS_HFS_1_OPMODE_MSK,
+ HFSTS_HFS_1_OPMODE_SHIFT,
+ hfsts_opmode_values,
+ nitems(hfsts_opmode_values) },
+ [HFSTS_DEC_ERROR] = { HFSTS_HFS_1_ERROR_MSK, HFSTS_HFS_1_ERROR_SHIFT,
+ hfsts_error_values, nitems(hfsts_error_values) },
+};
+
+static int
+hfsts_sysctl_decode(SYSCTL_HANDLER_ARGS)
+{
+ struct hfsts_softc *sc = arg1;
+ const struct hfsts_decode *d = &hfsts_decodes[arg2];
+ uint32_t val;
+
+ val = (hfsts_read_hfs(sc->sc_dev, 0) & d->mask) >> d->shift;
+ return (SYSCTL_OUT_STR(req, hfsts_decode(d->tbl, d->tbl_sz, val)));
+}
+
+static int
+hfsts_sysctl_summary(SYSCTL_HANDLER_ARGS)
+{
+ struct hfsts_softc *sc = arg1;
+ uint32_t hfs1, cws, opmode, error;
+ uint32_t mfg_mode, fpt_bad, bup_fail, fw_init_cmpl, update_inprog;
+
+ hfs1 = hfsts_read_hfs(sc->sc_dev, 0);
+ cws = hfs1 & HFSTS_HFS_1_CWS_MSK;
+ opmode = (hfs1 & HFSTS_HFS_1_OPMODE_MSK) >> HFSTS_HFS_1_OPMODE_SHIFT;
+ error = (hfs1 & HFSTS_HFS_1_ERROR_MSK) >> HFSTS_HFS_1_ERROR_SHIFT;
+ mfg_mode = (hfs1 & HFSTS_HFS_1_MFG_MODE_MSK) != 0;
+ fpt_bad = (hfs1 & HFSTS_HFS_1_FPT_BAD_MSK) != 0;
+ bup_fail = (hfs1 & HFSTS_HFS_1_BUP_FAIL_MSK) != 0;
+ fw_init_cmpl = (hfs1 & HFSTS_HFS_1_FW_INIT_CMPL_MSK) != 0;
+ update_inprog = (hfs1 & HFSTS_HFS_1_UPDATE_INPROG_MSK) != 0;
+
+ return (SYSCTL_OUT_STR(req, hfsts_state_summary(cws, opmode, error,
+ mfg_mode, fpt_bad, bup_fail, fw_init_cmpl, update_inprog)));
+}
+
+/* Table-driven sysctl registration; skipped when sc_hfs_count < min_hfs. */
+typedef int hfsts_sysctl_handler_t(SYSCTL_HANDLER_ARGS);
+
+struct hfsts_sysctl_def {
+ const char *name;
+ int ctltype;
+ intmax_t arg2;
+ hfsts_sysctl_handler_t *handler;
+ const char *fmt;
+ const char *descr;
+ uint8_t min_hfs;
+};
+
+static const struct hfsts_sysctl_def hfsts_sysctls[] = {
+ { "hfs1", CTLTYPE_U32, 0, hfsts_sysctl_raw, "IU",
+ "Host Firmware Status 1 (raw, live)", 1 },
+ { "hfs2", CTLTYPE_U32, 1, hfsts_sysctl_raw, "IU",
+ "Host Firmware Status 2 (raw, live)", 2 },
+ { "hfs3", CTLTYPE_U32, 2, hfsts_sysctl_raw, "IU",
+ "Host Firmware Status 3 (raw, live)", 6 },
+ { "hfs4", CTLTYPE_U32, 3, hfsts_sysctl_raw, "IU",
+ "Host Firmware Status 4 (raw, live)", 6 },
+ { "hfs5", CTLTYPE_U32, 4, hfsts_sysctl_raw, "IU",
+ "Host Firmware Status 5 (raw, live)", 6 },
+ { "hfs6", CTLTYPE_U32, 5, hfsts_sysctl_raw, "IU",
+ "Host Firmware Status 6 (raw, live)", 6 },
+ { "d0i3_supported", CTLTYPE_U32, HFSTS_FLD_D0I3_SUPPORTED,
+ hfsts_sysctl_field, "IU",
+ "ME D0i3 capability (from HFS1, live)", 6 },
+ { "opmode", CTLTYPE_U32, HFSTS_FLD_OPMODE, hfsts_sysctl_field, "IU",
+ "ME operation mode (from HFS1, live)", 1 },
+ { "pm_event", CTLTYPE_U32, HFSTS_FLD_PM_EVENT, hfsts_sysctl_field,
+ "IU", "ME power management event (from HFS2, live)", 2 },
+ { "fw_sku", CTLTYPE_U32, HFSTS_FLD_FW_SKU, hfsts_sysctl_field, "IU",
+ "ME firmware SKU (from HFS3, live)", 6 },
+ { "mfg_mode", CTLTYPE_U32, HFSTS_FLD_MFG_MODE, hfsts_sysctl_field,
+ "IU", "ME running in Manufacturing Mode (from HFS1, live)", 1 },
+ { "update_in_progress", CTLTYPE_U32, HFSTS_FLD_UPDATE_INPROG,
+ hfsts_sysctl_field, "IU",
+ "ME firmware update in progress (from HFS1, live)", 1 },
+ { "state", CTLTYPE_STRING, HFSTS_DEC_CWS, hfsts_sysctl_decode, "A",
+ "ME Current Working State (from HFS1, live)", 1 },
+ { "opstate", CTLTYPE_STRING, HFSTS_DEC_OPSTATE, hfsts_sysctl_decode,
+ "A", "ME Current Operation State (from HFS1, live)", 1 },
+ { "opmode_str", CTLTYPE_STRING, HFSTS_DEC_OPMODE, hfsts_sysctl_decode,
+ "A", "ME Current Operation Mode (from HFS1, live)", 1 },
+ { "error", CTLTYPE_STRING, HFSTS_DEC_ERROR, hfsts_sysctl_decode, "A",
+ "ME Error Code (from HFS1, live)", 1 },
+ { "summary", CTLTYPE_STRING, 0, hfsts_sysctl_summary, "A",
+ "One-word ME state summary (live)", 1 },
+};
+
+static int
+hfsts_probe(device_t dev)
+{
+ const struct hfsts_device *id;
+
+ if (pci_get_vendor(dev) != VENDORID_INTEL)
+ return (ENXIO);
+
+ id = hfsts_find_device(dev);
+ if (id == NULL)
+ return (ENXIO);
+ device_set_desc(dev, id->name);
+ return (BUS_PROBE_GENERIC);
+}
+
+static int
+hfsts_attach(device_t dev)
+{
+ const struct hfsts_device *id;
+ struct hfsts_softc *sc;
+ struct sysctl_oid_list *children;
+ const char *summary;
+ uint32_t hfs1;
+ uint32_t opmode;
+ uint32_t cws, opstate, error, mfg_mode, fpt_bad, fw_init_cmpl;
+ uint32_t bup_fail, update_inprog, boot_opt;
+ u_int i;
+
+ sc = device_get_softc(dev);
+ sc->sc_dev = dev;
+ id = hfsts_find_device(dev);
+ if (id == NULL)
+ return (ENXIO);
+ sc->sc_hfs_count = id->hfs_count;
+
+ /* One-shot read for the attach-time log line only; sysctls re-read. */
+ hfs1 = hfsts_read_hfs(dev, 0);
+ opmode = (hfs1 & HFSTS_HFS_1_OPMODE_MSK) >> HFSTS_HFS_1_OPMODE_SHIFT;
+ cws = hfs1 & HFSTS_HFS_1_CWS_MSK;
+ opstate = (hfs1 & HFSTS_HFS_1_OPSTATE_MSK) >> HFSTS_HFS_1_OPSTATE_SHIFT;
+ error = (hfs1 & HFSTS_HFS_1_ERROR_MSK) >> HFSTS_HFS_1_ERROR_SHIFT;
+ mfg_mode = (hfs1 & HFSTS_HFS_1_MFG_MODE_MSK) != 0;
+ fpt_bad = (hfs1 & HFSTS_HFS_1_FPT_BAD_MSK) != 0;
+ fw_init_cmpl = (hfs1 & HFSTS_HFS_1_FW_INIT_CMPL_MSK) != 0;
+ bup_fail = (hfs1 & HFSTS_HFS_1_BUP_FAIL_MSK) != 0;
+ update_inprog = (hfs1 & HFSTS_HFS_1_UPDATE_INPROG_MSK) != 0;
+ boot_opt = (hfs1 & HFSTS_HFS_1_BOOT_OPT_MSK) != 0;
+ summary = hfsts_state_summary(cws, opmode, error, mfg_mode, fpt_bad,
+ bup_fail, fw_init_cmpl, update_inprog);
+
+ if (bootverbose) {
+ 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",
+ HFSTS_DECODE(hfsts_cws_values, cws),
+ HFSTS_DECODE(hfsts_opstate_values, opstate),
+ HFSTS_DECODE(hfsts_opmode_values, opmode),
+ HFSTS_DECODE(hfsts_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);
+ children = SYSCTL_CHILDREN(sc->sc_sysctlnode);
+
+ for (i = 0; i < nitems(hfsts_sysctls); i++) {
+ const struct hfsts_sysctl_def *d = &hfsts_sysctls[i];
+
+ if (sc->sc_hfs_count < d->min_hfs)
+ continue;
+ sysctl_add_oid(sc->sc_sysctlctx, children, OID_AUTO, d->name,
+ d->ctltype | CTLFLAG_RD | CTLFLAG_MPSAFE, sc, d->arg2,
+ d->handler, d->fmt, __DESCR(d->descr), NULL);
+ }
+
+ return (0);
+}
+
+static int
+hfsts_detach(device_t dev)
+{
+
+ return (0);
+}
+
+static device_method_t hfsts_methods[] = {
+ DEVMETHOD(device_probe, hfsts_probe),
+ DEVMETHOD(device_attach, hfsts_attach),
+ DEVMETHOD(device_detach, hfsts_detach),
+ DEVMETHOD_END
+};
+
+static driver_t hfsts_driver = {
+ "hfsts",
+ hfsts_methods,
+ sizeof(struct hfsts_softc)
+};
+
+DRIVER_MODULE(hfsts, pci, hfsts_driver, 0, 0);
+MODULE_VERSION(hfsts, 1);
+MODULE_DEPEND(hfsts, pci, 1, 1, 1);
diff --git a/sys/dev/intel/hfsts_isa.c b/sys/dev/intel/hfsts_isa.c
new file mode 100644
--- /dev/null
+++ b/sys/dev/intel/hfsts_isa.c
@@ -0,0 +1,237 @@
+/*
+ * Copyright (c) 2026 Abdelkader Boudih <seuros@FreeBSD.org>
+ *
+ * SPDX-License-Identifier: BSD-2-Clause
+ */
+
+/*
+ * hfstsfd(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 hfsts_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. identify()
+ * checks the LPC bridge's device ID against hfsts_fd_lpc_ids[] and only adds
+ * a child for those generations, instead of assuming that every PCH's
+ * disable mechanism or ME capability is known.
+ */
+
+#include <sys/param.h>
+#include <sys/bus.h>
+#include <sys/kernel.h>
+#include <sys/module.h>
+#include <sys/sysctl.h>
+
+#include <vm/vm.h>
+#include <vm/pmap.h>
+
+#include <machine/bus.h>
+#include <machine/pmap.h>
+
+#include <dev/pci/pcivar.h>
+#include <dev/ichwd/ichwd.h>
+#include <dev/intel/hfsts.h>
+
+#define HFSTS_FD_RCBA_OFFSET 0xf0
+#define HFSTS_FD_RCBA_EN 0x00000001
+#define HFSTS_FD_RCBA_MASK 0xffffc000
+#define HFSTS_FD_FD2_OFFSET 0x3428
+#define HFSTS_FD_FD2_MEI1_DIS 0x00000002
+#define HFSTS_FD_FD2_MEI2_DIS 0x00000004
+/* Page containing FD2, and FD2's offset within that mapped page. */
+#define HFSTS_FD_FD2_PAGE (HFSTS_FD_FD2_OFFSET & ~(vm_paddr_t)PAGE_MASK)
+#define HFSTS_FD_FD2_PAGE_OFF (HFSTS_FD_FD2_OFFSET & PAGE_MASK)
+
+static const uint16_t hfsts_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_PPT16, DEVICEID_PPT17, DEVICEID_PPT18, DEVICEID_PPT19,
+ DEVICEID_PPT20, DEVICEID_PPT21, DEVICEID_PPT22, DEVICEID_PPT23,
+ DEVICEID_PPT24, DEVICEID_PPT25, DEVICEID_PPT26, DEVICEID_PPT27,
+ DEVICEID_PPT28, DEVICEID_PPT29, DEVICEID_PPT30, DEVICEID_PPT31,
+ 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 hfsts_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
+hfsts_fd_lpc_known(uint16_t devid)
+{
+ u_int i;
+
+ for (i = 0; i < nitems(hfsts_fd_lpc_ids); i++) {
+ if (hfsts_fd_lpc_ids[i] == devid)
+ return (true);
+ }
+ return (false);
+}
+
+static void
+hfsts_fd_identify(driver_t *driver, device_t parent)
+{
+ device_t isab;
+
+ if (device_find_child(parent, driver->name, 0) != NULL)
+ return;
+
+ isab = device_get_parent(parent);
+ if (!is_pci_device(isab) || pci_get_vendor(isab) != VENDORID_INTEL ||
+ !hfsts_fd_lpc_known(pci_get_device(isab)))
+ return;
+
+ /* MEI enumerated on PCI, so it is not firmware-hidden. */
+ if (hfsts_pci_present())
+ return;
+
+ BUS_ADD_CHILD(parent, 0, driver->name, 0);
+}
+
+static int
+hfsts_fd_probe(device_t dev)
+{
+
+ device_set_desc(dev, "Intel ME Function Disable check");
+ return (BUS_PROBE_NOWILDCARD);
+}
+
+static void
+hfsts_fd_set_status(struct hfsts_fd_softc *sc, const char *status)
+{
+
+ strlcpy(sc->sc_status, status, sizeof(sc->sc_status));
+}
+
+static int
+hfsts_fd_attach(device_t dev)
+{
+ struct hfsts_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));
+
+ rcba = pci_read_config(isab, HFSTS_FD_RCBA_OFFSET, 4);
+ if ((rcba & HFSTS_FD_RCBA_EN) == 0 || (rcba & HFSTS_FD_RCBA_MASK) == 0) {
+ device_printf(dev,
+ "RCBA not enabled, cannot read Function Disable register\n");
+ hfsts_fd_set_status(sc, "rcba-disabled");
+ goto done;
+ }
+
+ bsh = (bus_space_handle_t)pmap_mapdev(
+ (rcba & HFSTS_FD_RCBA_MASK) + HFSTS_FD_FD2_PAGE, PAGE_SIZE);
+ fd2 = bus_space_read_4(X86_BUS_SPACE_MEM, bsh, HFSTS_FD_FD2_PAGE_OFF);
+ pmap_unmapdev((void *)bsh, PAGE_SIZE);
+
+ sc->sc_fd2 = fd2;
+ sc->sc_mei1_disabled = (fd2 & HFSTS_FD_FD2_MEI1_DIS) != 0;
+ sc->sc_mei2_disabled = (fd2 & HFSTS_FD_FD2_MEI2_DIS) != 0;
+ sc->sc_checked = true;
+ hfsts_fd_set_status(sc, "checked");
+
+ if (bootverbose) {
+ 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
+hfsts_fd_detach(device_t dev)
+{
+
+ return (0);
+}
+
+static device_method_t hfsts_fd_methods[] = {
+ DEVMETHOD(device_identify, hfsts_fd_identify),
+ DEVMETHOD(device_probe, hfsts_fd_probe),
+ DEVMETHOD(device_attach, hfsts_fd_attach),
+ DEVMETHOD(device_detach, hfsts_fd_detach),
+ DEVMETHOD_END
+};
+
+static driver_t hfsts_fd_driver = {
+ "hfstsfd",
+ hfsts_fd_methods,
+ sizeof(struct hfsts_fd_softc)
+};
+
+DRIVER_MODULE(hfstsfd, isa, hfsts_fd_driver, 0, 0);
+MODULE_VERSION(hfstsfd, 1);
diff --git a/sys/modules/Makefile b/sys/modules/Makefile
--- a/sys/modules/Makefile
+++ b/sys/modules/Makefile
@@ -138,6 +138,7 @@
${_glxsb} \
gpio \
${_gve} \
+ ${_hfsts} \
hid \
${_hpt27xx} \
${_hptiop} \
@@ -875,6 +876,7 @@
_asmc= asmc
_ccp= ccp
_enic= enic
+_hfsts= hfsts
_iavf= iavf
_ioat= ioat
_iwx= iwx
diff --git a/sys/modules/hfsts/Makefile b/sys/modules/hfsts/Makefile
new file mode 100644
--- /dev/null
+++ b/sys/modules/hfsts/Makefile
@@ -0,0 +1,11 @@
+# SPDX-License-Identifier: BSD-2-Clause
+#
+# Copyright (c) 2026 Abdelkader Boudih <seuros@FreeBSD.org>
+
+.PATH: ${SRCTOP}/sys/dev/intel
+
+KMOD= hfsts
+SRCS= hfsts.c hfsts_isa.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
Tue, Sep 8, 3:09 AM (2 h, 6 m)
Storage Engine
blob
Storage Format
Raw Data
Storage Handle
38441192
Default Alt Text
D58863.id185127.diff (36 KB)
Attached To
Mode
D58863: intel: add hfsts debug-probe driver for Intel ME/HECI firmware status
Attached
Detach File
Event Timeline
Log In to Comment