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