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