xref: /freebsd/sys/amd64/vmm/amd/ivrs_drv.c (revision a2aef24aa3c8458e4036735dd6928b4ef77294e5)
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, *end;
190 	int range_start_id = 0, range_end_id = 0;
191 	uint32_t *extended;
192 	uint8_t all_data = 0, range_data = 0;
193 	bool range_enable_ats = false, enable_ats;
194 
195 	softc->start_dev_rid = ~0;
196 	softc->end_dev_rid = 0;
197 
198 	de = (ACPI_IVRS_DE_HEADER *) ((uint8_t *)ivhd +
199 	    sizeof(ACPI_IVRS_HARDWARE));
200 	end = (ACPI_IVRS_DE_HEADER *) ((uint8_t *)ivhd +
201 	    ivhd->Header.Length);
202 
203 	while (de < (ACPI_IVRS_DE_HEADER *) end) {
204 		softc->start_dev_rid = MIN(softc->start_dev_rid, de->Id);
205 		softc->end_dev_rid = MAX(softc->end_dev_rid, de->Id);
206 		switch (de->Type) {
207 		case ACPI_IVRS_TYPE_ALL:
208 			all_data = de->DataSetting;
209 			break;
210 
211 		case ACPI_IVRS_TYPE_SELECT:
212 		case ACPI_IVRS_TYPE_ALIAS_SELECT:
213 		case ACPI_IVRS_TYPE_EXT_SELECT:
214 			enable_ats = false;
215 			if (de->Type == ACPI_IVRS_TYPE_EXT_SELECT) {
216 				extended = (uint32_t *)(de + 1);
217 				enable_ats =
218 				    (*extended & IVHD_DEV_EXT_ATS_DISABLE) ?
219 					false : true;
220 			}
221 			ivhd_dev_add_entry(softc, de->Id, de->Id,
222 			    de->DataSetting | all_data, enable_ats);
223 			break;
224 
225 		case ACPI_IVRS_TYPE_START:
226 		case ACPI_IVRS_TYPE_ALIAS_START:
227 		case ACPI_IVRS_TYPE_EXT_START:
228 			range_start_id = de->Id;
229 			range_data = de->DataSetting;
230 			if (de->Type == ACPI_IVRS_TYPE_EXT_START) {
231 				extended = (uint32_t *)(de + 1);
232 				range_enable_ats =
233 				    (*extended & IVHD_DEV_EXT_ATS_DISABLE) ?
234 					false : true;
235 			}
236 			break;
237 
238 		case ACPI_IVRS_TYPE_END:
239 			range_end_id = de->Id;
240 			ivhd_dev_add_entry(softc, range_start_id, range_end_id,
241 				range_data | all_data, range_enable_ats);
242 			range_start_id = range_end_id = 0;
243 			range_data = 0;
244 			all_data = 0;
245 			break;
246 
247 		case ACPI_IVRS_TYPE_PAD4:
248 			break;
249 
250 		case ACPI_IVRS_TYPE_SPECIAL:
251 			/* HPET or IOAPIC */
252 			break;
253 		default:
254 			if ((de->Type < 5) ||
255 			    (de->Type >= ACPI_IVRS_TYPE_PAD8))
256 				device_printf(softc->dev,
257 				    "Unknown dev entry:0x%x\n", de->Type);
258 		}
259 
260 		if (softc->dev_cfg_cnt >
261 			(sizeof(softc->dev_cfg) / sizeof(softc->dev_cfg[0]))) {
262 			device_printf(softc->dev,
263 			    "WARN Too many device entries.\n");
264 			return (EINVAL);
265 		}
266 		de++;
267 	}
268 
269 	KASSERT((softc->end_dev_rid >= softc->start_dev_rid),
270 	    ("Device end[0x%x] < start[0x%x.\n",
271 	    softc->end_dev_rid, softc->start_dev_rid));
272 
273 	return (0);
274 }
275 
276 static void
277 ivhd_identify(driver_t *driver, device_t parent)
278 {
279 	ACPI_TABLE_IVRS *ivrs;
280 	ACPI_IVRS_HARDWARE *ivhd;
281 	ACPI_STATUS status;
282 	uint32_t info;
283 	int i, count = 0;
284 
285 	if (acpi_disabled("ivhd"))
286 		return;
287 
288 	status = AcpiGetTable(ACPI_SIG_IVRS, 1, (ACPI_TABLE_HEADER **)&ivrs);
289 	if (ACPI_FAILURE(status))
290 		return;
291 
292 	if (ivrs->Header.Length == 0) {
293 		return;
294 	}
295 
296 	info = ivrs->Info;
297 	printf("AMD-Vi IVRS VAsize = %d PAsize = %d GVAsize = %d flags:%b\n",
298 		REG_BITS(info, 21, 15), REG_BITS(info, 14, 8),
299 		REG_BITS(info, 7, 5), REG_BITS(info, 22, 22),
300 		"\020\001HtAtsResv");
301 
302 	ivrs_hdr_iterate_tbl(ivhd_count_iter, NULL);
303 	if (!ivhd_count)
304 		return;
305 
306 	ivhd_devs = malloc(sizeof(device_t) * ivhd_count, M_DEVBUF,
307 		M_WAITOK | M_ZERO);
308 	for (i = 0; i < ivhd_count; i++) {
309 		ivhd = ivhd_find_by_index(i);
310 		if (ivhd == NULL) {
311 			printf("Can't find IVHD entry%d\n", i);
312 			continue;
313 		}
314 
315 		ivhd_devs[i] = BUS_ADD_CHILD(parent, 1, "ivhd", i);
316 		/*
317 		 * XXX: In case device was not destroyed before, add will fail.
318 		 * locate the old device instance.
319 		 */
320 		if (ivhd_devs[i] == NULL) {
321 			ivhd_devs[i] = device_find_child(parent, "ivhd", i);
322 			if (ivhd_devs[i] == NULL) {
323 				printf("AMD-Vi: cant find AMD-Vi dev%d\n", i);
324 				break;
325 			}
326 		}
327 		count++;
328 	}
329 
330 	/*
331 	 * Update device count in case failed to attach.
332 	 */
333 	ivhd_count = count;
334 }
335 
336 static int
337 ivhd_probe(device_t dev)
338 {
339 
340 	if (acpi_get_handle(dev) != NULL)
341 		return (ENXIO);
342 	device_set_desc(dev, "AMD-Vi/IOMMU or ivhd");
343 
344 	return (BUS_PROBE_NOWILDCARD);
345 }
346 
347 static int
348 ivhd_print_cap(struct amdvi_softc *softc, ACPI_IVRS_HARDWARE * ivhd)
349 {
350 	device_t dev;
351 	int max_ptp_level;
352 
353 	dev = softc->dev;
354 	device_printf(dev, "Flag:%b\n", softc->ivhd_flag,
355 	    "\020\001HtTunEn\002PassPW\003ResPassPW\004Isoc\005IotlbSup"
356 	    "\006Coherent\007PreFSup\008PPRSup");
357 	/*
358 	 * If no extended feature[EFR], its rev1 with maximum paging level as 7.
359 	 */
360 	max_ptp_level = 7;
361 	if (softc->ivhd_efr) {
362 		device_printf(dev, "EFR HATS = %d GATS = %d GLXSup = %d "
363 		    "MsiNumPr = %d PNBanks= %d PNCounters= %d\n"
364 		    "max PASID = %d EFR: %b \n",
365 		    REG_BITS(softc->ivhd_efr, 31, 30),
366 		    REG_BITS(softc->ivhd_efr, 29, 28),
367 		    REG_BITS(softc->ivhd_efr, 4, 3),
368 		    REG_BITS(softc->ivhd_efr, 27, 23),
369 		    REG_BITS(softc->ivhd_efr, 22, 17),
370 		    REG_BITS(softc->ivhd_efr, 16, 13),
371 		    REG_BITS(softc->ivhd_efr, 12, 8),
372 		    softc->ivhd_efr, "\020\001XTSup\002NXSup\003GTSup\005IASup"
373 		    "\006GASup\007HESup\008PPRSup");
374 
375 		max_ptp_level = REG_BITS(softc->ivhd_efr, 31, 30) + 4;
376 	}
377 
378 	/* Make sure device support minimum page level as requested by user. */
379 	if (max_ptp_level < amdvi_ptp_level) {
380 		device_printf(dev, "Insufficient PTP level:%d\n",
381 		    max_ptp_level);
382 		return (EINVAL);
383 	}
384 
385 	device_printf(softc->dev, "max supported paging level:%d restricting to: %d\n",
386 	    max_ptp_level, amdvi_ptp_level);
387 	device_printf(softc->dev, "device supported range "
388 	    "[0x%x - 0x%x]\n", softc->start_dev_rid, softc->end_dev_rid);
389 
390 	return (0);
391 }
392 
393 static int
394 ivhd_attach(device_t dev)
395 {
396 	ACPI_IVRS_HARDWARE *ivhd;
397 	struct amdvi_softc *softc;
398 	int status, unit;
399 
400 	unit = device_get_unit(dev);
401 	/* Make sure its same device for which attach is called. */
402 	if (ivhd_devs[unit] != dev)
403 		panic("Not same device old %p new %p", ivhd_devs[unit], dev);
404 
405 	softc = device_get_softc(dev);
406 	softc->dev = dev;
407 	ivhd = ivhd_find_by_index(unit);
408 	if (ivhd == NULL)
409 		return (EINVAL);
410 
411 	softc->pci_seg = ivhd->PciSegmentGroup;
412 	softc->pci_rid = ivhd->Header.DeviceId;
413 	softc->ivhd_flag = ivhd->Header.Flags;
414 	softc->ivhd_efr = ivhd->Reserved;
415 	/*
416 	 * PCI capability has more capabilities that are not part of IVRS.
417 	 */
418 	softc->cap_off = ivhd->CapabilityOffset;
419 
420 #ifdef notyet
421 	/* IVHD Info bit[4:0] is event MSI/X number. */
422 	softc->event_msix = ivhd->Info & 0x1F;
423 #endif
424 	softc->ctrl = (struct amdvi_ctrl *) PHYS_TO_DMAP(ivhd->BaseAddress);
425 	status = ivhd_dev_parse(ivhd, softc);
426 	if (status != 0) {
427 		device_printf(dev,
428 		    "endpoint device parsing error=%d\n", status);
429 	}
430 
431 	status = ivhd_print_cap(softc, ivhd);
432 	if (status != 0) {
433 		return (status);
434 	}
435 
436 	status = amdvi_setup_hw(softc);
437 	if (status != 0) {
438 		device_printf(dev, "couldn't be initialised, error=%d\n",
439 		    status);
440 		return (status);
441 	}
442 
443 	return (0);
444 }
445 
446 static int
447 ivhd_detach(device_t dev)
448 {
449 	struct amdvi_softc *softc;
450 
451 	softc = device_get_softc(dev);
452 
453 	amdvi_teardown_hw(softc);
454 
455 	/*
456 	 * XXX: delete the device.
457 	 * don't allow detach, return EBUSY.
458 	 */
459 	return (0);
460 }
461 
462 static int
463 ivhd_suspend(device_t dev)
464 {
465 
466 	return (0);
467 }
468 
469 static int
470 ivhd_resume(device_t dev)
471 {
472 
473 	return (0);
474 }
475 
476 static device_method_t ivhd_methods[] = {
477 	DEVMETHOD(device_identify, ivhd_identify),
478 	DEVMETHOD(device_probe, ivhd_probe),
479 	DEVMETHOD(device_attach, ivhd_attach),
480 	DEVMETHOD(device_detach, ivhd_detach),
481 	DEVMETHOD(device_suspend, ivhd_suspend),
482 	DEVMETHOD(device_resume, ivhd_resume),
483 	DEVMETHOD_END
484 };
485 
486 static driver_t ivhd_driver = {
487 	"ivhd",
488 	ivhd_methods,
489 	sizeof(struct amdvi_softc),
490 };
491 
492 static devclass_t ivhd_devclass;
493 
494 /*
495  * Load this module at the end after PCI re-probing to configure interrupt.
496  */
497 DRIVER_MODULE_ORDERED(ivhd, acpi, ivhd_driver, ivhd_devclass, 0, 0,
498 		      SI_ORDER_ANY);
499 MODULE_DEPEND(ivhd, acpi, 1, 1, 1);
500 MODULE_DEPEND(ivhd, pci, 1, 1, 1);
501