1 /* 2 * Copyright (c) 2026 Abdelkader Boudih <seuros@FreeBSD.org> 3 * 4 * SPDX-License-Identifier: BSD-2-Clause 5 */ 6 7 /* 8 * hfstsfd(4) - detects HECI hidden via the PCH Function Disable register. 9 * 10 * The MEI PCI function never enumerates when firmware (coreboot's 11 * PCH_DISABLE_MEI1/2) has disabled it, so there is no PCI device instance 12 * for hfsts_attach() to run against. This is an isa-attached identify 13 * driver instead, modeled on ichwd_identify() in sys/dev/ichwd/ichwd.c, 14 * which faces the same problem for the ICH watchdog. 15 * 16 * RCBA (LPC bridge config offset 0xF0) and FD2 (RCBA+0x3428, bit 1 = 17 * MEI1 disabled, bit 2 = MEI2 disabled) use this layout on Ibex Peak, 18 * Cougar Point, Panther Point, Lynx Point, and Wildcat Point. identify() 19 * checks the LPC bridge's device ID against hfsts_fd_lpc_ids[] and only adds 20 * a child for those generations, instead of assuming that every PCH's 21 * disable mechanism or ME capability is known. 22 */ 23 24 #include <sys/param.h> 25 #include <sys/bus.h> 26 #include <sys/kernel.h> 27 #include <sys/module.h> 28 #include <sys/sysctl.h> 29 30 #include <vm/vm.h> 31 #include <vm/pmap.h> 32 33 #include <machine/bus.h> 34 #include <machine/pmap.h> 35 36 #include <dev/pci/pcivar.h> 37 #include <dev/ichwd/ichwd.h> 38 #include <dev/intel/hfsts.h> 39 40 #define HFSTS_FD_RCBA_OFFSET 0xf0 41 #define HFSTS_FD_RCBA_EN 0x00000001 42 #define HFSTS_FD_RCBA_MASK 0xffffc000 43 #define HFSTS_FD_FD2_OFFSET 0x3428 44 #define HFSTS_FD_FD2_MEI1_DIS 0x00000002 45 #define HFSTS_FD_FD2_MEI2_DIS 0x00000004 46 /* Page containing FD2, and FD2's offset within that mapped page. */ 47 #define HFSTS_FD_FD2_PAGE (HFSTS_FD_FD2_OFFSET & ~(vm_paddr_t)PAGE_MASK) 48 #define HFSTS_FD_FD2_PAGE_OFF (HFSTS_FD_FD2_OFFSET & PAGE_MASK) 49 50 static const uint16_t hfsts_fd_lpc_ids[] = { 51 DEVICEID_LPT0, DEVICEID_LPT1, DEVICEID_LPT2, DEVICEID_LPT3, 52 DEVICEID_LPT4, DEVICEID_LPT5, DEVICEID_LPT6, DEVICEID_LPT7, 53 DEVICEID_LPT8, DEVICEID_LPT9, DEVICEID_LPT10, DEVICEID_LPT11, 54 DEVICEID_LPT12, DEVICEID_LPT13, DEVICEID_LPT14, DEVICEID_LPT15, 55 DEVICEID_LPT16, DEVICEID_LPT17, DEVICEID_LPT18, DEVICEID_LPT19, 56 DEVICEID_LPT20, DEVICEID_LPT21, DEVICEID_LPT22, DEVICEID_LPT23, 57 DEVICEID_LPT24, DEVICEID_LPT25, DEVICEID_LPT26, DEVICEID_LPT27, 58 DEVICEID_LPT28, DEVICEID_LPT29, DEVICEID_LPT30, DEVICEID_LPT31, 59 DEVICEID_LPT_LP0, DEVICEID_LPT_LP1, DEVICEID_LPT_LP2, 60 DEVICEID_LPT_LP3, DEVICEID_LPT_LP4, DEVICEID_LPT_LP5, 61 DEVICEID_LPT_LP6, DEVICEID_LPT_LP7, 62 DEVICEID_WCPT1, DEVICEID_WCPT2, DEVICEID_WCPT3, DEVICEID_WCPT4, 63 DEVICEID_WCPT6, 64 DEVICEID_WCPT_LP1, DEVICEID_WCPT_LP2, DEVICEID_WCPT_LP3, 65 DEVICEID_WCPT_LP5, DEVICEID_WCPT_LP6, DEVICEID_WCPT_LP7, 66 DEVICEID_WCPT_LP9, 67 DEVICEID_CPT0, DEVICEID_CPT1, DEVICEID_CPT2, DEVICEID_CPT3, 68 DEVICEID_CPT4, DEVICEID_CPT5, DEVICEID_CPT6, DEVICEID_CPT7, 69 DEVICEID_CPT8, DEVICEID_CPT9, DEVICEID_CPT10, DEVICEID_CPT11, 70 DEVICEID_CPT12, DEVICEID_CPT13, DEVICEID_CPT14, DEVICEID_CPT15, 71 DEVICEID_CPT16, DEVICEID_CPT17, DEVICEID_CPT18, DEVICEID_CPT19, 72 DEVICEID_CPT20, DEVICEID_CPT21, DEVICEID_CPT22, DEVICEID_CPT23, 73 DEVICEID_CPT24, DEVICEID_CPT25, DEVICEID_CPT26, DEVICEID_CPT27, 74 DEVICEID_CPT28, DEVICEID_CPT29, DEVICEID_CPT30, DEVICEID_CPT31, 75 DEVICEID_PPT0, DEVICEID_PPT1, DEVICEID_PPT2, DEVICEID_PPT3, 76 DEVICEID_PPT4, DEVICEID_PPT5, DEVICEID_PPT6, DEVICEID_PPT7, 77 DEVICEID_PPT8, DEVICEID_PPT9, DEVICEID_PPT10, DEVICEID_PPT11, 78 DEVICEID_PPT12, DEVICEID_PPT13, DEVICEID_PPT14, DEVICEID_PPT15, 79 DEVICEID_PPT16, DEVICEID_PPT17, DEVICEID_PPT18, DEVICEID_PPT19, 80 DEVICEID_PPT20, DEVICEID_PPT21, DEVICEID_PPT22, DEVICEID_PPT23, 81 DEVICEID_PPT24, DEVICEID_PPT25, DEVICEID_PPT26, DEVICEID_PPT27, 82 DEVICEID_PPT28, DEVICEID_PPT29, DEVICEID_PPT30, DEVICEID_PPT31, 83 DEVICEID_PCH, DEVICEID_PCHM, DEVICEID_P55, DEVICEID_PM55, 84 DEVICEID_H55, DEVICEID_QM57, DEVICEID_H57, DEVICEID_HM55, 85 DEVICEID_Q57, DEVICEID_HM57, DEVICEID_PCHMSFF, DEVICEID_QS57, 86 }; 87 88 struct hfsts_fd_softc { 89 device_t sc_dev; 90 char sc_status[24]; 91 uint32_t sc_fd2; 92 uint32_t sc_mei1_disabled; 93 uint32_t sc_mei2_disabled; 94 bool sc_checked; 95 }; 96 97 static bool 98 hfsts_fd_lpc_known(uint16_t devid) 99 { 100 u_int i; 101 102 for (i = 0; i < nitems(hfsts_fd_lpc_ids); i++) { 103 if (hfsts_fd_lpc_ids[i] == devid) 104 return (true); 105 } 106 return (false); 107 } 108 109 static void 110 hfsts_fd_identify(driver_t *driver, device_t parent) 111 { 112 device_t isab; 113 114 if (device_find_child(parent, driver->name, 0) != NULL) 115 return; 116 117 isab = device_get_parent(parent); 118 if (!is_pci_device(isab) || pci_get_vendor(isab) != VENDORID_INTEL || 119 !hfsts_fd_lpc_known(pci_get_device(isab))) 120 return; 121 122 /* MEI enumerated on PCI, so it is not firmware-hidden. */ 123 if (hfsts_pci_present()) 124 return; 125 126 BUS_ADD_CHILD(parent, 0, driver->name, 0); 127 } 128 129 static int 130 hfsts_fd_probe(device_t dev) 131 { 132 133 device_set_desc(dev, "Intel ME Function Disable check"); 134 return (BUS_PROBE_NOWILDCARD); 135 } 136 137 static void 138 hfsts_fd_set_status(struct hfsts_fd_softc *sc, const char *status) 139 { 140 141 strlcpy(sc->sc_status, status, sizeof(sc->sc_status)); 142 } 143 144 static int 145 hfsts_fd_attach(device_t dev) 146 { 147 struct hfsts_fd_softc *sc; 148 struct sysctl_ctx_list *ctx; 149 struct sysctl_oid *tree; 150 device_t isab; 151 vm_paddr_t rcba; 152 bus_space_handle_t bsh; 153 uint32_t fd2; 154 155 sc = device_get_softc(dev); 156 sc->sc_dev = dev; 157 158 isab = device_get_parent(device_get_parent(dev)); 159 160 rcba = pci_read_config(isab, HFSTS_FD_RCBA_OFFSET, 4); 161 if ((rcba & HFSTS_FD_RCBA_EN) == 0 || (rcba & HFSTS_FD_RCBA_MASK) == 0) { 162 device_printf(dev, 163 "RCBA not enabled, cannot read Function Disable register\n"); 164 hfsts_fd_set_status(sc, "rcba-disabled"); 165 goto done; 166 } 167 168 bsh = (bus_space_handle_t)pmap_mapdev( 169 (rcba & HFSTS_FD_RCBA_MASK) + HFSTS_FD_FD2_PAGE, PAGE_SIZE); 170 fd2 = bus_space_read_4(X86_BUS_SPACE_MEM, bsh, HFSTS_FD_FD2_PAGE_OFF); 171 pmap_unmapdev((void *)bsh, PAGE_SIZE); 172 173 sc->sc_fd2 = fd2; 174 sc->sc_mei1_disabled = (fd2 & HFSTS_FD_FD2_MEI1_DIS) != 0; 175 sc->sc_mei2_disabled = (fd2 & HFSTS_FD_FD2_MEI2_DIS) != 0; 176 sc->sc_checked = true; 177 hfsts_fd_set_status(sc, "checked"); 178 179 if (bootverbose) { 180 if (sc->sc_mei1_disabled) { 181 device_printf(dev, "HECI1 disabled via PCH Function " 182 "Disable register (silicon present, hidden by " 183 "firmware)\n"); 184 } else { 185 device_printf(dev, "HECI1 not disabled - MEI should " 186 "be enumerable as a normal PCI function\n"); 187 } 188 if (sc->sc_mei2_disabled) 189 device_printf(dev, "HECI2 disabled via PCH Function " 190 "Disable register\n"); 191 } 192 193 done: 194 ctx = device_get_sysctl_ctx(dev); 195 tree = device_get_sysctl_tree(dev); 196 197 SYSCTL_ADD_STRING(ctx, SYSCTL_CHILDREN(tree), OID_AUTO, "status", 198 CTLFLAG_RD, sc->sc_status, 0, 199 "Function Disable check result"); 200 if (!sc->sc_checked) 201 return (0); 202 SYSCTL_ADD_U32(ctx, SYSCTL_CHILDREN(tree), OID_AUTO, "fd2", 203 CTLFLAG_RD, &sc->sc_fd2, 0, 204 "Function Disable 2 register (raw)"); 205 SYSCTL_ADD_U32(ctx, SYSCTL_CHILDREN(tree), OID_AUTO, "mei1_disabled", 206 CTLFLAG_RD, &sc->sc_mei1_disabled, 0, 207 "HECI1 disabled via Function Disable register"); 208 SYSCTL_ADD_U32(ctx, SYSCTL_CHILDREN(tree), OID_AUTO, "mei2_disabled", 209 CTLFLAG_RD, &sc->sc_mei2_disabled, 0, 210 "HECI2 disabled via Function Disable register"); 211 212 return (0); 213 } 214 215 static int 216 hfsts_fd_detach(device_t dev) 217 { 218 219 return (0); 220 } 221 222 static device_method_t hfsts_fd_methods[] = { 223 DEVMETHOD(device_identify, hfsts_fd_identify), 224 DEVMETHOD(device_probe, hfsts_fd_probe), 225 DEVMETHOD(device_attach, hfsts_fd_attach), 226 DEVMETHOD(device_detach, hfsts_fd_detach), 227 DEVMETHOD_END 228 }; 229 230 static driver_t hfsts_fd_driver = { 231 "hfstsfd", 232 hfsts_fd_methods, 233 sizeof(struct hfsts_fd_softc) 234 }; 235 236 DRIVER_MODULE(hfstsfd, isa, hfsts_fd_driver, 0, 0); 237 MODULE_VERSION(hfstsfd, 1); 238