xref: /freebsd/sys/amd64/vmm/amd/ivrs_drv.c (revision 0183e0151669735d62584fbba9125ed90716af5e)
1 /*-
2  * Copyright (c) 2016, Anish Gupta (anish@freebsd.org)
3  * All rights reserved.
4  *
5  * Redistribution and use in source and binary forms, with or without
6  * modification, are permitted provided that the following conditions
7  * are met:
8  * 1. Redistributions of source code must retain the above copyright
9  *    notice unmodified, this list of conditions, and the following
10  *    disclaimer.
11  * 2. Redistributions in binary form must reproduce the above copyright
12  *    notice, this list of conditions and the following disclaimer in the
13  *    documentation and/or other materials provided with the distribution.
14  *
15  * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
16  * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
17  * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
18  * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
19  * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
20  * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
21  * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
22  * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
23  * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
24  * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
25  */
26 
27 #include <sys/cdefs.h>
28 __FBSDID("$FreeBSD$");
29 
30 #include "opt_acpi.h"
31 #include <sys/param.h>
32 #include <sys/bus.h>
33 #include <sys/kernel.h>
34 #include <sys/module.h>
35 #include <sys/malloc.h>
36 
37 #include <machine/vmparam.h>
38 
39 #include <vm/vm.h>
40 #include <vm/pmap.h>
41 
42 #include <contrib/dev/acpica/include/acpi.h>
43 #include <contrib/dev/acpica/include/accommon.h>
44 #include <dev/acpica/acpivar.h>
45 
46 #include "io/iommu.h"
47 #include "amdvi_priv.h"
48 
49 device_t *ivhd_devs;			/* IVHD or AMD-Vi device list. */
50 int	ivhd_count;			/* Number of IVHD or AMD-Vi devices. */
51 
52 extern int amdvi_ptp_level;		/* Page table levels. */
53 
54 typedef int (*ivhd_iter_t)(ACPI_IVRS_HEADER * ptr, void *arg);
55 
56 /*
57  * Iterate IVRS table for IVHD and IVMD device type.
58  */
59 static void
60 ivrs_hdr_iterate_tbl(ivhd_iter_t iter, void *arg)
61 {
62 	ACPI_TABLE_IVRS *ivrs;
63 	ACPI_IVRS_HEADER *ivrs_hdr, *end;
64 	ACPI_STATUS status;
65 
66 	status = AcpiGetTable(ACPI_SIG_IVRS, 1, (ACPI_TABLE_HEADER **)&ivrs);
67 	if (ACPI_FAILURE(status))
68 		return;
69 
70 	if (ivrs->Header.Length == 0) {
71 		return;
72 	}
73 
74 	ivrs_hdr = (ACPI_IVRS_HEADER *)(ivrs + 1);
75 	end = (ACPI_IVRS_HEADER *)((char *)ivrs + ivrs->Header.Length);
76 
77 	while (ivrs_hdr < end) {
78 		switch (ivrs_hdr->Type) {
79 		case ACPI_IVRS_TYPE_HARDWARE:	/* Legacy */
80 		case 0x11:
81 		case 0x40: 			/* ACPI HID */
82 			if (!iter(ivrs_hdr, arg))
83 				return;
84 			break;
85 
86 		case ACPI_IVRS_TYPE_MEMORY1:
87 		case ACPI_IVRS_TYPE_MEMORY2:
88 		case ACPI_IVRS_TYPE_MEMORY3:
89 			if (!iter(ivrs_hdr, arg))
90 				return;
91 
92 			break;
93 
94 		default:
95 			printf("AMD-Vi:Not IVHD/IVMD type(%d)", ivrs_hdr->Type);
96 
97 		}
98 
99 		ivrs_hdr = (ACPI_IVRS_HEADER *)((uint8_t *)ivrs_hdr +
100 			ivrs_hdr->Length);
101 		if (ivrs_hdr->Length < 0) {
102 			printf("AMD-Vi:IVHD/IVMD is corrupted, length : %d\n", ivrs_hdr->Length);
103 			break;
104 		}
105 	}
106 }
107 
108 static  int
109 ivrs_is_ivhd(UINT8 type)
110 {
111 
112 	if ((type == ACPI_IVRS_TYPE_HARDWARE) || (type == 0x11)	|| (type == 0x40))
113 		return (1);
114 
115 	return (0);
116 }
117 
118 /* Count the number of AMD-Vi devices in the system. */
119 static int
120 ivhd_count_iter(ACPI_IVRS_HEADER * ivrs_he, void *arg)
121 {
122 
123 	if (ivrs_is_ivhd(ivrs_he->Type))
124 		ivhd_count++;
125 
126 	return (1);
127 }
128 
129 struct find_ivrs_hdr_args {
130 	int	i;
131 	ACPI_IVRS_HEADER *ptr;
132 };
133 
134 static int
135 ivrs_hdr_find_iter(ACPI_IVRS_HEADER * ivrs_hdr, void *args)
136 {
137 	struct find_ivrs_hdr_args *fi;
138 
139 	fi = (struct find_ivrs_hdr_args *)args;
140 	if (ivrs_is_ivhd(ivrs_hdr->Type)) {
141 		if (fi->i == 0) {
142 			fi->ptr = ivrs_hdr;
143 			return (0);
144 		}
145 		fi->i--;
146 	}
147 
148 	return (1);
149 }
150 
151 static ACPI_IVRS_HARDWARE *
152 ivhd_find_by_index(int idx)
153 {
154 	struct find_ivrs_hdr_args fi;
155 
156 	fi.i = idx;
157 	fi.ptr = NULL;
158 
159 	ivrs_hdr_iterate_tbl(ivrs_hdr_find_iter, &fi);
160 
161 	return ((ACPI_IVRS_HARDWARE *)fi.ptr);
162 }
163 
164 static void
165 ivhd_dev_add_entry(struct amdvi_softc *softc, uint32_t start_id,
166     uint32_t end_id, uint8_t cfg, bool ats)
167 {
168 	struct ivhd_dev_cfg *dev_cfg;
169 
170 	/* If device doesn't have special data, don't add it. */
171 	if (!cfg)
172 		return;
173 
174 	dev_cfg = &softc->dev_cfg[softc->dev_cfg_cnt++];
175 	dev_cfg->start_id = start_id;
176 	dev_cfg->end_id = end_id;
177 	dev_cfg->data = cfg;
178 	dev_cfg->enable_ats = ats;
179 }
180 
181 /*
182  * Record device attributes as suggested by BIOS.
183  */
184 static int
185 ivhd_dev_parse(ACPI_IVRS_HARDWARE * ivhd, struct amdvi_softc *softc)
186 {
187 	ACPI_IVRS_DE_HEADER *de, *end;
188 	int range_start_id = 0, range_end_id = 0;
189 	uint32_t *extended;
190 	uint8_t all_data = 0, range_data = 0;
191 	bool range_enable_ats = false, enable_ats;
192 
193 	softc->start_dev_rid = ~0;
194 	softc->end_dev_rid = 0;
195 
196 	de = (ACPI_IVRS_DE_HEADER *) ((uint8_t *)ivhd +
197 	    sizeof(ACPI_IVRS_HARDWARE));
198 	end = (ACPI_IVRS_DE_HEADER *) ((uint8_t *)ivhd +
199 	    ivhd->Header.Length);
200 
201 	while (de < (ACPI_IVRS_DE_HEADER *) end) {
202 		softc->start_dev_rid = MIN(softc->start_dev_rid, de->Id);
203 		softc->end_dev_rid = MAX(softc->end_dev_rid, de->Id);
204 		switch (de->Type) {
205 		case ACPI_IVRS_TYPE_ALL:
206 			all_data = de->DataSetting;
207 			break;
208 
209 		case ACPI_IVRS_TYPE_SELECT:
210 		case ACPI_IVRS_TYPE_ALIAS_SELECT:
211 		case ACPI_IVRS_TYPE_EXT_SELECT:
212 			enable_ats = false;
213 			if (de->Type == ACPI_IVRS_TYPE_EXT_SELECT) {
214 				extended = (uint32_t *)(de + 1);
215 				enable_ats =
216 				    (*extended & IVHD_DEV_EXT_ATS_DISABLE) ?
217 					false : true;
218 			}
219 			ivhd_dev_add_entry(softc, de->Id, de->Id,
220 			    de->DataSetting | all_data, enable_ats);
221 			break;
222 
223 		case ACPI_IVRS_TYPE_START:
224 		case ACPI_IVRS_TYPE_ALIAS_START:
225 		case ACPI_IVRS_TYPE_EXT_START:
226 			range_start_id = de->Id;
227 			range_data = de->DataSetting;
228 			if (de->Type == ACPI_IVRS_TYPE_EXT_START) {
229 				extended = (uint32_t *)(de + 1);
230 				range_enable_ats =
231 				    (*extended & IVHD_DEV_EXT_ATS_DISABLE) ?
232 					false : true;
233 			}
234 			break;
235 
236 		case ACPI_IVRS_TYPE_END:
237 			range_end_id = de->Id;
238 			ivhd_dev_add_entry(softc, range_start_id, range_end_id,
239 				range_data | all_data, range_enable_ats);
240 			range_start_id = range_end_id = 0;
241 			range_data = 0;
242 			all_data = 0;
243 			break;
244 
245 		case ACPI_IVRS_TYPE_PAD4:
246 			break;
247 
248 		case ACPI_IVRS_TYPE_SPECIAL:
249 			/* HPET or IOAPIC */
250 			break;
251 		default:
252 			if ((de->Type < 5) ||
253 			    (de->Type >= ACPI_IVRS_TYPE_PAD8))
254 				device_printf(softc->dev,
255 				    "Unknown dev entry:0x%x\n", de->Type);
256 		}
257 
258 		if (softc->dev_cfg_cnt >
259 			(sizeof(softc->dev_cfg) / sizeof(softc->dev_cfg[0]))) {
260 			device_printf(softc->dev,
261 			    "WARN Too many device entries.\n");
262 			return (EINVAL);
263 		}
264 		de++;
265 	}
266 
267 	KASSERT((softc->end_dev_rid >= softc->start_dev_rid),
268 	    ("Device end[0x%x] < start[0x%x.\n",
269 	    softc->end_dev_rid, softc->start_dev_rid));
270 
271 	return (0);
272 }
273 
274 static void
275 ivhd_identify(driver_t *driver, device_t parent)
276 {
277 	ACPI_TABLE_IVRS *ivrs;
278 	ACPI_IVRS_HARDWARE *ivhd;
279 	ACPI_STATUS status;
280 	uint32_t info;
281 	int i, count = 0;
282 
283 	if (acpi_disabled("ivhd"))
284 		return;
285 
286 	status = AcpiGetTable(ACPI_SIG_IVRS, 1, (ACPI_TABLE_HEADER **)&ivrs);
287 	if (ACPI_FAILURE(status))
288 		return;
289 
290 	if (ivrs->Header.Length == 0) {
291 		return;
292 	}
293 
294 	info = ivrs->Info;
295 	printf("AMD-Vi IVRS VAsize = %d PAsize = %d GVAsize = %d flags:%b\n",
296 		REG_BITS(info, 21, 15), REG_BITS(info, 14, 8),
297 		REG_BITS(info, 7, 5), REG_BITS(info, 22, 22),
298 		"\020\001HtAtsResv");
299 
300 	ivrs_hdr_iterate_tbl(ivhd_count_iter, NULL);
301 	if (!ivhd_count)
302 		return;
303 
304 	ivhd_devs = malloc(sizeof(device_t) * ivhd_count, M_DEVBUF,
305 		M_WAITOK | M_ZERO);
306 	for (i = 0; i < ivhd_count; i++) {
307 		ivhd = ivhd_find_by_index(i);
308 		if (ivhd == NULL) {
309 			printf("Can't find IVHD entry%d\n", i);
310 			continue;
311 		}
312 
313 		ivhd_devs[i] = BUS_ADD_CHILD(parent, 1, "ivhd", i);
314 		/*
315 		 * XXX: In case device was not destroyed before, add will fail.
316 		 * locate the old device instance.
317 		 */
318 		if (ivhd_devs[i] == NULL) {
319 			ivhd_devs[i] = device_find_child(parent, "ivhd", i);
320 			if (ivhd_devs[i] == NULL) {
321 				printf("AMD-Vi: cant find AMD-Vi dev%d\n", i);
322 				break;
323 			}
324 		}
325 		count++;
326 	}
327 
328 	/*
329 	 * Update device count in case failed to attach.
330 	 */
331 	ivhd_count = count;
332 }
333 
334 static int
335 ivhd_probe(device_t dev)
336 {
337 
338 	if (acpi_get_handle(dev) != NULL)
339 		return (ENXIO);
340 	device_set_desc(dev, "AMD-Vi/IOMMU or ivhd");
341 
342 	return (BUS_PROBE_NOWILDCARD);
343 }
344 
345 static int
346 ivhd_print_cap(struct amdvi_softc *softc, ACPI_IVRS_HARDWARE * ivhd)
347 {
348 	device_t dev;
349 	int max_ptp_level;
350 
351 	dev = softc->dev;
352 	device_printf(dev, "Flag:%b\n", softc->ivhd_flag,
353 	    "\020\001HtTunEn\002PassPW\003ResPassPW\004Isoc\005IotlbSup"
354 	    "\006Coherent\007PreFSup\008PPRSup");
355 	/*
356 	 * If no extended feature[EFR], its rev1 with maximum paging level as 7.
357 	 */
358 	max_ptp_level = 7;
359 	if (softc->ivhd_efr) {
360 		device_printf(dev, "EFR HATS = %d GATS = %d GLXSup = %d "
361 		    "MsiNumPr = %d PNBanks= %d PNCounters= %d\n"
362 		    "max PASID = %d EFR: %b \n",
363 		    REG_BITS(softc->ivhd_efr, 31, 30),
364 		    REG_BITS(softc->ivhd_efr, 29, 28),
365 		    REG_BITS(softc->ivhd_efr, 4, 3),
366 		    REG_BITS(softc->ivhd_efr, 27, 23),
367 		    REG_BITS(softc->ivhd_efr, 22, 17),
368 		    REG_BITS(softc->ivhd_efr, 16, 13),
369 		    REG_BITS(softc->ivhd_efr, 12, 8),
370 		    softc->ivhd_efr, "\020\001XTSup\002NXSup\003GTSup\005IASup"
371 		    "\006GASup\007HESup\008PPRSup");
372 
373 		max_ptp_level = REG_BITS(softc->ivhd_efr, 31, 30) + 4;
374 	}
375 
376 	/* Make sure device support minimum page level as requested by user. */
377 	if (max_ptp_level < amdvi_ptp_level) {
378 		device_printf(dev, "Insufficient PTP level:%d\n",
379 		    max_ptp_level);
380 		return (EINVAL);
381 	}
382 
383 	device_printf(softc->dev, "max supported paging level:%d restricting to: %d\n",
384 	    max_ptp_level, amdvi_ptp_level);
385 	device_printf(softc->dev, "device supported range "
386 	    "[0x%x - 0x%x]\n", softc->start_dev_rid, softc->end_dev_rid);
387 
388 	return (0);
389 }
390 
391 static int
392 ivhd_attach(device_t dev)
393 {
394 	ACPI_IVRS_HARDWARE *ivhd;
395 	struct amdvi_softc *softc;
396 	int status, unit;
397 
398 	unit = device_get_unit(dev);
399 	/* Make sure its same device for which attach is called. */
400 	if (ivhd_devs[unit] != dev)
401 		panic("Not same device old %p new %p", ivhd_devs[unit], dev);
402 
403 	softc = device_get_softc(dev);
404 	softc->dev = dev;
405 	ivhd = ivhd_find_by_index(unit);
406 	if (ivhd == NULL)
407 		return (EINVAL);
408 
409 	softc->pci_seg = ivhd->PciSegmentGroup;
410 	softc->pci_rid = ivhd->Header.DeviceId;
411 	softc->ivhd_flag = ivhd->Header.Flags;
412 	softc->ivhd_efr = ivhd->Reserved;
413 	/*
414 	 * PCI capability has more capabilities that are not part of IVRS.
415 	 */
416 	softc->cap_off = ivhd->CapabilityOffset;
417 
418 #ifdef notyet
419 	/* IVHD Info bit[4:0] is event MSI/X number. */
420 	softc->event_msix = ivhd->Info & 0x1F;
421 #endif
422 	softc->ctrl = (struct amdvi_ctrl *) PHYS_TO_DMAP(ivhd->BaseAddress);
423 	status = ivhd_dev_parse(ivhd, softc);
424 	if (status != 0) {
425 		device_printf(dev,
426 		    "endpoint device parsing error=%d\n", status);
427 	}
428 
429 	status = ivhd_print_cap(softc, ivhd);
430 	if (status != 0) {
431 		return (status);
432 	}
433 
434 	status = amdvi_setup_hw(softc);
435 	if (status != 0) {
436 		device_printf(dev, "couldn't be initialised, error=%d\n",
437 		    status);
438 		return (status);
439 	}
440 
441 	return (0);
442 }
443 
444 static int
445 ivhd_detach(device_t dev)
446 {
447 	struct amdvi_softc *softc;
448 
449 	softc = device_get_softc(dev);
450 
451 	amdvi_teardown_hw(softc);
452 
453 	/*
454 	 * XXX: delete the device.
455 	 * don't allow detach, return EBUSY.
456 	 */
457 	return (0);
458 }
459 
460 static int
461 ivhd_suspend(device_t dev)
462 {
463 
464 	return (0);
465 }
466 
467 static int
468 ivhd_resume(device_t dev)
469 {
470 
471 	return (0);
472 }
473 
474 static device_method_t ivhd_methods[] = {
475 	DEVMETHOD(device_identify, ivhd_identify),
476 	DEVMETHOD(device_probe, ivhd_probe),
477 	DEVMETHOD(device_attach, ivhd_attach),
478 	DEVMETHOD(device_detach, ivhd_detach),
479 	DEVMETHOD(device_suspend, ivhd_suspend),
480 	DEVMETHOD(device_resume, ivhd_resume),
481 	DEVMETHOD_END
482 };
483 
484 static driver_t ivhd_driver = {
485 	"ivhd",
486 	ivhd_methods,
487 	sizeof(struct amdvi_softc),
488 };
489 
490 static devclass_t ivhd_devclass;
491 
492 /*
493  * Load this module at the end after PCI re-probing to configure interrupt.
494  */
495 DRIVER_MODULE_ORDERED(ivhd, acpi, ivhd_driver, ivhd_devclass, 0, 0,
496 		      SI_ORDER_ANY);
497 MODULE_DEPEND(ivhd, acpi, 1, 1, 1);
498 MODULE_DEPEND(ivhd, pci, 1, 1, 1);
499