Page MenuHomeFreeBSD

D58863.id185127.diff
No OneTemporary

D58863.id185127.diff

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

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)

Event Timeline