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