amd_iommu_acpi.c revision 94f1124e84b3daba182b39a5df7b044074cb1c71
/*
* 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 2008 Sun Microsystems, Inc. All rights reserved.
* Use is subject to license terms.
*/
#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:
cmn_err(CE_PANIC, "%s: Unexpected Range End Deventry",
amd_iommu_modname);
/*NOTREACHED*/
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;
default:
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;
}
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);
}
}
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:
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);
}
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(void)
{
int i;
amd_iommu_acpi_ivhd_t *hinfop;
for (i = AMD_IOMMU_ACPI_INFO_HASH_SZ; i >= 0; i--) {
/*LINTED*/
if (hinfop = amd_iommu_acpi_ivhd_hash[i])
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--) {
/*LINTED*/
if (minfop = amd_iommu_acpi_ivmd_hash[i])
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;
} else {
cmn_err(CE_PANIC, "IVHD not found for deviceid: %x", deviceid);
}
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;
} else {
cmn_err(CE_PANIC, "IVMD not found for deviceid: %x", deviceid);
}
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);
}