/* * CDDL HEADER START * * The contents of this file are subject to the terms of the * Common Development and Distribution License (the "License"). * You may not use this file except in compliance with the License. * * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE * or http://www.opensolaris.org/os/licensing. * See the License for the specific language governing permissions * and limitations under the License. * * When distributing Covered Code, include this CDDL HEADER in each * file and include the License file at usr/src/OPENSOLARIS.LICENSE. * If applicable, add the following below this CDDL HEADER, with the * fields enclosed by brackets "[]" replaced with your own identifying * information: Portions Copyright [yyyy] [name of copyright owner] * * CDDL HEADER END */ /* * Copyright (c) 2009, 2010, Oracle and/or its affiliates. All rights reserved. */ #include "amd_iommu_acpi.h" #include "amd_iommu_impl.h" static int create_acpi_hash(amd_iommu_acpi_t *acpi); static void amd_iommu_acpi_table_fini(amd_iommu_acpi_t **acpipp); static void dump_acpi_aliases(void); /* * Globals */ static amd_iommu_acpi_global_t *amd_iommu_acpi_global; static amd_iommu_acpi_ivhd_t **amd_iommu_acpi_ivhd_hash; static amd_iommu_acpi_ivmd_t **amd_iommu_acpi_ivmd_hash; static int type_byte_size(char *cp) { uint8_t type8 = *((uint8_t *)cp); uint8_t len_bits; len_bits = AMD_IOMMU_REG_GET8(&type8, AMD_IOMMU_ACPI_DEVENTRY_LEN); switch (len_bits) { case 0: return (4); case 1: return (8); case 2: return (16); case 3: return (32); default: cmn_err(CE_WARN, "%s: Invalid deventry len: %d", amd_iommu_modname, len_bits); return (len_bits); } /*NOTREACHED*/ } static void process_4byte_deventry(ivhd_container_t *c, char *cp) { int entry_type = *((uint8_t *)cp); ivhd_deventry_t deventry = {0}; ivhd_deventry_t *devp; uint8_t datsetting8; align_16_t al = {0}; int i; /* 4 byte entry */ deventry.idev_len = 4; deventry.idev_deviceid = -1; deventry.idev_src_deviceid = -1; for (i = 0; i < 2; i++) { al.ent8[i] = *((uint8_t *)&cp[i + 1]); } switch (entry_type) { case 1: deventry.idev_type = DEVENTRY_ALL; break; case 2: deventry.idev_type = DEVENTRY_SELECT; deventry.idev_deviceid = al.ent16; break; case 3: deventry.idev_type = DEVENTRY_RANGE; deventry.idev_deviceid = al.ent16; break; case 4: deventry.idev_type = DEVENTRY_RANGE_END; deventry.idev_deviceid = al.ent16; ASSERT(cp[3] == 0); break; case 0: ASSERT(al.ent16 == 0); ASSERT(cp[3] == 0); default: return; } devp = kmem_alloc(sizeof (ivhd_deventry_t), KM_SLEEP); *devp = deventry; if (c->ivhdc_first_deventry == NULL) c->ivhdc_first_deventry = devp; else c->ivhdc_last_deventry->idev_next = devp; c->ivhdc_last_deventry = devp; if (entry_type == 4) return; datsetting8 = (*((uint8_t *)&cp[3])); devp->idev_Lint1Pass = AMD_IOMMU_REG_GET8(&datsetting8, AMD_IOMMU_ACPI_LINT1PASS); devp->idev_Lint0Pass = AMD_IOMMU_REG_GET8(&datsetting8, AMD_IOMMU_ACPI_LINT0PASS); devp->idev_SysMgt = AMD_IOMMU_REG_GET8(&datsetting8, AMD_IOMMU_ACPI_SYSMGT); ASSERT(AMD_IOMMU_REG_GET8(&datsetting8, AMD_IOMMU_ACPI_DATRSV) == 0); devp->idev_NMIPass = AMD_IOMMU_REG_GET8(&datsetting8, AMD_IOMMU_ACPI_NMIPASS); devp->idev_ExtIntPass = AMD_IOMMU_REG_GET8(&datsetting8, AMD_IOMMU_ACPI_EXTINTPASS); devp->idev_INITPass = AMD_IOMMU_REG_GET8(&datsetting8, AMD_IOMMU_ACPI_INITPASS); } static void process_8byte_deventry(ivhd_container_t *c, char *cp) { uint8_t datsetting8; int entry_type = (uint8_t)*cp; ivhd_deventry_t deventry = {0}; ivhd_deventry_t *devp; align_16_t al1 = {0}; align_16_t al2 = {0}; align_32_t al3 = {0}; int i; /* Length is 8 bytes */ deventry.idev_len = 8; deventry.idev_deviceid = -1; deventry.idev_src_deviceid = -1; for (i = 0; i < 2; i++) { al1.ent8[i] = *((uint8_t *)&cp[i+1]); al2.ent8[i] = *((uint8_t *)&cp[i+5]); } datsetting8 = *((uint8_t *)&cp[3]); switch (entry_type) { case 66: deventry.idev_type = DEVENTRY_ALIAS_SELECT; deventry.idev_deviceid = al1.ent16; deventry.idev_src_deviceid = al2.ent16; ASSERT(cp[4] == 0); ASSERT(cp[7] == 0); break; case 67: deventry.idev_type = DEVENTRY_ALIAS_RANGE; deventry.idev_deviceid = al1.ent16; deventry.idev_src_deviceid = al2.ent16; ASSERT(cp[4] == 0); ASSERT(cp[7] == 0); break; case 70: deventry.idev_type = DEVENTRY_EXTENDED_SELECT; deventry.idev_deviceid = al1.ent16; break; case 71: deventry.idev_type = DEVENTRY_EXTENDED_RANGE; deventry.idev_deviceid = al1.ent16; break; case 72: deventry.idev_type = DEVENTRY_SPECIAL_DEVICE; ASSERT(al1.ent16 == 0); deventry.idev_deviceid = -1; deventry.idev_handle = cp[4]; deventry.idev_variety = cp[7]; deventry.idev_src_deviceid = al2.ent16; default: #ifdef BROKEN_ASSERT for (i = 0; i < 7; i++) { ASSERT(cp[i] == 0); } #endif return; } devp = kmem_alloc(sizeof (ivhd_deventry_t), KM_SLEEP); *devp = deventry; if (c->ivhdc_first_deventry == NULL) c->ivhdc_first_deventry = devp; else c->ivhdc_last_deventry->idev_next = devp; c->ivhdc_last_deventry = devp; devp->idev_Lint1Pass = AMD_IOMMU_REG_GET8(&datsetting8, AMD_IOMMU_ACPI_LINT1PASS); devp->idev_Lint0Pass = AMD_IOMMU_REG_GET8(&datsetting8, AMD_IOMMU_ACPI_LINT0PASS); devp->idev_SysMgt = AMD_IOMMU_REG_GET8(&datsetting8, AMD_IOMMU_ACPI_SYSMGT); ASSERT(AMD_IOMMU_REG_GET8(&datsetting8, AMD_IOMMU_ACPI_DATRSV) == 0); devp->idev_NMIPass = AMD_IOMMU_REG_GET8(&datsetting8, AMD_IOMMU_ACPI_NMIPASS); devp->idev_ExtIntPass = AMD_IOMMU_REG_GET8(&datsetting8, AMD_IOMMU_ACPI_EXTINTPASS); devp->idev_INITPass = AMD_IOMMU_REG_GET8(&datsetting8, AMD_IOMMU_ACPI_INITPASS); if (entry_type != 70 && entry_type != 71) { return; } /* Type 70 and 71 */ for (i = 0; i < 4; i++) { al3.ent8[i] = *((uint8_t *)&cp[i+4]); } devp->idev_AtsDisabled = AMD_IOMMU_REG_GET8(&al3.ent32, AMD_IOMMU_ACPI_ATSDISABLED); ASSERT(AMD_IOMMU_REG_GET8(&al3.ent32, AMD_IOMMU_ACPI_EXTDATRSV) == 0); } static void process_ivhd(amd_iommu_acpi_t *acpi, ivhd_t *ivhdp) { ivhd_container_t *c; caddr_t ivhd_end; caddr_t ivhd_tot_end; caddr_t cp; ASSERT(ivhdp->ivhd_type == 0x10); c = kmem_zalloc(sizeof (ivhd_container_t), KM_SLEEP); c->ivhdc_ivhd = kmem_alloc(sizeof (ivhd_t), KM_SLEEP); *(c->ivhdc_ivhd) = *ivhdp; if (acpi->acp_first_ivhdc == NULL) acpi->acp_first_ivhdc = c; else acpi->acp_last_ivhdc->ivhdc_next = c; acpi->acp_last_ivhdc = c; ivhd_end = (caddr_t)ivhdp + sizeof (ivhd_t); ivhd_tot_end = (caddr_t)ivhdp + ivhdp->ivhd_len; for (cp = ivhd_end; cp < ivhd_tot_end; cp += type_byte_size(cp)) { /* 16 byte and 32 byte size are currently reserved */ switch (type_byte_size(cp)) { case 4: process_4byte_deventry(c, cp); break; case 8: process_8byte_deventry(c, cp); break; case 16: case 32: /* Reserved */ break; default: cmn_err(CE_WARN, "%s: unsupported length for device " "entry in ACPI IVRS table's IVHD entry", amd_iommu_modname); break; } } } static void process_ivmd(amd_iommu_acpi_t *acpi, ivmd_t *ivmdp) { ivmd_container_t *c; ASSERT(ivmdp->ivmd_type != 0x10); c = kmem_zalloc(sizeof (ivmd_container_t), KM_SLEEP); c->ivmdc_ivmd = kmem_alloc(sizeof (ivmd_t), KM_SLEEP); *(c->ivmdc_ivmd) = *ivmdp; if (acpi->acp_first_ivmdc == NULL) acpi->acp_first_ivmdc = c; else acpi->acp_last_ivmdc->ivmdc_next = c; acpi->acp_last_ivmdc = c; } int amd_iommu_acpi_init(void) { ivrs_t *ivrsp; caddr_t ivrsp_end; caddr_t table_end; caddr_t cp; uint8_t type8; amd_iommu_acpi_t *acpi; align_ivhd_t al_vhd = {0}; align_ivmd_t al_vmd = {0}; if (AcpiGetTable(IVRS_SIG, 1, (ACPI_TABLE_HEADER **)&ivrsp) != AE_OK) { cmn_err(CE_NOTE, "!amd_iommu: No AMD IOMMU ACPI IVRS table"); return (DDI_FAILURE); } /* * Reserved field must be 0 */ ASSERT(ivrsp->ivrs_resv == 0); ASSERT(AMD_IOMMU_REG_GET32(&ivrsp->ivrs_ivinfo, AMD_IOMMU_ACPI_IVINFO_RSV1) == 0); ASSERT(AMD_IOMMU_REG_GET32(&ivrsp->ivrs_ivinfo, AMD_IOMMU_ACPI_IVINFO_RSV2) == 0); ivrsp_end = (caddr_t)ivrsp + sizeof (struct ivrs); table_end = (caddr_t)ivrsp + ivrsp->ivrs_hdr.Length; acpi = kmem_zalloc(sizeof (amd_iommu_acpi_t), KM_SLEEP); acpi->acp_ivrs = kmem_alloc(sizeof (ivrs_t), KM_SLEEP); *(acpi->acp_ivrs) = *ivrsp; for (cp = ivrsp_end; cp < table_end; cp += (al_vhd.ivhdp)->ivhd_len) { al_vhd.cp = cp; if (al_vhd.ivhdp->ivhd_type == 0x10) process_ivhd(acpi, al_vhd.ivhdp); } for (cp = ivrsp_end; cp < table_end; cp += (al_vmd.ivmdp)->ivmd_len) { al_vmd.cp = cp; type8 = al_vmd.ivmdp->ivmd_type; if (type8 == 0x20 || type8 == 0x21 || type8 == 0x22) process_ivmd(acpi, al_vmd.ivmdp); } if (create_acpi_hash(acpi) != DDI_SUCCESS) { return (DDI_FAILURE); } amd_iommu_acpi_table_fini(&acpi); ASSERT(acpi == NULL); if (amd_iommu_debug & AMD_IOMMU_DEBUG_ACPI) { dump_acpi_aliases(); debug_enter("dump"); } return (DDI_SUCCESS); } static ivhd_deventry_t * free_ivhd_deventry(ivhd_deventry_t *devp) { ivhd_deventry_t *next = devp->idev_next; kmem_free(devp, sizeof (ivhd_deventry_t)); return (next); } static ivhd_container_t * free_ivhd_container(ivhd_container_t *ivhdcp) { ivhd_container_t *next = ivhdcp->ivhdc_next; ivhd_deventry_t *devp; for (devp = ivhdcp->ivhdc_first_deventry; devp; ) { devp = free_ivhd_deventry(devp); } kmem_free(ivhdcp->ivhdc_ivhd, sizeof (ivhd_t)); kmem_free(ivhdcp, sizeof (ivhd_container_t)); return (next); } static ivmd_container_t * free_ivmd_container(ivmd_container_t *ivmdcp) { ivmd_container_t *next = ivmdcp->ivmdc_next; kmem_free(ivmdcp->ivmdc_ivmd, sizeof (ivmd_t)); kmem_free(ivmdcp, sizeof (ivmd_container_t)); return (next); } void amd_iommu_acpi_fini(void) { } /* * TODO: Do we need to free the ACPI table for om GetFirmwareTable() */ static void amd_iommu_acpi_table_fini(amd_iommu_acpi_t **acpipp) { amd_iommu_acpi_t *acpi = *acpipp; ivhd_container_t *ivhdcp; ivmd_container_t *ivmdcp; ASSERT(acpi); for (ivhdcp = acpi->acp_first_ivhdc; ivhdcp; ) { ivhdcp = free_ivhd_container(ivhdcp); } for (ivmdcp = acpi->acp_first_ivmdc; ivmdcp; ) { ivmdcp = free_ivmd_container(ivmdcp); } kmem_free(acpi->acp_ivrs, sizeof (struct ivrs)); kmem_free(acpi, sizeof (amd_iommu_acpi_t)); *acpipp = NULL; } static uint16_t deviceid_hashfn(uint16_t deviceid) { return (deviceid % AMD_IOMMU_ACPI_INFO_HASH_SZ); } static void add_deventry_info(ivhd_t *ivhdp, ivhd_deventry_t *deventry, amd_iommu_acpi_ivhd_t **hash) { static amd_iommu_acpi_ivhd_t *last; amd_iommu_acpi_ivhd_t *acpi_ivhdp; uint8_t uint8_flags; uint16_t uint16_info; uint16_t idx; if (deventry->idev_type == DEVENTRY_RANGE_END) { ASSERT(last); acpi_ivhdp = last; last = NULL; ASSERT(acpi_ivhdp->ach_dev_type == DEVENTRY_RANGE || acpi_ivhdp->ach_dev_type == DEVENTRY_ALIAS_RANGE || acpi_ivhdp->ach_dev_type == DEVENTRY_EXTENDED_RANGE); ASSERT(acpi_ivhdp->ach_deviceid_end == -1); acpi_ivhdp->ach_deviceid_end = deventry->idev_deviceid; /* TODO ASSERT data is 0 */ return; } ASSERT(last == NULL); acpi_ivhdp = kmem_zalloc(sizeof (*acpi_ivhdp), KM_SLEEP); uint8_flags = ivhdp->ivhd_flags; #ifdef BROKEN_ASSERT ASSERT(AMD_IOMMU_REG_GET8(&uint8_flags, AMD_IOMMU_ACPI_IVHD_FLAGS_RSV) == 0); #endif acpi_ivhdp->ach_IotlbSup = AMD_IOMMU_REG_GET8(&uint8_flags, AMD_IOMMU_ACPI_IVHD_FLAGS_IOTLBSUP); acpi_ivhdp->ach_Isoc = AMD_IOMMU_REG_GET8(&uint8_flags, AMD_IOMMU_ACPI_IVHD_FLAGS_ISOC); acpi_ivhdp->ach_ResPassPW = AMD_IOMMU_REG_GET8(&uint8_flags, AMD_IOMMU_ACPI_IVHD_FLAGS_RESPASSPW); acpi_ivhdp->ach_PassPW = AMD_IOMMU_REG_GET8(&uint8_flags, AMD_IOMMU_ACPI_IVHD_FLAGS_PASSPW); acpi_ivhdp->ach_HtTunEn = AMD_IOMMU_REG_GET8(&uint8_flags, AMD_IOMMU_ACPI_IVHD_FLAGS_HTTUNEN); /* IVHD fields */ acpi_ivhdp->ach_IOMMU_deviceid = ivhdp->ivhd_deviceid; acpi_ivhdp->ach_IOMMU_cap_off = ivhdp->ivhd_cap_off; acpi_ivhdp->ach_IOMMU_reg_base = ivhdp->ivhd_reg_base; acpi_ivhdp->ach_IOMMU_pci_seg = ivhdp->ivhd_pci_seg; /* IVHD IOMMU info fields */ uint16_info = ivhdp->ivhd_iommu_info; #ifdef BROKEN_ASSERT ASSERT(AMD_IOMMU_REG_GET16(&uint16_info, AMD_IOMMU_ACPI_IOMMU_INFO_RSV1) == 0); #endif acpi_ivhdp->ach_IOMMU_UnitID = AMD_IOMMU_REG_GET16(&uint16_info, AMD_IOMMU_ACPI_IOMMU_INFO_UNITID); ASSERT(AMD_IOMMU_REG_GET16(&uint16_info, AMD_IOMMU_ACPI_IOMMU_INFO_RSV2) == 0); acpi_ivhdp->ach_IOMMU_MSInum = AMD_IOMMU_REG_GET16(&uint16_info, AMD_IOMMU_ACPI_IOMMU_INFO_MSINUM); /* Initialize deviceids to -1 */ acpi_ivhdp->ach_deviceid_start = -1; acpi_ivhdp->ach_deviceid_end = -1; acpi_ivhdp->ach_src_deviceid = -1; /* All range type entries are put on hash entry 0 */ switch (deventry->idev_type) { case DEVENTRY_ALL: acpi_ivhdp->ach_deviceid_start = 0; acpi_ivhdp->ach_deviceid_end = (uint16_t)-1; acpi_ivhdp->ach_dev_type = DEVENTRY_ALL; idx = AMD_IOMMU_ACPI_INFO_HASH_SZ; break; case DEVENTRY_SELECT: acpi_ivhdp->ach_deviceid_start = deventry->idev_deviceid; acpi_ivhdp->ach_deviceid_end = deventry->idev_deviceid; acpi_ivhdp->ach_dev_type = DEVENTRY_SELECT; idx = deviceid_hashfn(deventry->idev_deviceid); break; case DEVENTRY_RANGE: acpi_ivhdp->ach_deviceid_start = deventry->idev_deviceid; acpi_ivhdp->ach_deviceid_end = -1; acpi_ivhdp->ach_dev_type = DEVENTRY_RANGE; idx = AMD_IOMMU_ACPI_INFO_HASH_SZ; last = acpi_ivhdp; break; case DEVENTRY_RANGE_END: idx = 0; cmn_err(CE_PANIC, "%s: Unexpected Range End Deventry", amd_iommu_modname); /*NOTREACHED*/ break; case DEVENTRY_ALIAS_SELECT: acpi_ivhdp->ach_deviceid_start = deventry->idev_deviceid; acpi_ivhdp->ach_deviceid_end = deventry->idev_deviceid; acpi_ivhdp->ach_src_deviceid = deventry->idev_src_deviceid; acpi_ivhdp->ach_dev_type = DEVENTRY_ALIAS_SELECT; idx = deviceid_hashfn(deventry->idev_deviceid); break; case DEVENTRY_ALIAS_RANGE: acpi_ivhdp->ach_deviceid_start = deventry->idev_deviceid; acpi_ivhdp->ach_deviceid_end = -1; acpi_ivhdp->ach_src_deviceid = deventry->idev_src_deviceid; acpi_ivhdp->ach_dev_type = DEVENTRY_ALIAS_RANGE; idx = AMD_IOMMU_ACPI_INFO_HASH_SZ; last = acpi_ivhdp; break; case DEVENTRY_EXTENDED_SELECT: acpi_ivhdp->ach_deviceid_start = deventry->idev_deviceid; acpi_ivhdp->ach_deviceid_end = deventry->idev_deviceid; acpi_ivhdp->ach_dev_type = DEVENTRY_EXTENDED_SELECT; idx = deviceid_hashfn(deventry->idev_deviceid); break; case DEVENTRY_EXTENDED_RANGE: acpi_ivhdp->ach_deviceid_start = deventry->idev_deviceid; acpi_ivhdp->ach_deviceid_end = -1; acpi_ivhdp->ach_dev_type = DEVENTRY_EXTENDED_RANGE; idx = AMD_IOMMU_ACPI_INFO_HASH_SZ; last = acpi_ivhdp; break; case DEVENTRY_SPECIAL_DEVICE: acpi_ivhdp->ach_deviceid_start = -1; acpi_ivhdp->ach_deviceid_end = -1; acpi_ivhdp->ach_src_deviceid = deventry->idev_src_deviceid; acpi_ivhdp->ach_special_handle = deventry->idev_handle; acpi_ivhdp->ach_special_variety = deventry->idev_variety; idx = AMD_IOMMU_ACPI_INFO_HASH_SZ; break; default: idx = 0; cmn_err(CE_PANIC, "%s: Unsupported deventry type", amd_iommu_modname); /*NOTREACHED*/ } acpi_ivhdp->ach_Lint1Pass = deventry->idev_Lint1Pass; acpi_ivhdp->ach_Lint0Pass = deventry->idev_Lint0Pass; acpi_ivhdp->ach_SysMgt = deventry->idev_SysMgt; acpi_ivhdp->ach_NMIPass = deventry->idev_NMIPass; acpi_ivhdp->ach_ExtIntPass = deventry->idev_ExtIntPass; acpi_ivhdp->ach_INITPass = deventry->idev_INITPass; /* extended data */ if (acpi_ivhdp->ach_dev_type == DEVENTRY_EXTENDED_SELECT || acpi_ivhdp->ach_dev_type == DEVENTRY_EXTENDED_RANGE) { acpi_ivhdp->ach_AtsDisabled = deventry->idev_AtsDisabled; } /* * Now add it to the hash */ ASSERT(hash[idx] != acpi_ivhdp); acpi_ivhdp->ach_next = hash[idx]; hash[idx] = acpi_ivhdp; } /* * A device entry may be declared implicitly as a source device ID * in an alias entry. This routine adds it to the hash */ static void add_implicit_deventry(ivhd_container_t *ivhdcp, amd_iommu_acpi_ivhd_t **hash) { ivhd_deventry_t *d; int deviceid; for (d = ivhdcp->ivhdc_first_deventry; d; d = d->idev_next) { if ((d->idev_type != DEVENTRY_ALIAS_SELECT) && (d->idev_type != DEVENTRY_ALIAS_RANGE)) continue; deviceid = d->idev_src_deviceid; if (amd_iommu_lookup_ivhd(deviceid) == NULL) { ivhd_deventry_t deventry; /* Fake a SELECT entry */ deventry.idev_type = DEVENTRY_SELECT; deventry.idev_len = 4; deventry.idev_deviceid = deviceid; deventry.idev_src_deviceid = -1; deventry.idev_Lint1Pass = d->idev_Lint1Pass; deventry.idev_Lint0Pass = d->idev_Lint0Pass; deventry.idev_SysMgt = d->idev_SysMgt; deventry.idev_NMIPass = d->idev_NMIPass; deventry.idev_ExtIntPass = d->idev_ExtIntPass; deventry.idev_INITPass = d->idev_INITPass; add_deventry_info(ivhdcp->ivhdc_ivhd, &deventry, hash); if (amd_iommu_debug & AMD_IOMMU_DEBUG_ACPI) { cmn_err(CE_NOTE, "Added implicit IVHD entry " "for: deviceid = %u", deviceid); } } } } static void add_ivhdc_info(ivhd_container_t *ivhdcp, amd_iommu_acpi_ivhd_t **hash) { ivhd_deventry_t *deventry; ivhd_t *ivhdp = ivhdcp->ivhdc_ivhd; for (deventry = ivhdcp->ivhdc_first_deventry; deventry; deventry = deventry->idev_next) { add_deventry_info(ivhdp, deventry, hash); } add_implicit_deventry(ivhdcp, hash); } static void add_ivhd_info(amd_iommu_acpi_t *acpi, amd_iommu_acpi_ivhd_t **hash) { ivhd_container_t *ivhdcp; for (ivhdcp = acpi->acp_first_ivhdc; ivhdcp; ivhdcp = ivhdcp->ivhdc_next) { add_ivhdc_info(ivhdcp, hash); } } static void set_ivmd_info(ivmd_t *ivmdp, amd_iommu_acpi_ivmd_t **hash) { amd_iommu_acpi_ivmd_t *acpi_ivmdp; uint8_t uint8_flags; uint16_t idx; uint8_flags = ivmdp->ivmd_flags; acpi_ivmdp = kmem_zalloc(sizeof (*acpi_ivmdp), KM_SLEEP); switch (ivmdp->ivmd_type) { case 0x20: acpi_ivmdp->acm_deviceid_start = 0; acpi_ivmdp->acm_deviceid_end = (uint16_t)-1; acpi_ivmdp->acm_dev_type = IVMD_DEVICEID_ALL; idx = AMD_IOMMU_ACPI_INFO_HASH_SZ; break; case 0x21: acpi_ivmdp->acm_deviceid_start = ivmdp->ivmd_deviceid; acpi_ivmdp->acm_deviceid_end = ivmdp->ivmd_deviceid; acpi_ivmdp->acm_dev_type = IVMD_DEVICEID_SELECT; idx = deviceid_hashfn(ivmdp->ivmd_deviceid); break; case 0x22: acpi_ivmdp->acm_deviceid_start = ivmdp->ivmd_deviceid; acpi_ivmdp->acm_deviceid_end = ivmdp->ivmd_auxdata; acpi_ivmdp->acm_dev_type = IVMD_DEVICEID_RANGE; idx = AMD_IOMMU_ACPI_INFO_HASH_SZ; break; default: idx = 0; cmn_err(CE_PANIC, "Unknown AMD IOMMU ACPI IVMD deviceid type: " "%x", ivmdp->ivmd_type); /*NOTREACHED*/ } ASSERT(AMD_IOMMU_REG_GET8(&uint8_flags, AMD_IOMMU_ACPI_IVMD_RSV) == 0); acpi_ivmdp->acm_ExclRange = AMD_IOMMU_REG_GET8(&uint8_flags, AMD_IOMMU_ACPI_IVMD_EXCL_RANGE); acpi_ivmdp->acm_IW = AMD_IOMMU_REG_GET8(&uint8_flags, AMD_IOMMU_ACPI_IVMD_IW); acpi_ivmdp->acm_IR = AMD_IOMMU_REG_GET8(&uint8_flags, AMD_IOMMU_ACPI_IVMD_IR); acpi_ivmdp->acm_Unity = AMD_IOMMU_REG_GET8(&uint8_flags, AMD_IOMMU_ACPI_IVMD_UNITY); acpi_ivmdp->acm_ivmd_phys_start = ivmdp->ivmd_phys_start; acpi_ivmdp->acm_ivmd_phys_len = ivmdp->ivmd_phys_len; acpi_ivmdp->acm_next = hash[idx]; hash[idx] = acpi_ivmdp; } static void add_ivmdc_info(ivmd_container_t *ivmdcp, amd_iommu_acpi_ivmd_t **hash) { set_ivmd_info(ivmdcp->ivmdc_ivmd, hash); } static void add_ivmd_info(amd_iommu_acpi_t *acpi, amd_iommu_acpi_ivmd_t **hash) { ivmd_container_t *ivmdcp; for (ivmdcp = acpi->acp_first_ivmdc; ivmdcp; ivmdcp = ivmdcp->ivmdc_next) { add_ivmdc_info(ivmdcp, hash); } } static void add_global_info(amd_iommu_acpi_t *acpi, amd_iommu_acpi_global_t *global) { uint32_t ivrs_ivinfo = acpi->acp_ivrs->ivrs_ivinfo; global->acg_HtAtsResv = AMD_IOMMU_REG_GET32(&ivrs_ivinfo, AMD_IOMMU_ACPI_HT_ATSRSV); global->acg_VAsize = AMD_IOMMU_REG_GET32(&ivrs_ivinfo, AMD_IOMMU_ACPI_VA_SIZE); global->acg_PAsize = AMD_IOMMU_REG_GET32(&ivrs_ivinfo, AMD_IOMMU_ACPI_PA_SIZE); } static int create_acpi_hash(amd_iommu_acpi_t *acpi) { /* Last hash entry is for deviceid ranges including "all" */ amd_iommu_acpi_global = kmem_zalloc(sizeof (amd_iommu_acpi_global_t), KM_SLEEP); amd_iommu_acpi_ivhd_hash = kmem_zalloc(sizeof (amd_iommu_acpi_ivhd_t *) * (AMD_IOMMU_ACPI_INFO_HASH_SZ + 1), KM_SLEEP); amd_iommu_acpi_ivmd_hash = kmem_zalloc(sizeof (amd_iommu_acpi_ivmd_t *) * (AMD_IOMMU_ACPI_INFO_HASH_SZ + 1), KM_SLEEP); add_global_info(acpi, amd_iommu_acpi_global); add_ivhd_info(acpi, amd_iommu_acpi_ivhd_hash); add_ivmd_info(acpi, amd_iommu_acpi_ivmd_hash); return (DDI_SUCCESS); } static void set_deventry(amd_iommu_t *iommu, int entry, amd_iommu_acpi_ivhd_t *hinfop) { uint64_t *dentry; dentry = (uint64_t *)(intptr_t) &iommu->aiomt_devtbl[entry * AMD_IOMMU_DEVTBL_ENTRY_SZ]; AMD_IOMMU_REG_SET64(&(dentry[1]), AMD_IOMMU_DEVTBL_SYSMGT, hinfop->ach_SysMgt); } /* Initialize device table according to IVHD */ int amd_iommu_acpi_init_devtbl(amd_iommu_t *iommu) { int i, j; amd_iommu_acpi_ivhd_t *hinfop; for (i = 0; i <= AMD_IOMMU_ACPI_INFO_HASH_SZ; i++) { for (hinfop = amd_iommu_acpi_ivhd_hash[i]; hinfop; hinfop = hinfop->ach_next) { if (hinfop->ach_IOMMU_deviceid != iommu->aiomt_bdf) continue; switch (hinfop->ach_dev_type) { case DEVENTRY_ALL: for (j = 0; j < AMD_IOMMU_MAX_DEVICEID; j++) set_deventry(iommu, j, hinfop); break; case DEVENTRY_SELECT: case DEVENTRY_EXTENDED_SELECT: set_deventry(iommu, hinfop->ach_deviceid_start, hinfop); break; case DEVENTRY_RANGE: case DEVENTRY_EXTENDED_RANGE: for (j = hinfop->ach_deviceid_start; j <= hinfop->ach_deviceid_end; j++) set_deventry(iommu, j, hinfop); break; case DEVENTRY_ALIAS_SELECT: case DEVENTRY_ALIAS_RANGE: case DEVENTRY_SPECIAL_DEVICE: set_deventry(iommu, hinfop->ach_src_deviceid, hinfop); break; default: cmn_err(CE_WARN, "%s: Unknown deventry type", amd_iommu_modname); return (DDI_FAILURE); } } } return (DDI_SUCCESS); } amd_iommu_acpi_global_t * amd_iommu_lookup_acpi_global(void) { ASSERT(amd_iommu_acpi_global); return (amd_iommu_acpi_global); } amd_iommu_acpi_ivhd_t * amd_iommu_lookup_all_ivhd(void) { amd_iommu_acpi_ivhd_t *hinfop; hinfop = amd_iommu_acpi_ivhd_hash[AMD_IOMMU_ACPI_INFO_HASH_SZ]; for (; hinfop; hinfop = hinfop->ach_next) { if (hinfop->ach_deviceid_start == 0 && hinfop->ach_deviceid_end == (uint16_t)-1) { break; } } return (hinfop); } amd_iommu_acpi_ivmd_t * amd_iommu_lookup_all_ivmd(void) { amd_iommu_acpi_ivmd_t *minfop; minfop = amd_iommu_acpi_ivmd_hash[AMD_IOMMU_ACPI_INFO_HASH_SZ]; for (; minfop; minfop = minfop->acm_next) { if (minfop->acm_deviceid_start == 0 && minfop->acm_deviceid_end == (uint16_t)-1) { break; } } return (minfop); } amd_iommu_acpi_ivhd_t * amd_iommu_lookup_any_ivhd(amd_iommu_t *iommu) { int i; amd_iommu_acpi_ivhd_t *hinfop; for (i = AMD_IOMMU_ACPI_INFO_HASH_SZ; i >= 0; i--) { hinfop = amd_iommu_acpi_ivhd_hash[i]; if ((hinfop != NULL) && hinfop->ach_IOMMU_deviceid == iommu->aiomt_bdf) break; } return (hinfop); } amd_iommu_acpi_ivmd_t * amd_iommu_lookup_any_ivmd(void) { int i; amd_iommu_acpi_ivmd_t *minfop; for (i = AMD_IOMMU_ACPI_INFO_HASH_SZ; i >= 0; i--) { if ((minfop = amd_iommu_acpi_ivmd_hash[i]) != NULL) break; } return (minfop); } static void dump_acpi_aliases(void) { amd_iommu_acpi_ivhd_t *hinfop; uint16_t idx; for (idx = 0; idx <= AMD_IOMMU_ACPI_INFO_HASH_SZ; idx++) { hinfop = amd_iommu_acpi_ivhd_hash[idx]; for (; hinfop; hinfop = hinfop->ach_next) { cmn_err(CE_NOTE, "start=%d, end=%d, src_bdf=%d", hinfop->ach_deviceid_start, hinfop->ach_deviceid_end, hinfop->ach_src_deviceid); } } } amd_iommu_acpi_ivhd_t * amd_iommu_lookup_ivhd(int32_t deviceid) { amd_iommu_acpi_ivhd_t *hinfop; uint16_t idx; if (amd_iommu_debug & AMD_IOMMU_DEBUG_ACPI) { cmn_err(CE_NOTE, "Attempting to get ACPI IVHD info " "for deviceid: %d", deviceid); } ASSERT(amd_iommu_acpi_ivhd_hash); /* check if special device */ if (deviceid == -1) { hinfop = amd_iommu_acpi_ivhd_hash[AMD_IOMMU_ACPI_INFO_HASH_SZ]; for (; hinfop; hinfop = hinfop->ach_next) { if (hinfop->ach_deviceid_start == -1 && hinfop->ach_deviceid_end == -1) { break; } } return (hinfop); } /* First search for an exact match */ idx = deviceid_hashfn(deviceid); range: hinfop = amd_iommu_acpi_ivhd_hash[idx]; for (; hinfop; hinfop = hinfop->ach_next) { if (deviceid < hinfop->ach_deviceid_start || deviceid > hinfop->ach_deviceid_end) continue; if (amd_iommu_debug & AMD_IOMMU_DEBUG_ACPI) { cmn_err(CE_NOTE, "Found ACPI IVHD match: %p, " "actual deviceid = %u, start = %u, end = %u", (void *)hinfop, deviceid, hinfop->ach_deviceid_start, hinfop->ach_deviceid_end); } goto out; } if (idx != AMD_IOMMU_ACPI_INFO_HASH_SZ) { idx = AMD_IOMMU_ACPI_INFO_HASH_SZ; goto range; } out: if (amd_iommu_debug & AMD_IOMMU_DEBUG_ACPI) { cmn_err(CE_NOTE, "%u: %s ACPI IVHD %p", deviceid, hinfop ? "GOT" : "Did NOT get", (void *)hinfop); } return (hinfop); } amd_iommu_acpi_ivmd_t * amd_iommu_lookup_ivmd(int32_t deviceid) { amd_iommu_acpi_ivmd_t *minfop; uint16_t idx; if (amd_iommu_debug & AMD_IOMMU_DEBUG_ACPI) { cmn_err(CE_NOTE, "Attempting to get ACPI IVMD info " "for deviceid: %u", deviceid); } ASSERT(amd_iommu_acpi_ivmd_hash); /* First search for an exact match */ idx = deviceid_hashfn(deviceid); range: minfop = amd_iommu_acpi_ivmd_hash[idx]; for (; minfop; minfop = minfop->acm_next) { if (deviceid < minfop->acm_deviceid_start && deviceid > minfop->acm_deviceid_end) continue; if (amd_iommu_debug & AMD_IOMMU_DEBUG_ACPI) { cmn_err(CE_NOTE, "Found ACPI IVMD match: %p, " "actual deviceid = %u, start = %u, end = %u", (void *)minfop, deviceid, minfop->acm_deviceid_start, minfop->acm_deviceid_end); } goto out; } if (idx != AMD_IOMMU_ACPI_INFO_HASH_SZ) { idx = AMD_IOMMU_ACPI_INFO_HASH_SZ; goto range; } out: if (amd_iommu_debug & AMD_IOMMU_DEBUG_ACPI) { cmn_err(CE_NOTE, "%u: %s ACPI IVMD info %p", deviceid, minfop ? "GOT" : "Did NOT get", (void *)minfop); } return (minfop); }