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