xref: /freebsd/sys/amd64/vmm/amd/ivrs_drv.c (revision 16038816)
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 	/* If device doesn't have special data, don't add it. */
188 	if (!cfg)
189 		return;
190 
191 	dev_cfg = &softc->dev_cfg[softc->dev_cfg_cnt++];
192 	dev_cfg->start_id = start_id;
193 	dev_cfg->end_id = end_id;
194 	dev_cfg->data = cfg;
195 	dev_cfg->enable_ats = ats;
196 }
197 
198 /*
199  * Record device attributes as suggested by BIOS.
200  */
201 static int
202 ivhd_dev_parse(ACPI_IVRS_HARDWARE1 *ivhd, struct amdvi_softc *softc)
203 {
204 	ACPI_IVRS_DE_HEADER *de;
205 	uint8_t *p, *end;
206 	int range_start_id = 0, range_end_id = 0;
207 	uint32_t *extended;
208 	uint8_t all_data = 0, range_data = 0;
209 	bool range_enable_ats = false, enable_ats;
210 
211 	softc->start_dev_rid = ~0;
212 	softc->end_dev_rid = 0;
213 
214 	switch (ivhd->Header.Type) {
215 		case IVRS_TYPE_HARDWARE_LEGACY:
216 			p = (uint8_t *)ivhd + sizeof(ACPI_IVRS_HARDWARE1);
217 			break;
218 
219 		case IVRS_TYPE_HARDWARE_EFR:
220 		case IVRS_TYPE_HARDWARE_MIXED:
221 			p = (uint8_t *)ivhd + sizeof(ACPI_IVRS_HARDWARE2);
222 			break;
223 
224 		default:
225 			device_printf(softc->dev,
226 				"unknown type: 0x%x\n", ivhd->Header.Type);
227 			return (-1);
228 	}
229 
230 	end = (uint8_t *)ivhd + ivhd->Header.Length;
231 
232 	while (p < end) {
233 		de = (ACPI_IVRS_DE_HEADER *)p;
234 		softc->start_dev_rid = MIN(softc->start_dev_rid, de->Id);
235 		softc->end_dev_rid = MAX(softc->end_dev_rid, de->Id);
236 		switch (de->Type) {
237 		case ACPI_IVRS_TYPE_ALL:
238 			all_data = de->DataSetting;
239 			break;
240 
241 		case ACPI_IVRS_TYPE_SELECT:
242 		case ACPI_IVRS_TYPE_ALIAS_SELECT:
243 		case ACPI_IVRS_TYPE_EXT_SELECT:
244 			enable_ats = false;
245 			if (de->Type == ACPI_IVRS_TYPE_EXT_SELECT) {
246 				extended = (uint32_t *)(de + 1);
247 				enable_ats =
248 				    (*extended & IVHD_DEV_EXT_ATS_DISABLE) ?
249 					false : true;
250 			}
251 			ivhd_dev_add_entry(softc, de->Id, de->Id,
252 			    de->DataSetting | all_data, enable_ats);
253 			break;
254 
255 		case ACPI_IVRS_TYPE_START:
256 		case ACPI_IVRS_TYPE_ALIAS_START:
257 		case ACPI_IVRS_TYPE_EXT_START:
258 			range_start_id = de->Id;
259 			range_data = de->DataSetting;
260 			if (de->Type == ACPI_IVRS_TYPE_EXT_START) {
261 				extended = (uint32_t *)(de + 1);
262 				range_enable_ats =
263 				    (*extended & IVHD_DEV_EXT_ATS_DISABLE) ?
264 					false : true;
265 			}
266 			break;
267 
268 		case ACPI_IVRS_TYPE_END:
269 			range_end_id = de->Id;
270 			ivhd_dev_add_entry(softc, range_start_id, range_end_id,
271 				range_data | all_data, range_enable_ats);
272 			range_start_id = range_end_id = 0;
273 			range_data = 0;
274 			all_data = 0;
275 			break;
276 
277 		case ACPI_IVRS_TYPE_PAD4:
278 			break;
279 
280 		case ACPI_IVRS_TYPE_SPECIAL:
281 			/* HPET or IOAPIC */
282 			break;
283 		default:
284 			if ((de->Type < 5) ||
285 			    (de->Type >= ACPI_IVRS_TYPE_PAD8))
286 				device_printf(softc->dev,
287 				    "Unknown dev entry:0x%x\n", de->Type);
288 		}
289 
290 		if (softc->dev_cfg_cnt >
291 			(sizeof(softc->dev_cfg) / sizeof(softc->dev_cfg[0]))) {
292 			device_printf(softc->dev,
293 			    "WARN Too many device entries.\n");
294 			return (EINVAL);
295 		}
296 		if (de->Type < 0x40)
297 			p += sizeof(ACPI_IVRS_DEVICE4);
298 		else if (de->Type < 0x80)
299 			p += sizeof(ACPI_IVRS_DEVICE8A);
300 		else {
301 			printf("Variable size IVHD type 0x%x not supported\n",
302 			    de->Type);
303 			break;
304 		}
305 	}
306 
307 	KASSERT((softc->end_dev_rid >= softc->start_dev_rid),
308 	    ("Device end[0x%x] < start[0x%x.\n",
309 	    softc->end_dev_rid, softc->start_dev_rid));
310 
311 	return (0);
312 }
313 
314 static bool
315 ivhd_is_newer(ACPI_IVRS_HEADER *old, ACPI_IVRS_HEADER  *new)
316 {
317 	if (old->DeviceId == new->DeviceId) {
318 		/*
319 		 * Newer IVRS header type take precedence.
320 		 */
321 		if (old->Type == IVRS_TYPE_HARDWARE_LEGACY &&
322 		    ((new->Type == IVRS_TYPE_HARDWARE_EFR) ||
323 		    (new->Type == IVRS_TYPE_HARDWARE_MIXED)))
324 			return (true);
325 
326 		/*
327 		 * Mixed format IVHD header type take precedence
328 		 * over fixed format IVHD header types.
329 		 */
330 		if (old->Type == IVRS_TYPE_HARDWARE_EFR &&
331 		    new->Type == IVRS_TYPE_HARDWARE_MIXED)
332 			return (true);
333 	}
334 
335 	return (false);
336 }
337 
338 static void
339 ivhd_identify(driver_t *driver, device_t parent)
340 {
341 	ACPI_TABLE_IVRS *ivrs;
342 	ACPI_IVRS_HARDWARE1 *ivhd;
343 	ACPI_STATUS status;
344 	int i, j, count = 0;
345 	uint32_t ivrs_ivinfo;
346 
347 	if (acpi_disabled("ivhd"))
348 		return;
349 
350 	status = AcpiGetTable(ACPI_SIG_IVRS, 1, (ACPI_TABLE_HEADER **)&ivrs);
351 	if (ACPI_FAILURE(status))
352 		return;
353 
354 	if (ivrs->Header.Length == 0) {
355 		return;
356 	}
357 
358 	ivrs_ivinfo = ivrs->Info;
359 	printf("AMD-Vi: IVRS Info VAsize = %d PAsize = %d GVAsize = %d"
360 	       " flags:%b\n",
361 		REG_BITS(ivrs_ivinfo, 21, 15), REG_BITS(ivrs_ivinfo, 14, 8),
362 		REG_BITS(ivrs_ivinfo, 7, 5), REG_BITS(ivrs_ivinfo, 22, 22),
363 		"\020\001EFRSup");
364 
365 	ivrs_hdr_iterate_tbl(ivhd_count_iter, &count);
366 	if (!count)
367 		return;
368 
369 	ivhd_hdrs = malloc(sizeof(void *) * count, M_DEVBUF,
370 		M_WAITOK | M_ZERO);
371 	for (i = 0; i < count; i++) {
372 		ivhd = ivhd_find_by_index(i);
373 		KASSERT(ivhd, ("ivhd%d is NULL\n", i));
374 
375 		/*
376 		 * Scan for presence of legacy and non-legacy device type
377 		 * for same IOMMU device and override the old one.
378 		 *
379 		 * If there is no existing IVHD to the same IOMMU device,
380 		 * the IVHD header pointer is appended.
381 		 */
382 		for (j = 0; j < ivhd_count; j++) {
383 			if (ivhd_is_newer(&ivhd_hdrs[j]->Header, &ivhd->Header))
384 				break;
385 		}
386 		ivhd_hdrs[j] = ivhd;
387 		if (j == ivhd_count)
388 			ivhd_count++;
389 	}
390 
391 	ivhd_devs = malloc(sizeof(device_t) * ivhd_count, M_DEVBUF,
392 		M_WAITOK | M_ZERO);
393 	for (i = 0, j = 0; i < ivhd_count; i++) {
394 		ivhd = ivhd_hdrs[i];
395 		KASSERT(ivhd, ("ivhd%d is NULL\n", i));
396 
397 		/*
398 		 * Use a high order to ensure that this driver is probed after
399 		 * the Host-PCI bridge and the root PCI bus.
400 		 */
401 		ivhd_devs[i] = BUS_ADD_CHILD(parent,
402 		    ACPI_DEV_BASE_ORDER + 10 * 10, "ivhd", i);
403 
404 		/*
405 		 * XXX: In case device was not destroyed before, add will fail.
406 		 * locate the old device instance.
407 		 */
408 		if (ivhd_devs[i] == NULL) {
409 			ivhd_devs[i] = device_find_child(parent, "ivhd", i);
410 			if (ivhd_devs[i] == NULL) {
411 				printf("AMD-Vi: cant find ivhd%d\n", i);
412 				break;
413 			}
414 		}
415 		j++;
416 	}
417 
418 	/*
419 	 * Update device count in case failed to attach.
420 	 */
421 	ivhd_count = j;
422 }
423 
424 static int
425 ivhd_probe(device_t dev)
426 {
427 	ACPI_IVRS_HARDWARE1 *ivhd;
428 	int unit;
429 
430 	if (acpi_get_handle(dev) != NULL)
431 		return (ENXIO);
432 
433 	unit = device_get_unit(dev);
434 	KASSERT((unit < ivhd_count),
435 		("ivhd unit %d > count %d", unit, ivhd_count));
436 	ivhd = ivhd_hdrs[unit];
437 	KASSERT(ivhd, ("ivhd is NULL"));
438 
439 	switch (ivhd->Header.Type) {
440 	case IVRS_TYPE_HARDWARE_EFR:
441 		device_set_desc(dev, "AMD-Vi/IOMMU ivhd with EFR");
442 		break;
443 
444 	case IVRS_TYPE_HARDWARE_MIXED:
445 		device_set_desc(dev, "AMD-Vi/IOMMU ivhd in mixed format");
446 		break;
447 
448 	case IVRS_TYPE_HARDWARE_LEGACY:
449         default:
450 		device_set_desc(dev, "AMD-Vi/IOMMU ivhd");
451 		break;
452 	}
453 
454 	return (BUS_PROBE_NOWILDCARD);
455 }
456 
457 static void
458 ivhd_print_flag(device_t dev, enum IvrsType ivhd_type, uint8_t flag)
459 {
460 	/*
461 	 * IVHD lgeacy type has two extra high bits in flag which has
462 	 * been moved to EFR for non-legacy device.
463 	 */
464 	switch (ivhd_type) {
465 	case IVRS_TYPE_HARDWARE_LEGACY:
466 		device_printf(dev, "Flag:%b\n", flag,
467 			"\020"
468 			"\001HtTunEn"
469 			"\002PassPW"
470 			"\003ResPassPW"
471 			"\004Isoc"
472 			"\005IotlbSup"
473 			"\006Coherent"
474 			"\007PreFSup"
475 			"\008PPRSup");
476 		break;
477 
478 	case IVRS_TYPE_HARDWARE_EFR:
479 	case IVRS_TYPE_HARDWARE_MIXED:
480 		device_printf(dev, "Flag:%b\n", flag,
481 			"\020"
482 			"\001HtTunEn"
483 			"\002PassPW"
484 			"\003ResPassPW"
485 			"\004Isoc"
486 			"\005IotlbSup"
487 			"\006Coherent");
488 		break;
489 
490 	default:
491 		device_printf(dev, "Can't decode flag of ivhd type :0x%x\n",
492 			ivhd_type);
493 		break;
494 	}
495 }
496 
497 /*
498  * Feature in legacy IVHD type(0x10) and attribute in newer type(0x11 and 0x40).
499  */
500 static void
501 ivhd_print_feature(device_t dev, enum IvrsType ivhd_type, uint32_t feature)
502 {
503 	switch (ivhd_type) {
504 	case IVRS_TYPE_HARDWARE_LEGACY:
505 		device_printf(dev, "Features(type:0x%x) HATS = %d GATS = %d"
506 			" MsiNumPPR = %d PNBanks= %d PNCounters= %d\n",
507 			ivhd_type,
508 			REG_BITS(feature, 31, 30),
509 			REG_BITS(feature, 29, 28),
510 			REG_BITS(feature, 27, 23),
511 			REG_BITS(feature, 22, 17),
512 			REG_BITS(feature, 16, 13));
513 		device_printf(dev, "max PASID = %d GLXSup = %d Feature:%b\n",
514 			REG_BITS(feature, 12, 8),
515 			REG_BITS(feature, 4, 3),
516 			feature,
517 			"\020"
518 			"\002NXSup"
519 			"\003GTSup"
520 			"\004<b4>"
521 			"\005IASup"
522 			"\006GASup"
523 			"\007HESup");
524 		break;
525 
526 	/* Fewer features or attributes are reported in non-legacy type. */
527 	case IVRS_TYPE_HARDWARE_EFR:
528 	case IVRS_TYPE_HARDWARE_MIXED:
529 		device_printf(dev, "Features(type:0x%x) MsiNumPPR = %d"
530 			" PNBanks= %d PNCounters= %d\n",
531 			ivhd_type,
532 			REG_BITS(feature, 27, 23),
533 			REG_BITS(feature, 22, 17),
534 			REG_BITS(feature, 16, 13));
535 		break;
536 
537 	default: /* Other ivhd type features are not decoded. */
538 		device_printf(dev, "Can't decode ivhd type :0x%x\n", ivhd_type);
539 	}
540 }
541 
542 /* Print extended features of IOMMU. */
543 static void
544 ivhd_print_ext_feature(device_t dev, uint64_t ext_feature)
545 {
546 	uint32_t ext_low, ext_high;
547 
548 	if (!ext_feature)
549 		return;
550 
551 	ext_low = ext_feature;
552 	device_printf(dev, "Extended features[31:0]:%b "
553 		"HATS = 0x%x GATS = 0x%x "
554 		"GLXSup = 0x%x SmiFSup = 0x%x SmiFRC = 0x%x "
555 		"GAMSup = 0x%x DualPortLogSup = 0x%x DualEventLogSup = 0x%x\n",
556 		(int)ext_low,
557 		"\020"
558 		"\001PreFSup"
559 		"\002PPRSup"
560 		"\003<b2>"
561 		"\004NXSup"
562 		"\005GTSup"
563 		"\006<b5>"
564 		"\007IASup"
565 		"\008GASup"
566 		"\009HESup"
567 		"\010PCSup",
568 		REG_BITS(ext_low, 11, 10),
569 		REG_BITS(ext_low, 13, 12),
570 		REG_BITS(ext_low, 15, 14),
571 		REG_BITS(ext_low, 17, 16),
572 		REG_BITS(ext_low, 20, 18),
573 		REG_BITS(ext_low, 23, 21),
574 		REG_BITS(ext_low, 25, 24),
575 		REG_BITS(ext_low, 29, 28));
576 
577 	ext_high = ext_feature >> 32;
578 	device_printf(dev, "Extended features[62:32]:%b "
579 		"Max PASID: 0x%x DevTblSegSup = 0x%x "
580 		"MarcSup = 0x%x\n",
581 		(int)(ext_high),
582 		"\020"
583 		"\006USSup"
584 		"\009PprOvrflwEarlySup"
585 		"\010PPRAutoRspSup"
586 		"\013BlKStopMrkSup"
587 		"\014PerfOptSup"
588 		"\015MsiCapMmioSup"
589 		"\017GIOSup"
590 		"\018HASup"
591 		"\019EPHSup"
592 		"\020AttrFWSup"
593 		"\021HDSup"
594 		"\023InvIotlbSup",
595 	    	REG_BITS(ext_high, 5, 0),
596 	    	REG_BITS(ext_high, 8, 7),
597 	    	REG_BITS(ext_high, 11, 10));
598 }
599 
600 static int
601 ivhd_print_cap(struct amdvi_softc *softc, ACPI_IVRS_HARDWARE1 * ivhd)
602 {
603 	device_t dev;
604 	int max_ptp_level;
605 
606 	dev = softc->dev;
607 
608 	ivhd_print_flag(dev, softc->ivhd_type, softc->ivhd_flag);
609 	ivhd_print_feature(dev, softc->ivhd_type, softc->ivhd_feature);
610 	ivhd_print_ext_feature(dev, softc->ext_feature);
611 	max_ptp_level = 7;
612 	/* Make sure device support minimum page level as requested by user. */
613 	if (max_ptp_level < amdvi_ptp_level) {
614 		device_printf(dev, "insufficient PTP level:%d\n",
615 			max_ptp_level);
616 		return (EINVAL);
617 	} else {
618 		device_printf(softc->dev, "supported paging level:%d, will use only: %d\n",
619 	    		max_ptp_level, amdvi_ptp_level);
620 	}
621 
622 	device_printf(softc->dev, "device range: 0x%x - 0x%x\n",
623 			softc->start_dev_rid, softc->end_dev_rid);
624 
625 	return (0);
626 }
627 
628 static int
629 ivhd_attach(device_t dev)
630 {
631 	ACPI_IVRS_HARDWARE1 *ivhd;
632 	ACPI_IVRS_HARDWARE2 *ivhd_efr;
633 	struct amdvi_softc *softc;
634 	int status, unit;
635 
636 	unit = device_get_unit(dev);
637 	KASSERT((unit < ivhd_count),
638 		("ivhd unit %d > count %d", unit, ivhd_count));
639 	/* Make sure its same device for which attach is called. */
640 	KASSERT((ivhd_devs[unit] == dev),
641 		("Not same device old %p new %p", ivhd_devs[unit], dev));
642 
643 	softc = device_get_softc(dev);
644 	softc->dev = dev;
645 	ivhd = ivhd_hdrs[unit];
646 	KASSERT(ivhd, ("ivhd is NULL"));
647 	softc->pci_dev = pci_find_bsf(PCI_RID2BUS(ivhd->Header.DeviceId),
648 	    PCI_RID2SLOT(ivhd->Header.DeviceId),
649 	    PCI_RID2FUNC(ivhd->Header.DeviceId));
650 
651 	softc->ivhd_type = ivhd->Header.Type;
652 	softc->pci_seg = ivhd->PciSegmentGroup;
653 	softc->pci_rid = ivhd->Header.DeviceId;
654 	softc->ivhd_flag = ivhd->Header.Flags;
655 	/*
656 	 * On lgeacy IVHD type(0x10), it is documented as feature
657 	 * but in newer type it is attribute.
658 	 */
659 	softc->ivhd_feature = ivhd->FeatureReporting;
660 	/*
661 	 * PCI capability has more capabilities that are not part of IVRS.
662 	 */
663 	softc->cap_off = ivhd->CapabilityOffset;
664 
665 #ifdef notyet
666 	/* IVHD Info bit[4:0] is event MSI/X number. */
667 	softc->event_msix = ivhd->Info & 0x1F;
668 #endif
669 	switch (ivhd->Header.Type) {
670 	case IVRS_TYPE_HARDWARE_EFR:
671 	case IVRS_TYPE_HARDWARE_MIXED:
672 		ivhd_efr = (ACPI_IVRS_HARDWARE2 *)ivhd;
673 		softc->ext_feature = ivhd_efr->EfrRegisterImage;
674 		break;
675 	}
676 
677 	softc->ctrl = (struct amdvi_ctrl *) PHYS_TO_DMAP(ivhd->BaseAddress);
678 	status = ivhd_dev_parse(ivhd, softc);
679 	if (status != 0) {
680 		device_printf(dev,
681 		    "endpoint device parsing error=%d\n", status);
682 	}
683 
684 	status = ivhd_print_cap(softc, ivhd);
685 	if (status != 0) {
686 		return (status);
687 	}
688 
689 	status = amdvi_setup_hw(softc);
690 	if (status != 0) {
691 		device_printf(dev, "couldn't be initialised, error=%d\n",
692 		    status);
693 		return (status);
694 	}
695 
696 	return (0);
697 }
698 
699 static int
700 ivhd_detach(device_t dev)
701 {
702 	struct amdvi_softc *softc;
703 
704 	softc = device_get_softc(dev);
705 
706 	amdvi_teardown_hw(softc);
707 
708 	/*
709 	 * XXX: delete the device.
710 	 * don't allow detach, return EBUSY.
711 	 */
712 	return (0);
713 }
714 
715 static int
716 ivhd_suspend(device_t dev)
717 {
718 
719 	return (0);
720 }
721 
722 static int
723 ivhd_resume(device_t dev)
724 {
725 
726 	return (0);
727 }
728 
729 static device_method_t ivhd_methods[] = {
730 	DEVMETHOD(device_identify, ivhd_identify),
731 	DEVMETHOD(device_probe, ivhd_probe),
732 	DEVMETHOD(device_attach, ivhd_attach),
733 	DEVMETHOD(device_detach, ivhd_detach),
734 	DEVMETHOD(device_suspend, ivhd_suspend),
735 	DEVMETHOD(device_resume, ivhd_resume),
736 	DEVMETHOD_END
737 };
738 
739 static driver_t ivhd_driver = {
740 	"ivhd",
741 	ivhd_methods,
742 	sizeof(struct amdvi_softc),
743 };
744 
745 static devclass_t ivhd_devclass;
746 
747 /*
748  * Load this module at the end after PCI re-probing to configure interrupt.
749  */
750 DRIVER_MODULE_ORDERED(ivhd, acpi, ivhd_driver, ivhd_devclass, 0, 0,
751 		      SI_ORDER_ANY);
752 MODULE_DEPEND(ivhd, acpi, 1, 1, 1);
753 MODULE_DEPEND(ivhd, pci, 1, 1, 1);
754