diff --git a/share/man/man4/Makefile b/share/man/man4/Makefile --- a/share/man/man4/Makefile +++ b/share/man/man4/Makefile @@ -211,6 +211,7 @@ h_ertt.4 \ hconf.4 \ hcons.4 \ + hfsts.4 \ hgame.4 \ hidbus.4 \ hidquirk.4 \ @@ -758,6 +759,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,219 @@ +.\" +.\" SPDX-License-Identifier: BSD-2-Clause +.\" +.\" Copyright (c) 2026 Abdelkader Boudih +.\" +.Dd September 6, 2026 +.Dt HFSTS 4 amd64 +.Os +.Sh NAME +.Nm hfsts , +.Nm hfstsfd +.Nd Intel Management Engine Host Firmware Status debug probe +.Sh SYNOPSIS +.Cd "device pci" +.Cd "device isa" +.Cd "device hfsts" +.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 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, one of +.Dq Normal , +.Dq Active , +.Dq Disabled , +or +.Dq Absent , +for consumers that do not want to parse the raw fields. +.Ss hfstsfd +.Nm hfstsfd +.Pq Function Disable +is a companion probe that reports whether the MEI function exists in +silicon but has been hidden by firmware. +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. +Instead, +.Nm hfstsfd +attaches below the PCI-ISA bridge device +and reads the Function Disable register in the chipset's RCBA space +to check whether HECI1/HECI2 are present but hidden. +If the MEI function is visible on the PCI bus, +.Nm hfstsfd +is not needed and does not attach; the two drivers are mutually +exclusive, so a system exposes either +.Va dev.hfsts.0 +or +.Va dev.hfstsfd.0 , +never both. +.Nm hfstsfd +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 such as +.Dq Normal +or +.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. +.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/amd64/conf/NOTES b/sys/amd64/conf/NOTES --- a/sys/amd64/conf/NOTES +++ b/sys/amd64/conf/NOTES @@ -114,6 +114,9 @@ device ioat # Intel I/OAT DMA engine +# Intel ME Host Firmware Status (HFSTS) debug probe +device hfsts + # EFI Runtime Services support options EFIRT 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 + * + * 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 + * + * 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 +#include +#include +#include +#include + +#include +#include +#include +#include + +/* 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 + * + * 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 +#include +#include +#include +#include + +#include +#include + +#include +#include + +#include +#include +#include + +#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} \ @@ -877,6 +878,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 + +.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