xref: /freebsd/sys/dev/intel/hfsts_isa.c (revision 8e5e2c391059eaa0280a32b951e3ab1fac278364)
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
hfsts_fd_lpc_known(uint16_t devid)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
hfsts_fd_identify(driver_t * driver,device_t parent)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
hfsts_fd_probe(device_t dev)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
hfsts_fd_set_status(struct hfsts_fd_softc * sc,const char * status)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
hfsts_fd_attach(device_t dev)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
hfsts_fd_detach(device_t dev)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