/* * Copyright (C) 2007 Advanced Micro Devices, Inc. * Author: Leo Duran * Author: Wei Wang - adapted to xen * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; If not, see . */ #include #include #include "iommu.h" static int __init get_iommu_msi_capabilities( u16 seg, u8 bus, u8 dev, u8 func, struct amd_iommu *iommu) { int pos; pos = pci_find_cap_offset(seg, bus, dev, func, PCI_CAP_ID_MSI); if ( !pos ) return -ENODEV; AMD_IOMMU_DEBUG("Found MSI capability block at %#x\n", pos); iommu->msi.msi_attrib.type = PCI_CAP_ID_MSI; iommu->msi.msi_attrib.pos = pos; iommu->msi.msi_attrib.is_64 = 1; return 0; } static int __init get_iommu_capabilities( u16 seg, u8 bus, u8 dev, u8 func, u16 cap_ptr, struct amd_iommu *iommu) { u8 type; iommu->cap.header = pci_conf_read32(PCI_SBDF(seg, bus, dev, func), cap_ptr); type = get_field_from_reg_u32(iommu->cap.header, PCI_CAP_TYPE_MASK, PCI_CAP_TYPE_SHIFT); if ( type != PCI_CAP_TYPE_IOMMU ) return -ENODEV; return 0; } void __init get_iommu_features(struct amd_iommu *iommu) { const struct amd_iommu *first; ASSERT( iommu->mmio_base ); if ( !(amd_iommu_acpi_info & ACPI_IVRS_EFR_SUP) ) { if ( !iommu_has_cap(iommu, PCI_CAP_EFRSUP_SHIFT) ) return; iommu->features.raw = readq(iommu->mmio_base + IOMMU_EXT_FEATURE_MMIO_OFFSET); if ( 4 + iommu->features.flds.hats < amd_iommu_max_paging_mode ) amd_iommu_max_paging_mode = 4 + iommu->features.flds.hats; } /* Don't log the same set of features over and over. */ first = list_first_entry(&amd_iommu_head, struct amd_iommu, list); if ( iommu != first && iommu->features.raw == first->features.raw ) return; printk("AMD-Vi: IOMMU Extended Features:\n"); #define FEAT(fld, str) do { \ if ( --((union amd_iommu_ext_features){}).flds.fld > 1 ) \ printk( "- " str ": %#x\n", iommu->features.flds.fld); \ else if ( iommu->features.flds.fld ) \ printk( "- " str "\n"); \ } while ( false ) FEAT(pref_sup, "Prefetch Pages Command"); FEAT(ppr_sup, "Peripheral Page Service Request"); FEAT(xt_sup, "x2APIC"); FEAT(nx_sup, "NX bit"); FEAT(gappi_sup, "Guest APIC Physical Processor Interrupt"); FEAT(ia_sup, "Invalidate All Command"); FEAT(ga_sup, "Guest APIC"); FEAT(he_sup, "Hardware Error Registers"); FEAT(pc_sup, "Performance Counters"); FEAT(hats, "Host Address Translation Size"); if ( iommu->features.flds.gt_sup ) { FEAT(gats, "Guest Address Translation Size"); FEAT(glx_sup, "Guest CR3 Root Table Level"); FEAT(pas_max, "Maximum PASID"); } FEAT(smif_sup, "SMI Filter Register"); FEAT(smif_rc, "SMI Filter Register Count"); FEAT(gam_sup, "Guest Virtual APIC Modes"); FEAT(dual_ppr_log_sup, "Dual PPR Log"); FEAT(dual_event_log_sup, "Dual Event Log"); FEAT(sats_sup, "Secure ATS"); FEAT(us_sup, "User / Supervisor Page Protection"); FEAT(dev_tbl_seg_sup, "Device Table Segmentation"); FEAT(ppr_early_of_sup, "PPR Log Overflow Early Warning"); FEAT(ppr_auto_rsp_sup, "PPR Automatic Response"); FEAT(marc_sup, "Memory Access Routing and Control"); FEAT(blk_stop_mrk_sup, "Block StopMark Message"); FEAT(perf_opt_sup , "Performance Optimization"); FEAT(msi_cap_mmio_sup, "MSI Capability MMIO Access"); FEAT(gio_sup, "Guest I/O Protection"); FEAT(ha_sup, "Host Access"); FEAT(eph_sup, "Enhanced PPR Handling"); FEAT(attr_fw_sup, "Attribute Forward"); FEAT(hd_sup, "Host Dirty"); FEAT(inv_iotlb_type_sup, "Invalidate IOTLB Type"); FEAT(viommu_sup, "Virtualized IOMMU"); FEAT(vm_guard_io_sup, "VMGuard I/O Support"); FEAT(vm_table_size, "VM Table Size"); FEAT(ga_update_dis_sup, "Guest Access Bit Update Disable"); #undef FEAT } int __init amd_iommu_detect_one_acpi( const struct acpi_ivrs_hardware *ivhd_block) { struct amd_iommu *iommu; u8 bus, dev, func; int rt = 0; if ( ivhd_block->header.length < sizeof(*ivhd_block) ) { AMD_IOMMU_ERROR("invalid IVHD block length\n"); return -ENODEV; } if ( !ivhd_block->header.device_id || !ivhd_block->capability_offset || !ivhd_block->base_address) { AMD_IOMMU_ERROR("invalid IVHD block\n"); return -ENODEV; } iommu = xzalloc(struct amd_iommu); if ( !iommu ) { AMD_IOMMU_ERROR("cannot allocate amd_iommu\n"); return -ENOMEM; } spin_lock_init(&iommu->lock); INIT_LIST_HEAD(&iommu->ats_devices); iommu->seg = ivhd_block->pci_segment_group; iommu->bdf = ivhd_block->header.device_id; iommu->cap_offset = ivhd_block->capability_offset; iommu->mmio_base_phys = ivhd_block->base_address; if ( ivhd_type != ACPI_IVRS_TYPE_HARDWARE ) iommu->features.raw = ivhd_block->efr_image; else if ( amd_iommu_acpi_info & ACPI_IVRS_EFR_SUP ) { union { uint32_t raw; struct { unsigned int xt_sup:1; unsigned int nx_sup:1; unsigned int gt_sup:1; unsigned int glx_sup:2; unsigned int ia_sup:1; unsigned int ga_sup:1; unsigned int he_sup:1; unsigned int pas_max:5; unsigned int pn_counters:4; unsigned int pn_banks:6; unsigned int msi_num_ppr:5; unsigned int gats:2; unsigned int hats:2; }; } attr = { .raw = ivhd_block->iommu_attr }; iommu->features.flds.xt_sup = attr.xt_sup; iommu->features.flds.nx_sup = attr.nx_sup; iommu->features.flds.gt_sup = attr.gt_sup; iommu->features.flds.glx_sup = attr.glx_sup; iommu->features.flds.ia_sup = attr.ia_sup; iommu->features.flds.ga_sup = attr.ga_sup; iommu->features.flds.pas_max = attr.pas_max; iommu->features.flds.gats = attr.gats; iommu->features.flds.hats = attr.hats; } else if ( list_empty(&amd_iommu_head) ) AMD_IOMMU_DEBUG("EFRSup not set in ACPI table; will fall back to hardware\n"); if ( (amd_iommu_acpi_info & ACPI_IVRS_EFR_SUP) && 4 + iommu->features.flds.hats < amd_iommu_max_paging_mode ) amd_iommu_max_paging_mode = 4 + iommu->features.flds.hats; /* override IOMMU HT flags */ iommu->ht_flags = ivhd_block->header.flags; bus = PCI_BUS(iommu->bdf); dev = PCI_SLOT(iommu->bdf); func = PCI_FUNC(iommu->bdf); rt = get_iommu_capabilities(iommu->seg, bus, dev, func, iommu->cap_offset, iommu); if ( rt ) goto out; rt = get_iommu_msi_capabilities(iommu->seg, bus, dev, func, iommu); if ( rt ) goto out; iommu->domid_map = iommu_init_domid(DOMID_INVALID); rt = -ENOMEM; if ( !iommu->domid_map ) goto out; rt = pci_ro_device(iommu->seg, bus, PCI_DEVFN(dev, func)); if ( rt ) printk(XENLOG_ERR "Could not mark config space of %pp read-only (%d)\n", &PCI_SBDF(iommu->seg, iommu->bdf), rt); list_add_tail(&iommu->list, &amd_iommu_head); rt = 0; out: if ( rt ) { xfree(iommu->domid_map); xfree(iommu); } return rt; }