162306a36Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0 262306a36Sopenharmony_ci#include <linux/dma-map-ops.h> 362306a36Sopenharmony_ci#include <linux/dma-direct.h> 462306a36Sopenharmony_ci#include <linux/iommu.h> 562306a36Sopenharmony_ci#include <linux/dmar.h> 662306a36Sopenharmony_ci#include <linux/export.h> 762306a36Sopenharmony_ci#include <linux/memblock.h> 862306a36Sopenharmony_ci#include <linux/gfp.h> 962306a36Sopenharmony_ci#include <linux/pci.h> 1062306a36Sopenharmony_ci#include <linux/amd-iommu.h> 1162306a36Sopenharmony_ci 1262306a36Sopenharmony_ci#include <asm/proto.h> 1362306a36Sopenharmony_ci#include <asm/dma.h> 1462306a36Sopenharmony_ci#include <asm/iommu.h> 1562306a36Sopenharmony_ci#include <asm/gart.h> 1662306a36Sopenharmony_ci#include <asm/x86_init.h> 1762306a36Sopenharmony_ci 1862306a36Sopenharmony_ci#include <xen/xen.h> 1962306a36Sopenharmony_ci#include <xen/swiotlb-xen.h> 2062306a36Sopenharmony_ci 2162306a36Sopenharmony_cistatic bool disable_dac_quirk __read_mostly; 2262306a36Sopenharmony_ci 2362306a36Sopenharmony_ciconst struct dma_map_ops *dma_ops; 2462306a36Sopenharmony_ciEXPORT_SYMBOL(dma_ops); 2562306a36Sopenharmony_ci 2662306a36Sopenharmony_ci#ifdef CONFIG_IOMMU_DEBUG 2762306a36Sopenharmony_ciint panic_on_overflow __read_mostly = 1; 2862306a36Sopenharmony_ciint force_iommu __read_mostly = 1; 2962306a36Sopenharmony_ci#else 3062306a36Sopenharmony_ciint panic_on_overflow __read_mostly = 0; 3162306a36Sopenharmony_ciint force_iommu __read_mostly = 0; 3262306a36Sopenharmony_ci#endif 3362306a36Sopenharmony_ci 3462306a36Sopenharmony_ciint iommu_merge __read_mostly = 0; 3562306a36Sopenharmony_ci 3662306a36Sopenharmony_ciint no_iommu __read_mostly; 3762306a36Sopenharmony_ci/* Set this to 1 if there is a HW IOMMU in the system */ 3862306a36Sopenharmony_ciint iommu_detected __read_mostly = 0; 3962306a36Sopenharmony_ci 4062306a36Sopenharmony_ci#ifdef CONFIG_SWIOTLB 4162306a36Sopenharmony_cibool x86_swiotlb_enable; 4262306a36Sopenharmony_cistatic unsigned int x86_swiotlb_flags; 4362306a36Sopenharmony_ci 4462306a36Sopenharmony_cistatic void __init pci_swiotlb_detect(void) 4562306a36Sopenharmony_ci{ 4662306a36Sopenharmony_ci /* don't initialize swiotlb if iommu=off (no_iommu=1) */ 4762306a36Sopenharmony_ci if (!no_iommu && max_possible_pfn > MAX_DMA32_PFN) 4862306a36Sopenharmony_ci x86_swiotlb_enable = true; 4962306a36Sopenharmony_ci 5062306a36Sopenharmony_ci /* 5162306a36Sopenharmony_ci * Set swiotlb to 1 so that bounce buffers are allocated and used for 5262306a36Sopenharmony_ci * devices that can't support DMA to encrypted memory. 5362306a36Sopenharmony_ci */ 5462306a36Sopenharmony_ci if (cc_platform_has(CC_ATTR_HOST_MEM_ENCRYPT)) 5562306a36Sopenharmony_ci x86_swiotlb_enable = true; 5662306a36Sopenharmony_ci 5762306a36Sopenharmony_ci /* 5862306a36Sopenharmony_ci * Guest with guest memory encryption currently perform all DMA through 5962306a36Sopenharmony_ci * bounce buffers as the hypervisor can't access arbitrary VM memory 6062306a36Sopenharmony_ci * that is not explicitly shared with it. 6162306a36Sopenharmony_ci */ 6262306a36Sopenharmony_ci if (cc_platform_has(CC_ATTR_GUEST_MEM_ENCRYPT)) { 6362306a36Sopenharmony_ci x86_swiotlb_enable = true; 6462306a36Sopenharmony_ci x86_swiotlb_flags |= SWIOTLB_FORCE; 6562306a36Sopenharmony_ci } 6662306a36Sopenharmony_ci} 6762306a36Sopenharmony_ci#else 6862306a36Sopenharmony_cistatic inline void __init pci_swiotlb_detect(void) 6962306a36Sopenharmony_ci{ 7062306a36Sopenharmony_ci} 7162306a36Sopenharmony_ci#define x86_swiotlb_flags 0 7262306a36Sopenharmony_ci#endif /* CONFIG_SWIOTLB */ 7362306a36Sopenharmony_ci 7462306a36Sopenharmony_ci#ifdef CONFIG_SWIOTLB_XEN 7562306a36Sopenharmony_cistatic bool xen_swiotlb_enabled(void) 7662306a36Sopenharmony_ci{ 7762306a36Sopenharmony_ci return xen_initial_domain() || x86_swiotlb_enable || 7862306a36Sopenharmony_ci (IS_ENABLED(CONFIG_XEN_PCIDEV_FRONTEND) && xen_pv_pci_possible); 7962306a36Sopenharmony_ci} 8062306a36Sopenharmony_ci 8162306a36Sopenharmony_cistatic void __init pci_xen_swiotlb_init(void) 8262306a36Sopenharmony_ci{ 8362306a36Sopenharmony_ci if (!xen_swiotlb_enabled()) 8462306a36Sopenharmony_ci return; 8562306a36Sopenharmony_ci x86_swiotlb_enable = true; 8662306a36Sopenharmony_ci x86_swiotlb_flags |= SWIOTLB_ANY; 8762306a36Sopenharmony_ci swiotlb_init_remap(true, x86_swiotlb_flags, xen_swiotlb_fixup); 8862306a36Sopenharmony_ci dma_ops = &xen_swiotlb_dma_ops; 8962306a36Sopenharmony_ci if (IS_ENABLED(CONFIG_PCI)) 9062306a36Sopenharmony_ci pci_request_acs(); 9162306a36Sopenharmony_ci} 9262306a36Sopenharmony_ci#else 9362306a36Sopenharmony_cistatic inline void __init pci_xen_swiotlb_init(void) 9462306a36Sopenharmony_ci{ 9562306a36Sopenharmony_ci} 9662306a36Sopenharmony_ci#endif /* CONFIG_SWIOTLB_XEN */ 9762306a36Sopenharmony_ci 9862306a36Sopenharmony_civoid __init pci_iommu_alloc(void) 9962306a36Sopenharmony_ci{ 10062306a36Sopenharmony_ci if (xen_pv_domain()) { 10162306a36Sopenharmony_ci pci_xen_swiotlb_init(); 10262306a36Sopenharmony_ci return; 10362306a36Sopenharmony_ci } 10462306a36Sopenharmony_ci pci_swiotlb_detect(); 10562306a36Sopenharmony_ci gart_iommu_hole_init(); 10662306a36Sopenharmony_ci amd_iommu_detect(); 10762306a36Sopenharmony_ci detect_intel_iommu(); 10862306a36Sopenharmony_ci swiotlb_init(x86_swiotlb_enable, x86_swiotlb_flags); 10962306a36Sopenharmony_ci} 11062306a36Sopenharmony_ci 11162306a36Sopenharmony_ci/* 11262306a36Sopenharmony_ci * See <Documentation/arch/x86/x86_64/boot-options.rst> for the iommu kernel 11362306a36Sopenharmony_ci * parameter documentation. 11462306a36Sopenharmony_ci */ 11562306a36Sopenharmony_cistatic __init int iommu_setup(char *p) 11662306a36Sopenharmony_ci{ 11762306a36Sopenharmony_ci iommu_merge = 1; 11862306a36Sopenharmony_ci 11962306a36Sopenharmony_ci if (!p) 12062306a36Sopenharmony_ci return -EINVAL; 12162306a36Sopenharmony_ci 12262306a36Sopenharmony_ci while (*p) { 12362306a36Sopenharmony_ci if (!strncmp(p, "off", 3)) 12462306a36Sopenharmony_ci no_iommu = 1; 12562306a36Sopenharmony_ci /* gart_parse_options has more force support */ 12662306a36Sopenharmony_ci if (!strncmp(p, "force", 5)) 12762306a36Sopenharmony_ci force_iommu = 1; 12862306a36Sopenharmony_ci if (!strncmp(p, "noforce", 7)) { 12962306a36Sopenharmony_ci iommu_merge = 0; 13062306a36Sopenharmony_ci force_iommu = 0; 13162306a36Sopenharmony_ci } 13262306a36Sopenharmony_ci 13362306a36Sopenharmony_ci if (!strncmp(p, "biomerge", 8)) { 13462306a36Sopenharmony_ci iommu_merge = 1; 13562306a36Sopenharmony_ci force_iommu = 1; 13662306a36Sopenharmony_ci } 13762306a36Sopenharmony_ci if (!strncmp(p, "panic", 5)) 13862306a36Sopenharmony_ci panic_on_overflow = 1; 13962306a36Sopenharmony_ci if (!strncmp(p, "nopanic", 7)) 14062306a36Sopenharmony_ci panic_on_overflow = 0; 14162306a36Sopenharmony_ci if (!strncmp(p, "merge", 5)) { 14262306a36Sopenharmony_ci iommu_merge = 1; 14362306a36Sopenharmony_ci force_iommu = 1; 14462306a36Sopenharmony_ci } 14562306a36Sopenharmony_ci if (!strncmp(p, "nomerge", 7)) 14662306a36Sopenharmony_ci iommu_merge = 0; 14762306a36Sopenharmony_ci if (!strncmp(p, "forcesac", 8)) 14862306a36Sopenharmony_ci pr_warn("forcesac option ignored.\n"); 14962306a36Sopenharmony_ci if (!strncmp(p, "allowdac", 8)) 15062306a36Sopenharmony_ci pr_warn("allowdac option ignored.\n"); 15162306a36Sopenharmony_ci if (!strncmp(p, "nodac", 5)) 15262306a36Sopenharmony_ci pr_warn("nodac option ignored.\n"); 15362306a36Sopenharmony_ci if (!strncmp(p, "usedac", 6)) { 15462306a36Sopenharmony_ci disable_dac_quirk = true; 15562306a36Sopenharmony_ci return 1; 15662306a36Sopenharmony_ci } 15762306a36Sopenharmony_ci#ifdef CONFIG_SWIOTLB 15862306a36Sopenharmony_ci if (!strncmp(p, "soft", 4)) 15962306a36Sopenharmony_ci x86_swiotlb_enable = true; 16062306a36Sopenharmony_ci#endif 16162306a36Sopenharmony_ci if (!strncmp(p, "pt", 2)) 16262306a36Sopenharmony_ci iommu_set_default_passthrough(true); 16362306a36Sopenharmony_ci if (!strncmp(p, "nopt", 4)) 16462306a36Sopenharmony_ci iommu_set_default_translated(true); 16562306a36Sopenharmony_ci 16662306a36Sopenharmony_ci gart_parse_options(p); 16762306a36Sopenharmony_ci 16862306a36Sopenharmony_ci p += strcspn(p, ","); 16962306a36Sopenharmony_ci if (*p == ',') 17062306a36Sopenharmony_ci ++p; 17162306a36Sopenharmony_ci } 17262306a36Sopenharmony_ci return 0; 17362306a36Sopenharmony_ci} 17462306a36Sopenharmony_ciearly_param("iommu", iommu_setup); 17562306a36Sopenharmony_ci 17662306a36Sopenharmony_cistatic int __init pci_iommu_init(void) 17762306a36Sopenharmony_ci{ 17862306a36Sopenharmony_ci x86_init.iommu.iommu_init(); 17962306a36Sopenharmony_ci 18062306a36Sopenharmony_ci#ifdef CONFIG_SWIOTLB 18162306a36Sopenharmony_ci /* An IOMMU turned us off. */ 18262306a36Sopenharmony_ci if (x86_swiotlb_enable) { 18362306a36Sopenharmony_ci pr_info("PCI-DMA: Using software bounce buffering for IO (SWIOTLB)\n"); 18462306a36Sopenharmony_ci swiotlb_print_info(); 18562306a36Sopenharmony_ci } else { 18662306a36Sopenharmony_ci swiotlb_exit(); 18762306a36Sopenharmony_ci } 18862306a36Sopenharmony_ci#endif 18962306a36Sopenharmony_ci 19062306a36Sopenharmony_ci return 0; 19162306a36Sopenharmony_ci} 19262306a36Sopenharmony_ci/* Must execute after PCI subsystem */ 19362306a36Sopenharmony_cirootfs_initcall(pci_iommu_init); 19462306a36Sopenharmony_ci 19562306a36Sopenharmony_ci#ifdef CONFIG_PCI 19662306a36Sopenharmony_ci/* Many VIA bridges seem to corrupt data for DAC. Disable it here */ 19762306a36Sopenharmony_ci 19862306a36Sopenharmony_cistatic int via_no_dac_cb(struct pci_dev *pdev, void *data) 19962306a36Sopenharmony_ci{ 20062306a36Sopenharmony_ci pdev->dev.bus_dma_limit = DMA_BIT_MASK(32); 20162306a36Sopenharmony_ci return 0; 20262306a36Sopenharmony_ci} 20362306a36Sopenharmony_ci 20462306a36Sopenharmony_cistatic void via_no_dac(struct pci_dev *dev) 20562306a36Sopenharmony_ci{ 20662306a36Sopenharmony_ci if (!disable_dac_quirk) { 20762306a36Sopenharmony_ci dev_info(&dev->dev, "disabling DAC on VIA PCI bridge\n"); 20862306a36Sopenharmony_ci pci_walk_bus(dev->subordinate, via_no_dac_cb, NULL); 20962306a36Sopenharmony_ci } 21062306a36Sopenharmony_ci} 21162306a36Sopenharmony_ciDECLARE_PCI_FIXUP_CLASS_FINAL(PCI_VENDOR_ID_VIA, PCI_ANY_ID, 21262306a36Sopenharmony_ci PCI_CLASS_BRIDGE_PCI, 8, via_no_dac); 21362306a36Sopenharmony_ci#endif 214