xref: /illumos-gate/usr/src/uts/intel/io/vmm/amd/ivrs_drv.c (revision 32640292339b07090f10ce34d455f98711077343)
17c8c0b82SPatrick Mooney /*-
2*32640292SAndy Fiddaman  * SPDX-License-Identifier: BSD-2-Clause
37c8c0b82SPatrick Mooney  *
47c8c0b82SPatrick Mooney  * Copyright (c) 2016, Anish Gupta (anish@freebsd.org)
57c8c0b82SPatrick Mooney  * Copyright (c) 2021 The FreeBSD Foundation
67c8c0b82SPatrick Mooney  * All rights reserved.
77c8c0b82SPatrick Mooney  *
87c8c0b82SPatrick Mooney  * Redistribution and use in source and binary forms, with or without
97c8c0b82SPatrick Mooney  * modification, are permitted provided that the following conditions
107c8c0b82SPatrick Mooney  * are met:
117c8c0b82SPatrick Mooney  * 1. Redistributions of source code must retain the above copyright
127c8c0b82SPatrick Mooney  *    notice unmodified, this list of conditions, and the following
137c8c0b82SPatrick Mooney  *    disclaimer.
147c8c0b82SPatrick Mooney  * 2. Redistributions in binary form must reproduce the above copyright
157c8c0b82SPatrick Mooney  *    notice, this list of conditions and the following disclaimer in the
167c8c0b82SPatrick Mooney  *    documentation and/or other materials provided with the distribution.
177c8c0b82SPatrick Mooney  *
187c8c0b82SPatrick Mooney  * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
197c8c0b82SPatrick Mooney  * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
207c8c0b82SPatrick Mooney  * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
217c8c0b82SPatrick Mooney  * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
227c8c0b82SPatrick Mooney  * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
237c8c0b82SPatrick Mooney  * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
247c8c0b82SPatrick Mooney  * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
257c8c0b82SPatrick Mooney  * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
267c8c0b82SPatrick Mooney  * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
277c8c0b82SPatrick Mooney  * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
287c8c0b82SPatrick Mooney  */
297c8c0b82SPatrick Mooney 
307c8c0b82SPatrick Mooney #include <sys/cdefs.h>
317c8c0b82SPatrick Mooney 
327c8c0b82SPatrick Mooney #include "opt_acpi.h"
337c8c0b82SPatrick Mooney #include <sys/param.h>
347c8c0b82SPatrick Mooney #include <sys/bus.h>
357c8c0b82SPatrick Mooney #include <sys/kernel.h>
367c8c0b82SPatrick Mooney #include <sys/module.h>
377c8c0b82SPatrick Mooney #include <sys/malloc.h>
387c8c0b82SPatrick Mooney 
397c8c0b82SPatrick Mooney #include <machine/vmparam.h>
407c8c0b82SPatrick Mooney 
417c8c0b82SPatrick Mooney #include <contrib/dev/acpica/include/acpi.h>
427c8c0b82SPatrick Mooney #include <contrib/dev/acpica/include/accommon.h>
437c8c0b82SPatrick Mooney #include <dev/acpica/acpivar.h>
447c8c0b82SPatrick Mooney #include <dev/pci/pcireg.h>
457c8c0b82SPatrick Mooney #include <dev/pci/pcivar.h>
467c8c0b82SPatrick Mooney 
477c8c0b82SPatrick Mooney #include "io/iommu.h"
487c8c0b82SPatrick Mooney #include "amdvi_priv.h"
497c8c0b82SPatrick Mooney 
507c8c0b82SPatrick Mooney device_t *ivhd_devs;			/* IVHD or AMD-Vi device list. */
517c8c0b82SPatrick Mooney int	ivhd_count;			/* Number of IVHD header. */
527c8c0b82SPatrick Mooney /*
537c8c0b82SPatrick Mooney  * Cached IVHD header list.
547c8c0b82SPatrick Mooney  * Single entry for each IVHD, filtered the legacy one.
557c8c0b82SPatrick Mooney  */
567c8c0b82SPatrick Mooney ACPI_IVRS_HARDWARE1 **ivhd_hdrs;
577c8c0b82SPatrick Mooney 
587c8c0b82SPatrick Mooney extern int amdvi_ptp_level;		/* Page table levels. */
597c8c0b82SPatrick Mooney 
607c8c0b82SPatrick Mooney typedef int (*ivhd_iter_t)(ACPI_IVRS_HEADER *ptr, void *arg);
617c8c0b82SPatrick Mooney /*
627c8c0b82SPatrick Mooney  * Iterate IVRS table for IVHD and IVMD device type.
637c8c0b82SPatrick Mooney  */
647c8c0b82SPatrick Mooney static void
ivrs_hdr_iterate_tbl(ivhd_iter_t iter,void * arg)657c8c0b82SPatrick Mooney ivrs_hdr_iterate_tbl(ivhd_iter_t iter, void *arg)
667c8c0b82SPatrick Mooney {
677c8c0b82SPatrick Mooney 	ACPI_TABLE_IVRS *ivrs;
687c8c0b82SPatrick Mooney 	ACPI_IVRS_HEADER *ivrs_hdr, *end;
697c8c0b82SPatrick Mooney 	ACPI_STATUS status;
707c8c0b82SPatrick Mooney 
717c8c0b82SPatrick Mooney 	status = AcpiGetTable(ACPI_SIG_IVRS, 1, (ACPI_TABLE_HEADER **)&ivrs);
727c8c0b82SPatrick Mooney 	if (ACPI_FAILURE(status))
737c8c0b82SPatrick Mooney 		return;
747c8c0b82SPatrick Mooney 
757c8c0b82SPatrick Mooney 	if (ivrs->Header.Length == 0) {
767c8c0b82SPatrick Mooney 		return;
777c8c0b82SPatrick Mooney 	}
787c8c0b82SPatrick Mooney 
797c8c0b82SPatrick Mooney 	ivrs_hdr = (ACPI_IVRS_HEADER *)(ivrs + 1);
807c8c0b82SPatrick Mooney 	end = (ACPI_IVRS_HEADER *)((char *)ivrs + ivrs->Header.Length);
817c8c0b82SPatrick Mooney 
827c8c0b82SPatrick Mooney 	while (ivrs_hdr < end) {
837c8c0b82SPatrick Mooney 		if ((uint8_t *)ivrs_hdr + ivrs_hdr->Length > (uint8_t *)end) {
847c8c0b82SPatrick Mooney 			printf("AMD-Vi:IVHD/IVMD is corrupted, length : %d\n",
857c8c0b82SPatrick Mooney 			    ivrs_hdr->Length);
867c8c0b82SPatrick Mooney 			break;
877c8c0b82SPatrick Mooney 		}
887c8c0b82SPatrick Mooney 
897c8c0b82SPatrick Mooney 		switch (ivrs_hdr->Type) {
907c8c0b82SPatrick Mooney 		case IVRS_TYPE_HARDWARE_LEGACY:	/* Legacy */
917c8c0b82SPatrick Mooney 		case IVRS_TYPE_HARDWARE_EFR:
927c8c0b82SPatrick Mooney 		case IVRS_TYPE_HARDWARE_MIXED:
937c8c0b82SPatrick Mooney 			if (!iter(ivrs_hdr, arg))
947c8c0b82SPatrick Mooney 				return;
957c8c0b82SPatrick Mooney 			break;
967c8c0b82SPatrick Mooney 
977c8c0b82SPatrick Mooney 		case ACPI_IVRS_TYPE_MEMORY1:
987c8c0b82SPatrick Mooney 		case ACPI_IVRS_TYPE_MEMORY2:
997c8c0b82SPatrick Mooney 		case ACPI_IVRS_TYPE_MEMORY3:
1007c8c0b82SPatrick Mooney 			if (!iter(ivrs_hdr, arg))
1017c8c0b82SPatrick Mooney 				return;
1027c8c0b82SPatrick Mooney 
1037c8c0b82SPatrick Mooney 			break;
1047c8c0b82SPatrick Mooney 
1057c8c0b82SPatrick Mooney 		default:
1067c8c0b82SPatrick Mooney 			printf("AMD-Vi:Not IVHD/IVMD type(%d)", ivrs_hdr->Type);
1077c8c0b82SPatrick Mooney 		}
1087c8c0b82SPatrick Mooney 
1097c8c0b82SPatrick Mooney 		ivrs_hdr = (ACPI_IVRS_HEADER *)((uint8_t *)ivrs_hdr +
1107c8c0b82SPatrick Mooney 			ivrs_hdr->Length);
1117c8c0b82SPatrick Mooney 	}
1127c8c0b82SPatrick Mooney }
1137c8c0b82SPatrick Mooney 
1147c8c0b82SPatrick Mooney static bool
ivrs_is_ivhd(UINT8 type)1157c8c0b82SPatrick Mooney ivrs_is_ivhd(UINT8 type)
1167c8c0b82SPatrick Mooney {
1177c8c0b82SPatrick Mooney 
1187c8c0b82SPatrick Mooney 	switch(type) {
1197c8c0b82SPatrick Mooney 	case IVRS_TYPE_HARDWARE_LEGACY:
1207c8c0b82SPatrick Mooney 	case IVRS_TYPE_HARDWARE_EFR:
1217c8c0b82SPatrick Mooney 	case IVRS_TYPE_HARDWARE_MIXED:
1227c8c0b82SPatrick Mooney 		return (true);
1237c8c0b82SPatrick Mooney 
1247c8c0b82SPatrick Mooney 	default:
1257c8c0b82SPatrick Mooney 		return (false);
1267c8c0b82SPatrick Mooney 	}
1277c8c0b82SPatrick Mooney }
1287c8c0b82SPatrick Mooney 
1297c8c0b82SPatrick Mooney /* Count the number of AMD-Vi devices in the system. */
1307c8c0b82SPatrick Mooney static int
ivhd_count_iter(ACPI_IVRS_HEADER * ivrs_he,void * arg)1317c8c0b82SPatrick Mooney ivhd_count_iter(ACPI_IVRS_HEADER * ivrs_he, void *arg)
1327c8c0b82SPatrick Mooney {
1337c8c0b82SPatrick Mooney 	int *count;
1347c8c0b82SPatrick Mooney 
1357c8c0b82SPatrick Mooney 	count = (int *)arg;
1367c8c0b82SPatrick Mooney 	if (ivrs_is_ivhd(ivrs_he->Type))
1377c8c0b82SPatrick Mooney 		(*count)++;
1387c8c0b82SPatrick Mooney 
1397c8c0b82SPatrick Mooney 	return (1);
1407c8c0b82SPatrick Mooney }
1417c8c0b82SPatrick Mooney 
1427c8c0b82SPatrick Mooney struct find_ivrs_hdr_args {
1437c8c0b82SPatrick Mooney 	int	i;
1447c8c0b82SPatrick Mooney 	ACPI_IVRS_HEADER *ptr;
1457c8c0b82SPatrick Mooney };
1467c8c0b82SPatrick Mooney 
1477c8c0b82SPatrick Mooney static int
ivrs_hdr_find_iter(ACPI_IVRS_HEADER * ivrs_hdr,void * args)1487c8c0b82SPatrick Mooney ivrs_hdr_find_iter(ACPI_IVRS_HEADER * ivrs_hdr, void *args)
1497c8c0b82SPatrick Mooney {
1507c8c0b82SPatrick Mooney 	struct find_ivrs_hdr_args *fi;
1517c8c0b82SPatrick Mooney 
1527c8c0b82SPatrick Mooney 	fi = (struct find_ivrs_hdr_args *)args;
1537c8c0b82SPatrick Mooney 	if (ivrs_is_ivhd(ivrs_hdr->Type)) {
1547c8c0b82SPatrick Mooney 		if (fi->i == 0) {
1557c8c0b82SPatrick Mooney 			fi->ptr = ivrs_hdr;
1567c8c0b82SPatrick Mooney 			return (0);
1577c8c0b82SPatrick Mooney 		}
1587c8c0b82SPatrick Mooney 		fi->i--;
1597c8c0b82SPatrick Mooney 	}
1607c8c0b82SPatrick Mooney 
1617c8c0b82SPatrick Mooney 	return (1);
1627c8c0b82SPatrick Mooney }
1637c8c0b82SPatrick Mooney 
1647c8c0b82SPatrick Mooney static ACPI_IVRS_HARDWARE1 *
ivhd_find_by_index(int idx)1657c8c0b82SPatrick Mooney ivhd_find_by_index(int idx)
1667c8c0b82SPatrick Mooney {
1677c8c0b82SPatrick Mooney 	struct find_ivrs_hdr_args fi;
1687c8c0b82SPatrick Mooney 
1697c8c0b82SPatrick Mooney 	fi.i = idx;
1707c8c0b82SPatrick Mooney 	fi.ptr = NULL;
1717c8c0b82SPatrick Mooney 
1727c8c0b82SPatrick Mooney 	ivrs_hdr_iterate_tbl(ivrs_hdr_find_iter, &fi);
1737c8c0b82SPatrick Mooney 
1747c8c0b82SPatrick Mooney 	return ((ACPI_IVRS_HARDWARE1 *)fi.ptr);
1757c8c0b82SPatrick Mooney }
1767c8c0b82SPatrick Mooney 
1777c8c0b82SPatrick Mooney static void
ivhd_dev_add_entry(struct amdvi_softc * softc,uint32_t start_id,uint32_t end_id,uint8_t cfg,bool ats)1787c8c0b82SPatrick Mooney ivhd_dev_add_entry(struct amdvi_softc *softc, uint32_t start_id,
1797c8c0b82SPatrick Mooney     uint32_t end_id, uint8_t cfg, bool ats)
1807c8c0b82SPatrick Mooney {
1817c8c0b82SPatrick Mooney 	struct ivhd_dev_cfg *dev_cfg;
1827c8c0b82SPatrick Mooney 
1837c8c0b82SPatrick Mooney 	KASSERT(softc->dev_cfg_cap >= softc->dev_cfg_cnt,
1847c8c0b82SPatrick Mooney 	    ("Impossible case: number of dev_cfg exceeding capacity"));
1857c8c0b82SPatrick Mooney 	if (softc->dev_cfg_cap == softc->dev_cfg_cnt) {
1867c8c0b82SPatrick Mooney 		if (softc->dev_cfg_cap == 0)
1877c8c0b82SPatrick Mooney 			softc->dev_cfg_cap = 1;
1887c8c0b82SPatrick Mooney 		else
1897c8c0b82SPatrick Mooney 			softc->dev_cfg_cap <<= 2;
1907c8c0b82SPatrick Mooney 		softc->dev_cfg = realloc(softc->dev_cfg,
1917c8c0b82SPatrick Mooney 		    sizeof(*softc->dev_cfg) * softc->dev_cfg_cap, M_DEVBUF,
1927c8c0b82SPatrick Mooney 		    M_WAITOK);
1937c8c0b82SPatrick Mooney 	}
1947c8c0b82SPatrick Mooney 
1957c8c0b82SPatrick Mooney 	dev_cfg = &softc->dev_cfg[softc->dev_cfg_cnt++];
1967c8c0b82SPatrick Mooney 	dev_cfg->start_id = start_id;
1977c8c0b82SPatrick Mooney 	dev_cfg->end_id = end_id;
1987c8c0b82SPatrick Mooney 	dev_cfg->data = cfg;
1997c8c0b82SPatrick Mooney 	dev_cfg->enable_ats = ats;
2007c8c0b82SPatrick Mooney }
2017c8c0b82SPatrick Mooney 
2027c8c0b82SPatrick Mooney /*
2037c8c0b82SPatrick Mooney  * Record device attributes as suggested by BIOS.
2047c8c0b82SPatrick Mooney  */
2057c8c0b82SPatrick Mooney static int
ivhd_dev_parse(ACPI_IVRS_HARDWARE1 * ivhd,struct amdvi_softc * softc)2067c8c0b82SPatrick Mooney ivhd_dev_parse(ACPI_IVRS_HARDWARE1 *ivhd, struct amdvi_softc *softc)
2077c8c0b82SPatrick Mooney {
2087c8c0b82SPatrick Mooney 	ACPI_IVRS_DE_HEADER *de;
2097c8c0b82SPatrick Mooney 	uint8_t *p, *end;
2107c8c0b82SPatrick Mooney 	int range_start_id = -1, range_end_id = -1, i;
2117c8c0b82SPatrick Mooney 	uint32_t *extended;
2127c8c0b82SPatrick Mooney 	uint8_t all_data = 0, range_data = 0;
2137c8c0b82SPatrick Mooney 	bool range_enable_ats = false, enable_ats;
2147c8c0b82SPatrick Mooney 
2157c8c0b82SPatrick Mooney 	switch (ivhd->Header.Type) {
2167c8c0b82SPatrick Mooney 		case IVRS_TYPE_HARDWARE_LEGACY:
2177c8c0b82SPatrick Mooney 			p = (uint8_t *)ivhd + sizeof(ACPI_IVRS_HARDWARE1);
2187c8c0b82SPatrick Mooney 			break;
2197c8c0b82SPatrick Mooney 
2207c8c0b82SPatrick Mooney 		case IVRS_TYPE_HARDWARE_EFR:
2217c8c0b82SPatrick Mooney 		case IVRS_TYPE_HARDWARE_MIXED:
2227c8c0b82SPatrick Mooney 			p = (uint8_t *)ivhd + sizeof(ACPI_IVRS_HARDWARE2);
2237c8c0b82SPatrick Mooney 			break;
2247c8c0b82SPatrick Mooney 
2257c8c0b82SPatrick Mooney 		default:
2267c8c0b82SPatrick Mooney 			device_printf(softc->dev,
2277c8c0b82SPatrick Mooney 				"unknown type: 0x%x\n", ivhd->Header.Type);
2287c8c0b82SPatrick Mooney 			return (-1);
2297c8c0b82SPatrick Mooney 	}
2307c8c0b82SPatrick Mooney 
2317c8c0b82SPatrick Mooney 	end = (uint8_t *)ivhd + ivhd->Header.Length;
2327c8c0b82SPatrick Mooney 
2337c8c0b82SPatrick Mooney 	while (p < end) {
2347c8c0b82SPatrick Mooney 		de = (ACPI_IVRS_DE_HEADER *)p;
2357c8c0b82SPatrick Mooney 		switch (de->Type) {
2367c8c0b82SPatrick Mooney 		case ACPI_IVRS_TYPE_ALL:
2377c8c0b82SPatrick Mooney 			all_data = de->DataSetting;
2387c8c0b82SPatrick Mooney 			for (i = 0; i < softc->dev_cfg_cnt; i++)
2397c8c0b82SPatrick Mooney 				softc->dev_cfg[i].data |= all_data;
2407c8c0b82SPatrick Mooney 			break;
2417c8c0b82SPatrick Mooney 
2427c8c0b82SPatrick Mooney 		case ACPI_IVRS_TYPE_SELECT:
2437c8c0b82SPatrick Mooney 		case ACPI_IVRS_TYPE_ALIAS_SELECT:
2447c8c0b82SPatrick Mooney 		case ACPI_IVRS_TYPE_EXT_SELECT:
2457c8c0b82SPatrick Mooney 			enable_ats = false;
2467c8c0b82SPatrick Mooney 			if (de->Type == ACPI_IVRS_TYPE_EXT_SELECT) {
2477c8c0b82SPatrick Mooney 				extended = (uint32_t *)(de + 1);
2487c8c0b82SPatrick Mooney 				enable_ats =
2497c8c0b82SPatrick Mooney 				    (*extended & IVHD_DEV_EXT_ATS_DISABLE) ?
2507c8c0b82SPatrick Mooney 					false : true;
2517c8c0b82SPatrick Mooney 			}
2527c8c0b82SPatrick Mooney 			ivhd_dev_add_entry(softc, de->Id, de->Id,
2537c8c0b82SPatrick Mooney 			    de->DataSetting | all_data, enable_ats);
2547c8c0b82SPatrick Mooney 			break;
2557c8c0b82SPatrick Mooney 
2567c8c0b82SPatrick Mooney 		case ACPI_IVRS_TYPE_START:
2577c8c0b82SPatrick Mooney 		case ACPI_IVRS_TYPE_ALIAS_START:
2587c8c0b82SPatrick Mooney 		case ACPI_IVRS_TYPE_EXT_START:
2597c8c0b82SPatrick Mooney 			if (range_start_id != -1) {
2607c8c0b82SPatrick Mooney 				device_printf(softc->dev,
2617c8c0b82SPatrick Mooney 				    "Unexpected start-of-range device entry\n");
2627c8c0b82SPatrick Mooney 				return (EINVAL);
2637c8c0b82SPatrick Mooney 			}
2647c8c0b82SPatrick Mooney 			range_start_id = de->Id;
2657c8c0b82SPatrick Mooney 			range_data = de->DataSetting;
2667c8c0b82SPatrick Mooney 			if (de->Type == ACPI_IVRS_TYPE_EXT_START) {
2677c8c0b82SPatrick Mooney 				extended = (uint32_t *)(de + 1);
2687c8c0b82SPatrick Mooney 				range_enable_ats =
2697c8c0b82SPatrick Mooney 				    (*extended & IVHD_DEV_EXT_ATS_DISABLE) ?
2707c8c0b82SPatrick Mooney 					false : true;
2717c8c0b82SPatrick Mooney 			}
2727c8c0b82SPatrick Mooney 			break;
2737c8c0b82SPatrick Mooney 
2747c8c0b82SPatrick Mooney 		case ACPI_IVRS_TYPE_END:
2757c8c0b82SPatrick Mooney 			if (range_start_id == -1) {
2767c8c0b82SPatrick Mooney 				device_printf(softc->dev,
2777c8c0b82SPatrick Mooney 				    "Unexpected end-of-range device entry\n");
2787c8c0b82SPatrick Mooney 				return (EINVAL);
2797c8c0b82SPatrick Mooney 			}
2807c8c0b82SPatrick Mooney 			range_end_id = de->Id;
2817c8c0b82SPatrick Mooney 			if (range_end_id < range_start_id) {
2827c8c0b82SPatrick Mooney 				device_printf(softc->dev,
2837c8c0b82SPatrick Mooney 				    "Device entry range going backward\n");
2847c8c0b82SPatrick Mooney 				return (EINVAL);
2857c8c0b82SPatrick Mooney 			}
2867c8c0b82SPatrick Mooney 			ivhd_dev_add_entry(softc, range_start_id, range_end_id,
2877c8c0b82SPatrick Mooney 			    range_data | all_data, range_enable_ats);
2887c8c0b82SPatrick Mooney 			range_start_id = range_end_id = -1;
2897c8c0b82SPatrick Mooney 			range_data = 0;
2907c8c0b82SPatrick Mooney 			all_data = 0;
2917c8c0b82SPatrick Mooney 			break;
2927c8c0b82SPatrick Mooney 
2937c8c0b82SPatrick Mooney 		case ACPI_IVRS_TYPE_PAD4:
2947c8c0b82SPatrick Mooney 			break;
2957c8c0b82SPatrick Mooney 
2967c8c0b82SPatrick Mooney 		case ACPI_IVRS_TYPE_SPECIAL:
2977c8c0b82SPatrick Mooney 			/* HPET or IOAPIC */
2987c8c0b82SPatrick Mooney 			break;
2997c8c0b82SPatrick Mooney 		default:
3007c8c0b82SPatrick Mooney 			if ((de->Type < 5) ||
3017c8c0b82SPatrick Mooney 			    (de->Type >= ACPI_IVRS_TYPE_PAD8))
3027c8c0b82SPatrick Mooney 				device_printf(softc->dev,
3037c8c0b82SPatrick Mooney 				    "Unknown dev entry:0x%x\n", de->Type);
3047c8c0b82SPatrick Mooney 		}
3057c8c0b82SPatrick Mooney 
3067c8c0b82SPatrick Mooney 		if (de->Type < 0x40)
3077c8c0b82SPatrick Mooney 			p += sizeof(ACPI_IVRS_DEVICE4);
3087c8c0b82SPatrick Mooney 		else if (de->Type < 0x80)
3097c8c0b82SPatrick Mooney 			p += sizeof(ACPI_IVRS_DEVICE8A);
3107c8c0b82SPatrick Mooney 		else {
3117c8c0b82SPatrick Mooney 			printf("Variable size IVHD type 0x%x not supported\n",
3127c8c0b82SPatrick Mooney 			    de->Type);
3137c8c0b82SPatrick Mooney 			break;
3147c8c0b82SPatrick Mooney 		}
3157c8c0b82SPatrick Mooney 	}
3167c8c0b82SPatrick Mooney 
3177c8c0b82SPatrick Mooney 	return (0);
3187c8c0b82SPatrick Mooney }
3197c8c0b82SPatrick Mooney 
3207c8c0b82SPatrick Mooney static bool
ivhd_is_newer(ACPI_IVRS_HEADER * old,ACPI_IVRS_HEADER * new)3217c8c0b82SPatrick Mooney ivhd_is_newer(ACPI_IVRS_HEADER *old, ACPI_IVRS_HEADER  *new)
3227c8c0b82SPatrick Mooney {
3237c8c0b82SPatrick Mooney 	if (old->DeviceId == new->DeviceId) {
3247c8c0b82SPatrick Mooney 		/*
3257c8c0b82SPatrick Mooney 		 * Newer IVRS header type take precedence.
3267c8c0b82SPatrick Mooney 		 */
3277c8c0b82SPatrick Mooney 		if (old->Type == IVRS_TYPE_HARDWARE_LEGACY &&
3287c8c0b82SPatrick Mooney 		    ((new->Type == IVRS_TYPE_HARDWARE_EFR) ||
3297c8c0b82SPatrick Mooney 		    (new->Type == IVRS_TYPE_HARDWARE_MIXED)))
3307c8c0b82SPatrick Mooney 			return (true);
3317c8c0b82SPatrick Mooney 
3327c8c0b82SPatrick Mooney 		/*
3337c8c0b82SPatrick Mooney 		 * Mixed format IVHD header type take precedence
3347c8c0b82SPatrick Mooney 		 * over fixed format IVHD header types.
3357c8c0b82SPatrick Mooney 		 */
3367c8c0b82SPatrick Mooney 		if (old->Type == IVRS_TYPE_HARDWARE_EFR &&
3377c8c0b82SPatrick Mooney 		    new->Type == IVRS_TYPE_HARDWARE_MIXED)
3387c8c0b82SPatrick Mooney 			return (true);
3397c8c0b82SPatrick Mooney 	}
3407c8c0b82SPatrick Mooney 
3417c8c0b82SPatrick Mooney 	return (false);
3427c8c0b82SPatrick Mooney }
3437c8c0b82SPatrick Mooney 
3447c8c0b82SPatrick Mooney static void
ivhd_identify(driver_t * driver,device_t parent)3457c8c0b82SPatrick Mooney ivhd_identify(driver_t *driver, device_t parent)
3467c8c0b82SPatrick Mooney {
3477c8c0b82SPatrick Mooney 	ACPI_TABLE_IVRS *ivrs;
3487c8c0b82SPatrick Mooney 	ACPI_IVRS_HARDWARE1 *ivhd;
3497c8c0b82SPatrick Mooney 	ACPI_STATUS status;
3507c8c0b82SPatrick Mooney 	int i, j, count = 0;
3517c8c0b82SPatrick Mooney 	uint32_t ivrs_ivinfo;
3527c8c0b82SPatrick Mooney 
3537c8c0b82SPatrick Mooney 	if (acpi_disabled("ivhd"))
3547c8c0b82SPatrick Mooney 		return;
3557c8c0b82SPatrick Mooney 
3567c8c0b82SPatrick Mooney 	status = AcpiGetTable(ACPI_SIG_IVRS, 1, (ACPI_TABLE_HEADER **)&ivrs);
3577c8c0b82SPatrick Mooney 	if (ACPI_FAILURE(status))
3587c8c0b82SPatrick Mooney 		return;
3597c8c0b82SPatrick Mooney 
3607c8c0b82SPatrick Mooney 	if (ivrs->Header.Length == 0) {
3617c8c0b82SPatrick Mooney 		return;
3627c8c0b82SPatrick Mooney 	}
3637c8c0b82SPatrick Mooney 
3647c8c0b82SPatrick Mooney 	ivrs_ivinfo = ivrs->Info;
3657c8c0b82SPatrick Mooney 	printf("AMD-Vi: IVRS Info VAsize = %d PAsize = %d GVAsize = %d"
3667c8c0b82SPatrick Mooney 	       " flags:%b\n",
3677c8c0b82SPatrick Mooney 		REG_BITS(ivrs_ivinfo, 21, 15), REG_BITS(ivrs_ivinfo, 14, 8),
3687c8c0b82SPatrick Mooney 		REG_BITS(ivrs_ivinfo, 7, 5), REG_BITS(ivrs_ivinfo, 22, 22),
3697c8c0b82SPatrick Mooney 		"\020\001EFRSup");
3707c8c0b82SPatrick Mooney 
3717c8c0b82SPatrick Mooney 	ivrs_hdr_iterate_tbl(ivhd_count_iter, &count);
3727c8c0b82SPatrick Mooney 	if (!count)
3737c8c0b82SPatrick Mooney 		return;
3747c8c0b82SPatrick Mooney 
3757c8c0b82SPatrick Mooney 	ivhd_hdrs = kmem_zalloc(sizeof(void *) * count, KM_SLEEP);
3767c8c0b82SPatrick Mooney 	for (i = 0; i < count; i++) {
3777c8c0b82SPatrick Mooney 		ivhd = ivhd_find_by_index(i);
3787c8c0b82SPatrick Mooney 		KASSERT(ivhd, ("ivhd%d is NULL\n", i));
3797c8c0b82SPatrick Mooney 
3807c8c0b82SPatrick Mooney 		/*
3817c8c0b82SPatrick Mooney 		 * Scan for presence of legacy and non-legacy device type
3827c8c0b82SPatrick Mooney 		 * for same IOMMU device and override the old one.
3837c8c0b82SPatrick Mooney 		 *
3847c8c0b82SPatrick Mooney 		 * If there is no existing IVHD to the same IOMMU device,
3857c8c0b82SPatrick Mooney 		 * the IVHD header pointer is appended.
3867c8c0b82SPatrick Mooney 		 */
3877c8c0b82SPatrick Mooney 		for (j = 0; j < ivhd_count; j++) {
3887c8c0b82SPatrick Mooney 			if (ivhd_is_newer(&ivhd_hdrs[j]->Header, &ivhd->Header))
3897c8c0b82SPatrick Mooney 				break;
3907c8c0b82SPatrick Mooney 		}
3917c8c0b82SPatrick Mooney 		ivhd_hdrs[j] = ivhd;
3927c8c0b82SPatrick Mooney 		if (j == ivhd_count)
3937c8c0b82SPatrick Mooney 			ivhd_count++;
3947c8c0b82SPatrick Mooney 	}
3957c8c0b82SPatrick Mooney 
3967c8c0b82SPatrick Mooney 	ivhd_devs = kmem_zalloc(sizeof(device_t) * ivhd_count, KM_SLEEP);
3977c8c0b82SPatrick Mooney 	for (i = 0, j = 0; i < ivhd_count; i++) {
3987c8c0b82SPatrick Mooney 		ivhd = ivhd_hdrs[i];
3997c8c0b82SPatrick Mooney 		KASSERT(ivhd, ("ivhd%d is NULL\n", i));
4007c8c0b82SPatrick Mooney 
4017c8c0b82SPatrick Mooney 		/*
4027c8c0b82SPatrick Mooney 		 * Use a high order to ensure that this driver is probed after
4037c8c0b82SPatrick Mooney 		 * the Host-PCI bridge and the root PCI bus.
4047c8c0b82SPatrick Mooney 		 */
4057c8c0b82SPatrick Mooney 		ivhd_devs[i] = BUS_ADD_CHILD(parent,
4067c8c0b82SPatrick Mooney 		    ACPI_DEV_BASE_ORDER + 10 * 10, "ivhd", i);
4077c8c0b82SPatrick Mooney 
4087c8c0b82SPatrick Mooney 		/*
4097c8c0b82SPatrick Mooney 		 * XXX: In case device was not destroyed before, add will fail.
4107c8c0b82SPatrick Mooney 		 * locate the old device instance.
4117c8c0b82SPatrick Mooney 		 */
4127c8c0b82SPatrick Mooney 		if (ivhd_devs[i] == NULL) {
4137c8c0b82SPatrick Mooney 			ivhd_devs[i] = device_find_child(parent, "ivhd", i);
4147c8c0b82SPatrick Mooney 			if (ivhd_devs[i] == NULL) {
4157c8c0b82SPatrick Mooney 				printf("AMD-Vi: cant find ivhd%d\n", i);
4167c8c0b82SPatrick Mooney 				break;
4177c8c0b82SPatrick Mooney 			}
4187c8c0b82SPatrick Mooney 		}
4197c8c0b82SPatrick Mooney 		j++;
4207c8c0b82SPatrick Mooney 	}
4217c8c0b82SPatrick Mooney 
4227c8c0b82SPatrick Mooney 	/*
4237c8c0b82SPatrick Mooney 	 * Update device count in case failed to attach.
4247c8c0b82SPatrick Mooney 	 */
4257c8c0b82SPatrick Mooney 	ivhd_count = j;
4267c8c0b82SPatrick Mooney }
4277c8c0b82SPatrick Mooney 
4287c8c0b82SPatrick Mooney static int
ivhd_probe(device_t dev)4297c8c0b82SPatrick Mooney ivhd_probe(device_t dev)
4307c8c0b82SPatrick Mooney {
4317c8c0b82SPatrick Mooney 	ACPI_IVRS_HARDWARE1 *ivhd;
4327c8c0b82SPatrick Mooney 	int unit;
4337c8c0b82SPatrick Mooney 
4347c8c0b82SPatrick Mooney 	if (acpi_get_handle(dev) != NULL)
4357c8c0b82SPatrick Mooney 		return (ENXIO);
4367c8c0b82SPatrick Mooney 
4377c8c0b82SPatrick Mooney 	unit = device_get_unit(dev);
4387c8c0b82SPatrick Mooney 	KASSERT((unit < ivhd_count),
4397c8c0b82SPatrick Mooney 		("ivhd unit %d > count %d", unit, ivhd_count));
4407c8c0b82SPatrick Mooney 	ivhd = ivhd_hdrs[unit];
4417c8c0b82SPatrick Mooney 	KASSERT(ivhd, ("ivhd is NULL"));
4427c8c0b82SPatrick Mooney 
4437c8c0b82SPatrick Mooney 	switch (ivhd->Header.Type) {
4447c8c0b82SPatrick Mooney 	case IVRS_TYPE_HARDWARE_EFR:
4457c8c0b82SPatrick Mooney 		device_set_desc(dev, "AMD-Vi/IOMMU ivhd with EFR");
4467c8c0b82SPatrick Mooney 		break;
4477c8c0b82SPatrick Mooney 
4487c8c0b82SPatrick Mooney 	case IVRS_TYPE_HARDWARE_MIXED:
4497c8c0b82SPatrick Mooney 		device_set_desc(dev, "AMD-Vi/IOMMU ivhd in mixed format");
4507c8c0b82SPatrick Mooney 		break;
4517c8c0b82SPatrick Mooney 
4527c8c0b82SPatrick Mooney 	case IVRS_TYPE_HARDWARE_LEGACY:
4537c8c0b82SPatrick Mooney         default:
4547c8c0b82SPatrick Mooney 		device_set_desc(dev, "AMD-Vi/IOMMU ivhd");
4557c8c0b82SPatrick Mooney 		break;
4567c8c0b82SPatrick Mooney 	}
4577c8c0b82SPatrick Mooney 
4587c8c0b82SPatrick Mooney 	return (BUS_PROBE_NOWILDCARD);
4597c8c0b82SPatrick Mooney }
4607c8c0b82SPatrick Mooney 
4617c8c0b82SPatrick Mooney static void
ivhd_print_flag(device_t dev,enum IvrsType ivhd_type,uint8_t flag)4627c8c0b82SPatrick Mooney ivhd_print_flag(device_t dev, enum IvrsType ivhd_type, uint8_t flag)
4637c8c0b82SPatrick Mooney {
4647c8c0b82SPatrick Mooney 	/*
4657c8c0b82SPatrick Mooney 	 * IVHD lgeacy type has two extra high bits in flag which has
4667c8c0b82SPatrick Mooney 	 * been moved to EFR for non-legacy device.
4677c8c0b82SPatrick Mooney 	 */
4687c8c0b82SPatrick Mooney 	switch (ivhd_type) {
4697c8c0b82SPatrick Mooney 	case IVRS_TYPE_HARDWARE_LEGACY:
4707c8c0b82SPatrick Mooney 		device_printf(dev, "Flag:%b\n", flag,
4717c8c0b82SPatrick Mooney 			"\020"
4727c8c0b82SPatrick Mooney 			"\001HtTunEn"
4737c8c0b82SPatrick Mooney 			"\002PassPW"
4747c8c0b82SPatrick Mooney 			"\003ResPassPW"
4757c8c0b82SPatrick Mooney 			"\004Isoc"
4767c8c0b82SPatrick Mooney 			"\005IotlbSup"
4777c8c0b82SPatrick Mooney 			"\006Coherent"
4787c8c0b82SPatrick Mooney 			"\007PreFSup"
4797c8c0b82SPatrick Mooney 			"\010PPRSup");
4807c8c0b82SPatrick Mooney 		break;
4817c8c0b82SPatrick Mooney 
4827c8c0b82SPatrick Mooney 	case IVRS_TYPE_HARDWARE_EFR:
4837c8c0b82SPatrick Mooney 	case IVRS_TYPE_HARDWARE_MIXED:
4847c8c0b82SPatrick Mooney 		device_printf(dev, "Flag:%b\n", flag,
4857c8c0b82SPatrick Mooney 			"\020"
4867c8c0b82SPatrick Mooney 			"\001HtTunEn"
4877c8c0b82SPatrick Mooney 			"\002PassPW"
4887c8c0b82SPatrick Mooney 			"\003ResPassPW"
4897c8c0b82SPatrick Mooney 			"\004Isoc"
4907c8c0b82SPatrick Mooney 			"\005IotlbSup"
4917c8c0b82SPatrick Mooney 			"\006Coherent");
4927c8c0b82SPatrick Mooney 		break;
4937c8c0b82SPatrick Mooney 
4947c8c0b82SPatrick Mooney 	default:
4957c8c0b82SPatrick Mooney 		device_printf(dev, "Can't decode flag of ivhd type :0x%x\n",
4967c8c0b82SPatrick Mooney 			ivhd_type);
4977c8c0b82SPatrick Mooney 		break;
4987c8c0b82SPatrick Mooney 	}
4997c8c0b82SPatrick Mooney }
5007c8c0b82SPatrick Mooney 
5017c8c0b82SPatrick Mooney /*
5027c8c0b82SPatrick Mooney  * Feature in legacy IVHD type(0x10) and attribute in newer type(0x11 and 0x40).
5037c8c0b82SPatrick Mooney  */
5047c8c0b82SPatrick Mooney static void
ivhd_print_feature(device_t dev,enum IvrsType ivhd_type,uint32_t feature)5057c8c0b82SPatrick Mooney ivhd_print_feature(device_t dev, enum IvrsType ivhd_type, uint32_t feature)
5067c8c0b82SPatrick Mooney {
5077c8c0b82SPatrick Mooney 	switch (ivhd_type) {
5087c8c0b82SPatrick Mooney 	case IVRS_TYPE_HARDWARE_LEGACY:
5097c8c0b82SPatrick Mooney 		device_printf(dev, "Features(type:0x%x) HATS = %d GATS = %d"
5107c8c0b82SPatrick Mooney 			" MsiNumPPR = %d PNBanks= %d PNCounters= %d\n",
5117c8c0b82SPatrick Mooney 			ivhd_type,
5127c8c0b82SPatrick Mooney 			REG_BITS(feature, 31, 30),
5137c8c0b82SPatrick Mooney 			REG_BITS(feature, 29, 28),
5147c8c0b82SPatrick Mooney 			REG_BITS(feature, 27, 23),
5157c8c0b82SPatrick Mooney 			REG_BITS(feature, 22, 17),
5167c8c0b82SPatrick Mooney 			REG_BITS(feature, 16, 13));
5177c8c0b82SPatrick Mooney 		device_printf(dev, "max PASID = %d GLXSup = %d Feature:%b\n",
5187c8c0b82SPatrick Mooney 			REG_BITS(feature, 12, 8),
5197c8c0b82SPatrick Mooney 			REG_BITS(feature, 4, 3),
5207c8c0b82SPatrick Mooney 			feature,
5217c8c0b82SPatrick Mooney 			"\020"
5227c8c0b82SPatrick Mooney 			"\002NXSup"
5237c8c0b82SPatrick Mooney 			"\003GTSup"
5247c8c0b82SPatrick Mooney 			"\004<b4>"
5257c8c0b82SPatrick Mooney 			"\005IASup"
5267c8c0b82SPatrick Mooney 			"\006GASup"
5277c8c0b82SPatrick Mooney 			"\007HESup");
5287c8c0b82SPatrick Mooney 		break;
5297c8c0b82SPatrick Mooney 
5307c8c0b82SPatrick Mooney 	/* Fewer features or attributes are reported in non-legacy type. */
5317c8c0b82SPatrick Mooney 	case IVRS_TYPE_HARDWARE_EFR:
5327c8c0b82SPatrick Mooney 	case IVRS_TYPE_HARDWARE_MIXED:
5337c8c0b82SPatrick Mooney 		device_printf(dev, "Features(type:0x%x) MsiNumPPR = %d"
5347c8c0b82SPatrick Mooney 			" PNBanks= %d PNCounters= %d\n",
5357c8c0b82SPatrick Mooney 			ivhd_type,
5367c8c0b82SPatrick Mooney 			REG_BITS(feature, 27, 23),
5377c8c0b82SPatrick Mooney 			REG_BITS(feature, 22, 17),
5387c8c0b82SPatrick Mooney 			REG_BITS(feature, 16, 13));
5397c8c0b82SPatrick Mooney 		break;
5407c8c0b82SPatrick Mooney 
5417c8c0b82SPatrick Mooney 	default: /* Other ivhd type features are not decoded. */
5427c8c0b82SPatrick Mooney 		device_printf(dev, "Can't decode ivhd type :0x%x\n", ivhd_type);
5437c8c0b82SPatrick Mooney 	}
5447c8c0b82SPatrick Mooney }
5457c8c0b82SPatrick Mooney 
5467c8c0b82SPatrick Mooney /* Print extended features of IOMMU. */
5477c8c0b82SPatrick Mooney static void
ivhd_print_ext_feature(device_t dev,uint64_t ext_feature)5487c8c0b82SPatrick Mooney ivhd_print_ext_feature(device_t dev, uint64_t ext_feature)
5497c8c0b82SPatrick Mooney {
5507c8c0b82SPatrick Mooney 	uint32_t ext_low, ext_high;
5517c8c0b82SPatrick Mooney 
5527c8c0b82SPatrick Mooney 	if (!ext_feature)
5537c8c0b82SPatrick Mooney 		return;
5547c8c0b82SPatrick Mooney 
5557c8c0b82SPatrick Mooney 	ext_low = ext_feature;
5567c8c0b82SPatrick Mooney 	device_printf(dev, "Extended features[31:0]:%b "
5577c8c0b82SPatrick Mooney 		"HATS = 0x%x GATS = 0x%x "
5587c8c0b82SPatrick Mooney 		"GLXSup = 0x%x SmiFSup = 0x%x SmiFRC = 0x%x "
5597c8c0b82SPatrick Mooney 		"GAMSup = 0x%x DualPortLogSup = 0x%x DualEventLogSup = 0x%x\n",
5607c8c0b82SPatrick Mooney 		(int)ext_low,
5617c8c0b82SPatrick Mooney 		"\020"
5627c8c0b82SPatrick Mooney 		"\001PreFSup"
5637c8c0b82SPatrick Mooney 		"\002PPRSup"
5647c8c0b82SPatrick Mooney 		"\003<b2>"
5657c8c0b82SPatrick Mooney 		"\004NXSup"
5667c8c0b82SPatrick Mooney 		"\005GTSup"
5677c8c0b82SPatrick Mooney 		"\006<b5>"
5687c8c0b82SPatrick Mooney 		"\007IASup"
5697c8c0b82SPatrick Mooney 		"\010GASup"
5707c8c0b82SPatrick Mooney 		"\011HESup"
5717c8c0b82SPatrick Mooney 		"\012PCSup",
5727c8c0b82SPatrick Mooney 		REG_BITS(ext_low, 11, 10),
5737c8c0b82SPatrick Mooney 		REG_BITS(ext_low, 13, 12),
5747c8c0b82SPatrick Mooney 		REG_BITS(ext_low, 15, 14),
5757c8c0b82SPatrick Mooney 		REG_BITS(ext_low, 17, 16),
5767c8c0b82SPatrick Mooney 		REG_BITS(ext_low, 20, 18),
5777c8c0b82SPatrick Mooney 		REG_BITS(ext_low, 23, 21),
5787c8c0b82SPatrick Mooney 		REG_BITS(ext_low, 25, 24),
5797c8c0b82SPatrick Mooney 		REG_BITS(ext_low, 29, 28));
5807c8c0b82SPatrick Mooney 
5817c8c0b82SPatrick Mooney 	ext_high = ext_feature >> 32;
5827c8c0b82SPatrick Mooney 	device_printf(dev, "Extended features[62:32]:%b "
5837c8c0b82SPatrick Mooney 		"Max PASID: 0x%x DevTblSegSup = 0x%x "
5847c8c0b82SPatrick Mooney 		"MarcSup = 0x%x\n",
5857c8c0b82SPatrick Mooney 		(int)(ext_high),
5867c8c0b82SPatrick Mooney 		"\020"
5877c8c0b82SPatrick Mooney 		"\006USSup"
5887c8c0b82SPatrick Mooney 		"\011PprOvrflwEarlySup"
5897c8c0b82SPatrick Mooney 		"\012PPRAutoRspSup"
5907c8c0b82SPatrick Mooney 		"\015BlKStopMrkSup"
5917c8c0b82SPatrick Mooney 		"\016PerfOptSup"
5927c8c0b82SPatrick Mooney 		"\017MsiCapMmioSup"
5937c8c0b82SPatrick Mooney 		"\021GIOSup"
5947c8c0b82SPatrick Mooney 		"\022HASup"
5957c8c0b82SPatrick Mooney 		"\023EPHSup"
5967c8c0b82SPatrick Mooney 		"\024AttrFWSup"
5977c8c0b82SPatrick Mooney 		"\025HDSup"
5987c8c0b82SPatrick Mooney 		"\027InvIotlbSup",
5997c8c0b82SPatrick Mooney 		REG_BITS(ext_high, 5, 0),
6007c8c0b82SPatrick Mooney 		REG_BITS(ext_high, 8, 7),
6017c8c0b82SPatrick Mooney 		REG_BITS(ext_high, 11, 10));
6027c8c0b82SPatrick Mooney }
6037c8c0b82SPatrick Mooney 
6047c8c0b82SPatrick Mooney static int
ivhd_print_cap(struct amdvi_softc * softc,ACPI_IVRS_HARDWARE1 * ivhd)6057c8c0b82SPatrick Mooney ivhd_print_cap(struct amdvi_softc *softc, ACPI_IVRS_HARDWARE1 * ivhd)
6067c8c0b82SPatrick Mooney {
6077c8c0b82SPatrick Mooney 	device_t dev;
6087c8c0b82SPatrick Mooney 	int max_ptp_level;
6097c8c0b82SPatrick Mooney 
6107c8c0b82SPatrick Mooney 	dev = softc->dev;
6117c8c0b82SPatrick Mooney 
6127c8c0b82SPatrick Mooney 	ivhd_print_flag(dev, softc->ivhd_type, softc->ivhd_flag);
6137c8c0b82SPatrick Mooney 	ivhd_print_feature(dev, softc->ivhd_type, softc->ivhd_feature);
6147c8c0b82SPatrick Mooney 	ivhd_print_ext_feature(dev, softc->ext_feature);
6157c8c0b82SPatrick Mooney 	max_ptp_level = 7;
6167c8c0b82SPatrick Mooney 	/* Make sure device support minimum page level as requested by user. */
6177c8c0b82SPatrick Mooney 	if (max_ptp_level < amdvi_ptp_level) {
6187c8c0b82SPatrick Mooney 		device_printf(dev, "insufficient PTP level:%d\n",
6197c8c0b82SPatrick Mooney 			max_ptp_level);
6207c8c0b82SPatrick Mooney 		return (EINVAL);
6217c8c0b82SPatrick Mooney 	} else {
6227c8c0b82SPatrick Mooney 		device_printf(softc->dev, "supported paging level:%d, will use only: %d\n",
6237c8c0b82SPatrick Mooney 			max_ptp_level, amdvi_ptp_level);
6247c8c0b82SPatrick Mooney 	}
6257c8c0b82SPatrick Mooney 
6267c8c0b82SPatrick Mooney 	return (0);
6277c8c0b82SPatrick Mooney }
6287c8c0b82SPatrick Mooney 
6297c8c0b82SPatrick Mooney static int
ivhd_attach(device_t dev)6307c8c0b82SPatrick Mooney ivhd_attach(device_t dev)
6317c8c0b82SPatrick Mooney {
6327c8c0b82SPatrick Mooney 	ACPI_IVRS_HARDWARE1 *ivhd;
6337c8c0b82SPatrick Mooney 	ACPI_IVRS_HARDWARE2 *ivhd_efr;
6347c8c0b82SPatrick Mooney 	struct amdvi_softc *softc;
6357c8c0b82SPatrick Mooney 	int status, unit;
6367c8c0b82SPatrick Mooney 
6377c8c0b82SPatrick Mooney 	unit = device_get_unit(dev);
6387c8c0b82SPatrick Mooney 	KASSERT((unit < ivhd_count),
6397c8c0b82SPatrick Mooney 		("ivhd unit %d > count %d", unit, ivhd_count));
6407c8c0b82SPatrick Mooney 	/* Make sure its same device for which attach is called. */
6417c8c0b82SPatrick Mooney 	KASSERT((ivhd_devs[unit] == dev),
6427c8c0b82SPatrick Mooney 		("Not same device old %p new %p", ivhd_devs[unit], dev));
6437c8c0b82SPatrick Mooney 
6447c8c0b82SPatrick Mooney 	softc = device_get_softc(dev);
6457c8c0b82SPatrick Mooney 	softc->dev = dev;
6467c8c0b82SPatrick Mooney 	ivhd = ivhd_hdrs[unit];
6477c8c0b82SPatrick Mooney 	KASSERT(ivhd, ("ivhd is NULL"));
6487c8c0b82SPatrick Mooney 	softc->pci_dev = pci_find_bsf(PCI_RID2BUS(ivhd->Header.DeviceId),
6497c8c0b82SPatrick Mooney 	    PCI_RID2SLOT(ivhd->Header.DeviceId),
6507c8c0b82SPatrick Mooney 	    PCI_RID2FUNC(ivhd->Header.DeviceId));
6517c8c0b82SPatrick Mooney 
6527c8c0b82SPatrick Mooney 	softc->ivhd_type = ivhd->Header.Type;
6537c8c0b82SPatrick Mooney 	softc->pci_seg = ivhd->PciSegmentGroup;
6547c8c0b82SPatrick Mooney 	softc->pci_rid = ivhd->Header.DeviceId;
6557c8c0b82SPatrick Mooney 	softc->ivhd_flag = ivhd->Header.Flags;
6567c8c0b82SPatrick Mooney 	/*
6577c8c0b82SPatrick Mooney 	 * On lgeacy IVHD type(0x10), it is documented as feature
6587c8c0b82SPatrick Mooney 	 * but in newer type it is attribute.
6597c8c0b82SPatrick Mooney 	 */
6607c8c0b82SPatrick Mooney 	softc->ivhd_feature = ivhd->FeatureReporting;
6617c8c0b82SPatrick Mooney 	/*
6627c8c0b82SPatrick Mooney 	 * PCI capability has more capabilities that are not part of IVRS.
6637c8c0b82SPatrick Mooney 	 */
6647c8c0b82SPatrick Mooney 	softc->cap_off = ivhd->CapabilityOffset;
6657c8c0b82SPatrick Mooney 
6667c8c0b82SPatrick Mooney #ifdef notyet
6677c8c0b82SPatrick Mooney 	/* IVHD Info bit[4:0] is event MSI/X number. */
6687c8c0b82SPatrick Mooney 	softc->event_msix = ivhd->Info & 0x1F;
6697c8c0b82SPatrick Mooney #endif
6707c8c0b82SPatrick Mooney 	switch (ivhd->Header.Type) {
6717c8c0b82SPatrick Mooney 	case IVRS_TYPE_HARDWARE_EFR:
6727c8c0b82SPatrick Mooney 	case IVRS_TYPE_HARDWARE_MIXED:
6737c8c0b82SPatrick Mooney 		ivhd_efr = (ACPI_IVRS_HARDWARE2 *)ivhd;
6747c8c0b82SPatrick Mooney 		softc->ext_feature = ivhd_efr->EfrRegisterImage;
6757c8c0b82SPatrick Mooney 		break;
6767c8c0b82SPatrick Mooney 	}
6777c8c0b82SPatrick Mooney 
6787c8c0b82SPatrick Mooney 	softc->ctrl = (struct amdvi_ctrl *) PHYS_TO_DMAP(ivhd->BaseAddress);
6797c8c0b82SPatrick Mooney 	status = ivhd_dev_parse(ivhd, softc);
6807c8c0b82SPatrick Mooney 	if (status != 0) {
6817c8c0b82SPatrick Mooney 		device_printf(dev,
6827c8c0b82SPatrick Mooney 		    "endpoint device parsing error=%d\n", status);
6837c8c0b82SPatrick Mooney 		goto fail;
6847c8c0b82SPatrick Mooney 	}
6857c8c0b82SPatrick Mooney 
6867c8c0b82SPatrick Mooney 	status = ivhd_print_cap(softc, ivhd);
6877c8c0b82SPatrick Mooney 	if (status != 0)
6887c8c0b82SPatrick Mooney 		goto fail;
6897c8c0b82SPatrick Mooney 
6907c8c0b82SPatrick Mooney 	status = amdvi_setup_hw(softc);
6917c8c0b82SPatrick Mooney 	if (status != 0) {
6927c8c0b82SPatrick Mooney 		device_printf(dev, "couldn't be initialised, error=%d\n",
6937c8c0b82SPatrick Mooney 		    status);
6947c8c0b82SPatrick Mooney 		goto fail;
6957c8c0b82SPatrick Mooney 	}
6967c8c0b82SPatrick Mooney 
6977c8c0b82SPatrick Mooney 	return (0);
6987c8c0b82SPatrick Mooney 
6997c8c0b82SPatrick Mooney fail:
7007c8c0b82SPatrick Mooney 	free(softc->dev_cfg, M_DEVBUF);
7017c8c0b82SPatrick Mooney 	return (status);
7027c8c0b82SPatrick Mooney }
7037c8c0b82SPatrick Mooney 
7047c8c0b82SPatrick Mooney static int
ivhd_detach(device_t dev)7057c8c0b82SPatrick Mooney ivhd_detach(device_t dev)
7067c8c0b82SPatrick Mooney {
7077c8c0b82SPatrick Mooney 	struct amdvi_softc *softc;
7087c8c0b82SPatrick Mooney 
7097c8c0b82SPatrick Mooney 	softc = device_get_softc(dev);
7107c8c0b82SPatrick Mooney 
7117c8c0b82SPatrick Mooney 	amdvi_teardown_hw(softc);
7127c8c0b82SPatrick Mooney 	free(softc->dev_cfg, M_DEVBUF);
7137c8c0b82SPatrick Mooney 
7147c8c0b82SPatrick Mooney 	/*
7157c8c0b82SPatrick Mooney 	 * XXX: delete the device.
7167c8c0b82SPatrick Mooney 	 * don't allow detach, return EBUSY.
7177c8c0b82SPatrick Mooney 	 */
7187c8c0b82SPatrick Mooney 	return (0);
7197c8c0b82SPatrick Mooney }
7207c8c0b82SPatrick Mooney 
7217c8c0b82SPatrick Mooney static int
ivhd_suspend(device_t dev)7227c8c0b82SPatrick Mooney ivhd_suspend(device_t dev)
7237c8c0b82SPatrick Mooney {
7247c8c0b82SPatrick Mooney 
7257c8c0b82SPatrick Mooney 	return (0);
7267c8c0b82SPatrick Mooney }
7277c8c0b82SPatrick Mooney 
7287c8c0b82SPatrick Mooney static int
ivhd_resume(device_t dev)7297c8c0b82SPatrick Mooney ivhd_resume(device_t dev)
7307c8c0b82SPatrick Mooney {
7317c8c0b82SPatrick Mooney 
7327c8c0b82SPatrick Mooney 	return (0);
7337c8c0b82SPatrick Mooney }
7347c8c0b82SPatrick Mooney 
7357c8c0b82SPatrick Mooney static device_method_t ivhd_methods[] = {
7367c8c0b82SPatrick Mooney 	DEVMETHOD(device_identify, ivhd_identify),
7377c8c0b82SPatrick Mooney 	DEVMETHOD(device_probe, ivhd_probe),
7387c8c0b82SPatrick Mooney 	DEVMETHOD(device_attach, ivhd_attach),
7397c8c0b82SPatrick Mooney 	DEVMETHOD(device_detach, ivhd_detach),
7407c8c0b82SPatrick Mooney 	DEVMETHOD(device_suspend, ivhd_suspend),
7417c8c0b82SPatrick Mooney 	DEVMETHOD(device_resume, ivhd_resume),
7427c8c0b82SPatrick Mooney 	DEVMETHOD_END
7437c8c0b82SPatrick Mooney };
7447c8c0b82SPatrick Mooney 
7457c8c0b82SPatrick Mooney static driver_t ivhd_driver = {
7467c8c0b82SPatrick Mooney 	"ivhd",
7477c8c0b82SPatrick Mooney 	ivhd_methods,
7487c8c0b82SPatrick Mooney 	sizeof(struct amdvi_softc),
7497c8c0b82SPatrick Mooney };
7507c8c0b82SPatrick Mooney 
7517c8c0b82SPatrick Mooney static devclass_t ivhd_devclass;
7527c8c0b82SPatrick Mooney 
7537c8c0b82SPatrick Mooney /*
7547c8c0b82SPatrick Mooney  * Load this module at the end after PCI re-probing to configure interrupt.
7557c8c0b82SPatrick Mooney  */
7567c8c0b82SPatrick Mooney DRIVER_MODULE_ORDERED(ivhd, acpi, ivhd_driver, ivhd_devclass, 0, 0,
7577c8c0b82SPatrick Mooney 		      SI_ORDER_ANY);
7587c8c0b82SPatrick Mooney MODULE_DEPEND(ivhd, acpi, 1, 1, 1);
7597c8c0b82SPatrick Mooney MODULE_DEPEND(ivhd, pci, 1, 1, 1);
760