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