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