diff --git a/sys/dev/ichwd/ichwd.c b/sys/dev/ichwd/ichwd.c index 71662a5..a4387a2 100644 --- a/sys/dev/ichwd/ichwd.c +++ b/sys/dev/ichwd/ichwd.c @@ -62,6 +62,7 @@ __FBSDID("$FreeBSD$"); #include #include #include +#include #include #include #include @@ -72,6 +73,14 @@ __FBSDID("$FreeBSD$"); #include + +static int use_global_smi = 0; + +SYSCTL_NODE(_hw, OID_AUTO, ichwd, CTLFLAG_RD, 0, "ichwd driver global parameters"); +TUNABLE_INT("hw.ichwd.use_global_smi", &use_global_smi); +SYSCTL_INT(_hw_ichwd, OID_AUTO, use_global_smi, CTLFLAG_RDTUN, &use_global_smi, 0, + "ichwd may try to change global SMI Enable bit if TCO SMI bit is locked"); + static struct ichwd_device ichwd_devices[] = { { DEVICEID_82801AA, "Intel 82801AA watchdog timer", 1 }, { DEVICEID_82801AB, "Intel 82801AB watchdog timer", 1 }, @@ -152,7 +161,37 @@ static devclass_t ichwd_devclass; static __inline void ichwd_smi_disable(struct ichwd_softc *sc) { - ichwd_write_smi_4(sc, SMI_EN, ichwd_read_smi_4(sc, SMI_EN) & ~SMI_TCO_EN); + uint32_t val; + + val = ichwd_read_smi_4(sc, SMI_EN); + + /* check if either TCO SMI is disabled or SMI is disabled globally */ + if ((val & (SMI_TCO_EN | SMI_GBL_EN)) != (SMI_TCO_EN | SMI_GBL_EN)) + return; + /* first try to disabled TCO SMI */ + val &= ~SMI_TCO_EN; + ichwd_write_smi_4(sc, SMI_EN, val); + + /* check if TCO SMI got disabled */ + val = ichwd_read_smi_4(sc, SMI_EN); + if ((val & SMI_TCO_EN) == 0) + return; + printf("could not disable TCO SMI, bit might be locked by BIOS\n"); + + /* check if user allowed to fiddle with global SMI settings */ + if (!use_global_smi) + return; + + /* try to disable SMI globally */ + val &= ~SMI_GBL_EN; + ichwd_write_smi_4(sc, SMI_EN, val); + + /* check results */ + val = ichwd_read_smi_4(sc, SMI_EN); + if (val & SMI_GBL_EN) + printf("could not disable SMI globally, bit might be locked by BIOS\n"); + else + printf("disabled SMI globally (permitted by use_global_smi tunable)\n"); } /* @@ -161,7 +200,30 @@ ichwd_smi_disable(struct ichwd_softc *sc) static __inline void ichwd_smi_enable(struct ichwd_softc *sc) { - ichwd_write_smi_4(sc, SMI_EN, ichwd_read_smi_4(sc, SMI_EN) | SMI_TCO_EN); + uint32_t val; + + val = ichwd_read_smi_4(sc, SMI_EN); + + /* check if both TCO SMI and global SMI are already enabled */ + if ((val & (SMI_TCO_EN | SMI_GBL_EN)) == (SMI_TCO_EN | SMI_GBL_EN)) + return; + + /* try to enable TCO SMI and global SMI if permitted */ + val |= SMI_TCO_EN; + if (use_global_smi) + val |= SMI_GBL_EN; + + /* bail out if SMI is globally disabled and user doesn't allow to change that */ + if ((val & SMI_GBL_EN) == 0) { + printf("could not enable TCO SMI because SMI is globally disabled\n"); + return; + } + ichwd_write_smi_4(sc, SMI_EN, val); + + /* check if we succeeded */ + val = ichwd_read_smi_4(sc, SMI_EN); + if ((val & (SMI_TCO_EN | SMI_GBL_EN)) != (SMI_TCO_EN | SMI_GBL_EN)) + printf("could not enable SMIs, bits might be locked by BIOS\n"); } /* diff --git a/sys/dev/ichwd/ichwd.h b/sys/dev/ichwd/ichwd.h index 51ed26d..8e2bdba 100644 --- a/sys/dev/ichwd/ichwd.h +++ b/sys/dev/ichwd/ichwd.h @@ -131,9 +131,9 @@ struct ichwd_softc { #define TCO1_CNT 0x08 /* TCO Control 1 */ #define TCO2_CNT 0x08 /* TCO Control 2 */ -/* bit definitions for SMI_EN and SMI_STS */ +/* SMI-related bit definitions */ +#define SMI_GBL_EN 0x0001 #define SMI_TCO_EN 0x2000 -#define SMI_TCO_STS 0x2000 /* timer value mask for TCO_RLD and TCO_TMR */ #define TCO_TIMER_MASK 0x1f