xref: /freebsd/sys/amd64/vmm/amd/ivrs_drv.c (revision d06955f9bdb1416d9196043ed781f9b36dae9adc)
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 		if ((uint8_t *)ivrs_hdr + ivrs_hdr->Length > (uint8_t *)end) {
79 			printf("AMD-Vi:IVHD/IVMD is corrupted, length : %d\n",
80 			    ivrs_hdr->Length);
81 			break;
82 		}
83 
84 		switch (ivrs_hdr->Type) {
85 		case ACPI_IVRS_TYPE_HARDWARE:	/* Legacy */
86 		case 0x11:
87 		case 0x40: 			/* ACPI HID */
88 			if (!iter(ivrs_hdr, arg))
89 				return;
90 			break;
91 
92 		case ACPI_IVRS_TYPE_MEMORY1:
93 		case ACPI_IVRS_TYPE_MEMORY2:
94 		case ACPI_IVRS_TYPE_MEMORY3:
95 			if (!iter(ivrs_hdr, arg))
96 				return;
97 
98 			break;
99 
100 		default:
101 			printf("AMD-Vi:Not IVHD/IVMD type(%d)", ivrs_hdr->Type);
102 
103 		}
104 
105 		ivrs_hdr = (ACPI_IVRS_HEADER *)((uint8_t *)ivrs_hdr +
106 			ivrs_hdr->Length);
107 	}
108 }
109 
110 static  int
111 ivrs_is_ivhd(UINT8 type)
112 {
113 
114 	if ((type == ACPI_IVRS_TYPE_HARDWARE) || (type == 0x11)	|| (type == 0x40))
115 		return (1);
116 
117 	return (0);
118 }
119 
120 /* Count the number of AMD-Vi devices in the system. */
121 static int
122 ivhd_count_iter(ACPI_IVRS_HEADER * ivrs_he, void *arg)
123 {
124 
125 	if (ivrs_is_ivhd(ivrs_he->Type))
126 		ivhd_count++;
127 
128 	return (1);
129 }
130 
131 struct find_ivrs_hdr_args {
132 	int	i;
133 	ACPI_IVRS_HEADER *ptr;
134 };
135 
136 static int
137 ivrs_hdr_find_iter(ACPI_IVRS_HEADER * ivrs_hdr, void *args)
138 {
139 	struct find_ivrs_hdr_args *fi;
140 
141 	fi = (struct find_ivrs_hdr_args *)args;
142 	if (ivrs_is_ivhd(ivrs_hdr->Type)) {
143 		if (fi->i == 0) {
144 			fi->ptr = ivrs_hdr;
145 			return (0);
146 		}
147 		fi->i--;
148 	}
149 
150 	return (1);
151 }
152 
153 static ACPI_IVRS_HARDWARE *
154 ivhd_find_by_index(int idx)
155 {
156 	struct find_ivrs_hdr_args fi;
157 
158 	fi.i = idx;
159 	fi.ptr = NULL;
160 
161 	ivrs_hdr_iterate_tbl(ivrs_hdr_find_iter, &fi);
162 
163 	return ((ACPI_IVRS_HARDWARE *)fi.ptr);
164 }
165 
166 static void
167 ivhd_dev_add_entry(struct amdvi_softc *softc, uint32_t start_id,
168     uint32_t end_id, uint8_t cfg, bool ats)
169 {
170 	struct ivhd_dev_cfg *dev_cfg;
171 
172 	/* If device doesn't have special data, don't add it. */
173 	if (!cfg)
174 		return;
175 
176 	dev_cfg = &softc->dev_cfg[softc->dev_cfg_cnt++];
177 	dev_cfg->start_id = start_id;
178 	dev_cfg->end_id = end_id;
179 	dev_cfg->data = cfg;
180 	dev_cfg->enable_ats = ats;
181 }
182 
183 /*
184  * Record device attributes as suggested by BIOS.
185  */
186 static int
187 ivhd_dev_parse(ACPI_IVRS_HARDWARE * ivhd, struct amdvi_softc *softc)
188 {
189 	ACPI_IVRS_DE_HEADER *de;
190 	uint8_t *p, *end;
191 	int range_start_id = 0, range_end_id = 0;
192 	uint32_t *extended;
193 	uint8_t all_data = 0, range_data = 0;
194 	bool range_enable_ats = false, enable_ats;
195 
196 	softc->start_dev_rid = ~0;
197 	softc->end_dev_rid = 0;
198 
199 	/*
200 	 * XXX The following actually depends on Header.Type and
201 	 * is only true for 0x10.
202 	 */
203 	p = (uint8_t *)ivhd + sizeof(ACPI_IVRS_HARDWARE);
204 	end = (uint8_t *)ivhd + ivhd->Header.Length;
205 
206 	while (p < end) {
207 		de = (ACPI_IVRS_DE_HEADER *)p;
208 		softc->start_dev_rid = MIN(softc->start_dev_rid, de->Id);
209 		softc->end_dev_rid = MAX(softc->end_dev_rid, de->Id);
210 		switch (de->Type) {
211 		case ACPI_IVRS_TYPE_ALL:
212 			all_data = de->DataSetting;
213 			break;
214 
215 		case ACPI_IVRS_TYPE_SELECT:
216 		case ACPI_IVRS_TYPE_ALIAS_SELECT:
217 		case ACPI_IVRS_TYPE_EXT_SELECT:
218 			enable_ats = false;
219 			if (de->Type == ACPI_IVRS_TYPE_EXT_SELECT) {
220 				extended = (uint32_t *)(de + 1);
221 				enable_ats =
222 				    (*extended & IVHD_DEV_EXT_ATS_DISABLE) ?
223 					false : true;
224 			}
225 			ivhd_dev_add_entry(softc, de->Id, de->Id,
226 			    de->DataSetting | all_data, enable_ats);
227 			break;
228 
229 		case ACPI_IVRS_TYPE_START:
230 		case ACPI_IVRS_TYPE_ALIAS_START:
231 		case ACPI_IVRS_TYPE_EXT_START:
232 			range_start_id = de->Id;
233 			range_data = de->DataSetting;
234 			if (de->Type == ACPI_IVRS_TYPE_EXT_START) {
235 				extended = (uint32_t *)(de + 1);
236 				range_enable_ats =
237 				    (*extended & IVHD_DEV_EXT_ATS_DISABLE) ?
238 					false : true;
239 			}
240 			break;
241 
242 		case ACPI_IVRS_TYPE_END:
243 			range_end_id = de->Id;
244 			ivhd_dev_add_entry(softc, range_start_id, range_end_id,
245 				range_data | all_data, range_enable_ats);
246 			range_start_id = range_end_id = 0;
247 			range_data = 0;
248 			all_data = 0;
249 			break;
250 
251 		case ACPI_IVRS_TYPE_PAD4:
252 			break;
253 
254 		case ACPI_IVRS_TYPE_SPECIAL:
255 			/* HPET or IOAPIC */
256 			break;
257 		default:
258 			if ((de->Type < 5) ||
259 			    (de->Type >= ACPI_IVRS_TYPE_PAD8))
260 				device_printf(softc->dev,
261 				    "Unknown dev entry:0x%x\n", de->Type);
262 		}
263 
264 		if (softc->dev_cfg_cnt >
265 			(sizeof(softc->dev_cfg) / sizeof(softc->dev_cfg[0]))) {
266 			device_printf(softc->dev,
267 			    "WARN Too many device entries.\n");
268 			return (EINVAL);
269 		}
270 		if (de->Type < 0x40)
271 			p += sizeof(ACPI_IVRS_DEVICE4);
272 		else if (de->Type < 0x80)
273 			p += sizeof(ACPI_IVRS_DEVICE8A);
274 		else {
275 			printf("Variable size IVHD type 0x%x not supported\n",
276 			    de->Type);
277 			break;
278 		}
279 	}
280 
281 	KASSERT((softc->end_dev_rid >= softc->start_dev_rid),
282 	    ("Device end[0x%x] < start[0x%x.\n",
283 	    softc->end_dev_rid, softc->start_dev_rid));
284 
285 	return (0);
286 }
287 
288 static void
289 ivhd_identify(driver_t *driver, device_t parent)
290 {
291 	ACPI_TABLE_IVRS *ivrs;
292 	ACPI_IVRS_HARDWARE *ivhd;
293 	ACPI_STATUS status;
294 	uint32_t info;
295 	int i, count = 0;
296 
297 	if (acpi_disabled("ivhd"))
298 		return;
299 
300 	status = AcpiGetTable(ACPI_SIG_IVRS, 1, (ACPI_TABLE_HEADER **)&ivrs);
301 	if (ACPI_FAILURE(status))
302 		return;
303 
304 	if (ivrs->Header.Length == 0) {
305 		return;
306 	}
307 
308 	info = ivrs->Info;
309 	printf("AMD-Vi IVRS VAsize = %d PAsize = %d GVAsize = %d flags:%b\n",
310 		REG_BITS(info, 21, 15), REG_BITS(info, 14, 8),
311 		REG_BITS(info, 7, 5), REG_BITS(info, 22, 22),
312 		"\020\001HtAtsResv");
313 
314 	ivrs_hdr_iterate_tbl(ivhd_count_iter, NULL);
315 	if (!ivhd_count)
316 		return;
317 
318 	ivhd_devs = malloc(sizeof(device_t) * ivhd_count, M_DEVBUF,
319 		M_WAITOK | M_ZERO);
320 	for (i = 0; i < ivhd_count; i++) {
321 		ivhd = ivhd_find_by_index(i);
322 		if (ivhd == NULL) {
323 			printf("Can't find IVHD entry%d\n", i);
324 			continue;
325 		}
326 
327 		/*
328 		 * Use a high order to ensure that this driver is probed after
329 		 * the Host-PCI bridge and the root PCI bus.
330 		 */
331 		ivhd_devs[i] = BUS_ADD_CHILD(parent,
332 		    ACPI_DEV_BASE_ORDER + 10 * 10, "ivhd", i);
333 
334 		/*
335 		 * XXX: In case device was not destroyed before, add will fail.
336 		 * locate the old device instance.
337 		 */
338 		if (ivhd_devs[i] == NULL) {
339 			ivhd_devs[i] = device_find_child(parent, "ivhd", i);
340 			if (ivhd_devs[i] == NULL) {
341 				printf("AMD-Vi: cant find AMD-Vi dev%d\n", i);
342 				break;
343 			}
344 		}
345 		count++;
346 	}
347 
348 	/*
349 	 * Update device count in case failed to attach.
350 	 */
351 	ivhd_count = count;
352 }
353 
354 static int
355 ivhd_probe(device_t dev)
356 {
357 
358 	if (acpi_get_handle(dev) != NULL)
359 		return (ENXIO);
360 	device_set_desc(dev, "AMD-Vi/IOMMU or ivhd");
361 
362 	return (BUS_PROBE_NOWILDCARD);
363 }
364 
365 static int
366 ivhd_print_cap(struct amdvi_softc *softc, ACPI_IVRS_HARDWARE * ivhd)
367 {
368 	device_t dev;
369 	int max_ptp_level;
370 
371 	dev = softc->dev;
372 	device_printf(dev, "Flag:%b\n", softc->ivhd_flag,
373 	    "\020\001HtTunEn\002PassPW\003ResPassPW\004Isoc\005IotlbSup"
374 	    "\006Coherent\007PreFSup\008PPRSup");
375 	/*
376 	 * If no extended feature[EFR], its rev1 with maximum paging level as 7.
377 	 */
378 	max_ptp_level = 7;
379 	if (softc->ivhd_efr) {
380 		device_printf(dev, "EFR HATS = %d GATS = %d GLXSup = %d "
381 		    "MsiNumPr = %d PNBanks= %d PNCounters= %d\n"
382 		    "max PASID = %d EFR: %b \n",
383 		    REG_BITS(softc->ivhd_efr, 31, 30),
384 		    REG_BITS(softc->ivhd_efr, 29, 28),
385 		    REG_BITS(softc->ivhd_efr, 4, 3),
386 		    REG_BITS(softc->ivhd_efr, 27, 23),
387 		    REG_BITS(softc->ivhd_efr, 22, 17),
388 		    REG_BITS(softc->ivhd_efr, 16, 13),
389 		    REG_BITS(softc->ivhd_efr, 12, 8),
390 		    softc->ivhd_efr, "\020\001XTSup\002NXSup\003GTSup\005IASup"
391 		    "\006GASup\007HESup\008PPRSup");
392 
393 		max_ptp_level = REG_BITS(softc->ivhd_efr, 31, 30) + 4;
394 	}
395 
396 	/* Make sure device support minimum page level as requested by user. */
397 	if (max_ptp_level < amdvi_ptp_level) {
398 		device_printf(dev, "Insufficient PTP level:%d\n",
399 		    max_ptp_level);
400 		return (EINVAL);
401 	}
402 
403 	device_printf(softc->dev, "max supported paging level:%d restricting to: %d\n",
404 	    max_ptp_level, amdvi_ptp_level);
405 	device_printf(softc->dev, "device supported range "
406 	    "[0x%x - 0x%x]\n", softc->start_dev_rid, softc->end_dev_rid);
407 
408 	return (0);
409 }
410 
411 static int
412 ivhd_attach(device_t dev)
413 {
414 	ACPI_IVRS_HARDWARE *ivhd;
415 	struct amdvi_softc *softc;
416 	int status, unit;
417 
418 	unit = device_get_unit(dev);
419 	/* Make sure its same device for which attach is called. */
420 	if (ivhd_devs[unit] != dev)
421 		panic("Not same device old %p new %p", ivhd_devs[unit], dev);
422 
423 	softc = device_get_softc(dev);
424 	softc->dev = dev;
425 	ivhd = ivhd_find_by_index(unit);
426 	if (ivhd == NULL)
427 		return (EINVAL);
428 
429 	softc->pci_seg = ivhd->PciSegmentGroup;
430 	softc->pci_rid = ivhd->Header.DeviceId;
431 	softc->ivhd_flag = ivhd->Header.Flags;
432 	softc->ivhd_efr = ivhd->Reserved;
433 	/*
434 	 * PCI capability has more capabilities that are not part of IVRS.
435 	 */
436 	softc->cap_off = ivhd->CapabilityOffset;
437 
438 #ifdef notyet
439 	/* IVHD Info bit[4:0] is event MSI/X number. */
440 	softc->event_msix = ivhd->Info & 0x1F;
441 #endif
442 	softc->ctrl = (struct amdvi_ctrl *) PHYS_TO_DMAP(ivhd->BaseAddress);
443 	status = ivhd_dev_parse(ivhd, softc);
444 	if (status != 0) {
445 		device_printf(dev,
446 		    "endpoint device parsing error=%d\n", status);
447 	}
448 
449 	status = ivhd_print_cap(softc, ivhd);
450 	if (status != 0) {
451 		return (status);
452 	}
453 
454 	status = amdvi_setup_hw(softc);
455 	if (status != 0) {
456 		device_printf(dev, "couldn't be initialised, error=%d\n",
457 		    status);
458 		return (status);
459 	}
460 
461 	return (0);
462 }
463 
464 static int
465 ivhd_detach(device_t dev)
466 {
467 	struct amdvi_softc *softc;
468 
469 	softc = device_get_softc(dev);
470 
471 	amdvi_teardown_hw(softc);
472 
473 	/*
474 	 * XXX: delete the device.
475 	 * don't allow detach, return EBUSY.
476 	 */
477 	return (0);
478 }
479 
480 static int
481 ivhd_suspend(device_t dev)
482 {
483 
484 	return (0);
485 }
486 
487 static int
488 ivhd_resume(device_t dev)
489 {
490 
491 	return (0);
492 }
493 
494 static device_method_t ivhd_methods[] = {
495 	DEVMETHOD(device_identify, ivhd_identify),
496 	DEVMETHOD(device_probe, ivhd_probe),
497 	DEVMETHOD(device_attach, ivhd_attach),
498 	DEVMETHOD(device_detach, ivhd_detach),
499 	DEVMETHOD(device_suspend, ivhd_suspend),
500 	DEVMETHOD(device_resume, ivhd_resume),
501 	DEVMETHOD_END
502 };
503 
504 static driver_t ivhd_driver = {
505 	"ivhd",
506 	ivhd_methods,
507 	sizeof(struct amdvi_softc),
508 };
509 
510 static devclass_t ivhd_devclass;
511 
512 /*
513  * Load this module at the end after PCI re-probing to configure interrupt.
514  */
515 DRIVER_MODULE_ORDERED(ivhd, acpi, ivhd_driver, ivhd_devclass, 0, 0,
516 		      SI_ORDER_ANY);
517 MODULE_DEPEND(ivhd, acpi, 1, 1, 1);
518 MODULE_DEPEND(ivhd, pci, 1, 1, 1);
519