xref: /freebsd/sys/amd64/vmm/amd/ivrs_drv.c (revision 7937bfbc0ca53fe7cdd0d54414f9296e273a518e)
1 /*-
2  * SPDX-License-Identifier: BSD-2-Clause
3  *
4  * Copyright (c) 2016, Anish Gupta (anish@freebsd.org)
5  * Copyright (c) 2021 The FreeBSD Foundation
6  * All rights reserved.
7  *
8  * Redistribution and use in source and binary forms, with or without
9  * modification, are permitted provided that the following conditions
10  * are met:
11  * 1. Redistributions of source code must retain the above copyright
12  *    notice unmodified, this list of conditions, and the following
13  *    disclaimer.
14  * 2. Redistributions in binary form must reproduce the above copyright
15  *    notice, this list of conditions and the following disclaimer in the
16  *    documentation and/or other materials provided with the distribution.
17  *
18  * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
19  * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
20  * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
21  * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
22  * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
23  * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
24  * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
25  * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
26  * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
27  * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
28  */
29 
30 #include <sys/cdefs.h>
31 #include "opt_acpi.h"
32 #include <sys/param.h>
33 #include <sys/bus.h>
34 #include <sys/kernel.h>
35 #include <sys/module.h>
36 #include <sys/malloc.h>
37 
38 #include <machine/vmparam.h>
39 
40 #include <vm/vm.h>
41 #include <vm/pmap.h>
42 
43 #include <contrib/dev/acpica/include/acpi.h>
44 #include <contrib/dev/acpica/include/accommon.h>
45 #include <dev/acpica/acpivar.h>
46 #include <dev/pci/pcireg.h>
47 #include <dev/pci/pcivar.h>
48 
49 #include "io/iommu.h"
50 #include "amdvi_priv.h"
51 
52 device_t *ivhd_devs;			/* IVHD or AMD-Vi device list. */
53 int	ivhd_count;			/* Number of IVHD header. */
54 /*
55  * Cached IVHD header list.
56  * Single entry for each IVHD, filtered the legacy one.
57  */
58 ACPI_IVRS_HARDWARE1 **ivhd_hdrs;
59 
60 extern int amdvi_ptp_level;		/* Page table levels. */
61 
62 typedef int (*ivhd_iter_t)(ACPI_IVRS_HEADER *ptr, void *arg);
63 /*
64  * Iterate IVRS table for IVHD and IVMD device type.
65  */
66 static void
67 ivrs_hdr_iterate_tbl(ivhd_iter_t iter, void *arg)
68 {
69 	ACPI_TABLE_IVRS *ivrs;
70 	ACPI_IVRS_HEADER *ivrs_hdr, *end;
71 	ACPI_STATUS status;
72 
73 	status = AcpiGetTable(ACPI_SIG_IVRS, 1, (ACPI_TABLE_HEADER **)&ivrs);
74 	if (ACPI_FAILURE(status))
75 		return;
76 
77 	if (ivrs->Header.Length == 0) {
78 		return;
79 	}
80 
81 	ivrs_hdr = (ACPI_IVRS_HEADER *)(ivrs + 1);
82 	end = (ACPI_IVRS_HEADER *)((char *)ivrs + ivrs->Header.Length);
83 
84 	while (ivrs_hdr < end) {
85 		if ((uint8_t *)ivrs_hdr + ivrs_hdr->Length > (uint8_t *)end) {
86 			printf("AMD-Vi:IVHD/IVMD is corrupted, length : %d\n",
87 			    ivrs_hdr->Length);
88 			break;
89 		}
90 
91 		switch (ivrs_hdr->Type) {
92 		case IVRS_TYPE_HARDWARE_LEGACY:	/* Legacy */
93 		case IVRS_TYPE_HARDWARE_EFR:
94 		case IVRS_TYPE_HARDWARE_MIXED:
95 			if (!iter(ivrs_hdr, arg))
96 				return;
97 			break;
98 
99 		case ACPI_IVRS_TYPE_MEMORY1:
100 		case ACPI_IVRS_TYPE_MEMORY2:
101 		case ACPI_IVRS_TYPE_MEMORY3:
102 			if (!iter(ivrs_hdr, arg))
103 				return;
104 
105 			break;
106 
107 		default:
108 			printf("AMD-Vi:Not IVHD/IVMD type(%d)", ivrs_hdr->Type);
109 		}
110 
111 		ivrs_hdr = (ACPI_IVRS_HEADER *)((uint8_t *)ivrs_hdr +
112 			ivrs_hdr->Length);
113 	}
114 }
115 
116 static bool
117 ivrs_is_ivhd(UINT8 type)
118 {
119 
120 	switch(type) {
121 	case IVRS_TYPE_HARDWARE_LEGACY:
122 	case IVRS_TYPE_HARDWARE_EFR:
123 	case IVRS_TYPE_HARDWARE_MIXED:
124 		return (true);
125 
126 	default:
127 		return (false);
128 	}
129 }
130 
131 /* Count the number of AMD-Vi devices in the system. */
132 static int
133 ivhd_count_iter(ACPI_IVRS_HEADER * ivrs_he, void *arg)
134 {
135 	int *count;
136 
137 	count = (int *)arg;
138 	if (ivrs_is_ivhd(ivrs_he->Type))
139 		(*count)++;
140 
141 	return (1);
142 }
143 
144 struct find_ivrs_hdr_args {
145 	int	i;
146 	ACPI_IVRS_HEADER *ptr;
147 };
148 
149 static int
150 ivrs_hdr_find_iter(ACPI_IVRS_HEADER * ivrs_hdr, void *args)
151 {
152 	struct find_ivrs_hdr_args *fi;
153 
154 	fi = (struct find_ivrs_hdr_args *)args;
155 	if (ivrs_is_ivhd(ivrs_hdr->Type)) {
156 		if (fi->i == 0) {
157 			fi->ptr = ivrs_hdr;
158 			return (0);
159 		}
160 		fi->i--;
161 	}
162 
163 	return (1);
164 }
165 
166 static ACPI_IVRS_HARDWARE1 *
167 ivhd_find_by_index(int idx)
168 {
169 	struct find_ivrs_hdr_args fi;
170 
171 	fi.i = idx;
172 	fi.ptr = NULL;
173 
174 	ivrs_hdr_iterate_tbl(ivrs_hdr_find_iter, &fi);
175 
176 	return ((ACPI_IVRS_HARDWARE1 *)fi.ptr);
177 }
178 
179 static void
180 ivhd_dev_add_entry(struct amdvi_softc *softc, uint32_t start_id,
181     uint32_t end_id, uint8_t cfg, bool ats)
182 {
183 	struct ivhd_dev_cfg *dev_cfg;
184 
185 	KASSERT(softc->dev_cfg_cap >= softc->dev_cfg_cnt,
186 	    ("Impossible case: number of dev_cfg exceeding capacity"));
187 	if (softc->dev_cfg_cap == softc->dev_cfg_cnt) {
188 		if (softc->dev_cfg_cap == 0)
189 			softc->dev_cfg_cap = 1;
190 		else
191 			softc->dev_cfg_cap <<= 2;
192 		softc->dev_cfg = realloc(softc->dev_cfg,
193 		    sizeof(*softc->dev_cfg) * softc->dev_cfg_cap, M_DEVBUF,
194 		    M_WAITOK);
195 	}
196 
197 	dev_cfg = &softc->dev_cfg[softc->dev_cfg_cnt++];
198 	dev_cfg->start_id = start_id;
199 	dev_cfg->end_id = end_id;
200 	dev_cfg->data = cfg;
201 	dev_cfg->enable_ats = ats;
202 }
203 
204 /*
205  * Record device attributes as suggested by BIOS.
206  */
207 static int
208 ivhd_dev_parse(ACPI_IVRS_HARDWARE1 *ivhd, struct amdvi_softc *softc)
209 {
210 	ACPI_IVRS_DE_HEADER *de;
211 	uint8_t *p, *end;
212 	int range_start_id = -1, range_end_id = -1, i;
213 	uint32_t *extended;
214 	uint8_t all_data = 0, range_data = 0;
215 	bool range_enable_ats = false, enable_ats;
216 
217 	switch (ivhd->Header.Type) {
218 		case IVRS_TYPE_HARDWARE_LEGACY:
219 			p = (uint8_t *)ivhd + sizeof(ACPI_IVRS_HARDWARE1);
220 			break;
221 
222 		case IVRS_TYPE_HARDWARE_EFR:
223 		case IVRS_TYPE_HARDWARE_MIXED:
224 			p = (uint8_t *)ivhd + sizeof(ACPI_IVRS_HARDWARE2);
225 			break;
226 
227 		default:
228 			device_printf(softc->dev,
229 				"unknown type: 0x%x\n", ivhd->Header.Type);
230 			return (-1);
231 	}
232 
233 	end = (uint8_t *)ivhd + ivhd->Header.Length;
234 
235 	while (p < end) {
236 		de = (ACPI_IVRS_DE_HEADER *)p;
237 		switch (de->Type) {
238 		case ACPI_IVRS_TYPE_ALL:
239 			all_data = de->DataSetting;
240 			for (i = 0; i < softc->dev_cfg_cnt; i++)
241 				softc->dev_cfg[i].data |= all_data;
242 			break;
243 
244 		case ACPI_IVRS_TYPE_SELECT:
245 		case ACPI_IVRS_TYPE_ALIAS_SELECT:
246 		case ACPI_IVRS_TYPE_EXT_SELECT:
247 			enable_ats = false;
248 			if (de->Type == ACPI_IVRS_TYPE_EXT_SELECT) {
249 				extended = (uint32_t *)(de + 1);
250 				enable_ats =
251 				    (*extended & IVHD_DEV_EXT_ATS_DISABLE) ?
252 					false : true;
253 			}
254 			ivhd_dev_add_entry(softc, de->Id, de->Id,
255 			    de->DataSetting | all_data, enable_ats);
256 			break;
257 
258 		case ACPI_IVRS_TYPE_START:
259 		case ACPI_IVRS_TYPE_ALIAS_START:
260 		case ACPI_IVRS_TYPE_EXT_START:
261 			if (range_start_id != -1) {
262 				device_printf(softc->dev,
263 				    "Unexpected start-of-range device entry\n");
264 				return (EINVAL);
265 			}
266 			range_start_id = de->Id;
267 			range_data = de->DataSetting;
268 			if (de->Type == ACPI_IVRS_TYPE_EXT_START) {
269 				extended = (uint32_t *)(de + 1);
270 				range_enable_ats =
271 				    (*extended & IVHD_DEV_EXT_ATS_DISABLE) ?
272 					false : true;
273 			}
274 			break;
275 
276 		case ACPI_IVRS_TYPE_END:
277 			if (range_start_id == -1) {
278 				device_printf(softc->dev,
279 				    "Unexpected end-of-range device entry\n");
280 				return (EINVAL);
281 			}
282 			range_end_id = de->Id;
283 			if (range_end_id < range_start_id) {
284 				device_printf(softc->dev,
285 				    "Device entry range going backward\n");
286 				return (EINVAL);
287 			}
288 			ivhd_dev_add_entry(softc, range_start_id, range_end_id,
289 			    range_data | all_data, range_enable_ats);
290 			range_start_id = range_end_id = -1;
291 			range_data = 0;
292 			all_data = 0;
293 			break;
294 
295 		case ACPI_IVRS_TYPE_PAD4:
296 			break;
297 
298 		case ACPI_IVRS_TYPE_SPECIAL:
299 			/* HPET or IOAPIC */
300 			break;
301 		default:
302 			if ((de->Type < 5) ||
303 			    (de->Type >= ACPI_IVRS_TYPE_PAD8))
304 				device_printf(softc->dev,
305 				    "Unknown dev entry:0x%x\n", de->Type);
306 		}
307 
308 		if (de->Type < 0x40)
309 			p += sizeof(ACPI_IVRS_DEVICE4);
310 		else if (de->Type < 0x80)
311 			p += sizeof(ACPI_IVRS_DEVICE8A);
312 		else {
313 			printf("Variable size IVHD type 0x%x not supported\n",
314 			    de->Type);
315 			break;
316 		}
317 	}
318 
319 	return (0);
320 }
321 
322 static bool
323 ivhd_is_newer(ACPI_IVRS_HEADER *old, ACPI_IVRS_HEADER  *new)
324 {
325 	if (old->DeviceId == new->DeviceId) {
326 		/*
327 		 * Newer IVRS header type take precedence.
328 		 */
329 		if (old->Type == IVRS_TYPE_HARDWARE_LEGACY &&
330 		    ((new->Type == IVRS_TYPE_HARDWARE_EFR) ||
331 		    (new->Type == IVRS_TYPE_HARDWARE_MIXED)))
332 			return (true);
333 
334 		/*
335 		 * Mixed format IVHD header type take precedence
336 		 * over fixed format IVHD header types.
337 		 */
338 		if (old->Type == IVRS_TYPE_HARDWARE_EFR &&
339 		    new->Type == IVRS_TYPE_HARDWARE_MIXED)
340 			return (true);
341 	}
342 
343 	return (false);
344 }
345 
346 static void
347 ivhd_identify(driver_t *driver, device_t parent)
348 {
349 	ACPI_TABLE_IVRS *ivrs;
350 	ACPI_IVRS_HARDWARE1 *ivhd;
351 	ACPI_STATUS status;
352 	int i, j, count = 0;
353 	uint32_t ivrs_ivinfo;
354 
355 	if (acpi_disabled("ivhd"))
356 		return;
357 
358 	status = AcpiGetTable(ACPI_SIG_IVRS, 1, (ACPI_TABLE_HEADER **)&ivrs);
359 	if (ACPI_FAILURE(status))
360 		return;
361 
362 	if (ivrs->Header.Length == 0) {
363 		return;
364 	}
365 
366 	ivrs_ivinfo = ivrs->Info;
367 	printf("AMD-Vi: IVRS Info VAsize = %d PAsize = %d GVAsize = %d"
368 	       " flags:%b\n",
369 		REG_BITS(ivrs_ivinfo, 21, 15), REG_BITS(ivrs_ivinfo, 14, 8),
370 		REG_BITS(ivrs_ivinfo, 7, 5), REG_BITS(ivrs_ivinfo, 22, 22),
371 		"\020\001EFRSup");
372 
373 	ivrs_hdr_iterate_tbl(ivhd_count_iter, &count);
374 	if (!count)
375 		return;
376 
377 	ivhd_hdrs = malloc(sizeof(void *) * count, M_DEVBUF,
378 		M_WAITOK | M_ZERO);
379 	for (i = 0; i < count; i++) {
380 		ivhd = ivhd_find_by_index(i);
381 		KASSERT(ivhd, ("ivhd%d is NULL\n", i));
382 
383 		/*
384 		 * Scan for presence of legacy and non-legacy device type
385 		 * for same IOMMU device and override the old one.
386 		 *
387 		 * If there is no existing IVHD to the same IOMMU device,
388 		 * the IVHD header pointer is appended.
389 		 */
390 		for (j = 0; j < ivhd_count; j++) {
391 			if (ivhd_is_newer(&ivhd_hdrs[j]->Header, &ivhd->Header))
392 				break;
393 		}
394 		ivhd_hdrs[j] = ivhd;
395 		if (j == ivhd_count)
396 			ivhd_count++;
397 	}
398 
399 	ivhd_devs = malloc(sizeof(device_t) * ivhd_count, M_DEVBUF,
400 		M_WAITOK | M_ZERO);
401 	for (i = 0, j = 0; i < ivhd_count; i++) {
402 		ivhd = ivhd_hdrs[i];
403 		KASSERT(ivhd, ("ivhd%d is NULL\n", i));
404 
405 		/*
406 		 * Use a high order to ensure that this driver is probed after
407 		 * the Host-PCI bridge and the root PCI bus.
408 		 */
409 		ivhd_devs[i] = BUS_ADD_CHILD(parent,
410 		    ACPI_DEV_BASE_ORDER + 10 * 10, "ivhd", i);
411 
412 		/*
413 		 * XXX: In case device was not destroyed before, add will fail.
414 		 * locate the old device instance.
415 		 */
416 		if (ivhd_devs[i] == NULL) {
417 			ivhd_devs[i] = device_find_child(parent, "ivhd", i);
418 			if (ivhd_devs[i] == NULL) {
419 				printf("AMD-Vi: can't find ivhd%d\n", i);
420 				break;
421 			}
422 		}
423 		j++;
424 	}
425 
426 	/*
427 	 * Update device count in case failed to attach.
428 	 */
429 	ivhd_count = j;
430 }
431 
432 static int
433 ivhd_probe(device_t dev)
434 {
435 	ACPI_IVRS_HARDWARE1 *ivhd;
436 	int unit;
437 
438 	if (acpi_get_handle(dev) != NULL)
439 		return (ENXIO);
440 
441 	unit = device_get_unit(dev);
442 	KASSERT((unit < ivhd_count),
443 		("ivhd unit %d > count %d", unit, ivhd_count));
444 	ivhd = ivhd_hdrs[unit];
445 	KASSERT(ivhd, ("ivhd is NULL"));
446 
447 	switch (ivhd->Header.Type) {
448 	case IVRS_TYPE_HARDWARE_EFR:
449 		device_set_desc(dev, "AMD-Vi/IOMMU ivhd with EFR");
450 		break;
451 
452 	case IVRS_TYPE_HARDWARE_MIXED:
453 		device_set_desc(dev, "AMD-Vi/IOMMU ivhd in mixed format");
454 		break;
455 
456 	case IVRS_TYPE_HARDWARE_LEGACY:
457         default:
458 		device_set_desc(dev, "AMD-Vi/IOMMU ivhd");
459 		break;
460 	}
461 
462 	return (BUS_PROBE_NOWILDCARD);
463 }
464 
465 static void
466 ivhd_print_flag(device_t dev, enum IvrsType ivhd_type, uint8_t flag)
467 {
468 	/*
469 	 * IVHD lgeacy type has two extra high bits in flag which has
470 	 * been moved to EFR for non-legacy device.
471 	 */
472 	switch (ivhd_type) {
473 	case IVRS_TYPE_HARDWARE_LEGACY:
474 		device_printf(dev, "Flag:%b\n", flag,
475 			"\020"
476 			"\001HtTunEn"
477 			"\002PassPW"
478 			"\003ResPassPW"
479 			"\004Isoc"
480 			"\005IotlbSup"
481 			"\006Coherent"
482 			"\007PreFSup"
483 			"\010PPRSup");
484 		break;
485 
486 	case IVRS_TYPE_HARDWARE_EFR:
487 	case IVRS_TYPE_HARDWARE_MIXED:
488 		device_printf(dev, "Flag:%b\n", flag,
489 			"\020"
490 			"\001HtTunEn"
491 			"\002PassPW"
492 			"\003ResPassPW"
493 			"\004Isoc"
494 			"\005IotlbSup"
495 			"\006Coherent");
496 		break;
497 
498 	default:
499 		device_printf(dev, "Can't decode flag of ivhd type :0x%x\n",
500 			ivhd_type);
501 		break;
502 	}
503 }
504 
505 /*
506  * Feature in legacy IVHD type(0x10) and attribute in newer type(0x11 and 0x40).
507  */
508 static void
509 ivhd_print_feature(device_t dev, enum IvrsType ivhd_type, uint32_t feature)
510 {
511 	switch (ivhd_type) {
512 	case IVRS_TYPE_HARDWARE_LEGACY:
513 		device_printf(dev, "Features(type:0x%x) HATS = %d GATS = %d"
514 			" MsiNumPPR = %d PNBanks= %d PNCounters= %d\n",
515 			ivhd_type,
516 			REG_BITS(feature, 31, 30),
517 			REG_BITS(feature, 29, 28),
518 			REG_BITS(feature, 27, 23),
519 			REG_BITS(feature, 22, 17),
520 			REG_BITS(feature, 16, 13));
521 		device_printf(dev, "max PASID = %d GLXSup = %d Feature:%b\n",
522 			REG_BITS(feature, 12, 8),
523 			REG_BITS(feature, 4, 3),
524 			feature,
525 			"\020"
526 			"\002NXSup"
527 			"\003GTSup"
528 			"\004<b4>"
529 			"\005IASup"
530 			"\006GASup"
531 			"\007HESup");
532 		break;
533 
534 	/* Fewer features or attributes are reported in non-legacy type. */
535 	case IVRS_TYPE_HARDWARE_EFR:
536 	case IVRS_TYPE_HARDWARE_MIXED:
537 		device_printf(dev, "Features(type:0x%x) MsiNumPPR = %d"
538 			" PNBanks= %d PNCounters= %d\n",
539 			ivhd_type,
540 			REG_BITS(feature, 27, 23),
541 			REG_BITS(feature, 22, 17),
542 			REG_BITS(feature, 16, 13));
543 		break;
544 
545 	default: /* Other ivhd type features are not decoded. */
546 		device_printf(dev, "Can't decode ivhd type :0x%x\n", ivhd_type);
547 	}
548 }
549 
550 /* Print extended features of IOMMU. */
551 static void
552 ivhd_print_ext_feature(device_t dev, uint64_t ext_feature)
553 {
554 	uint32_t ext_low, ext_high;
555 
556 	if (!ext_feature)
557 		return;
558 
559 	ext_low = ext_feature;
560 	device_printf(dev, "Extended features[31:0]:%b "
561 		"HATS = 0x%x GATS = 0x%x "
562 		"GLXSup = 0x%x SmiFSup = 0x%x SmiFRC = 0x%x "
563 		"GAMSup = 0x%x DualPortLogSup = 0x%x DualEventLogSup = 0x%x\n",
564 		(int)ext_low,
565 		"\020"
566 		"\001PreFSup"
567 		"\002PPRSup"
568 		"\003<b2>"
569 		"\004NXSup"
570 		"\005GTSup"
571 		"\006<b5>"
572 		"\007IASup"
573 		"\010GASup"
574 		"\011HESup"
575 		"\012PCSup",
576 		REG_BITS(ext_low, 11, 10),
577 		REG_BITS(ext_low, 13, 12),
578 		REG_BITS(ext_low, 15, 14),
579 		REG_BITS(ext_low, 17, 16),
580 		REG_BITS(ext_low, 20, 18),
581 		REG_BITS(ext_low, 23, 21),
582 		REG_BITS(ext_low, 25, 24),
583 		REG_BITS(ext_low, 29, 28));
584 
585 	ext_high = ext_feature >> 32;
586 	device_printf(dev, "Extended features[62:32]:%b "
587 		"Max PASID: 0x%x DevTblSegSup = 0x%x "
588 		"MarcSup = 0x%x\n",
589 		(int)(ext_high),
590 		"\020"
591 		"\006USSup"
592 		"\011PprOvrflwEarlySup"
593 		"\012PPRAutoRspSup"
594 		"\015BlKStopMrkSup"
595 		"\016PerfOptSup"
596 		"\017MsiCapMmioSup"
597 		"\021GIOSup"
598 		"\022HASup"
599 		"\023EPHSup"
600 		"\024AttrFWSup"
601 		"\025HDSup"
602 		"\027InvIotlbSup",
603 	    	REG_BITS(ext_high, 5, 0),
604 	    	REG_BITS(ext_high, 8, 7),
605 	    	REG_BITS(ext_high, 11, 10));
606 }
607 
608 static int
609 ivhd_print_cap(struct amdvi_softc *softc, ACPI_IVRS_HARDWARE1 * ivhd)
610 {
611 	device_t dev;
612 	int max_ptp_level;
613 
614 	dev = softc->dev;
615 
616 	ivhd_print_flag(dev, softc->ivhd_type, softc->ivhd_flag);
617 	ivhd_print_feature(dev, softc->ivhd_type, softc->ivhd_feature);
618 	ivhd_print_ext_feature(dev, softc->ext_feature);
619 	max_ptp_level = 7;
620 	/* Make sure device support minimum page level as requested by user. */
621 	if (max_ptp_level < amdvi_ptp_level) {
622 		device_printf(dev, "insufficient PTP level:%d\n",
623 			max_ptp_level);
624 		return (EINVAL);
625 	} else {
626 		device_printf(softc->dev, "supported paging level:%d, will use only: %d\n",
627 	    		max_ptp_level, amdvi_ptp_level);
628 	}
629 
630 	return (0);
631 }
632 
633 static int
634 ivhd_attach(device_t dev)
635 {
636 	ACPI_IVRS_HARDWARE1 *ivhd;
637 	ACPI_IVRS_HARDWARE2 *ivhd_efr;
638 	struct amdvi_softc *softc;
639 	int status, unit;
640 
641 	unit = device_get_unit(dev);
642 	KASSERT((unit < ivhd_count),
643 		("ivhd unit %d > count %d", unit, ivhd_count));
644 	/* Make sure its same device for which attach is called. */
645 	KASSERT((ivhd_devs[unit] == dev),
646 		("Not same device old %p new %p", ivhd_devs[unit], dev));
647 
648 	softc = device_get_softc(dev);
649 	softc->dev = dev;
650 	ivhd = ivhd_hdrs[unit];
651 	KASSERT(ivhd, ("ivhd is NULL"));
652 	softc->pci_dev = pci_find_dbsf(ivhd->PciSegmentGroup,
653 	    PCI_RID2BUS(ivhd->Header.DeviceId),
654 	    PCI_RID2SLOT(ivhd->Header.DeviceId),
655 	    PCI_RID2FUNC(ivhd->Header.DeviceId));
656 
657 	softc->ivhd_type = ivhd->Header.Type;
658 	softc->pci_seg = ivhd->PciSegmentGroup;
659 	softc->pci_rid = ivhd->Header.DeviceId;
660 	softc->ivhd_flag = ivhd->Header.Flags;
661 	/*
662 	 * On lgeacy IVHD type(0x10), it is documented as feature
663 	 * but in newer type it is attribute.
664 	 */
665 	softc->ivhd_feature = ivhd->FeatureReporting;
666 	/*
667 	 * PCI capability has more capabilities that are not part of IVRS.
668 	 */
669 	softc->cap_off = ivhd->CapabilityOffset;
670 
671 #ifdef notyet
672 	/* IVHD Info bit[4:0] is event MSI/X number. */
673 	softc->event_msix = ivhd->Info & 0x1F;
674 #endif
675 	switch (ivhd->Header.Type) {
676 	case IVRS_TYPE_HARDWARE_EFR:
677 	case IVRS_TYPE_HARDWARE_MIXED:
678 		ivhd_efr = (ACPI_IVRS_HARDWARE2 *)ivhd;
679 		softc->ext_feature = ivhd_efr->EfrRegisterImage;
680 		break;
681 	}
682 
683 	softc->ctrl = (struct amdvi_ctrl *) PHYS_TO_DMAP(ivhd->BaseAddress);
684 	status = ivhd_dev_parse(ivhd, softc);
685 	if (status != 0) {
686 		device_printf(dev,
687 		    "endpoint device parsing error=%d\n", status);
688 		goto fail;
689 	}
690 
691 	status = ivhd_print_cap(softc, ivhd);
692 	if (status != 0)
693 		goto fail;
694 
695 	status = amdvi_setup_hw(softc);
696 	if (status != 0) {
697 		device_printf(dev, "couldn't be initialised, error=%d\n",
698 		    status);
699 		goto fail;
700 	}
701 
702 	return (0);
703 
704 fail:
705 	free(softc->dev_cfg, M_DEVBUF);
706 	return (status);
707 }
708 
709 static int
710 ivhd_detach(device_t dev)
711 {
712 	struct amdvi_softc *softc;
713 
714 	softc = device_get_softc(dev);
715 
716 	amdvi_teardown_hw(softc);
717 	free(softc->dev_cfg, M_DEVBUF);
718 
719 	/*
720 	 * XXX: delete the device.
721 	 * don't allow detach, return EBUSY.
722 	 */
723 	return (0);
724 }
725 
726 static int
727 ivhd_suspend(device_t dev)
728 {
729 
730 	return (0);
731 }
732 
733 static int
734 ivhd_resume(device_t dev)
735 {
736 
737 	return (0);
738 }
739 
740 static device_method_t ivhd_methods[] = {
741 	DEVMETHOD(device_identify, ivhd_identify),
742 	DEVMETHOD(device_probe, ivhd_probe),
743 	DEVMETHOD(device_attach, ivhd_attach),
744 	DEVMETHOD(device_detach, ivhd_detach),
745 	DEVMETHOD(device_suspend, ivhd_suspend),
746 	DEVMETHOD(device_resume, ivhd_resume),
747 	DEVMETHOD_END
748 };
749 
750 static driver_t ivhd_driver = {
751 	"ivhd",
752 	ivhd_methods,
753 	sizeof(struct amdvi_softc),
754 };
755 
756 /*
757  * Load this module at the end after PCI re-probing to configure interrupt.
758  */
759 DRIVER_MODULE_ORDERED(ivhd, acpi, ivhd_driver, 0, 0, SI_ORDER_ANY);
760 MODULE_DEPEND(ivhd, acpi, 1, 1, 1);
761 MODULE_DEPEND(ivhd, pci, 1, 1, 1);
762