7d87efa8fdfb9453670f2832df666fdae8291a84jmcp * CDDL HEADER START
7d87efa8fdfb9453670f2832df666fdae8291a84jmcp * The contents of this file are subject to the terms of the
7d87efa8fdfb9453670f2832df666fdae8291a84jmcp * Common Development and Distribution License (the "License").
7d87efa8fdfb9453670f2832df666fdae8291a84jmcp * You may not use this file except in compliance with the License.
7d87efa8fdfb9453670f2832df666fdae8291a84jmcp * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
7d87efa8fdfb9453670f2832df666fdae8291a84jmcp * See the License for the specific language governing permissions
7d87efa8fdfb9453670f2832df666fdae8291a84jmcp * and limitations under the License.
7d87efa8fdfb9453670f2832df666fdae8291a84jmcp * When distributing Covered Code, include this CDDL HEADER in each
7d87efa8fdfb9453670f2832df666fdae8291a84jmcp * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
7d87efa8fdfb9453670f2832df666fdae8291a84jmcp * If applicable, add the following below this CDDL HEADER, with the
7d87efa8fdfb9453670f2832df666fdae8291a84jmcp * fields enclosed by brackets "[]" replaced with your own identifying
7d87efa8fdfb9453670f2832df666fdae8291a84jmcp * information: Portions Copyright [yyyy] [name of copyright owner]
7d87efa8fdfb9453670f2832df666fdae8291a84jmcp * CDDL HEADER END
ba758cf1b2fe06a606303351c36a766f2f9f6665Jerry Gilliam * Copyright (c) 2009, 2010, Oracle and/or its affiliates. All rights reserved.
7d87efa8fdfb9453670f2832df666fdae8291a84jmcp 0U, /* dma_attr_addr_lo */
7d87efa8fdfb9453670f2832df666fdae8291a84jmcp 0 /* dma_attr_flags */
7d87efa8fdfb9453670f2832df666fdae8291a84jmcpstatic struct {
7d87efa8fdfb9453670f2832df666fdae8291a84jmcp/*ARGSUSED*/
7d87efa8fdfb9453670f2832df666fdae8291a84jmcpamd_iommu_get_src_bdf(amd_iommu_t *iommu, int32_t bdf, int32_t *src_bdfp)
ba758cf1b2fe06a606303351c36a766f2f9f6665Jerry Gilliam cmn_err(CE_WARN, "No IVHD entry for 0x%x", bdf);
7d87efa8fdfb9453670f2832df666fdae8291a84jmcp/*ARGSUSED*/
7d87efa8fdfb9453670f2832df666fdae8291a84jmcpamd_iommu_get_domain(amd_iommu_t *iommu, dev_info_t *rdip, int alias,
7d87efa8fdfb9453670f2832df666fdae8291a84jmcp uint16_t deviceid, domain_id_t *domainid, const char *path)
7d87efa8fdfb9453670f2832df666fdae8291a84jmcp const char *f = "amd_iommu_get_domain";
7d87efa8fdfb9453670f2832df666fdae8291a84jmcp/*ARGSUSED*/
7d87efa8fdfb9453670f2832df666fdae8291a84jmcp sizeof (amd_iommu_domain_t *) * AMD_IOMMU_DOMAIN_HASH_SZ, KM_SLEEP);
7d87efa8fdfb9453670f2832df666fdae8291a84jmcp/*ARGSUSED*/
7d87efa8fdfb9453670f2832df666fdae8291a84jmcp sizeof (amd_iommu_domain_t *) * AMD_IOMMU_DOMAIN_HASH_SZ);
7d87efa8fdfb9453670f2832df666fdae8291a84jmcpamd_iommu_lookup_domain(amd_iommu_t *iommu, domain_id_t domainid,
7d87efa8fdfb9453670f2832df666fdae8291a84jmcp for (dp = amd_iommu_domain_table[idx]; dp; dp = dp->d_next) {
7d87efa8fdfb9453670f2832df666fdae8291a84jmcp (void) snprintf(name, sizeof (name), "dvma_idx%d_domain%d",
7d87efa8fdfb9453670f2832df666fdae8291a84jmcp dp->d_vmem = vmem_create(name, (void *)(uintptr_t)base, size,
7d87efa8fdfb9453670f2832df666fdae8291a84jmcpstatic void
7d87efa8fdfb9453670f2832df666fdae8291a84jmcpamd_iommu_teardown_domain(amd_iommu_t *iommu, amd_iommu_domain_t *dp)
7d87efa8fdfb9453670f2832df666fdae8291a84jmcp const char *f = "amd_iommu_teardown_domain";
7d87efa8fdfb9453670f2832df666fdae8291a84jmcp if (amd_iommu_cmd(iommu, AMD_IOMMU_CMD_INVAL_IOMMU_PAGES,
7d87efa8fdfb9453670f2832df666fdae8291a84jmcp "Failed to invalidate domain in IOMMU HW cache",
7d87efa8fdfb9453670f2832df666fdae8291a84jmcpamd_iommu_get_deviceid(amd_iommu_t *iommu, dev_info_t *rdip, int32_t *deviceid,
7d87efa8fdfb9453670f2832df666fdae8291a84jmcp const char *f = "amd_iommu_get_deviceid";
7d87efa8fdfb9453670f2832df666fdae8291a84jmcp /* be conservative. Always assume an alias */
7d87efa8fdfb9453670f2832df666fdae8291a84jmcp /* Check for special special devices (rdip == NULL) */
7d87efa8fdfb9453670f2832df666fdae8291a84jmcp if (amd_iommu_get_src_bdf(iommu, -1, &src_bdf) != DDI_SUCCESS) {
7d87efa8fdfb9453670f2832df666fdae8291a84jmcp "%s: %s%d: idx=%d, failed to get SRC BDF "
7d87efa8fdfb9453670f2832df666fdae8291a84jmcp "for special-device",
7d87efa8fdfb9453670f2832df666fdae8291a84jmcp cmn_err(CE_NOTE, "%s: attempting to get deviceid for %s",
7d87efa8fdfb9453670f2832df666fdae8291a84jmcp cmn_err(CE_WARN, "%s: %s%d: idx = %d, failed to get PCI dip "
7d87efa8fdfb9453670f2832df666fdae8291a84jmcp "for rdip=%p, path = %s",
7d87efa8fdfb9453670f2832df666fdae8291a84jmcp if (acpica_get_bdf(pci_dip, &bus, &device, &func) != DDI_SUCCESS) {
7d87efa8fdfb9453670f2832df666fdae8291a84jmcp cmn_err(CE_WARN, "%s: %s%d: idx=%d, failed to get BDF for "
7d87efa8fdfb9453670f2832df666fdae8291a84jmcp "PCI dip (%p). rdip path = %s",
7d87efa8fdfb9453670f2832df666fdae8291a84jmcp cmn_err(CE_WARN, "%s: %s%d: idx=%d, invalid BDF(%d,%d,%d) "
7d87efa8fdfb9453670f2832df666fdae8291a84jmcp "for PCI dip (%p). rdip path = %s", f, driver, instance,
7d87efa8fdfb9453670f2832df666fdae8291a84jmcp bdf = ((uint8_t)bus << 8) | ((uint8_t)device << 3) | (uint8_t)func;
7d87efa8fdfb9453670f2832df666fdae8291a84jmcp if (amd_iommu_get_src_bdf(iommu, bdf, &src_bdf) != DDI_SUCCESS) {
7d87efa8fdfb9453670f2832df666fdae8291a84jmcp cmn_err(CE_WARN, "%s: %s%d: idx=%d, failed to get SRC BDF "
7d87efa8fdfb9453670f2832df666fdae8291a84jmcp "for PCI dip (%p) rdip path = %s.",
7d87efa8fdfb9453670f2832df666fdae8291a84jmcp f, driver, instance, iommu->aiomt_idx, (void *)pci_dip,
7d87efa8fdfb9453670f2832df666fdae8291a84jmcp/*ARGSUSED*/
7d87efa8fdfb9453670f2832df666fdae8291a84jmcpinit_devtbl(amd_iommu_t *iommu, uint64_t *devtbl_entry, domain_id_t domainid,
7d87efa8fdfb9453670f2832df666fdae8291a84jmcp /* If already passthru, don't touch */
7d87efa8fdfb9453670f2832df666fdae8291a84jmcp if (AMD_IOMMU_REG_GET64(&(devtbl_entry[0]), AMD_IOMMU_DEVTBL_V) == 0 &&
7d87efa8fdfb9453670f2832df666fdae8291a84jmcp AMD_IOMMU_REG_GET64(&(devtbl_entry[0]), AMD_IOMMU_DEVTBL_TV) == 0) {
7d87efa8fdfb9453670f2832df666fdae8291a84jmcp return (0);
7d87efa8fdfb9453670f2832df666fdae8291a84jmcp if (AMD_IOMMU_REG_GET64(&(devtbl_entry[0]), AMD_IOMMU_DEVTBL_V) == 1 &&
7d87efa8fdfb9453670f2832df666fdae8291a84jmcp AMD_IOMMU_REG_GET64(&(devtbl_entry[0]), AMD_IOMMU_DEVTBL_TV) == 1) {
7d87efa8fdfb9453670f2832df666fdae8291a84jmcp ASSERT(dp->d_domainid == AMD_IOMMU_REG_GET64(&(devtbl_entry[1]),
7d87efa8fdfb9453670f2832df666fdae8291a84jmcp return (0);
7d87efa8fdfb9453670f2832df666fdae8291a84jmcp /* New devtbl entry for this domain. Bump up the domain ref-count */
7d87efa8fdfb9453670f2832df666fdae8291a84jmcp AMD_IOMMU_REG_SET64(&(entry[1]), AMD_IOMMU_DEVTBL_EX, 1);
7d87efa8fdfb9453670f2832df666fdae8291a84jmcp AMD_IOMMU_REG_SET64(&(entry[1]), AMD_IOMMU_DEVTBL_SD, 0);
7d87efa8fdfb9453670f2832df666fdae8291a84jmcp AMD_IOMMU_REG_SET64(&(entry[1]), AMD_IOMMU_DEVTBL_CACHE, 0);
7d87efa8fdfb9453670f2832df666fdae8291a84jmcp AMD_IOMMU_REG_SET64(&(entry[1]), AMD_IOMMU_DEVTBL_IOCTL, 1);
7d87efa8fdfb9453670f2832df666fdae8291a84jmcp AMD_IOMMU_REG_SET64(&(entry[1]), AMD_IOMMU_DEVTBL_SA, 0);
7d87efa8fdfb9453670f2832df666fdae8291a84jmcp AMD_IOMMU_REG_SET64(&(entry[1]), AMD_IOMMU_DEVTBL_SE, 1);
7d87efa8fdfb9453670f2832df666fdae8291a84jmcp AMD_IOMMU_REG_SET64(&(entry[1]), AMD_IOMMU_DEVTBL_DOMAINID,
7d87efa8fdfb9453670f2832df666fdae8291a84jmcp AMD_IOMMU_REG_SET64(&(entry[0]), AMD_IOMMU_DEVTBL_IW, 1);
7d87efa8fdfb9453670f2832df666fdae8291a84jmcp AMD_IOMMU_REG_SET64(&(entry[0]), AMD_IOMMU_DEVTBL_IR, 1);
7d87efa8fdfb9453670f2832df666fdae8291a84jmcp AMD_IOMMU_REG_SET64(&(entry[0]), AMD_IOMMU_DEVTBL_ROOT_PGTBL,
7d87efa8fdfb9453670f2832df666fdae8291a84jmcp AMD_IOMMU_REG_SET64(&(entry[0]), AMD_IOMMU_DEVTBL_PG_MODE,
7d87efa8fdfb9453670f2832df666fdae8291a84jmcp /* we did an actual init */
7d87efa8fdfb9453670f2832df666fdae8291a84jmcp return (1);
7d87efa8fdfb9453670f2832df666fdae8291a84jmcpamd_iommu_set_passthru(amd_iommu_t *iommu, dev_info_t *rdip)
7d87efa8fdfb9453670f2832df666fdae8291a84jmcp const char *f = "amd_iommu_set_passthru";
7d87efa8fdfb9453670f2832df666fdae8291a84jmcp if (amd_iommu_get_deviceid(iommu, rdip, &deviceid, &alias, path)
7d87efa8fdfb9453670f2832df666fdae8291a84jmcp /* No deviceid */
7d87efa8fdfb9453670f2832df666fdae8291a84jmcp cmn_err(CE_WARN, "%s: %s%d: IOMMU idx=%d, deviceid (%u) "
7d87efa8fdfb9453670f2832df666fdae8291a84jmcp "for rdip (%p) exceeds device table size (%u), path=%s",
7d87efa8fdfb9453670f2832df666fdae8291a84jmcp /*LINTED*/
7d87efa8fdfb9453670f2832df666fdae8291a84jmcp V = AMD_IOMMU_REG_GET64(&(devtbl_entry[0]), AMD_IOMMU_DEVTBL_V);
7d87efa8fdfb9453670f2832df666fdae8291a84jmcp TV = AMD_IOMMU_REG_GET64(&(devtbl_entry[0]), AMD_IOMMU_DEVTBL_TV);
7d87efa8fdfb9453670f2832df666fdae8291a84jmcp /* Already passthru */
7d87efa8fdfb9453670f2832df666fdae8291a84jmcp if (V == 0 && TV == 0) {
7d87efa8fdfb9453670f2832df666fdae8291a84jmcp /* Existing translations */
7d87efa8fdfb9453670f2832df666fdae8291a84jmcp /* Invalid setting */
7d87efa8fdfb9453670f2832df666fdae8291a84jmcp AMD_IOMMU_REG_SET64(&(devtbl_entry[0]), AMD_IOMMU_DEVTBL_V, 0);
7d87efa8fdfb9453670f2832df666fdae8291a84jmcp (void) amd_iommu_cmd(iommu, AMD_IOMMU_CMD_INVAL_DEVTAB_ENTRY,
7d87efa8fdfb9453670f2832df666fdae8291a84jmcpamd_iommu_set_devtbl_entry(amd_iommu_t *iommu, dev_info_t *rdip,
7d87efa8fdfb9453670f2832df666fdae8291a84jmcp domain_id_t domainid, uint16_t deviceid, amd_iommu_domain_t *dp,
7d87efa8fdfb9453670f2832df666fdae8291a84jmcp const char *path)
7d87efa8fdfb9453670f2832df666fdae8291a84jmcp const char *f = "amd_iommu_set_devtbl_entry";
ac48dfe87039078897ed719af26744eca776508cVikram Hegde cmn_err(CE_NOTE, "%s: attempting to set devtbl entry for %s",
7d87efa8fdfb9453670f2832df666fdae8291a84jmcp cmn_err(CE_WARN, "%s: %s%d: IOMMU idx=%d, deviceid (%u) "
7d87efa8fdfb9453670f2832df666fdae8291a84jmcp "for rdip (%p) exceeds device table size (%u), path=%s",
7d87efa8fdfb9453670f2832df666fdae8291a84jmcp /*LINTED*/
ac48dfe87039078897ed719af26744eca776508cVikram Hegde cmn_err(CE_NOTE, "%s: deviceid=%u devtbl entry (%p) for %s",
7d87efa8fdfb9453670f2832df666fdae8291a84jmcp f, deviceid, (void *)(uintptr_t)(*devtbl_entry), path);
ac48dfe87039078897ed719af26744eca776508cVikram Hegde * Flush internal caches, need to do this if we came up from
ac48dfe87039078897ed719af26744eca776508cVikram Hegde error = amd_iommu_cmd(iommu, AMD_IOMMU_CMD_INVAL_DEVTAB_ENTRY,
ac48dfe87039078897ed719af26744eca776508cVikram Hegde "Failed to invalidate domain in IOMMU HW cache",
ac48dfe87039078897ed719af26744eca776508cVikram Hegde cmdargs.ca_addr = (uintptr_t)0x7FFFFFFFFFFFF000;
ac48dfe87039078897ed719af26744eca776508cVikram Hegde error = amd_iommu_cmd(iommu, AMD_IOMMU_CMD_INVAL_IOMMU_PAGES,
ac48dfe87039078897ed719af26744eca776508cVikram Hegde "Failed to invalidate translations in IOMMU HW cache",
ac48dfe87039078897ed719af26744eca776508cVikram Hegde /* Initialize device table entry */
7d87efa8fdfb9453670f2832df666fdae8291a84jmcp error = amd_iommu_cmd(iommu, AMD_IOMMU_CMD_INVAL_DEVTAB_ENTRY,
7d87efa8fdfb9453670f2832df666fdae8291a84jmcpamd_iommu_clear_devtbl_entry(amd_iommu_t *iommu, dev_info_t *rdip,
7d87efa8fdfb9453670f2832df666fdae8291a84jmcp domain_id_t domainid, uint16_t deviceid, amd_iommu_domain_t *dp,
7d87efa8fdfb9453670f2832df666fdae8291a84jmcp const char *driver = ddi_driver_name(iommu->aiomt_dip);
7d87efa8fdfb9453670f2832df666fdae8291a84jmcp const char *f = "amd_iommu_clear_devtbl_entry";
7d87efa8fdfb9453670f2832df666fdae8291a84jmcp cmn_err(CE_NOTE, "%s: attempting to clear devtbl entry for "
7d87efa8fdfb9453670f2832df666fdae8291a84jmcp "domainid = %d, deviceid = %u, path = %s",
7d87efa8fdfb9453670f2832df666fdae8291a84jmcp cmn_err(CE_WARN, "%s: %s%d: IOMMU idx=%d, deviceid (%u) "
7d87efa8fdfb9453670f2832df666fdae8291a84jmcp "for rdip (%p) exceeds device table size (%u), path = %s",
7d87efa8fdfb9453670f2832df666fdae8291a84jmcp /*LINTED*/
ac48dfe87039078897ed719af26744eca776508cVikram Hegde cmn_err(CE_NOTE, "%s: deviceid=%u devtbl entry (%p) for %s",
7d87efa8fdfb9453670f2832df666fdae8291a84jmcp f, deviceid, (void *)(uintptr_t)(*devtbl_entry), path);
7d87efa8fdfb9453670f2832df666fdae8291a84jmcp if (AMD_IOMMU_REG_GET64(&(devtbl_entry[0]), AMD_IOMMU_DEVTBL_TV) == 0) {
7d87efa8fdfb9453670f2832df666fdae8291a84jmcp /* Nothing to do */
7d87efa8fdfb9453670f2832df666fdae8291a84jmcp ASSERT(dp->d_pgtable_root_4K == AMD_IOMMU_REG_GET64(&(devtbl_entry[0]),
7d87efa8fdfb9453670f2832df666fdae8291a84jmcp ASSERT(domainid == AMD_IOMMU_REG_GET64(&(devtbl_entry[1]),
7d87efa8fdfb9453670f2832df666fdae8291a84jmcp AMD_IOMMU_REG_SET64(&(devtbl_entry[0]), AMD_IOMMU_DEVTBL_TV, 0);
7d87efa8fdfb9453670f2832df666fdae8291a84jmcp AMD_IOMMU_REG_SET64(&(devtbl_entry[0]), AMD_IOMMU_DEVTBL_ROOT_PGTBL, 0);
7d87efa8fdfb9453670f2832df666fdae8291a84jmcp AMD_IOMMU_REG_SET64(&(devtbl_entry[0]), AMD_IOMMU_DEVTBL_V, 1);
7d87efa8fdfb9453670f2832df666fdae8291a84jmcp error = amd_iommu_cmd(iommu, AMD_IOMMU_CMD_INVAL_DEVTAB_ENTRY,
7d87efa8fdfb9453670f2832df666fdae8291a84jmcpamd_iommu_page_table_hash_init(amd_iommu_page_table_hash_t *ampt)
7d87efa8fdfb9453670f2832df666fdae8291a84jmcp ampt->ampt_hash = kmem_zalloc(sizeof (amd_iommu_page_table_t *) *
7d87efa8fdfb9453670f2832df666fdae8291a84jmcpamd_iommu_page_table_hash_fini(amd_iommu_page_table_hash_t *ampt)
7d87efa8fdfb9453670f2832df666fdae8291a84jmcp sizeof (amd_iommu_page_table_t *) * AMD_IOMMU_PGTABLE_HASH_SZ);
7d87efa8fdfb9453670f2832df666fdae8291a84jmcpstatic void
7d87efa8fdfb9453670f2832df666fdae8291a84jmcpamd_iommu_insert_pgtable_hash(amd_iommu_page_table_t *pt)
7d87efa8fdfb9453670f2832df666fdae8291a84jmcp uint64_t pa_4K = ((uint64_t)pt->pt_cookie.dmac_cookie_addr) >> 12;
7d87efa8fdfb9453670f2832df666fdae8291a84jmcp ASSERT((pt->pt_cookie.dmac_cookie_addr & AMD_IOMMU_PGTABLE_ALIGN) == 0);
7d87efa8fdfb9453670f2832df666fdae8291a84jmcp pt->pt_next = amd_iommu_page_table_hash.ampt_hash[idx];
7d87efa8fdfb9453670f2832df666fdae8291a84jmcpstatic void
7d87efa8fdfb9453670f2832df666fdae8291a84jmcpamd_iommu_remove_pgtable_hash(amd_iommu_page_table_t *pt)
7d87efa8fdfb9453670f2832df666fdae8291a84jmcp uint64_t pa_4K = (pt->pt_cookie.dmac_cookie_addr >> 12);
7d87efa8fdfb9453670f2832df666fdae8291a84jmcp ASSERT((pt->pt_cookie.dmac_cookie_addr & AMD_IOMMU_PGTABLE_ALIGN) == 0);
7d87efa8fdfb9453670f2832df666fdae8291a84jmcp amd_iommu_page_table_hash.ampt_hash[idx] = pt->pt_next;
7d87efa8fdfb9453670f2832df666fdae8291a84jmcpamd_iommu_lookup_pgtable_hash(domain_id_t domainid, uint64_t pgtable_pa_4K)
7d87efa8fdfb9453670f2832df666fdae8291a84jmcp if ((pt->pt_cookie.dmac_cookie_addr >> 12) == pgtable_pa_4K) {
7d87efa8fdfb9453670f2832df666fdae8291a84jmcp/*ARGSUSED*/
7d87efa8fdfb9453670f2832df666fdae8291a84jmcpamd_iommu_lookup_pgtable(amd_iommu_t *iommu, amd_iommu_page_table_t *ppt,
7d87efa8fdfb9453670f2832df666fdae8291a84jmcp ASSERT(level > 0 && level <= AMD_IOMMU_PGTABLE_MAXLEVEL);
7d87efa8fdfb9453670f2832df666fdae8291a84jmcp if (AMD_IOMMU_REG_GET64(pdtep, AMD_IOMMU_PTDE_PR) == 0) {
7d87efa8fdfb9453670f2832df666fdae8291a84jmcp pgtable_pa_4K = AMD_IOMMU_REG_GET64(pdtep, AMD_IOMMU_PTDE_ADDR);
7d87efa8fdfb9453670f2832df666fdae8291a84jmcp return (amd_iommu_lookup_pgtable_hash(dp->d_domainid, pgtable_pa_4K));
7d87efa8fdfb9453670f2832df666fdae8291a84jmcp for (i = 0; i < AMD_IOMMU_PGTABLE_SZ / (sizeof (*pte_array)); i++) {
7d87efa8fdfb9453670f2832df666fdae8291a84jmcpamd_iommu_alloc_pgtable(amd_iommu_t *iommu, domain_id_t domainid,
7d87efa8fdfb9453670f2832df666fdae8291a84jmcp const char *path, amd_iommu_page_table_t **ptp, int km_flags)
7d87efa8fdfb9453670f2832df666fdae8291a84jmcp const char *f = "amd_iommu_alloc_pgtable";
7d87efa8fdfb9453670f2832df666fdae8291a84jmcp pt = kmem_zalloc(sizeof (amd_iommu_page_table_t), km_flags);
7d87efa8fdfb9453670f2832df666fdae8291a84jmcp * Each page table is 4K in size
7d87efa8fdfb9453670f2832df666fdae8291a84jmcp * Alloc a DMA handle. Use the IOMMU dip as we want this DMA
7d87efa8fdfb9453670f2832df666fdae8291a84jmcp * to *not* enter the IOMMU - no recursive entrance.
7d87efa8fdfb9453670f2832df666fdae8291a84jmcp err = ddi_dma_alloc_handle(idip, &amd_iommu_pgtable_dma_attr,
7d87efa8fdfb9453670f2832df666fdae8291a84jmcp km_flags == KM_SLEEP ? DDI_DMA_SLEEP : DDI_DMA_DONTWAIT,
7d87efa8fdfb9453670f2832df666fdae8291a84jmcp cmn_err(CE_WARN, "%s: %s%d: domainid = %d, path = %s. "
7d87efa8fdfb9453670f2832df666fdae8291a84jmcp "Cannot alloc DMA handle for IO Page Table",
7d87efa8fdfb9453670f2832df666fdae8291a84jmcp return (err == DDI_DMA_NORESOURCES ? err : DDI_DMA_NOMAPPING);
7d87efa8fdfb9453670f2832df666fdae8291a84jmcp * Alloc memory for IO Page Table.
7d87efa8fdfb9453670f2832df666fdae8291a84jmcp * XXX remove size_t cast kludge
7d87efa8fdfb9453670f2832df666fdae8291a84jmcp err = ddi_dma_mem_alloc(pt->pt_dma_hdl, pt->pt_mem_reqsz,
7d87efa8fdfb9453670f2832df666fdae8291a84jmcp &amd_iommu_devacc, DDI_DMA_CONSISTENT|IOMEM_DATA_UNCACHED,
7d87efa8fdfb9453670f2832df666fdae8291a84jmcp km_flags == KM_SLEEP ? DDI_DMA_SLEEP : DDI_DMA_DONTWAIT,
7d87efa8fdfb9453670f2832df666fdae8291a84jmcp "Cannot allocate DMA memory for IO Page table",
7d87efa8fdfb9453670f2832df666fdae8291a84jmcp * The Page table DMA VA must be 4K aligned and
7d87efa8fdfb9453670f2832df666fdae8291a84jmcp * size >= than requested memory.
7d87efa8fdfb9453670f2832df666fdae8291a84jmcp ASSERT(((uint64_t)(uintptr_t)pt->pt_pgtblva & AMD_IOMMU_PGTABLE_ALIGN)
7d87efa8fdfb9453670f2832df666fdae8291a84jmcp * Now bind the handle
7d87efa8fdfb9453670f2832df666fdae8291a84jmcp km_flags == KM_SLEEP ? DDI_DMA_SLEEP : DDI_DMA_DONTWAIT,
7d87efa8fdfb9453670f2832df666fdae8291a84jmcp "Cannot bind memory for DMA to IO Page Tables. "
7d87efa8fdfb9453670f2832df666fdae8291a84jmcp "bufrealsz=%p",
7d87efa8fdfb9453670f2832df666fdae8291a84jmcp return (err == DDI_DMA_PARTIAL_MAP ? DDI_DMA_NOMAPPING :
7d87efa8fdfb9453670f2832df666fdae8291a84jmcp * We assume the DMA engine on the IOMMU is capable of handling the
7d87efa8fdfb9453670f2832df666fdae8291a84jmcp * whole page table in a single cookie. If not and multiple cookies
7d87efa8fdfb9453670f2832df666fdae8291a84jmcp * are needed we fail.
7d87efa8fdfb9453670f2832df666fdae8291a84jmcp "Cannot handle multiple "
7d87efa8fdfb9453670f2832df666fdae8291a84jmcp "cookies for DMA to IO page Table, #cookies=%u",
7d87efa8fdfb9453670f2832df666fdae8291a84jmcp * The address in the cookie must be 4K aligned and >= table size
7d87efa8fdfb9453670f2832df666fdae8291a84jmcp ASSERT((pt->pt_cookie.dmac_cookie_addr & AMD_IOMMU_PGTABLE_ALIGN) == 0);
7d87efa8fdfb9453670f2832df666fdae8291a84jmcpstatic void
7d87efa8fdfb9453670f2832df666fdae8291a84jmcpamd_iommu_free_pgtable(amd_iommu_t *iommu, amd_iommu_page_table_t *pt)
7d87efa8fdfb9453670f2832df666fdae8291a84jmcp const char *f = "amd_iommu_free_pgtable";
7d87efa8fdfb9453670f2832df666fdae8291a84jmcp for (i = 0; i < AMD_IOMMU_PGTABLE_SZ / (sizeof (*pte_array)); i++) {
7d87efa8fdfb9453670f2832df666fdae8291a84jmcp /* Unbind the handle */
7d87efa8fdfb9453670f2832df666fdae8291a84jmcp if (ddi_dma_unbind_handle(pt->pt_dma_hdl) != DDI_SUCCESS) {
7d87efa8fdfb9453670f2832df666fdae8291a84jmcp "Failed to unbind handle: %p for IOMMU Page Table",
7d87efa8fdfb9453670f2832df666fdae8291a84jmcp f, driver, instance, iommu->aiomt_idx, pt->pt_domainid,
7d87efa8fdfb9453670f2832df666fdae8291a84jmcp /* Free the table memory allocated for DMA */
7d87efa8fdfb9453670f2832df666fdae8291a84jmcp /* Free the DMA handle */
7d87efa8fdfb9453670f2832df666fdae8291a84jmcpinit_pde(amd_iommu_page_table_t *ppt, amd_iommu_page_table_t *pt)
7d87efa8fdfb9453670f2832df666fdae8291a84jmcp uint64_t next_pgtable_pa_4K = (pt->pt_cookie.dmac_cookie_addr) >> 12;
7d87efa8fdfb9453670f2832df666fdae8291a84jmcp /* nothing to set. PDE is already set */
7d87efa8fdfb9453670f2832df666fdae8291a84jmcp if (AMD_IOMMU_REG_GET64(pdep, AMD_IOMMU_PTDE_PR) == 1) {
7d87efa8fdfb9453670f2832df666fdae8291a84jmcp /* Page Directories are always RW */
7d87efa8fdfb9453670f2832df666fdae8291a84jmcp ASSERT(AMD_IOMMU_REG_GET64(pdep, AMD_IOMMU_PTDE_PR) == 1);
7d87efa8fdfb9453670f2832df666fdae8291a84jmcpinit_pte(amd_iommu_page_table_t *pt, uint64_t pa, uint16_t index,
7d87efa8fdfb9453670f2832df666fdae8291a84jmcp /* nothing to set if PTE is already set */
7d87efa8fdfb9453670f2832df666fdae8291a84jmcp if (AMD_IOMMU_REG_GET64(ptep, AMD_IOMMU_PTDE_PR) == 1) {
7d87efa8fdfb9453670f2832df666fdae8291a84jmcp * Adjust current permissions
7d87efa8fdfb9453670f2832df666fdae8291a84jmcp * DDI_DMA_WRITE means direction of DMA is MEM -> I/O
7d87efa8fdfb9453670f2832df666fdae8291a84jmcp * so that requires Memory READ permissions i.e. sense
7d87efa8fdfb9453670f2832df666fdae8291a84jmcp * is inverted.
7d87efa8fdfb9453670f2832df666fdae8291a84jmcp * Note: either or both of DD_DMA_READ/WRITE may be set
7d87efa8fdfb9453670f2832df666fdae8291a84jmcp /* see comment above about inverting sense of RD/WR */
7d87efa8fdfb9453670f2832df666fdae8291a84jmcp /* TODO what is correct for FC and U */
7d87efa8fdfb9453670f2832df666fdae8291a84jmcp ASSERT(AMD_IOMMU_REG_GET64(ptep, AMD_IOMMU_PTDE_PR) == 1);
7d87efa8fdfb9453670f2832df666fdae8291a84jmcpstatic void
7d87efa8fdfb9453670f2832df666fdae8291a84jmcpinit_pt(amd_iommu_page_table_t *pt, amd_iommu_domain_t *dp,
7d87efa8fdfb9453670f2832df666fdae8291a84jmcp dp->d_pgtable_root_4K = (pt->pt_cookie.dmac_cookie_addr) >> 12;
7d87efa8fdfb9453670f2832df666fdae8291a84jmcp ASSERT(level >= 1 && level < AMD_IOMMU_PGTABLE_MAXLEVEL);
7d87efa8fdfb9453670f2832df666fdae8291a84jmcpamd_iommu_setup_1_pgtable(amd_iommu_t *iommu, dev_info_t *rdip,
7d87efa8fdfb9453670f2832df666fdae8291a84jmcp amd_iommu_page_table_t **ptp, uint16_t *next_idxp, const char *path,
7d87efa8fdfb9453670f2832df666fdae8291a84jmcp const char *f = "amd_iommu_setup_1_pgtable";
7d87efa8fdfb9453670f2832df666fdae8291a84jmcp ASSERT(level > 0 && level <= AMD_IOMMU_PGTABLE_MAXLEVEL);
7d87efa8fdfb9453670f2832df666fdae8291a84jmcp /* Check if page table is already allocated */
7d87efa8fdfb9453670f2832df666fdae8291a84jmcp if (pt = amd_iommu_lookup_pgtable(iommu, ppt, dp, level, index)) {
7d87efa8fdfb9453670f2832df666fdae8291a84jmcp if ((error = amd_iommu_alloc_pgtable(iommu, domainid, path, &pt,
7d87efa8fdfb9453670f2832df666fdae8291a84jmcp cmn_err(CE_WARN, "%s: %s%d: idx = %u, domainid = %d, va = %p "
7d87efa8fdfb9453670f2832df666fdae8291a84jmcp error = init_pte(pt, pa, AMD_IOMMU_VA_BITS(va, level), dmareq);
7d87efa8fdfb9453670f2832df666fdae8291a84jmcptypedef enum {
7d87efa8fdfb9453670f2832df666fdae8291a84jmcp if (AMD_IOMMU_REG_GET64(ptdep, AMD_IOMMU_PTDE_PR) == 1) {
7d87efa8fdfb9453670f2832df666fdae8291a84jmcpamd_iommu_create_pgtables(amd_iommu_t *iommu, dev_info_t *rdip,
7d87efa8fdfb9453670f2832df666fdae8291a84jmcp amd_iommu_domain_t *dp, const char *path, int km_flags)
7d87efa8fdfb9453670f2832df666fdae8291a84jmcp const char *f = "amd_iommu_create_pgtables";
7d87efa8fdfb9453670f2832df666fdae8291a84jmcp "deviceid = %u, va = %p, pa = %p, path = %s",
7d87efa8fdfb9453670f2832df666fdae8291a84jmcp /* No need for pagetables. Just set up device table entry */
7d87efa8fdfb9453670f2832df666fdae8291a84jmcp if ((error = amd_iommu_setup_1_pgtable(iommu, rdip, dmareq,
7d87efa8fdfb9453670f2832df666fdae8291a84jmcp "deviceid=%u, va= %p, pa = %p, Failed to setup "
7d87efa8fdfb9453670f2832df666fdae8291a84jmcp "page table(s) at level = %d, path = %s.",
7d87efa8fdfb9453670f2832df666fdae8291a84jmcp if ((error = amd_iommu_set_devtbl_entry(iommu, rdip, domainid, deviceid,
7d87efa8fdfb9453670f2832df666fdae8291a84jmcp cmn_err(CE_WARN, "%s: %s%d: idx=%d: rdip=%p, deviceid=%u, "
7d87efa8fdfb9453670f2832df666fdae8291a84jmcp "domainid=%d."
7d87efa8fdfb9453670f2832df666fdae8291a84jmcp "Failed to set device table entry for path %s.",
7d87efa8fdfb9453670f2832df666fdae8291a84jmcp iommu->aiomt_idx, (void *)rdip, deviceid, domainid, path);
7d87efa8fdfb9453670f2832df666fdae8291a84jmcpamd_iommu_destroy_pgtables(amd_iommu_t *iommu, dev_info_t *rdip,
7d87efa8fdfb9453670f2832df666fdae8291a84jmcp uint64_t pageva, uint16_t deviceid, domain_id_t domainid,
7d87efa8fdfb9453670f2832df666fdae8291a84jmcp amd_iommu_domain_t *dp, map_type_t type, int *domain_freed, char *path)
7d87efa8fdfb9453670f2832df666fdae8291a84jmcp const char *driver = ddi_driver_name(iommu->aiomt_dip);
7d87efa8fdfb9453670f2832df666fdae8291a84jmcp const char *f = "amd_iommu_destroy_pgtables";
7d87efa8fdfb9453670f2832df666fdae8291a84jmcp "deviceid = %u, va = %p, path = %s",
7d87efa8fdfb9453670f2832df666fdae8291a84jmcp * there are no pagetables for the passthru domain.
7d87efa8fdfb9453670f2832df666fdae8291a84jmcp * Just the device table entry
7d87efa8fdfb9453670f2832df666fdae8291a84jmcp for (level = AMD_IOMMU_PGTABLE_MAXLEVEL; level > 0; level--) {
7d87efa8fdfb9453670f2832df666fdae8291a84jmcp pt = amd_iommu_lookup_pgtable(iommu, ppt, dp, level, index);
7d87efa8fdfb9453670f2832df666fdae8291a84jmcp if (level == 0) {
7d87efa8fdfb9453670f2832df666fdae8291a84jmcp pa_4K = AMD_IOMMU_REG_GET64(ptep, AMD_IOMMU_PTDE_ADDR);
7d87efa8fdfb9453670f2832df666fdae8291a84jmcp if (amd_iommu_unity_map || type == AMD_IOMMU_UNITY_MAP) {
7d87efa8fdfb9453670f2832df666fdae8291a84jmcp for (++level; level <= AMD_IOMMU_PGTABLE_MAXLEVEL; level++) {
7d87efa8fdfb9453670f2832df666fdae8291a84jmcp * Now teardown the IOMMU HW caches if applicable
7d87efa8fdfb9453670f2832df666fdae8291a84jmcp } else if (invalidate_pde) {
7d87efa8fdfb9453670f2832df666fdae8291a84jmcp if (amd_iommu_cmd(iommu, AMD_IOMMU_CMD_INVAL_IOMMU_PAGES,
7d87efa8fdfb9453670f2832df666fdae8291a84jmcp "rdip=%p. Failed to invalidate IOMMU HW cache "
7d87efa8fdfb9453670f2832df666fdae8291a84jmcp error = amd_iommu_clear_devtbl_entry(iommu, rdip, domainid,
7d87efa8fdfb9453670f2832df666fdae8291a84jmcp cmn_err(CE_PANIC, "Unsupported error code: %d", error);
7d87efa8fdfb9453670f2832df666fdae8291a84jmcp /*NOTREACHED*/
7d87efa8fdfb9453670f2832df666fdae8291a84jmcpamd_iommu_map_pa2va(amd_iommu_t *iommu, dev_info_t *rdip, ddi_dma_attr_t *attrp,
7d87efa8fdfb9453670f2832df666fdae8291a84jmcp struct ddi_dma_req *dmareq, uint64_t start_pa, uint64_t pa_sz,
7d87efa8fdfb9453670f2832df666fdae8291a84jmcp const char *driver = ddi_driver_name(iommu->aiomt_dip);
7d87efa8fdfb9453670f2832df666fdae8291a84jmcp const char *f = "amd_iommu_map_pa2va";
7d87efa8fdfb9453670f2832df666fdae8291a84jmcp * First get deviceid
7d87efa8fdfb9453670f2832df666fdae8291a84jmcp if (amd_iommu_get_deviceid(iommu, rdip, &deviceid, &alias, path)
7d87efa8fdfb9453670f2832df666fdae8291a84jmcp "Failed to get device ID for %s.", f, driver, instance,
7d87efa8fdfb9453670f2832df666fdae8291a84jmcp * Next get the domain for this rdip
7d87efa8fdfb9453670f2832df666fdae8291a84jmcp if (amd_iommu_get_domain(iommu, rdip, alias, deviceid, &domainid, path)
7d87efa8fdfb9453670f2832df666fdae8291a84jmcp cmn_err(CE_WARN, "%s: %s%d: idx=%d: rdip=%p, path=%s. "
7d87efa8fdfb9453670f2832df666fdae8291a84jmcp dp = amd_iommu_lookup_domain(iommu, domainid, type, km_flags);
7d87efa8fdfb9453670f2832df666fdae8291a84jmcp cmn_err(CE_WARN, "%s: %s%d: idx=%d: domainid=%d, rdip=%p. "
7d87efa8fdfb9453670f2832df666fdae8291a84jmcp "Failed to get device ID for %s.", f, driver, instance,
7d87efa8fdfb9453670f2832df666fdae8291a84jmcp cmn_err(CE_NOTE, "pa = %p, pfn_new = %p, pfn_start = %p, "
7d87efa8fdfb9453670f2832df666fdae8291a84jmcp "pgshift = %d",
7d87efa8fdfb9453670f2832df666fdae8291a84jmcp if (amd_iommu_unity_map || type == AMD_IOMMU_UNITY_MAP) {
7d87efa8fdfb9453670f2832df666fdae8291a84jmcp if (start_va == 0) {
ac48dfe87039078897ed719af26744eca776508cVikram Hegde cmn_err(CE_NOTE, "%s: attempting to create page tables "
7d87efa8fdfb9453670f2832df666fdae8291a84jmcp "for pfn = %p, va = %p, path = %s",
7d87efa8fdfb9453670f2832df666fdae8291a84jmcp if (amd_iommu_unity_map || type == AMD_IOMMU_UNITY_MAP) {
7d87efa8fdfb9453670f2832df666fdae8291a84jmcp if ((error = amd_iommu_create_pgtables(iommu, rdip, dmareq,
ba758cf1b2fe06a606303351c36a766f2f9f6665Jerry Gilliam cmn_err(CE_NOTE, "%s: successfully created page tables "
7d87efa8fdfb9453670f2832df666fdae8291a84jmcp "for pfn = %p, vapg = %p, path = %s",
7d87efa8fdfb9453670f2832df666fdae8291a84jmcpamd_iommu_unmap_va(amd_iommu_t *iommu, dev_info_t *rdip, uint64_t start_va,
7d87efa8fdfb9453670f2832df666fdae8291a84jmcp const char *driver = ddi_driver_name(iommu->aiomt_dip);
7d87efa8fdfb9453670f2832df666fdae8291a84jmcp const char *f = "amd_iommu_unmap_va";
7d87efa8fdfb9453670f2832df666fdae8291a84jmcp * First get deviceid
7d87efa8fdfb9453670f2832df666fdae8291a84jmcp if (amd_iommu_get_deviceid(iommu, rdip, &deviceid, &alias, path)
7d87efa8fdfb9453670f2832df666fdae8291a84jmcp "Failed to get device ID for %s.", f, driver, instance,
7d87efa8fdfb9453670f2832df666fdae8291a84jmcp * Next get the domain for this rdip
7d87efa8fdfb9453670f2832df666fdae8291a84jmcp if (amd_iommu_get_domain(iommu, rdip, alias, deviceid, &domainid, path)
7d87efa8fdfb9453670f2832df666fdae8291a84jmcp cmn_err(CE_WARN, "%s: %s%d: idx=%d: rdip=%p, path=%s. "
7d87efa8fdfb9453670f2832df666fdae8291a84jmcp /* should never result in domain allocation/vmem_create */
7d87efa8fdfb9453670f2832df666fdae8291a84jmcp dp = amd_iommu_lookup_domain(iommu, domainid, AMD_IOMMU_INVALID_MAP,
7d87efa8fdfb9453670f2832df666fdae8291a84jmcp cmn_err(CE_WARN, "%s: %s%d: idx=%d: domainid=%d, rdip=%p. "
7d87efa8fdfb9453670f2832df666fdae8291a84jmcp "Failed to get device ID for %s.", f, driver, instance,
7d87efa8fdfb9453670f2832df666fdae8291a84jmcp * vmem_xalloc() must be paired with vmem_xfree
7d87efa8fdfb9453670f2832df666fdae8291a84jmcp if (type == AMD_IOMMU_VMEM_MAP && !amd_iommu_unity_map) {
7d87efa8fdfb9453670f2832df666fdae8291a84jmcp (void *)(uintptr_t)(pg_start << MMU_PAGESHIFT), actual_sz);