162306a36Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-only 262306a36Sopenharmony_ci/* 362306a36Sopenharmony_ci * Copyright (C) 2007-2010 Advanced Micro Devices, Inc. 462306a36Sopenharmony_ci * Author: Joerg Roedel <jroedel@suse.de> 562306a36Sopenharmony_ci * Leo Duran <leo.duran@amd.com> 662306a36Sopenharmony_ci */ 762306a36Sopenharmony_ci 862306a36Sopenharmony_ci#define pr_fmt(fmt) "AMD-Vi: " fmt 962306a36Sopenharmony_ci#define dev_fmt(fmt) pr_fmt(fmt) 1062306a36Sopenharmony_ci 1162306a36Sopenharmony_ci#include <linux/pci.h> 1262306a36Sopenharmony_ci#include <linux/acpi.h> 1362306a36Sopenharmony_ci#include <linux/list.h> 1462306a36Sopenharmony_ci#include <linux/bitmap.h> 1562306a36Sopenharmony_ci#include <linux/slab.h> 1662306a36Sopenharmony_ci#include <linux/syscore_ops.h> 1762306a36Sopenharmony_ci#include <linux/interrupt.h> 1862306a36Sopenharmony_ci#include <linux/msi.h> 1962306a36Sopenharmony_ci#include <linux/irq.h> 2062306a36Sopenharmony_ci#include <linux/amd-iommu.h> 2162306a36Sopenharmony_ci#include <linux/export.h> 2262306a36Sopenharmony_ci#include <linux/kmemleak.h> 2362306a36Sopenharmony_ci#include <linux/cc_platform.h> 2462306a36Sopenharmony_ci#include <linux/iopoll.h> 2562306a36Sopenharmony_ci#include <asm/pci-direct.h> 2662306a36Sopenharmony_ci#include <asm/iommu.h> 2762306a36Sopenharmony_ci#include <asm/apic.h> 2862306a36Sopenharmony_ci#include <asm/gart.h> 2962306a36Sopenharmony_ci#include <asm/x86_init.h> 3062306a36Sopenharmony_ci#include <asm/io_apic.h> 3162306a36Sopenharmony_ci#include <asm/irq_remapping.h> 3262306a36Sopenharmony_ci#include <asm/set_memory.h> 3362306a36Sopenharmony_ci 3462306a36Sopenharmony_ci#include <linux/crash_dump.h> 3562306a36Sopenharmony_ci 3662306a36Sopenharmony_ci#include "amd_iommu.h" 3762306a36Sopenharmony_ci#include "../irq_remapping.h" 3862306a36Sopenharmony_ci 3962306a36Sopenharmony_ci/* 4062306a36Sopenharmony_ci * definitions for the ACPI scanning code 4162306a36Sopenharmony_ci */ 4262306a36Sopenharmony_ci#define IVRS_HEADER_LENGTH 48 4362306a36Sopenharmony_ci 4462306a36Sopenharmony_ci#define ACPI_IVHD_TYPE_MAX_SUPPORTED 0x40 4562306a36Sopenharmony_ci#define ACPI_IVMD_TYPE_ALL 0x20 4662306a36Sopenharmony_ci#define ACPI_IVMD_TYPE 0x21 4762306a36Sopenharmony_ci#define ACPI_IVMD_TYPE_RANGE 0x22 4862306a36Sopenharmony_ci 4962306a36Sopenharmony_ci#define IVHD_DEV_ALL 0x01 5062306a36Sopenharmony_ci#define IVHD_DEV_SELECT 0x02 5162306a36Sopenharmony_ci#define IVHD_DEV_SELECT_RANGE_START 0x03 5262306a36Sopenharmony_ci#define IVHD_DEV_RANGE_END 0x04 5362306a36Sopenharmony_ci#define IVHD_DEV_ALIAS 0x42 5462306a36Sopenharmony_ci#define IVHD_DEV_ALIAS_RANGE 0x43 5562306a36Sopenharmony_ci#define IVHD_DEV_EXT_SELECT 0x46 5662306a36Sopenharmony_ci#define IVHD_DEV_EXT_SELECT_RANGE 0x47 5762306a36Sopenharmony_ci#define IVHD_DEV_SPECIAL 0x48 5862306a36Sopenharmony_ci#define IVHD_DEV_ACPI_HID 0xf0 5962306a36Sopenharmony_ci 6062306a36Sopenharmony_ci#define UID_NOT_PRESENT 0 6162306a36Sopenharmony_ci#define UID_IS_INTEGER 1 6262306a36Sopenharmony_ci#define UID_IS_CHARACTER 2 6362306a36Sopenharmony_ci 6462306a36Sopenharmony_ci#define IVHD_SPECIAL_IOAPIC 1 6562306a36Sopenharmony_ci#define IVHD_SPECIAL_HPET 2 6662306a36Sopenharmony_ci 6762306a36Sopenharmony_ci#define IVHD_FLAG_HT_TUN_EN_MASK 0x01 6862306a36Sopenharmony_ci#define IVHD_FLAG_PASSPW_EN_MASK 0x02 6962306a36Sopenharmony_ci#define IVHD_FLAG_RESPASSPW_EN_MASK 0x04 7062306a36Sopenharmony_ci#define IVHD_FLAG_ISOC_EN_MASK 0x08 7162306a36Sopenharmony_ci 7262306a36Sopenharmony_ci#define IVMD_FLAG_EXCL_RANGE 0x08 7362306a36Sopenharmony_ci#define IVMD_FLAG_IW 0x04 7462306a36Sopenharmony_ci#define IVMD_FLAG_IR 0x02 7562306a36Sopenharmony_ci#define IVMD_FLAG_UNITY_MAP 0x01 7662306a36Sopenharmony_ci 7762306a36Sopenharmony_ci#define ACPI_DEVFLAG_INITPASS 0x01 7862306a36Sopenharmony_ci#define ACPI_DEVFLAG_EXTINT 0x02 7962306a36Sopenharmony_ci#define ACPI_DEVFLAG_NMI 0x04 8062306a36Sopenharmony_ci#define ACPI_DEVFLAG_SYSMGT1 0x10 8162306a36Sopenharmony_ci#define ACPI_DEVFLAG_SYSMGT2 0x20 8262306a36Sopenharmony_ci#define ACPI_DEVFLAG_LINT0 0x40 8362306a36Sopenharmony_ci#define ACPI_DEVFLAG_LINT1 0x80 8462306a36Sopenharmony_ci#define ACPI_DEVFLAG_ATSDIS 0x10000000 8562306a36Sopenharmony_ci 8662306a36Sopenharmony_ci#define LOOP_TIMEOUT 2000000 8762306a36Sopenharmony_ci 8862306a36Sopenharmony_ci#define IVRS_GET_SBDF_ID(seg, bus, dev, fn) (((seg & 0xffff) << 16) | ((bus & 0xff) << 8) \ 8962306a36Sopenharmony_ci | ((dev & 0x1f) << 3) | (fn & 0x7)) 9062306a36Sopenharmony_ci 9162306a36Sopenharmony_ci/* 9262306a36Sopenharmony_ci * ACPI table definitions 9362306a36Sopenharmony_ci * 9462306a36Sopenharmony_ci * These data structures are laid over the table to parse the important values 9562306a36Sopenharmony_ci * out of it. 9662306a36Sopenharmony_ci */ 9762306a36Sopenharmony_ci 9862306a36Sopenharmony_ci/* 9962306a36Sopenharmony_ci * structure describing one IOMMU in the ACPI table. Typically followed by one 10062306a36Sopenharmony_ci * or more ivhd_entrys. 10162306a36Sopenharmony_ci */ 10262306a36Sopenharmony_cistruct ivhd_header { 10362306a36Sopenharmony_ci u8 type; 10462306a36Sopenharmony_ci u8 flags; 10562306a36Sopenharmony_ci u16 length; 10662306a36Sopenharmony_ci u16 devid; 10762306a36Sopenharmony_ci u16 cap_ptr; 10862306a36Sopenharmony_ci u64 mmio_phys; 10962306a36Sopenharmony_ci u16 pci_seg; 11062306a36Sopenharmony_ci u16 info; 11162306a36Sopenharmony_ci u32 efr_attr; 11262306a36Sopenharmony_ci 11362306a36Sopenharmony_ci /* Following only valid on IVHD type 11h and 40h */ 11462306a36Sopenharmony_ci u64 efr_reg; /* Exact copy of MMIO_EXT_FEATURES */ 11562306a36Sopenharmony_ci u64 efr_reg2; 11662306a36Sopenharmony_ci} __attribute__((packed)); 11762306a36Sopenharmony_ci 11862306a36Sopenharmony_ci/* 11962306a36Sopenharmony_ci * A device entry describing which devices a specific IOMMU translates and 12062306a36Sopenharmony_ci * which requestor ids they use. 12162306a36Sopenharmony_ci */ 12262306a36Sopenharmony_cistruct ivhd_entry { 12362306a36Sopenharmony_ci u8 type; 12462306a36Sopenharmony_ci u16 devid; 12562306a36Sopenharmony_ci u8 flags; 12662306a36Sopenharmony_ci struct_group(ext_hid, 12762306a36Sopenharmony_ci u32 ext; 12862306a36Sopenharmony_ci u32 hidh; 12962306a36Sopenharmony_ci ); 13062306a36Sopenharmony_ci u64 cid; 13162306a36Sopenharmony_ci u8 uidf; 13262306a36Sopenharmony_ci u8 uidl; 13362306a36Sopenharmony_ci u8 uid; 13462306a36Sopenharmony_ci} __attribute__((packed)); 13562306a36Sopenharmony_ci 13662306a36Sopenharmony_ci/* 13762306a36Sopenharmony_ci * An AMD IOMMU memory definition structure. It defines things like exclusion 13862306a36Sopenharmony_ci * ranges for devices and regions that should be unity mapped. 13962306a36Sopenharmony_ci */ 14062306a36Sopenharmony_cistruct ivmd_header { 14162306a36Sopenharmony_ci u8 type; 14262306a36Sopenharmony_ci u8 flags; 14362306a36Sopenharmony_ci u16 length; 14462306a36Sopenharmony_ci u16 devid; 14562306a36Sopenharmony_ci u16 aux; 14662306a36Sopenharmony_ci u16 pci_seg; 14762306a36Sopenharmony_ci u8 resv[6]; 14862306a36Sopenharmony_ci u64 range_start; 14962306a36Sopenharmony_ci u64 range_length; 15062306a36Sopenharmony_ci} __attribute__((packed)); 15162306a36Sopenharmony_ci 15262306a36Sopenharmony_cibool amd_iommu_dump; 15362306a36Sopenharmony_cibool amd_iommu_irq_remap __read_mostly; 15462306a36Sopenharmony_ci 15562306a36Sopenharmony_cienum io_pgtable_fmt amd_iommu_pgtable = AMD_IOMMU_V1; 15662306a36Sopenharmony_ci/* Guest page table level */ 15762306a36Sopenharmony_ciint amd_iommu_gpt_level = PAGE_MODE_4_LEVEL; 15862306a36Sopenharmony_ci 15962306a36Sopenharmony_ciint amd_iommu_guest_ir = AMD_IOMMU_GUEST_IR_VAPIC; 16062306a36Sopenharmony_cistatic int amd_iommu_xt_mode = IRQ_REMAP_XAPIC_MODE; 16162306a36Sopenharmony_ci 16262306a36Sopenharmony_cistatic bool amd_iommu_detected; 16362306a36Sopenharmony_cistatic bool amd_iommu_disabled __initdata; 16462306a36Sopenharmony_cistatic bool amd_iommu_force_enable __initdata; 16562306a36Sopenharmony_cistatic bool amd_iommu_irtcachedis; 16662306a36Sopenharmony_cistatic int amd_iommu_target_ivhd_type; 16762306a36Sopenharmony_ci 16862306a36Sopenharmony_ci/* Global EFR and EFR2 registers */ 16962306a36Sopenharmony_ciu64 amd_iommu_efr; 17062306a36Sopenharmony_ciu64 amd_iommu_efr2; 17162306a36Sopenharmony_ci 17262306a36Sopenharmony_ci/* SNP is enabled on the system? */ 17362306a36Sopenharmony_cibool amd_iommu_snp_en; 17462306a36Sopenharmony_ciEXPORT_SYMBOL(amd_iommu_snp_en); 17562306a36Sopenharmony_ci 17662306a36Sopenharmony_ciLIST_HEAD(amd_iommu_pci_seg_list); /* list of all PCI segments */ 17762306a36Sopenharmony_ciLIST_HEAD(amd_iommu_list); /* list of all AMD IOMMUs in the 17862306a36Sopenharmony_ci system */ 17962306a36Sopenharmony_ci 18062306a36Sopenharmony_ci/* Array to assign indices to IOMMUs*/ 18162306a36Sopenharmony_cistruct amd_iommu *amd_iommus[MAX_IOMMUS]; 18262306a36Sopenharmony_ci 18362306a36Sopenharmony_ci/* Number of IOMMUs present in the system */ 18462306a36Sopenharmony_cistatic int amd_iommus_present; 18562306a36Sopenharmony_ci 18662306a36Sopenharmony_ci/* IOMMUs have a non-present cache? */ 18762306a36Sopenharmony_cibool amd_iommu_np_cache __read_mostly; 18862306a36Sopenharmony_cibool amd_iommu_iotlb_sup __read_mostly = true; 18962306a36Sopenharmony_ci 19062306a36Sopenharmony_ciu32 amd_iommu_max_pasid __read_mostly = ~0; 19162306a36Sopenharmony_ci 19262306a36Sopenharmony_cibool amd_iommu_v2_present __read_mostly; 19362306a36Sopenharmony_cistatic bool amd_iommu_pc_present __read_mostly; 19462306a36Sopenharmony_cibool amdr_ivrs_remap_support __read_mostly; 19562306a36Sopenharmony_ci 19662306a36Sopenharmony_cibool amd_iommu_force_isolation __read_mostly; 19762306a36Sopenharmony_ci 19862306a36Sopenharmony_ci/* 19962306a36Sopenharmony_ci * AMD IOMMU allows up to 2^16 different protection domains. This is a bitmap 20062306a36Sopenharmony_ci * to know which ones are already in use. 20162306a36Sopenharmony_ci */ 20262306a36Sopenharmony_ciunsigned long *amd_iommu_pd_alloc_bitmap; 20362306a36Sopenharmony_ci 20462306a36Sopenharmony_cienum iommu_init_state { 20562306a36Sopenharmony_ci IOMMU_START_STATE, 20662306a36Sopenharmony_ci IOMMU_IVRS_DETECTED, 20762306a36Sopenharmony_ci IOMMU_ACPI_FINISHED, 20862306a36Sopenharmony_ci IOMMU_ENABLED, 20962306a36Sopenharmony_ci IOMMU_PCI_INIT, 21062306a36Sopenharmony_ci IOMMU_INTERRUPTS_EN, 21162306a36Sopenharmony_ci IOMMU_INITIALIZED, 21262306a36Sopenharmony_ci IOMMU_NOT_FOUND, 21362306a36Sopenharmony_ci IOMMU_INIT_ERROR, 21462306a36Sopenharmony_ci IOMMU_CMDLINE_DISABLED, 21562306a36Sopenharmony_ci}; 21662306a36Sopenharmony_ci 21762306a36Sopenharmony_ci/* Early ioapic and hpet maps from kernel command line */ 21862306a36Sopenharmony_ci#define EARLY_MAP_SIZE 4 21962306a36Sopenharmony_cistatic struct devid_map __initdata early_ioapic_map[EARLY_MAP_SIZE]; 22062306a36Sopenharmony_cistatic struct devid_map __initdata early_hpet_map[EARLY_MAP_SIZE]; 22162306a36Sopenharmony_cistatic struct acpihid_map_entry __initdata early_acpihid_map[EARLY_MAP_SIZE]; 22262306a36Sopenharmony_ci 22362306a36Sopenharmony_cistatic int __initdata early_ioapic_map_size; 22462306a36Sopenharmony_cistatic int __initdata early_hpet_map_size; 22562306a36Sopenharmony_cistatic int __initdata early_acpihid_map_size; 22662306a36Sopenharmony_ci 22762306a36Sopenharmony_cistatic bool __initdata cmdline_maps; 22862306a36Sopenharmony_ci 22962306a36Sopenharmony_cistatic enum iommu_init_state init_state = IOMMU_START_STATE; 23062306a36Sopenharmony_ci 23162306a36Sopenharmony_cistatic int amd_iommu_enable_interrupts(void); 23262306a36Sopenharmony_cistatic int __init iommu_go_to_state(enum iommu_init_state state); 23362306a36Sopenharmony_cistatic void init_device_table_dma(struct amd_iommu_pci_seg *pci_seg); 23462306a36Sopenharmony_ci 23562306a36Sopenharmony_cistatic bool amd_iommu_pre_enabled = true; 23662306a36Sopenharmony_ci 23762306a36Sopenharmony_cistatic u32 amd_iommu_ivinfo __initdata; 23862306a36Sopenharmony_ci 23962306a36Sopenharmony_cibool translation_pre_enabled(struct amd_iommu *iommu) 24062306a36Sopenharmony_ci{ 24162306a36Sopenharmony_ci return (iommu->flags & AMD_IOMMU_FLAG_TRANS_PRE_ENABLED); 24262306a36Sopenharmony_ci} 24362306a36Sopenharmony_ci 24462306a36Sopenharmony_cistatic void clear_translation_pre_enabled(struct amd_iommu *iommu) 24562306a36Sopenharmony_ci{ 24662306a36Sopenharmony_ci iommu->flags &= ~AMD_IOMMU_FLAG_TRANS_PRE_ENABLED; 24762306a36Sopenharmony_ci} 24862306a36Sopenharmony_ci 24962306a36Sopenharmony_cistatic void init_translation_status(struct amd_iommu *iommu) 25062306a36Sopenharmony_ci{ 25162306a36Sopenharmony_ci u64 ctrl; 25262306a36Sopenharmony_ci 25362306a36Sopenharmony_ci ctrl = readq(iommu->mmio_base + MMIO_CONTROL_OFFSET); 25462306a36Sopenharmony_ci if (ctrl & (1<<CONTROL_IOMMU_EN)) 25562306a36Sopenharmony_ci iommu->flags |= AMD_IOMMU_FLAG_TRANS_PRE_ENABLED; 25662306a36Sopenharmony_ci} 25762306a36Sopenharmony_ci 25862306a36Sopenharmony_cistatic inline unsigned long tbl_size(int entry_size, int last_bdf) 25962306a36Sopenharmony_ci{ 26062306a36Sopenharmony_ci unsigned shift = PAGE_SHIFT + 26162306a36Sopenharmony_ci get_order((last_bdf + 1) * entry_size); 26262306a36Sopenharmony_ci 26362306a36Sopenharmony_ci return 1UL << shift; 26462306a36Sopenharmony_ci} 26562306a36Sopenharmony_ci 26662306a36Sopenharmony_ciint amd_iommu_get_num_iommus(void) 26762306a36Sopenharmony_ci{ 26862306a36Sopenharmony_ci return amd_iommus_present; 26962306a36Sopenharmony_ci} 27062306a36Sopenharmony_ci 27162306a36Sopenharmony_ci/* 27262306a36Sopenharmony_ci * Iterate through all the IOMMUs to get common EFR 27362306a36Sopenharmony_ci * masks among all IOMMUs and warn if found inconsistency. 27462306a36Sopenharmony_ci */ 27562306a36Sopenharmony_cistatic void get_global_efr(void) 27662306a36Sopenharmony_ci{ 27762306a36Sopenharmony_ci struct amd_iommu *iommu; 27862306a36Sopenharmony_ci 27962306a36Sopenharmony_ci for_each_iommu(iommu) { 28062306a36Sopenharmony_ci u64 tmp = iommu->features; 28162306a36Sopenharmony_ci u64 tmp2 = iommu->features2; 28262306a36Sopenharmony_ci 28362306a36Sopenharmony_ci if (list_is_first(&iommu->list, &amd_iommu_list)) { 28462306a36Sopenharmony_ci amd_iommu_efr = tmp; 28562306a36Sopenharmony_ci amd_iommu_efr2 = tmp2; 28662306a36Sopenharmony_ci continue; 28762306a36Sopenharmony_ci } 28862306a36Sopenharmony_ci 28962306a36Sopenharmony_ci if (amd_iommu_efr == tmp && 29062306a36Sopenharmony_ci amd_iommu_efr2 == tmp2) 29162306a36Sopenharmony_ci continue; 29262306a36Sopenharmony_ci 29362306a36Sopenharmony_ci pr_err(FW_BUG 29462306a36Sopenharmony_ci "Found inconsistent EFR/EFR2 %#llx,%#llx (global %#llx,%#llx) on iommu%d (%04x:%02x:%02x.%01x).\n", 29562306a36Sopenharmony_ci tmp, tmp2, amd_iommu_efr, amd_iommu_efr2, 29662306a36Sopenharmony_ci iommu->index, iommu->pci_seg->id, 29762306a36Sopenharmony_ci PCI_BUS_NUM(iommu->devid), PCI_SLOT(iommu->devid), 29862306a36Sopenharmony_ci PCI_FUNC(iommu->devid)); 29962306a36Sopenharmony_ci 30062306a36Sopenharmony_ci amd_iommu_efr &= tmp; 30162306a36Sopenharmony_ci amd_iommu_efr2 &= tmp2; 30262306a36Sopenharmony_ci } 30362306a36Sopenharmony_ci 30462306a36Sopenharmony_ci pr_info("Using global IVHD EFR:%#llx, EFR2:%#llx\n", amd_iommu_efr, amd_iommu_efr2); 30562306a36Sopenharmony_ci} 30662306a36Sopenharmony_ci 30762306a36Sopenharmony_cistatic bool check_feature_on_all_iommus(u64 mask) 30862306a36Sopenharmony_ci{ 30962306a36Sopenharmony_ci return !!(amd_iommu_efr & mask); 31062306a36Sopenharmony_ci} 31162306a36Sopenharmony_ci 31262306a36Sopenharmony_cistatic inline int check_feature_gpt_level(void) 31362306a36Sopenharmony_ci{ 31462306a36Sopenharmony_ci return ((amd_iommu_efr >> FEATURE_GATS_SHIFT) & FEATURE_GATS_MASK); 31562306a36Sopenharmony_ci} 31662306a36Sopenharmony_ci 31762306a36Sopenharmony_ci/* 31862306a36Sopenharmony_ci * For IVHD type 0x11/0x40, EFR is also available via IVHD. 31962306a36Sopenharmony_ci * Default to IVHD EFR since it is available sooner 32062306a36Sopenharmony_ci * (i.e. before PCI init). 32162306a36Sopenharmony_ci */ 32262306a36Sopenharmony_cistatic void __init early_iommu_features_init(struct amd_iommu *iommu, 32362306a36Sopenharmony_ci struct ivhd_header *h) 32462306a36Sopenharmony_ci{ 32562306a36Sopenharmony_ci if (amd_iommu_ivinfo & IOMMU_IVINFO_EFRSUP) { 32662306a36Sopenharmony_ci iommu->features = h->efr_reg; 32762306a36Sopenharmony_ci iommu->features2 = h->efr_reg2; 32862306a36Sopenharmony_ci } 32962306a36Sopenharmony_ci if (amd_iommu_ivinfo & IOMMU_IVINFO_DMA_REMAP) 33062306a36Sopenharmony_ci amdr_ivrs_remap_support = true; 33162306a36Sopenharmony_ci} 33262306a36Sopenharmony_ci 33362306a36Sopenharmony_ci/* Access to l1 and l2 indexed register spaces */ 33462306a36Sopenharmony_ci 33562306a36Sopenharmony_cistatic u32 iommu_read_l1(struct amd_iommu *iommu, u16 l1, u8 address) 33662306a36Sopenharmony_ci{ 33762306a36Sopenharmony_ci u32 val; 33862306a36Sopenharmony_ci 33962306a36Sopenharmony_ci pci_write_config_dword(iommu->dev, 0xf8, (address | l1 << 16)); 34062306a36Sopenharmony_ci pci_read_config_dword(iommu->dev, 0xfc, &val); 34162306a36Sopenharmony_ci return val; 34262306a36Sopenharmony_ci} 34362306a36Sopenharmony_ci 34462306a36Sopenharmony_cistatic void iommu_write_l1(struct amd_iommu *iommu, u16 l1, u8 address, u32 val) 34562306a36Sopenharmony_ci{ 34662306a36Sopenharmony_ci pci_write_config_dword(iommu->dev, 0xf8, (address | l1 << 16 | 1 << 31)); 34762306a36Sopenharmony_ci pci_write_config_dword(iommu->dev, 0xfc, val); 34862306a36Sopenharmony_ci pci_write_config_dword(iommu->dev, 0xf8, (address | l1 << 16)); 34962306a36Sopenharmony_ci} 35062306a36Sopenharmony_ci 35162306a36Sopenharmony_cistatic u32 iommu_read_l2(struct amd_iommu *iommu, u8 address) 35262306a36Sopenharmony_ci{ 35362306a36Sopenharmony_ci u32 val; 35462306a36Sopenharmony_ci 35562306a36Sopenharmony_ci pci_write_config_dword(iommu->dev, 0xf0, address); 35662306a36Sopenharmony_ci pci_read_config_dword(iommu->dev, 0xf4, &val); 35762306a36Sopenharmony_ci return val; 35862306a36Sopenharmony_ci} 35962306a36Sopenharmony_ci 36062306a36Sopenharmony_cistatic void iommu_write_l2(struct amd_iommu *iommu, u8 address, u32 val) 36162306a36Sopenharmony_ci{ 36262306a36Sopenharmony_ci pci_write_config_dword(iommu->dev, 0xf0, (address | 1 << 8)); 36362306a36Sopenharmony_ci pci_write_config_dword(iommu->dev, 0xf4, val); 36462306a36Sopenharmony_ci} 36562306a36Sopenharmony_ci 36662306a36Sopenharmony_ci/**************************************************************************** 36762306a36Sopenharmony_ci * 36862306a36Sopenharmony_ci * AMD IOMMU MMIO register space handling functions 36962306a36Sopenharmony_ci * 37062306a36Sopenharmony_ci * These functions are used to program the IOMMU device registers in 37162306a36Sopenharmony_ci * MMIO space required for that driver. 37262306a36Sopenharmony_ci * 37362306a36Sopenharmony_ci ****************************************************************************/ 37462306a36Sopenharmony_ci 37562306a36Sopenharmony_ci/* 37662306a36Sopenharmony_ci * This function set the exclusion range in the IOMMU. DMA accesses to the 37762306a36Sopenharmony_ci * exclusion range are passed through untranslated 37862306a36Sopenharmony_ci */ 37962306a36Sopenharmony_cistatic void iommu_set_exclusion_range(struct amd_iommu *iommu) 38062306a36Sopenharmony_ci{ 38162306a36Sopenharmony_ci u64 start = iommu->exclusion_start & PAGE_MASK; 38262306a36Sopenharmony_ci u64 limit = (start + iommu->exclusion_length - 1) & PAGE_MASK; 38362306a36Sopenharmony_ci u64 entry; 38462306a36Sopenharmony_ci 38562306a36Sopenharmony_ci if (!iommu->exclusion_start) 38662306a36Sopenharmony_ci return; 38762306a36Sopenharmony_ci 38862306a36Sopenharmony_ci entry = start | MMIO_EXCL_ENABLE_MASK; 38962306a36Sopenharmony_ci memcpy_toio(iommu->mmio_base + MMIO_EXCL_BASE_OFFSET, 39062306a36Sopenharmony_ci &entry, sizeof(entry)); 39162306a36Sopenharmony_ci 39262306a36Sopenharmony_ci entry = limit; 39362306a36Sopenharmony_ci memcpy_toio(iommu->mmio_base + MMIO_EXCL_LIMIT_OFFSET, 39462306a36Sopenharmony_ci &entry, sizeof(entry)); 39562306a36Sopenharmony_ci} 39662306a36Sopenharmony_ci 39762306a36Sopenharmony_cistatic void iommu_set_cwwb_range(struct amd_iommu *iommu) 39862306a36Sopenharmony_ci{ 39962306a36Sopenharmony_ci u64 start = iommu_virt_to_phys((void *)iommu->cmd_sem); 40062306a36Sopenharmony_ci u64 entry = start & PM_ADDR_MASK; 40162306a36Sopenharmony_ci 40262306a36Sopenharmony_ci if (!check_feature_on_all_iommus(FEATURE_SNP)) 40362306a36Sopenharmony_ci return; 40462306a36Sopenharmony_ci 40562306a36Sopenharmony_ci /* Note: 40662306a36Sopenharmony_ci * Re-purpose Exclusion base/limit registers for Completion wait 40762306a36Sopenharmony_ci * write-back base/limit. 40862306a36Sopenharmony_ci */ 40962306a36Sopenharmony_ci memcpy_toio(iommu->mmio_base + MMIO_EXCL_BASE_OFFSET, 41062306a36Sopenharmony_ci &entry, sizeof(entry)); 41162306a36Sopenharmony_ci 41262306a36Sopenharmony_ci /* Note: 41362306a36Sopenharmony_ci * Default to 4 Kbytes, which can be specified by setting base 41462306a36Sopenharmony_ci * address equal to the limit address. 41562306a36Sopenharmony_ci */ 41662306a36Sopenharmony_ci memcpy_toio(iommu->mmio_base + MMIO_EXCL_LIMIT_OFFSET, 41762306a36Sopenharmony_ci &entry, sizeof(entry)); 41862306a36Sopenharmony_ci} 41962306a36Sopenharmony_ci 42062306a36Sopenharmony_ci/* Programs the physical address of the device table into the IOMMU hardware */ 42162306a36Sopenharmony_cistatic void iommu_set_device_table(struct amd_iommu *iommu) 42262306a36Sopenharmony_ci{ 42362306a36Sopenharmony_ci u64 entry; 42462306a36Sopenharmony_ci u32 dev_table_size = iommu->pci_seg->dev_table_size; 42562306a36Sopenharmony_ci void *dev_table = (void *)get_dev_table(iommu); 42662306a36Sopenharmony_ci 42762306a36Sopenharmony_ci BUG_ON(iommu->mmio_base == NULL); 42862306a36Sopenharmony_ci 42962306a36Sopenharmony_ci entry = iommu_virt_to_phys(dev_table); 43062306a36Sopenharmony_ci entry |= (dev_table_size >> 12) - 1; 43162306a36Sopenharmony_ci memcpy_toio(iommu->mmio_base + MMIO_DEV_TABLE_OFFSET, 43262306a36Sopenharmony_ci &entry, sizeof(entry)); 43362306a36Sopenharmony_ci} 43462306a36Sopenharmony_ci 43562306a36Sopenharmony_ci/* Generic functions to enable/disable certain features of the IOMMU. */ 43662306a36Sopenharmony_cistatic void iommu_feature_enable(struct amd_iommu *iommu, u8 bit) 43762306a36Sopenharmony_ci{ 43862306a36Sopenharmony_ci u64 ctrl; 43962306a36Sopenharmony_ci 44062306a36Sopenharmony_ci ctrl = readq(iommu->mmio_base + MMIO_CONTROL_OFFSET); 44162306a36Sopenharmony_ci ctrl |= (1ULL << bit); 44262306a36Sopenharmony_ci writeq(ctrl, iommu->mmio_base + MMIO_CONTROL_OFFSET); 44362306a36Sopenharmony_ci} 44462306a36Sopenharmony_ci 44562306a36Sopenharmony_cistatic void iommu_feature_disable(struct amd_iommu *iommu, u8 bit) 44662306a36Sopenharmony_ci{ 44762306a36Sopenharmony_ci u64 ctrl; 44862306a36Sopenharmony_ci 44962306a36Sopenharmony_ci ctrl = readq(iommu->mmio_base + MMIO_CONTROL_OFFSET); 45062306a36Sopenharmony_ci ctrl &= ~(1ULL << bit); 45162306a36Sopenharmony_ci writeq(ctrl, iommu->mmio_base + MMIO_CONTROL_OFFSET); 45262306a36Sopenharmony_ci} 45362306a36Sopenharmony_ci 45462306a36Sopenharmony_cistatic void iommu_set_inv_tlb_timeout(struct amd_iommu *iommu, int timeout) 45562306a36Sopenharmony_ci{ 45662306a36Sopenharmony_ci u64 ctrl; 45762306a36Sopenharmony_ci 45862306a36Sopenharmony_ci ctrl = readq(iommu->mmio_base + MMIO_CONTROL_OFFSET); 45962306a36Sopenharmony_ci ctrl &= ~CTRL_INV_TO_MASK; 46062306a36Sopenharmony_ci ctrl |= (timeout << CONTROL_INV_TIMEOUT) & CTRL_INV_TO_MASK; 46162306a36Sopenharmony_ci writeq(ctrl, iommu->mmio_base + MMIO_CONTROL_OFFSET); 46262306a36Sopenharmony_ci} 46362306a36Sopenharmony_ci 46462306a36Sopenharmony_ci/* Function to enable the hardware */ 46562306a36Sopenharmony_cistatic void iommu_enable(struct amd_iommu *iommu) 46662306a36Sopenharmony_ci{ 46762306a36Sopenharmony_ci iommu_feature_enable(iommu, CONTROL_IOMMU_EN); 46862306a36Sopenharmony_ci} 46962306a36Sopenharmony_ci 47062306a36Sopenharmony_cistatic void iommu_disable(struct amd_iommu *iommu) 47162306a36Sopenharmony_ci{ 47262306a36Sopenharmony_ci if (!iommu->mmio_base) 47362306a36Sopenharmony_ci return; 47462306a36Sopenharmony_ci 47562306a36Sopenharmony_ci /* Disable command buffer */ 47662306a36Sopenharmony_ci iommu_feature_disable(iommu, CONTROL_CMDBUF_EN); 47762306a36Sopenharmony_ci 47862306a36Sopenharmony_ci /* Disable event logging and event interrupts */ 47962306a36Sopenharmony_ci iommu_feature_disable(iommu, CONTROL_EVT_INT_EN); 48062306a36Sopenharmony_ci iommu_feature_disable(iommu, CONTROL_EVT_LOG_EN); 48162306a36Sopenharmony_ci 48262306a36Sopenharmony_ci /* Disable IOMMU GA_LOG */ 48362306a36Sopenharmony_ci iommu_feature_disable(iommu, CONTROL_GALOG_EN); 48462306a36Sopenharmony_ci iommu_feature_disable(iommu, CONTROL_GAINT_EN); 48562306a36Sopenharmony_ci 48662306a36Sopenharmony_ci /* Disable IOMMU PPR logging */ 48762306a36Sopenharmony_ci iommu_feature_disable(iommu, CONTROL_PPRLOG_EN); 48862306a36Sopenharmony_ci iommu_feature_disable(iommu, CONTROL_PPRINT_EN); 48962306a36Sopenharmony_ci 49062306a36Sopenharmony_ci /* Disable IOMMU hardware itself */ 49162306a36Sopenharmony_ci iommu_feature_disable(iommu, CONTROL_IOMMU_EN); 49262306a36Sopenharmony_ci 49362306a36Sopenharmony_ci /* Clear IRTE cache disabling bit */ 49462306a36Sopenharmony_ci iommu_feature_disable(iommu, CONTROL_IRTCACHEDIS); 49562306a36Sopenharmony_ci} 49662306a36Sopenharmony_ci 49762306a36Sopenharmony_ci/* 49862306a36Sopenharmony_ci * mapping and unmapping functions for the IOMMU MMIO space. Each AMD IOMMU in 49962306a36Sopenharmony_ci * the system has one. 50062306a36Sopenharmony_ci */ 50162306a36Sopenharmony_cistatic u8 __iomem * __init iommu_map_mmio_space(u64 address, u64 end) 50262306a36Sopenharmony_ci{ 50362306a36Sopenharmony_ci if (!request_mem_region(address, end, "amd_iommu")) { 50462306a36Sopenharmony_ci pr_err("Can not reserve memory region %llx-%llx for mmio\n", 50562306a36Sopenharmony_ci address, end); 50662306a36Sopenharmony_ci pr_err("This is a BIOS bug. Please contact your hardware vendor\n"); 50762306a36Sopenharmony_ci return NULL; 50862306a36Sopenharmony_ci } 50962306a36Sopenharmony_ci 51062306a36Sopenharmony_ci return (u8 __iomem *)ioremap(address, end); 51162306a36Sopenharmony_ci} 51262306a36Sopenharmony_ci 51362306a36Sopenharmony_cistatic void __init iommu_unmap_mmio_space(struct amd_iommu *iommu) 51462306a36Sopenharmony_ci{ 51562306a36Sopenharmony_ci if (iommu->mmio_base) 51662306a36Sopenharmony_ci iounmap(iommu->mmio_base); 51762306a36Sopenharmony_ci release_mem_region(iommu->mmio_phys, iommu->mmio_phys_end); 51862306a36Sopenharmony_ci} 51962306a36Sopenharmony_ci 52062306a36Sopenharmony_cistatic inline u32 get_ivhd_header_size(struct ivhd_header *h) 52162306a36Sopenharmony_ci{ 52262306a36Sopenharmony_ci u32 size = 0; 52362306a36Sopenharmony_ci 52462306a36Sopenharmony_ci switch (h->type) { 52562306a36Sopenharmony_ci case 0x10: 52662306a36Sopenharmony_ci size = 24; 52762306a36Sopenharmony_ci break; 52862306a36Sopenharmony_ci case 0x11: 52962306a36Sopenharmony_ci case 0x40: 53062306a36Sopenharmony_ci size = 40; 53162306a36Sopenharmony_ci break; 53262306a36Sopenharmony_ci } 53362306a36Sopenharmony_ci return size; 53462306a36Sopenharmony_ci} 53562306a36Sopenharmony_ci 53662306a36Sopenharmony_ci/**************************************************************************** 53762306a36Sopenharmony_ci * 53862306a36Sopenharmony_ci * The functions below belong to the first pass of AMD IOMMU ACPI table 53962306a36Sopenharmony_ci * parsing. In this pass we try to find out the highest device id this 54062306a36Sopenharmony_ci * code has to handle. Upon this information the size of the shared data 54162306a36Sopenharmony_ci * structures is determined later. 54262306a36Sopenharmony_ci * 54362306a36Sopenharmony_ci ****************************************************************************/ 54462306a36Sopenharmony_ci 54562306a36Sopenharmony_ci/* 54662306a36Sopenharmony_ci * This function calculates the length of a given IVHD entry 54762306a36Sopenharmony_ci */ 54862306a36Sopenharmony_cistatic inline int ivhd_entry_length(u8 *ivhd) 54962306a36Sopenharmony_ci{ 55062306a36Sopenharmony_ci u32 type = ((struct ivhd_entry *)ivhd)->type; 55162306a36Sopenharmony_ci 55262306a36Sopenharmony_ci if (type < 0x80) { 55362306a36Sopenharmony_ci return 0x04 << (*ivhd >> 6); 55462306a36Sopenharmony_ci } else if (type == IVHD_DEV_ACPI_HID) { 55562306a36Sopenharmony_ci /* For ACPI_HID, offset 21 is uid len */ 55662306a36Sopenharmony_ci return *((u8 *)ivhd + 21) + 22; 55762306a36Sopenharmony_ci } 55862306a36Sopenharmony_ci return 0; 55962306a36Sopenharmony_ci} 56062306a36Sopenharmony_ci 56162306a36Sopenharmony_ci/* 56262306a36Sopenharmony_ci * After reading the highest device id from the IOMMU PCI capability header 56362306a36Sopenharmony_ci * this function looks if there is a higher device id defined in the ACPI table 56462306a36Sopenharmony_ci */ 56562306a36Sopenharmony_cistatic int __init find_last_devid_from_ivhd(struct ivhd_header *h) 56662306a36Sopenharmony_ci{ 56762306a36Sopenharmony_ci u8 *p = (void *)h, *end = (void *)h; 56862306a36Sopenharmony_ci struct ivhd_entry *dev; 56962306a36Sopenharmony_ci int last_devid = -EINVAL; 57062306a36Sopenharmony_ci 57162306a36Sopenharmony_ci u32 ivhd_size = get_ivhd_header_size(h); 57262306a36Sopenharmony_ci 57362306a36Sopenharmony_ci if (!ivhd_size) { 57462306a36Sopenharmony_ci pr_err("Unsupported IVHD type %#x\n", h->type); 57562306a36Sopenharmony_ci return -EINVAL; 57662306a36Sopenharmony_ci } 57762306a36Sopenharmony_ci 57862306a36Sopenharmony_ci p += ivhd_size; 57962306a36Sopenharmony_ci end += h->length; 58062306a36Sopenharmony_ci 58162306a36Sopenharmony_ci while (p < end) { 58262306a36Sopenharmony_ci dev = (struct ivhd_entry *)p; 58362306a36Sopenharmony_ci switch (dev->type) { 58462306a36Sopenharmony_ci case IVHD_DEV_ALL: 58562306a36Sopenharmony_ci /* Use maximum BDF value for DEV_ALL */ 58662306a36Sopenharmony_ci return 0xffff; 58762306a36Sopenharmony_ci case IVHD_DEV_SELECT: 58862306a36Sopenharmony_ci case IVHD_DEV_RANGE_END: 58962306a36Sopenharmony_ci case IVHD_DEV_ALIAS: 59062306a36Sopenharmony_ci case IVHD_DEV_EXT_SELECT: 59162306a36Sopenharmony_ci /* all the above subfield types refer to device ids */ 59262306a36Sopenharmony_ci if (dev->devid > last_devid) 59362306a36Sopenharmony_ci last_devid = dev->devid; 59462306a36Sopenharmony_ci break; 59562306a36Sopenharmony_ci default: 59662306a36Sopenharmony_ci break; 59762306a36Sopenharmony_ci } 59862306a36Sopenharmony_ci p += ivhd_entry_length(p); 59962306a36Sopenharmony_ci } 60062306a36Sopenharmony_ci 60162306a36Sopenharmony_ci WARN_ON(p != end); 60262306a36Sopenharmony_ci 60362306a36Sopenharmony_ci return last_devid; 60462306a36Sopenharmony_ci} 60562306a36Sopenharmony_ci 60662306a36Sopenharmony_cistatic int __init check_ivrs_checksum(struct acpi_table_header *table) 60762306a36Sopenharmony_ci{ 60862306a36Sopenharmony_ci int i; 60962306a36Sopenharmony_ci u8 checksum = 0, *p = (u8 *)table; 61062306a36Sopenharmony_ci 61162306a36Sopenharmony_ci for (i = 0; i < table->length; ++i) 61262306a36Sopenharmony_ci checksum += p[i]; 61362306a36Sopenharmony_ci if (checksum != 0) { 61462306a36Sopenharmony_ci /* ACPI table corrupt */ 61562306a36Sopenharmony_ci pr_err(FW_BUG "IVRS invalid checksum\n"); 61662306a36Sopenharmony_ci return -ENODEV; 61762306a36Sopenharmony_ci } 61862306a36Sopenharmony_ci 61962306a36Sopenharmony_ci return 0; 62062306a36Sopenharmony_ci} 62162306a36Sopenharmony_ci 62262306a36Sopenharmony_ci/* 62362306a36Sopenharmony_ci * Iterate over all IVHD entries in the ACPI table and find the highest device 62462306a36Sopenharmony_ci * id which we need to handle. This is the first of three functions which parse 62562306a36Sopenharmony_ci * the ACPI table. So we check the checksum here. 62662306a36Sopenharmony_ci */ 62762306a36Sopenharmony_cistatic int __init find_last_devid_acpi(struct acpi_table_header *table, u16 pci_seg) 62862306a36Sopenharmony_ci{ 62962306a36Sopenharmony_ci u8 *p = (u8 *)table, *end = (u8 *)table; 63062306a36Sopenharmony_ci struct ivhd_header *h; 63162306a36Sopenharmony_ci int last_devid, last_bdf = 0; 63262306a36Sopenharmony_ci 63362306a36Sopenharmony_ci p += IVRS_HEADER_LENGTH; 63462306a36Sopenharmony_ci 63562306a36Sopenharmony_ci end += table->length; 63662306a36Sopenharmony_ci while (p < end) { 63762306a36Sopenharmony_ci h = (struct ivhd_header *)p; 63862306a36Sopenharmony_ci if (h->pci_seg == pci_seg && 63962306a36Sopenharmony_ci h->type == amd_iommu_target_ivhd_type) { 64062306a36Sopenharmony_ci last_devid = find_last_devid_from_ivhd(h); 64162306a36Sopenharmony_ci 64262306a36Sopenharmony_ci if (last_devid < 0) 64362306a36Sopenharmony_ci return -EINVAL; 64462306a36Sopenharmony_ci if (last_devid > last_bdf) 64562306a36Sopenharmony_ci last_bdf = last_devid; 64662306a36Sopenharmony_ci } 64762306a36Sopenharmony_ci p += h->length; 64862306a36Sopenharmony_ci } 64962306a36Sopenharmony_ci WARN_ON(p != end); 65062306a36Sopenharmony_ci 65162306a36Sopenharmony_ci return last_bdf; 65262306a36Sopenharmony_ci} 65362306a36Sopenharmony_ci 65462306a36Sopenharmony_ci/**************************************************************************** 65562306a36Sopenharmony_ci * 65662306a36Sopenharmony_ci * The following functions belong to the code path which parses the ACPI table 65762306a36Sopenharmony_ci * the second time. In this ACPI parsing iteration we allocate IOMMU specific 65862306a36Sopenharmony_ci * data structures, initialize the per PCI segment device/alias/rlookup table 65962306a36Sopenharmony_ci * and also basically initialize the hardware. 66062306a36Sopenharmony_ci * 66162306a36Sopenharmony_ci ****************************************************************************/ 66262306a36Sopenharmony_ci 66362306a36Sopenharmony_ci/* Allocate per PCI segment device table */ 66462306a36Sopenharmony_cistatic inline int __init alloc_dev_table(struct amd_iommu_pci_seg *pci_seg) 66562306a36Sopenharmony_ci{ 66662306a36Sopenharmony_ci pci_seg->dev_table = (void *)__get_free_pages(GFP_KERNEL | __GFP_ZERO | GFP_DMA32, 66762306a36Sopenharmony_ci get_order(pci_seg->dev_table_size)); 66862306a36Sopenharmony_ci if (!pci_seg->dev_table) 66962306a36Sopenharmony_ci return -ENOMEM; 67062306a36Sopenharmony_ci 67162306a36Sopenharmony_ci return 0; 67262306a36Sopenharmony_ci} 67362306a36Sopenharmony_ci 67462306a36Sopenharmony_cistatic inline void free_dev_table(struct amd_iommu_pci_seg *pci_seg) 67562306a36Sopenharmony_ci{ 67662306a36Sopenharmony_ci free_pages((unsigned long)pci_seg->dev_table, 67762306a36Sopenharmony_ci get_order(pci_seg->dev_table_size)); 67862306a36Sopenharmony_ci pci_seg->dev_table = NULL; 67962306a36Sopenharmony_ci} 68062306a36Sopenharmony_ci 68162306a36Sopenharmony_ci/* Allocate per PCI segment IOMMU rlookup table. */ 68262306a36Sopenharmony_cistatic inline int __init alloc_rlookup_table(struct amd_iommu_pci_seg *pci_seg) 68362306a36Sopenharmony_ci{ 68462306a36Sopenharmony_ci pci_seg->rlookup_table = (void *)__get_free_pages( 68562306a36Sopenharmony_ci GFP_KERNEL | __GFP_ZERO, 68662306a36Sopenharmony_ci get_order(pci_seg->rlookup_table_size)); 68762306a36Sopenharmony_ci if (pci_seg->rlookup_table == NULL) 68862306a36Sopenharmony_ci return -ENOMEM; 68962306a36Sopenharmony_ci 69062306a36Sopenharmony_ci return 0; 69162306a36Sopenharmony_ci} 69262306a36Sopenharmony_ci 69362306a36Sopenharmony_cistatic inline void free_rlookup_table(struct amd_iommu_pci_seg *pci_seg) 69462306a36Sopenharmony_ci{ 69562306a36Sopenharmony_ci free_pages((unsigned long)pci_seg->rlookup_table, 69662306a36Sopenharmony_ci get_order(pci_seg->rlookup_table_size)); 69762306a36Sopenharmony_ci pci_seg->rlookup_table = NULL; 69862306a36Sopenharmony_ci} 69962306a36Sopenharmony_ci 70062306a36Sopenharmony_cistatic inline int __init alloc_irq_lookup_table(struct amd_iommu_pci_seg *pci_seg) 70162306a36Sopenharmony_ci{ 70262306a36Sopenharmony_ci pci_seg->irq_lookup_table = (void *)__get_free_pages( 70362306a36Sopenharmony_ci GFP_KERNEL | __GFP_ZERO, 70462306a36Sopenharmony_ci get_order(pci_seg->rlookup_table_size)); 70562306a36Sopenharmony_ci kmemleak_alloc(pci_seg->irq_lookup_table, 70662306a36Sopenharmony_ci pci_seg->rlookup_table_size, 1, GFP_KERNEL); 70762306a36Sopenharmony_ci if (pci_seg->irq_lookup_table == NULL) 70862306a36Sopenharmony_ci return -ENOMEM; 70962306a36Sopenharmony_ci 71062306a36Sopenharmony_ci return 0; 71162306a36Sopenharmony_ci} 71262306a36Sopenharmony_ci 71362306a36Sopenharmony_cistatic inline void free_irq_lookup_table(struct amd_iommu_pci_seg *pci_seg) 71462306a36Sopenharmony_ci{ 71562306a36Sopenharmony_ci kmemleak_free(pci_seg->irq_lookup_table); 71662306a36Sopenharmony_ci free_pages((unsigned long)pci_seg->irq_lookup_table, 71762306a36Sopenharmony_ci get_order(pci_seg->rlookup_table_size)); 71862306a36Sopenharmony_ci pci_seg->irq_lookup_table = NULL; 71962306a36Sopenharmony_ci} 72062306a36Sopenharmony_ci 72162306a36Sopenharmony_cistatic int __init alloc_alias_table(struct amd_iommu_pci_seg *pci_seg) 72262306a36Sopenharmony_ci{ 72362306a36Sopenharmony_ci int i; 72462306a36Sopenharmony_ci 72562306a36Sopenharmony_ci pci_seg->alias_table = (void *)__get_free_pages(GFP_KERNEL, 72662306a36Sopenharmony_ci get_order(pci_seg->alias_table_size)); 72762306a36Sopenharmony_ci if (!pci_seg->alias_table) 72862306a36Sopenharmony_ci return -ENOMEM; 72962306a36Sopenharmony_ci 73062306a36Sopenharmony_ci /* 73162306a36Sopenharmony_ci * let all alias entries point to itself 73262306a36Sopenharmony_ci */ 73362306a36Sopenharmony_ci for (i = 0; i <= pci_seg->last_bdf; ++i) 73462306a36Sopenharmony_ci pci_seg->alias_table[i] = i; 73562306a36Sopenharmony_ci 73662306a36Sopenharmony_ci return 0; 73762306a36Sopenharmony_ci} 73862306a36Sopenharmony_ci 73962306a36Sopenharmony_cistatic void __init free_alias_table(struct amd_iommu_pci_seg *pci_seg) 74062306a36Sopenharmony_ci{ 74162306a36Sopenharmony_ci free_pages((unsigned long)pci_seg->alias_table, 74262306a36Sopenharmony_ci get_order(pci_seg->alias_table_size)); 74362306a36Sopenharmony_ci pci_seg->alias_table = NULL; 74462306a36Sopenharmony_ci} 74562306a36Sopenharmony_ci 74662306a36Sopenharmony_ci/* 74762306a36Sopenharmony_ci * Allocates the command buffer. This buffer is per AMD IOMMU. We can 74862306a36Sopenharmony_ci * write commands to that buffer later and the IOMMU will execute them 74962306a36Sopenharmony_ci * asynchronously 75062306a36Sopenharmony_ci */ 75162306a36Sopenharmony_cistatic int __init alloc_command_buffer(struct amd_iommu *iommu) 75262306a36Sopenharmony_ci{ 75362306a36Sopenharmony_ci iommu->cmd_buf = (void *)__get_free_pages(GFP_KERNEL | __GFP_ZERO, 75462306a36Sopenharmony_ci get_order(CMD_BUFFER_SIZE)); 75562306a36Sopenharmony_ci 75662306a36Sopenharmony_ci return iommu->cmd_buf ? 0 : -ENOMEM; 75762306a36Sopenharmony_ci} 75862306a36Sopenharmony_ci 75962306a36Sopenharmony_ci/* 76062306a36Sopenharmony_ci * Interrupt handler has processed all pending events and adjusted head 76162306a36Sopenharmony_ci * and tail pointer. Reset overflow mask and restart logging again. 76262306a36Sopenharmony_ci */ 76362306a36Sopenharmony_cistatic void amd_iommu_restart_log(struct amd_iommu *iommu, const char *evt_type, 76462306a36Sopenharmony_ci u8 cntrl_intr, u8 cntrl_log, 76562306a36Sopenharmony_ci u32 status_run_mask, u32 status_overflow_mask) 76662306a36Sopenharmony_ci{ 76762306a36Sopenharmony_ci u32 status; 76862306a36Sopenharmony_ci 76962306a36Sopenharmony_ci status = readl(iommu->mmio_base + MMIO_STATUS_OFFSET); 77062306a36Sopenharmony_ci if (status & status_run_mask) 77162306a36Sopenharmony_ci return; 77262306a36Sopenharmony_ci 77362306a36Sopenharmony_ci pr_info_ratelimited("IOMMU %s log restarting\n", evt_type); 77462306a36Sopenharmony_ci 77562306a36Sopenharmony_ci iommu_feature_disable(iommu, cntrl_log); 77662306a36Sopenharmony_ci iommu_feature_disable(iommu, cntrl_intr); 77762306a36Sopenharmony_ci 77862306a36Sopenharmony_ci writel(status_overflow_mask, iommu->mmio_base + MMIO_STATUS_OFFSET); 77962306a36Sopenharmony_ci 78062306a36Sopenharmony_ci iommu_feature_enable(iommu, cntrl_intr); 78162306a36Sopenharmony_ci iommu_feature_enable(iommu, cntrl_log); 78262306a36Sopenharmony_ci} 78362306a36Sopenharmony_ci 78462306a36Sopenharmony_ci/* 78562306a36Sopenharmony_ci * This function restarts event logging in case the IOMMU experienced 78662306a36Sopenharmony_ci * an event log buffer overflow. 78762306a36Sopenharmony_ci */ 78862306a36Sopenharmony_civoid amd_iommu_restart_event_logging(struct amd_iommu *iommu) 78962306a36Sopenharmony_ci{ 79062306a36Sopenharmony_ci amd_iommu_restart_log(iommu, "Event", CONTROL_EVT_INT_EN, 79162306a36Sopenharmony_ci CONTROL_EVT_LOG_EN, MMIO_STATUS_EVT_RUN_MASK, 79262306a36Sopenharmony_ci MMIO_STATUS_EVT_OVERFLOW_MASK); 79362306a36Sopenharmony_ci} 79462306a36Sopenharmony_ci 79562306a36Sopenharmony_ci/* 79662306a36Sopenharmony_ci * This function restarts event logging in case the IOMMU experienced 79762306a36Sopenharmony_ci * GA log overflow. 79862306a36Sopenharmony_ci */ 79962306a36Sopenharmony_civoid amd_iommu_restart_ga_log(struct amd_iommu *iommu) 80062306a36Sopenharmony_ci{ 80162306a36Sopenharmony_ci amd_iommu_restart_log(iommu, "GA", CONTROL_GAINT_EN, 80262306a36Sopenharmony_ci CONTROL_GALOG_EN, MMIO_STATUS_GALOG_RUN_MASK, 80362306a36Sopenharmony_ci MMIO_STATUS_GALOG_OVERFLOW_MASK); 80462306a36Sopenharmony_ci} 80562306a36Sopenharmony_ci 80662306a36Sopenharmony_ci/* 80762306a36Sopenharmony_ci * This function restarts ppr logging in case the IOMMU experienced 80862306a36Sopenharmony_ci * PPR log overflow. 80962306a36Sopenharmony_ci */ 81062306a36Sopenharmony_civoid amd_iommu_restart_ppr_log(struct amd_iommu *iommu) 81162306a36Sopenharmony_ci{ 81262306a36Sopenharmony_ci amd_iommu_restart_log(iommu, "PPR", CONTROL_PPRINT_EN, 81362306a36Sopenharmony_ci CONTROL_PPRLOG_EN, MMIO_STATUS_PPR_RUN_MASK, 81462306a36Sopenharmony_ci MMIO_STATUS_PPR_OVERFLOW_MASK); 81562306a36Sopenharmony_ci} 81662306a36Sopenharmony_ci 81762306a36Sopenharmony_ci/* 81862306a36Sopenharmony_ci * This function resets the command buffer if the IOMMU stopped fetching 81962306a36Sopenharmony_ci * commands from it. 82062306a36Sopenharmony_ci */ 82162306a36Sopenharmony_cistatic void amd_iommu_reset_cmd_buffer(struct amd_iommu *iommu) 82262306a36Sopenharmony_ci{ 82362306a36Sopenharmony_ci iommu_feature_disable(iommu, CONTROL_CMDBUF_EN); 82462306a36Sopenharmony_ci 82562306a36Sopenharmony_ci writel(0x00, iommu->mmio_base + MMIO_CMD_HEAD_OFFSET); 82662306a36Sopenharmony_ci writel(0x00, iommu->mmio_base + MMIO_CMD_TAIL_OFFSET); 82762306a36Sopenharmony_ci iommu->cmd_buf_head = 0; 82862306a36Sopenharmony_ci iommu->cmd_buf_tail = 0; 82962306a36Sopenharmony_ci 83062306a36Sopenharmony_ci iommu_feature_enable(iommu, CONTROL_CMDBUF_EN); 83162306a36Sopenharmony_ci} 83262306a36Sopenharmony_ci 83362306a36Sopenharmony_ci/* 83462306a36Sopenharmony_ci * This function writes the command buffer address to the hardware and 83562306a36Sopenharmony_ci * enables it. 83662306a36Sopenharmony_ci */ 83762306a36Sopenharmony_cistatic void iommu_enable_command_buffer(struct amd_iommu *iommu) 83862306a36Sopenharmony_ci{ 83962306a36Sopenharmony_ci u64 entry; 84062306a36Sopenharmony_ci 84162306a36Sopenharmony_ci BUG_ON(iommu->cmd_buf == NULL); 84262306a36Sopenharmony_ci 84362306a36Sopenharmony_ci entry = iommu_virt_to_phys(iommu->cmd_buf); 84462306a36Sopenharmony_ci entry |= MMIO_CMD_SIZE_512; 84562306a36Sopenharmony_ci 84662306a36Sopenharmony_ci memcpy_toio(iommu->mmio_base + MMIO_CMD_BUF_OFFSET, 84762306a36Sopenharmony_ci &entry, sizeof(entry)); 84862306a36Sopenharmony_ci 84962306a36Sopenharmony_ci amd_iommu_reset_cmd_buffer(iommu); 85062306a36Sopenharmony_ci} 85162306a36Sopenharmony_ci 85262306a36Sopenharmony_ci/* 85362306a36Sopenharmony_ci * This function disables the command buffer 85462306a36Sopenharmony_ci */ 85562306a36Sopenharmony_cistatic void iommu_disable_command_buffer(struct amd_iommu *iommu) 85662306a36Sopenharmony_ci{ 85762306a36Sopenharmony_ci iommu_feature_disable(iommu, CONTROL_CMDBUF_EN); 85862306a36Sopenharmony_ci} 85962306a36Sopenharmony_ci 86062306a36Sopenharmony_cistatic void __init free_command_buffer(struct amd_iommu *iommu) 86162306a36Sopenharmony_ci{ 86262306a36Sopenharmony_ci free_pages((unsigned long)iommu->cmd_buf, get_order(CMD_BUFFER_SIZE)); 86362306a36Sopenharmony_ci} 86462306a36Sopenharmony_ci 86562306a36Sopenharmony_cistatic void *__init iommu_alloc_4k_pages(struct amd_iommu *iommu, 86662306a36Sopenharmony_ci gfp_t gfp, size_t size) 86762306a36Sopenharmony_ci{ 86862306a36Sopenharmony_ci int order = get_order(size); 86962306a36Sopenharmony_ci void *buf = (void *)__get_free_pages(gfp, order); 87062306a36Sopenharmony_ci 87162306a36Sopenharmony_ci if (buf && 87262306a36Sopenharmony_ci check_feature_on_all_iommus(FEATURE_SNP) && 87362306a36Sopenharmony_ci set_memory_4k((unsigned long)buf, (1 << order))) { 87462306a36Sopenharmony_ci free_pages((unsigned long)buf, order); 87562306a36Sopenharmony_ci buf = NULL; 87662306a36Sopenharmony_ci } 87762306a36Sopenharmony_ci 87862306a36Sopenharmony_ci return buf; 87962306a36Sopenharmony_ci} 88062306a36Sopenharmony_ci 88162306a36Sopenharmony_ci/* allocates the memory where the IOMMU will log its events to */ 88262306a36Sopenharmony_cistatic int __init alloc_event_buffer(struct amd_iommu *iommu) 88362306a36Sopenharmony_ci{ 88462306a36Sopenharmony_ci iommu->evt_buf = iommu_alloc_4k_pages(iommu, GFP_KERNEL | __GFP_ZERO, 88562306a36Sopenharmony_ci EVT_BUFFER_SIZE); 88662306a36Sopenharmony_ci 88762306a36Sopenharmony_ci return iommu->evt_buf ? 0 : -ENOMEM; 88862306a36Sopenharmony_ci} 88962306a36Sopenharmony_ci 89062306a36Sopenharmony_cistatic void iommu_enable_event_buffer(struct amd_iommu *iommu) 89162306a36Sopenharmony_ci{ 89262306a36Sopenharmony_ci u64 entry; 89362306a36Sopenharmony_ci 89462306a36Sopenharmony_ci BUG_ON(iommu->evt_buf == NULL); 89562306a36Sopenharmony_ci 89662306a36Sopenharmony_ci entry = iommu_virt_to_phys(iommu->evt_buf) | EVT_LEN_MASK; 89762306a36Sopenharmony_ci 89862306a36Sopenharmony_ci memcpy_toio(iommu->mmio_base + MMIO_EVT_BUF_OFFSET, 89962306a36Sopenharmony_ci &entry, sizeof(entry)); 90062306a36Sopenharmony_ci 90162306a36Sopenharmony_ci /* set head and tail to zero manually */ 90262306a36Sopenharmony_ci writel(0x00, iommu->mmio_base + MMIO_EVT_HEAD_OFFSET); 90362306a36Sopenharmony_ci writel(0x00, iommu->mmio_base + MMIO_EVT_TAIL_OFFSET); 90462306a36Sopenharmony_ci 90562306a36Sopenharmony_ci iommu_feature_enable(iommu, CONTROL_EVT_LOG_EN); 90662306a36Sopenharmony_ci} 90762306a36Sopenharmony_ci 90862306a36Sopenharmony_ci/* 90962306a36Sopenharmony_ci * This function disables the event log buffer 91062306a36Sopenharmony_ci */ 91162306a36Sopenharmony_cistatic void iommu_disable_event_buffer(struct amd_iommu *iommu) 91262306a36Sopenharmony_ci{ 91362306a36Sopenharmony_ci iommu_feature_disable(iommu, CONTROL_EVT_LOG_EN); 91462306a36Sopenharmony_ci} 91562306a36Sopenharmony_ci 91662306a36Sopenharmony_cistatic void __init free_event_buffer(struct amd_iommu *iommu) 91762306a36Sopenharmony_ci{ 91862306a36Sopenharmony_ci free_pages((unsigned long)iommu->evt_buf, get_order(EVT_BUFFER_SIZE)); 91962306a36Sopenharmony_ci} 92062306a36Sopenharmony_ci 92162306a36Sopenharmony_ci/* allocates the memory where the IOMMU will log its events to */ 92262306a36Sopenharmony_cistatic int __init alloc_ppr_log(struct amd_iommu *iommu) 92362306a36Sopenharmony_ci{ 92462306a36Sopenharmony_ci iommu->ppr_log = iommu_alloc_4k_pages(iommu, GFP_KERNEL | __GFP_ZERO, 92562306a36Sopenharmony_ci PPR_LOG_SIZE); 92662306a36Sopenharmony_ci 92762306a36Sopenharmony_ci return iommu->ppr_log ? 0 : -ENOMEM; 92862306a36Sopenharmony_ci} 92962306a36Sopenharmony_ci 93062306a36Sopenharmony_cistatic void iommu_enable_ppr_log(struct amd_iommu *iommu) 93162306a36Sopenharmony_ci{ 93262306a36Sopenharmony_ci u64 entry; 93362306a36Sopenharmony_ci 93462306a36Sopenharmony_ci if (iommu->ppr_log == NULL) 93562306a36Sopenharmony_ci return; 93662306a36Sopenharmony_ci 93762306a36Sopenharmony_ci iommu_feature_enable(iommu, CONTROL_PPR_EN); 93862306a36Sopenharmony_ci 93962306a36Sopenharmony_ci entry = iommu_virt_to_phys(iommu->ppr_log) | PPR_LOG_SIZE_512; 94062306a36Sopenharmony_ci 94162306a36Sopenharmony_ci memcpy_toio(iommu->mmio_base + MMIO_PPR_LOG_OFFSET, 94262306a36Sopenharmony_ci &entry, sizeof(entry)); 94362306a36Sopenharmony_ci 94462306a36Sopenharmony_ci /* set head and tail to zero manually */ 94562306a36Sopenharmony_ci writel(0x00, iommu->mmio_base + MMIO_PPR_HEAD_OFFSET); 94662306a36Sopenharmony_ci writel(0x00, iommu->mmio_base + MMIO_PPR_TAIL_OFFSET); 94762306a36Sopenharmony_ci 94862306a36Sopenharmony_ci iommu_feature_enable(iommu, CONTROL_PPRLOG_EN); 94962306a36Sopenharmony_ci iommu_feature_enable(iommu, CONTROL_PPRINT_EN); 95062306a36Sopenharmony_ci} 95162306a36Sopenharmony_ci 95262306a36Sopenharmony_cistatic void __init free_ppr_log(struct amd_iommu *iommu) 95362306a36Sopenharmony_ci{ 95462306a36Sopenharmony_ci free_pages((unsigned long)iommu->ppr_log, get_order(PPR_LOG_SIZE)); 95562306a36Sopenharmony_ci} 95662306a36Sopenharmony_ci 95762306a36Sopenharmony_cistatic void free_ga_log(struct amd_iommu *iommu) 95862306a36Sopenharmony_ci{ 95962306a36Sopenharmony_ci#ifdef CONFIG_IRQ_REMAP 96062306a36Sopenharmony_ci free_pages((unsigned long)iommu->ga_log, get_order(GA_LOG_SIZE)); 96162306a36Sopenharmony_ci free_pages((unsigned long)iommu->ga_log_tail, get_order(8)); 96262306a36Sopenharmony_ci#endif 96362306a36Sopenharmony_ci} 96462306a36Sopenharmony_ci 96562306a36Sopenharmony_ci#ifdef CONFIG_IRQ_REMAP 96662306a36Sopenharmony_cistatic int iommu_ga_log_enable(struct amd_iommu *iommu) 96762306a36Sopenharmony_ci{ 96862306a36Sopenharmony_ci u32 status, i; 96962306a36Sopenharmony_ci u64 entry; 97062306a36Sopenharmony_ci 97162306a36Sopenharmony_ci if (!iommu->ga_log) 97262306a36Sopenharmony_ci return -EINVAL; 97362306a36Sopenharmony_ci 97462306a36Sopenharmony_ci entry = iommu_virt_to_phys(iommu->ga_log) | GA_LOG_SIZE_512; 97562306a36Sopenharmony_ci memcpy_toio(iommu->mmio_base + MMIO_GA_LOG_BASE_OFFSET, 97662306a36Sopenharmony_ci &entry, sizeof(entry)); 97762306a36Sopenharmony_ci entry = (iommu_virt_to_phys(iommu->ga_log_tail) & 97862306a36Sopenharmony_ci (BIT_ULL(52)-1)) & ~7ULL; 97962306a36Sopenharmony_ci memcpy_toio(iommu->mmio_base + MMIO_GA_LOG_TAIL_OFFSET, 98062306a36Sopenharmony_ci &entry, sizeof(entry)); 98162306a36Sopenharmony_ci writel(0x00, iommu->mmio_base + MMIO_GA_HEAD_OFFSET); 98262306a36Sopenharmony_ci writel(0x00, iommu->mmio_base + MMIO_GA_TAIL_OFFSET); 98362306a36Sopenharmony_ci 98462306a36Sopenharmony_ci 98562306a36Sopenharmony_ci iommu_feature_enable(iommu, CONTROL_GAINT_EN); 98662306a36Sopenharmony_ci iommu_feature_enable(iommu, CONTROL_GALOG_EN); 98762306a36Sopenharmony_ci 98862306a36Sopenharmony_ci for (i = 0; i < LOOP_TIMEOUT; ++i) { 98962306a36Sopenharmony_ci status = readl(iommu->mmio_base + MMIO_STATUS_OFFSET); 99062306a36Sopenharmony_ci if (status & (MMIO_STATUS_GALOG_RUN_MASK)) 99162306a36Sopenharmony_ci break; 99262306a36Sopenharmony_ci udelay(10); 99362306a36Sopenharmony_ci } 99462306a36Sopenharmony_ci 99562306a36Sopenharmony_ci if (WARN_ON(i >= LOOP_TIMEOUT)) 99662306a36Sopenharmony_ci return -EINVAL; 99762306a36Sopenharmony_ci 99862306a36Sopenharmony_ci return 0; 99962306a36Sopenharmony_ci} 100062306a36Sopenharmony_ci 100162306a36Sopenharmony_cistatic int iommu_init_ga_log(struct amd_iommu *iommu) 100262306a36Sopenharmony_ci{ 100362306a36Sopenharmony_ci if (!AMD_IOMMU_GUEST_IR_VAPIC(amd_iommu_guest_ir)) 100462306a36Sopenharmony_ci return 0; 100562306a36Sopenharmony_ci 100662306a36Sopenharmony_ci iommu->ga_log = (u8 *)__get_free_pages(GFP_KERNEL | __GFP_ZERO, 100762306a36Sopenharmony_ci get_order(GA_LOG_SIZE)); 100862306a36Sopenharmony_ci if (!iommu->ga_log) 100962306a36Sopenharmony_ci goto err_out; 101062306a36Sopenharmony_ci 101162306a36Sopenharmony_ci iommu->ga_log_tail = (u8 *)__get_free_pages(GFP_KERNEL | __GFP_ZERO, 101262306a36Sopenharmony_ci get_order(8)); 101362306a36Sopenharmony_ci if (!iommu->ga_log_tail) 101462306a36Sopenharmony_ci goto err_out; 101562306a36Sopenharmony_ci 101662306a36Sopenharmony_ci return 0; 101762306a36Sopenharmony_cierr_out: 101862306a36Sopenharmony_ci free_ga_log(iommu); 101962306a36Sopenharmony_ci return -EINVAL; 102062306a36Sopenharmony_ci} 102162306a36Sopenharmony_ci#endif /* CONFIG_IRQ_REMAP */ 102262306a36Sopenharmony_ci 102362306a36Sopenharmony_cistatic int __init alloc_cwwb_sem(struct amd_iommu *iommu) 102462306a36Sopenharmony_ci{ 102562306a36Sopenharmony_ci iommu->cmd_sem = iommu_alloc_4k_pages(iommu, GFP_KERNEL | __GFP_ZERO, 1); 102662306a36Sopenharmony_ci 102762306a36Sopenharmony_ci return iommu->cmd_sem ? 0 : -ENOMEM; 102862306a36Sopenharmony_ci} 102962306a36Sopenharmony_ci 103062306a36Sopenharmony_cistatic void __init free_cwwb_sem(struct amd_iommu *iommu) 103162306a36Sopenharmony_ci{ 103262306a36Sopenharmony_ci if (iommu->cmd_sem) 103362306a36Sopenharmony_ci free_page((unsigned long)iommu->cmd_sem); 103462306a36Sopenharmony_ci} 103562306a36Sopenharmony_ci 103662306a36Sopenharmony_cistatic void iommu_enable_xt(struct amd_iommu *iommu) 103762306a36Sopenharmony_ci{ 103862306a36Sopenharmony_ci#ifdef CONFIG_IRQ_REMAP 103962306a36Sopenharmony_ci /* 104062306a36Sopenharmony_ci * XT mode (32-bit APIC destination ID) requires 104162306a36Sopenharmony_ci * GA mode (128-bit IRTE support) as a prerequisite. 104262306a36Sopenharmony_ci */ 104362306a36Sopenharmony_ci if (AMD_IOMMU_GUEST_IR_GA(amd_iommu_guest_ir) && 104462306a36Sopenharmony_ci amd_iommu_xt_mode == IRQ_REMAP_X2APIC_MODE) 104562306a36Sopenharmony_ci iommu_feature_enable(iommu, CONTROL_XT_EN); 104662306a36Sopenharmony_ci#endif /* CONFIG_IRQ_REMAP */ 104762306a36Sopenharmony_ci} 104862306a36Sopenharmony_ci 104962306a36Sopenharmony_cistatic void iommu_enable_gt(struct amd_iommu *iommu) 105062306a36Sopenharmony_ci{ 105162306a36Sopenharmony_ci if (!iommu_feature(iommu, FEATURE_GT)) 105262306a36Sopenharmony_ci return; 105362306a36Sopenharmony_ci 105462306a36Sopenharmony_ci iommu_feature_enable(iommu, CONTROL_GT_EN); 105562306a36Sopenharmony_ci} 105662306a36Sopenharmony_ci 105762306a36Sopenharmony_ci/* sets a specific bit in the device table entry. */ 105862306a36Sopenharmony_cistatic void __set_dev_entry_bit(struct dev_table_entry *dev_table, 105962306a36Sopenharmony_ci u16 devid, u8 bit) 106062306a36Sopenharmony_ci{ 106162306a36Sopenharmony_ci int i = (bit >> 6) & 0x03; 106262306a36Sopenharmony_ci int _bit = bit & 0x3f; 106362306a36Sopenharmony_ci 106462306a36Sopenharmony_ci dev_table[devid].data[i] |= (1UL << _bit); 106562306a36Sopenharmony_ci} 106662306a36Sopenharmony_ci 106762306a36Sopenharmony_cistatic void set_dev_entry_bit(struct amd_iommu *iommu, u16 devid, u8 bit) 106862306a36Sopenharmony_ci{ 106962306a36Sopenharmony_ci struct dev_table_entry *dev_table = get_dev_table(iommu); 107062306a36Sopenharmony_ci 107162306a36Sopenharmony_ci return __set_dev_entry_bit(dev_table, devid, bit); 107262306a36Sopenharmony_ci} 107362306a36Sopenharmony_ci 107462306a36Sopenharmony_cistatic int __get_dev_entry_bit(struct dev_table_entry *dev_table, 107562306a36Sopenharmony_ci u16 devid, u8 bit) 107662306a36Sopenharmony_ci{ 107762306a36Sopenharmony_ci int i = (bit >> 6) & 0x03; 107862306a36Sopenharmony_ci int _bit = bit & 0x3f; 107962306a36Sopenharmony_ci 108062306a36Sopenharmony_ci return (dev_table[devid].data[i] & (1UL << _bit)) >> _bit; 108162306a36Sopenharmony_ci} 108262306a36Sopenharmony_ci 108362306a36Sopenharmony_cistatic int get_dev_entry_bit(struct amd_iommu *iommu, u16 devid, u8 bit) 108462306a36Sopenharmony_ci{ 108562306a36Sopenharmony_ci struct dev_table_entry *dev_table = get_dev_table(iommu); 108662306a36Sopenharmony_ci 108762306a36Sopenharmony_ci return __get_dev_entry_bit(dev_table, devid, bit); 108862306a36Sopenharmony_ci} 108962306a36Sopenharmony_ci 109062306a36Sopenharmony_cistatic bool __copy_device_table(struct amd_iommu *iommu) 109162306a36Sopenharmony_ci{ 109262306a36Sopenharmony_ci u64 int_ctl, int_tab_len, entry = 0; 109362306a36Sopenharmony_ci struct amd_iommu_pci_seg *pci_seg = iommu->pci_seg; 109462306a36Sopenharmony_ci struct dev_table_entry *old_devtb = NULL; 109562306a36Sopenharmony_ci u32 lo, hi, devid, old_devtb_size; 109662306a36Sopenharmony_ci phys_addr_t old_devtb_phys; 109762306a36Sopenharmony_ci u16 dom_id, dte_v, irq_v; 109862306a36Sopenharmony_ci gfp_t gfp_flag; 109962306a36Sopenharmony_ci u64 tmp; 110062306a36Sopenharmony_ci 110162306a36Sopenharmony_ci /* Each IOMMU use separate device table with the same size */ 110262306a36Sopenharmony_ci lo = readl(iommu->mmio_base + MMIO_DEV_TABLE_OFFSET); 110362306a36Sopenharmony_ci hi = readl(iommu->mmio_base + MMIO_DEV_TABLE_OFFSET + 4); 110462306a36Sopenharmony_ci entry = (((u64) hi) << 32) + lo; 110562306a36Sopenharmony_ci 110662306a36Sopenharmony_ci old_devtb_size = ((entry & ~PAGE_MASK) + 1) << 12; 110762306a36Sopenharmony_ci if (old_devtb_size != pci_seg->dev_table_size) { 110862306a36Sopenharmony_ci pr_err("The device table size of IOMMU:%d is not expected!\n", 110962306a36Sopenharmony_ci iommu->index); 111062306a36Sopenharmony_ci return false; 111162306a36Sopenharmony_ci } 111262306a36Sopenharmony_ci 111362306a36Sopenharmony_ci /* 111462306a36Sopenharmony_ci * When SME is enabled in the first kernel, the entry includes the 111562306a36Sopenharmony_ci * memory encryption mask(sme_me_mask), we must remove the memory 111662306a36Sopenharmony_ci * encryption mask to obtain the true physical address in kdump kernel. 111762306a36Sopenharmony_ci */ 111862306a36Sopenharmony_ci old_devtb_phys = __sme_clr(entry) & PAGE_MASK; 111962306a36Sopenharmony_ci 112062306a36Sopenharmony_ci if (old_devtb_phys >= 0x100000000ULL) { 112162306a36Sopenharmony_ci pr_err("The address of old device table is above 4G, not trustworthy!\n"); 112262306a36Sopenharmony_ci return false; 112362306a36Sopenharmony_ci } 112462306a36Sopenharmony_ci old_devtb = (cc_platform_has(CC_ATTR_HOST_MEM_ENCRYPT) && is_kdump_kernel()) 112562306a36Sopenharmony_ci ? (__force void *)ioremap_encrypted(old_devtb_phys, 112662306a36Sopenharmony_ci pci_seg->dev_table_size) 112762306a36Sopenharmony_ci : memremap(old_devtb_phys, pci_seg->dev_table_size, MEMREMAP_WB); 112862306a36Sopenharmony_ci 112962306a36Sopenharmony_ci if (!old_devtb) 113062306a36Sopenharmony_ci return false; 113162306a36Sopenharmony_ci 113262306a36Sopenharmony_ci gfp_flag = GFP_KERNEL | __GFP_ZERO | GFP_DMA32; 113362306a36Sopenharmony_ci pci_seg->old_dev_tbl_cpy = (void *)__get_free_pages(gfp_flag, 113462306a36Sopenharmony_ci get_order(pci_seg->dev_table_size)); 113562306a36Sopenharmony_ci if (pci_seg->old_dev_tbl_cpy == NULL) { 113662306a36Sopenharmony_ci pr_err("Failed to allocate memory for copying old device table!\n"); 113762306a36Sopenharmony_ci memunmap(old_devtb); 113862306a36Sopenharmony_ci return false; 113962306a36Sopenharmony_ci } 114062306a36Sopenharmony_ci 114162306a36Sopenharmony_ci for (devid = 0; devid <= pci_seg->last_bdf; ++devid) { 114262306a36Sopenharmony_ci pci_seg->old_dev_tbl_cpy[devid] = old_devtb[devid]; 114362306a36Sopenharmony_ci dom_id = old_devtb[devid].data[1] & DEV_DOMID_MASK; 114462306a36Sopenharmony_ci dte_v = old_devtb[devid].data[0] & DTE_FLAG_V; 114562306a36Sopenharmony_ci 114662306a36Sopenharmony_ci if (dte_v && dom_id) { 114762306a36Sopenharmony_ci pci_seg->old_dev_tbl_cpy[devid].data[0] = old_devtb[devid].data[0]; 114862306a36Sopenharmony_ci pci_seg->old_dev_tbl_cpy[devid].data[1] = old_devtb[devid].data[1]; 114962306a36Sopenharmony_ci __set_bit(dom_id, amd_iommu_pd_alloc_bitmap); 115062306a36Sopenharmony_ci /* If gcr3 table existed, mask it out */ 115162306a36Sopenharmony_ci if (old_devtb[devid].data[0] & DTE_FLAG_GV) { 115262306a36Sopenharmony_ci tmp = DTE_GCR3_VAL_B(~0ULL) << DTE_GCR3_SHIFT_B; 115362306a36Sopenharmony_ci tmp |= DTE_GCR3_VAL_C(~0ULL) << DTE_GCR3_SHIFT_C; 115462306a36Sopenharmony_ci pci_seg->old_dev_tbl_cpy[devid].data[1] &= ~tmp; 115562306a36Sopenharmony_ci tmp = DTE_GCR3_VAL_A(~0ULL) << DTE_GCR3_SHIFT_A; 115662306a36Sopenharmony_ci tmp |= DTE_FLAG_GV; 115762306a36Sopenharmony_ci pci_seg->old_dev_tbl_cpy[devid].data[0] &= ~tmp; 115862306a36Sopenharmony_ci } 115962306a36Sopenharmony_ci } 116062306a36Sopenharmony_ci 116162306a36Sopenharmony_ci irq_v = old_devtb[devid].data[2] & DTE_IRQ_REMAP_ENABLE; 116262306a36Sopenharmony_ci int_ctl = old_devtb[devid].data[2] & DTE_IRQ_REMAP_INTCTL_MASK; 116362306a36Sopenharmony_ci int_tab_len = old_devtb[devid].data[2] & DTE_INTTABLEN_MASK; 116462306a36Sopenharmony_ci if (irq_v && (int_ctl || int_tab_len)) { 116562306a36Sopenharmony_ci if ((int_ctl != DTE_IRQ_REMAP_INTCTL) || 116662306a36Sopenharmony_ci (int_tab_len != DTE_INTTABLEN)) { 116762306a36Sopenharmony_ci pr_err("Wrong old irq remapping flag: %#x\n", devid); 116862306a36Sopenharmony_ci memunmap(old_devtb); 116962306a36Sopenharmony_ci return false; 117062306a36Sopenharmony_ci } 117162306a36Sopenharmony_ci 117262306a36Sopenharmony_ci pci_seg->old_dev_tbl_cpy[devid].data[2] = old_devtb[devid].data[2]; 117362306a36Sopenharmony_ci } 117462306a36Sopenharmony_ci } 117562306a36Sopenharmony_ci memunmap(old_devtb); 117662306a36Sopenharmony_ci 117762306a36Sopenharmony_ci return true; 117862306a36Sopenharmony_ci} 117962306a36Sopenharmony_ci 118062306a36Sopenharmony_cistatic bool copy_device_table(void) 118162306a36Sopenharmony_ci{ 118262306a36Sopenharmony_ci struct amd_iommu *iommu; 118362306a36Sopenharmony_ci struct amd_iommu_pci_seg *pci_seg; 118462306a36Sopenharmony_ci 118562306a36Sopenharmony_ci if (!amd_iommu_pre_enabled) 118662306a36Sopenharmony_ci return false; 118762306a36Sopenharmony_ci 118862306a36Sopenharmony_ci pr_warn("Translation is already enabled - trying to copy translation structures\n"); 118962306a36Sopenharmony_ci 119062306a36Sopenharmony_ci /* 119162306a36Sopenharmony_ci * All IOMMUs within PCI segment shares common device table. 119262306a36Sopenharmony_ci * Hence copy device table only once per PCI segment. 119362306a36Sopenharmony_ci */ 119462306a36Sopenharmony_ci for_each_pci_segment(pci_seg) { 119562306a36Sopenharmony_ci for_each_iommu(iommu) { 119662306a36Sopenharmony_ci if (pci_seg->id != iommu->pci_seg->id) 119762306a36Sopenharmony_ci continue; 119862306a36Sopenharmony_ci if (!__copy_device_table(iommu)) 119962306a36Sopenharmony_ci return false; 120062306a36Sopenharmony_ci break; 120162306a36Sopenharmony_ci } 120262306a36Sopenharmony_ci } 120362306a36Sopenharmony_ci 120462306a36Sopenharmony_ci return true; 120562306a36Sopenharmony_ci} 120662306a36Sopenharmony_ci 120762306a36Sopenharmony_civoid amd_iommu_apply_erratum_63(struct amd_iommu *iommu, u16 devid) 120862306a36Sopenharmony_ci{ 120962306a36Sopenharmony_ci int sysmgt; 121062306a36Sopenharmony_ci 121162306a36Sopenharmony_ci sysmgt = get_dev_entry_bit(iommu, devid, DEV_ENTRY_SYSMGT1) | 121262306a36Sopenharmony_ci (get_dev_entry_bit(iommu, devid, DEV_ENTRY_SYSMGT2) << 1); 121362306a36Sopenharmony_ci 121462306a36Sopenharmony_ci if (sysmgt == 0x01) 121562306a36Sopenharmony_ci set_dev_entry_bit(iommu, devid, DEV_ENTRY_IW); 121662306a36Sopenharmony_ci} 121762306a36Sopenharmony_ci 121862306a36Sopenharmony_ci/* 121962306a36Sopenharmony_ci * This function takes the device specific flags read from the ACPI 122062306a36Sopenharmony_ci * table and sets up the device table entry with that information 122162306a36Sopenharmony_ci */ 122262306a36Sopenharmony_cistatic void __init set_dev_entry_from_acpi(struct amd_iommu *iommu, 122362306a36Sopenharmony_ci u16 devid, u32 flags, u32 ext_flags) 122462306a36Sopenharmony_ci{ 122562306a36Sopenharmony_ci if (flags & ACPI_DEVFLAG_INITPASS) 122662306a36Sopenharmony_ci set_dev_entry_bit(iommu, devid, DEV_ENTRY_INIT_PASS); 122762306a36Sopenharmony_ci if (flags & ACPI_DEVFLAG_EXTINT) 122862306a36Sopenharmony_ci set_dev_entry_bit(iommu, devid, DEV_ENTRY_EINT_PASS); 122962306a36Sopenharmony_ci if (flags & ACPI_DEVFLAG_NMI) 123062306a36Sopenharmony_ci set_dev_entry_bit(iommu, devid, DEV_ENTRY_NMI_PASS); 123162306a36Sopenharmony_ci if (flags & ACPI_DEVFLAG_SYSMGT1) 123262306a36Sopenharmony_ci set_dev_entry_bit(iommu, devid, DEV_ENTRY_SYSMGT1); 123362306a36Sopenharmony_ci if (flags & ACPI_DEVFLAG_SYSMGT2) 123462306a36Sopenharmony_ci set_dev_entry_bit(iommu, devid, DEV_ENTRY_SYSMGT2); 123562306a36Sopenharmony_ci if (flags & ACPI_DEVFLAG_LINT0) 123662306a36Sopenharmony_ci set_dev_entry_bit(iommu, devid, DEV_ENTRY_LINT0_PASS); 123762306a36Sopenharmony_ci if (flags & ACPI_DEVFLAG_LINT1) 123862306a36Sopenharmony_ci set_dev_entry_bit(iommu, devid, DEV_ENTRY_LINT1_PASS); 123962306a36Sopenharmony_ci 124062306a36Sopenharmony_ci amd_iommu_apply_erratum_63(iommu, devid); 124162306a36Sopenharmony_ci 124262306a36Sopenharmony_ci amd_iommu_set_rlookup_table(iommu, devid); 124362306a36Sopenharmony_ci} 124462306a36Sopenharmony_ci 124562306a36Sopenharmony_ciint __init add_special_device(u8 type, u8 id, u32 *devid, bool cmd_line) 124662306a36Sopenharmony_ci{ 124762306a36Sopenharmony_ci struct devid_map *entry; 124862306a36Sopenharmony_ci struct list_head *list; 124962306a36Sopenharmony_ci 125062306a36Sopenharmony_ci if (type == IVHD_SPECIAL_IOAPIC) 125162306a36Sopenharmony_ci list = &ioapic_map; 125262306a36Sopenharmony_ci else if (type == IVHD_SPECIAL_HPET) 125362306a36Sopenharmony_ci list = &hpet_map; 125462306a36Sopenharmony_ci else 125562306a36Sopenharmony_ci return -EINVAL; 125662306a36Sopenharmony_ci 125762306a36Sopenharmony_ci list_for_each_entry(entry, list, list) { 125862306a36Sopenharmony_ci if (!(entry->id == id && entry->cmd_line)) 125962306a36Sopenharmony_ci continue; 126062306a36Sopenharmony_ci 126162306a36Sopenharmony_ci pr_info("Command-line override present for %s id %d - ignoring\n", 126262306a36Sopenharmony_ci type == IVHD_SPECIAL_IOAPIC ? "IOAPIC" : "HPET", id); 126362306a36Sopenharmony_ci 126462306a36Sopenharmony_ci *devid = entry->devid; 126562306a36Sopenharmony_ci 126662306a36Sopenharmony_ci return 0; 126762306a36Sopenharmony_ci } 126862306a36Sopenharmony_ci 126962306a36Sopenharmony_ci entry = kzalloc(sizeof(*entry), GFP_KERNEL); 127062306a36Sopenharmony_ci if (!entry) 127162306a36Sopenharmony_ci return -ENOMEM; 127262306a36Sopenharmony_ci 127362306a36Sopenharmony_ci entry->id = id; 127462306a36Sopenharmony_ci entry->devid = *devid; 127562306a36Sopenharmony_ci entry->cmd_line = cmd_line; 127662306a36Sopenharmony_ci 127762306a36Sopenharmony_ci list_add_tail(&entry->list, list); 127862306a36Sopenharmony_ci 127962306a36Sopenharmony_ci return 0; 128062306a36Sopenharmony_ci} 128162306a36Sopenharmony_ci 128262306a36Sopenharmony_cistatic int __init add_acpi_hid_device(u8 *hid, u8 *uid, u32 *devid, 128362306a36Sopenharmony_ci bool cmd_line) 128462306a36Sopenharmony_ci{ 128562306a36Sopenharmony_ci struct acpihid_map_entry *entry; 128662306a36Sopenharmony_ci struct list_head *list = &acpihid_map; 128762306a36Sopenharmony_ci 128862306a36Sopenharmony_ci list_for_each_entry(entry, list, list) { 128962306a36Sopenharmony_ci if (strcmp(entry->hid, hid) || 129062306a36Sopenharmony_ci (*uid && *entry->uid && strcmp(entry->uid, uid)) || 129162306a36Sopenharmony_ci !entry->cmd_line) 129262306a36Sopenharmony_ci continue; 129362306a36Sopenharmony_ci 129462306a36Sopenharmony_ci pr_info("Command-line override for hid:%s uid:%s\n", 129562306a36Sopenharmony_ci hid, uid); 129662306a36Sopenharmony_ci *devid = entry->devid; 129762306a36Sopenharmony_ci return 0; 129862306a36Sopenharmony_ci } 129962306a36Sopenharmony_ci 130062306a36Sopenharmony_ci entry = kzalloc(sizeof(*entry), GFP_KERNEL); 130162306a36Sopenharmony_ci if (!entry) 130262306a36Sopenharmony_ci return -ENOMEM; 130362306a36Sopenharmony_ci 130462306a36Sopenharmony_ci memcpy(entry->uid, uid, strlen(uid)); 130562306a36Sopenharmony_ci memcpy(entry->hid, hid, strlen(hid)); 130662306a36Sopenharmony_ci entry->devid = *devid; 130762306a36Sopenharmony_ci entry->cmd_line = cmd_line; 130862306a36Sopenharmony_ci entry->root_devid = (entry->devid & (~0x7)); 130962306a36Sopenharmony_ci 131062306a36Sopenharmony_ci pr_info("%s, add hid:%s, uid:%s, rdevid:%d\n", 131162306a36Sopenharmony_ci entry->cmd_line ? "cmd" : "ivrs", 131262306a36Sopenharmony_ci entry->hid, entry->uid, entry->root_devid); 131362306a36Sopenharmony_ci 131462306a36Sopenharmony_ci list_add_tail(&entry->list, list); 131562306a36Sopenharmony_ci return 0; 131662306a36Sopenharmony_ci} 131762306a36Sopenharmony_ci 131862306a36Sopenharmony_cistatic int __init add_early_maps(void) 131962306a36Sopenharmony_ci{ 132062306a36Sopenharmony_ci int i, ret; 132162306a36Sopenharmony_ci 132262306a36Sopenharmony_ci for (i = 0; i < early_ioapic_map_size; ++i) { 132362306a36Sopenharmony_ci ret = add_special_device(IVHD_SPECIAL_IOAPIC, 132462306a36Sopenharmony_ci early_ioapic_map[i].id, 132562306a36Sopenharmony_ci &early_ioapic_map[i].devid, 132662306a36Sopenharmony_ci early_ioapic_map[i].cmd_line); 132762306a36Sopenharmony_ci if (ret) 132862306a36Sopenharmony_ci return ret; 132962306a36Sopenharmony_ci } 133062306a36Sopenharmony_ci 133162306a36Sopenharmony_ci for (i = 0; i < early_hpet_map_size; ++i) { 133262306a36Sopenharmony_ci ret = add_special_device(IVHD_SPECIAL_HPET, 133362306a36Sopenharmony_ci early_hpet_map[i].id, 133462306a36Sopenharmony_ci &early_hpet_map[i].devid, 133562306a36Sopenharmony_ci early_hpet_map[i].cmd_line); 133662306a36Sopenharmony_ci if (ret) 133762306a36Sopenharmony_ci return ret; 133862306a36Sopenharmony_ci } 133962306a36Sopenharmony_ci 134062306a36Sopenharmony_ci for (i = 0; i < early_acpihid_map_size; ++i) { 134162306a36Sopenharmony_ci ret = add_acpi_hid_device(early_acpihid_map[i].hid, 134262306a36Sopenharmony_ci early_acpihid_map[i].uid, 134362306a36Sopenharmony_ci &early_acpihid_map[i].devid, 134462306a36Sopenharmony_ci early_acpihid_map[i].cmd_line); 134562306a36Sopenharmony_ci if (ret) 134662306a36Sopenharmony_ci return ret; 134762306a36Sopenharmony_ci } 134862306a36Sopenharmony_ci 134962306a36Sopenharmony_ci return 0; 135062306a36Sopenharmony_ci} 135162306a36Sopenharmony_ci 135262306a36Sopenharmony_ci/* 135362306a36Sopenharmony_ci * Takes a pointer to an AMD IOMMU entry in the ACPI table and 135462306a36Sopenharmony_ci * initializes the hardware and our data structures with it. 135562306a36Sopenharmony_ci */ 135662306a36Sopenharmony_cistatic int __init init_iommu_from_acpi(struct amd_iommu *iommu, 135762306a36Sopenharmony_ci struct ivhd_header *h) 135862306a36Sopenharmony_ci{ 135962306a36Sopenharmony_ci u8 *p = (u8 *)h; 136062306a36Sopenharmony_ci u8 *end = p, flags = 0; 136162306a36Sopenharmony_ci u16 devid = 0, devid_start = 0, devid_to = 0, seg_id; 136262306a36Sopenharmony_ci u32 dev_i, ext_flags = 0; 136362306a36Sopenharmony_ci bool alias = false; 136462306a36Sopenharmony_ci struct ivhd_entry *e; 136562306a36Sopenharmony_ci struct amd_iommu_pci_seg *pci_seg = iommu->pci_seg; 136662306a36Sopenharmony_ci u32 ivhd_size; 136762306a36Sopenharmony_ci int ret; 136862306a36Sopenharmony_ci 136962306a36Sopenharmony_ci 137062306a36Sopenharmony_ci ret = add_early_maps(); 137162306a36Sopenharmony_ci if (ret) 137262306a36Sopenharmony_ci return ret; 137362306a36Sopenharmony_ci 137462306a36Sopenharmony_ci amd_iommu_apply_ivrs_quirks(); 137562306a36Sopenharmony_ci 137662306a36Sopenharmony_ci /* 137762306a36Sopenharmony_ci * First save the recommended feature enable bits from ACPI 137862306a36Sopenharmony_ci */ 137962306a36Sopenharmony_ci iommu->acpi_flags = h->flags; 138062306a36Sopenharmony_ci 138162306a36Sopenharmony_ci /* 138262306a36Sopenharmony_ci * Done. Now parse the device entries 138362306a36Sopenharmony_ci */ 138462306a36Sopenharmony_ci ivhd_size = get_ivhd_header_size(h); 138562306a36Sopenharmony_ci if (!ivhd_size) { 138662306a36Sopenharmony_ci pr_err("Unsupported IVHD type %#x\n", h->type); 138762306a36Sopenharmony_ci return -EINVAL; 138862306a36Sopenharmony_ci } 138962306a36Sopenharmony_ci 139062306a36Sopenharmony_ci p += ivhd_size; 139162306a36Sopenharmony_ci 139262306a36Sopenharmony_ci end += h->length; 139362306a36Sopenharmony_ci 139462306a36Sopenharmony_ci 139562306a36Sopenharmony_ci while (p < end) { 139662306a36Sopenharmony_ci e = (struct ivhd_entry *)p; 139762306a36Sopenharmony_ci seg_id = pci_seg->id; 139862306a36Sopenharmony_ci 139962306a36Sopenharmony_ci switch (e->type) { 140062306a36Sopenharmony_ci case IVHD_DEV_ALL: 140162306a36Sopenharmony_ci 140262306a36Sopenharmony_ci DUMP_printk(" DEV_ALL\t\t\tflags: %02x\n", e->flags); 140362306a36Sopenharmony_ci 140462306a36Sopenharmony_ci for (dev_i = 0; dev_i <= pci_seg->last_bdf; ++dev_i) 140562306a36Sopenharmony_ci set_dev_entry_from_acpi(iommu, dev_i, e->flags, 0); 140662306a36Sopenharmony_ci break; 140762306a36Sopenharmony_ci case IVHD_DEV_SELECT: 140862306a36Sopenharmony_ci 140962306a36Sopenharmony_ci DUMP_printk(" DEV_SELECT\t\t\t devid: %04x:%02x:%02x.%x " 141062306a36Sopenharmony_ci "flags: %02x\n", 141162306a36Sopenharmony_ci seg_id, PCI_BUS_NUM(e->devid), 141262306a36Sopenharmony_ci PCI_SLOT(e->devid), 141362306a36Sopenharmony_ci PCI_FUNC(e->devid), 141462306a36Sopenharmony_ci e->flags); 141562306a36Sopenharmony_ci 141662306a36Sopenharmony_ci devid = e->devid; 141762306a36Sopenharmony_ci set_dev_entry_from_acpi(iommu, devid, e->flags, 0); 141862306a36Sopenharmony_ci break; 141962306a36Sopenharmony_ci case IVHD_DEV_SELECT_RANGE_START: 142062306a36Sopenharmony_ci 142162306a36Sopenharmony_ci DUMP_printk(" DEV_SELECT_RANGE_START\t " 142262306a36Sopenharmony_ci "devid: %04x:%02x:%02x.%x flags: %02x\n", 142362306a36Sopenharmony_ci seg_id, PCI_BUS_NUM(e->devid), 142462306a36Sopenharmony_ci PCI_SLOT(e->devid), 142562306a36Sopenharmony_ci PCI_FUNC(e->devid), 142662306a36Sopenharmony_ci e->flags); 142762306a36Sopenharmony_ci 142862306a36Sopenharmony_ci devid_start = e->devid; 142962306a36Sopenharmony_ci flags = e->flags; 143062306a36Sopenharmony_ci ext_flags = 0; 143162306a36Sopenharmony_ci alias = false; 143262306a36Sopenharmony_ci break; 143362306a36Sopenharmony_ci case IVHD_DEV_ALIAS: 143462306a36Sopenharmony_ci 143562306a36Sopenharmony_ci DUMP_printk(" DEV_ALIAS\t\t\t devid: %04x:%02x:%02x.%x " 143662306a36Sopenharmony_ci "flags: %02x devid_to: %02x:%02x.%x\n", 143762306a36Sopenharmony_ci seg_id, PCI_BUS_NUM(e->devid), 143862306a36Sopenharmony_ci PCI_SLOT(e->devid), 143962306a36Sopenharmony_ci PCI_FUNC(e->devid), 144062306a36Sopenharmony_ci e->flags, 144162306a36Sopenharmony_ci PCI_BUS_NUM(e->ext >> 8), 144262306a36Sopenharmony_ci PCI_SLOT(e->ext >> 8), 144362306a36Sopenharmony_ci PCI_FUNC(e->ext >> 8)); 144462306a36Sopenharmony_ci 144562306a36Sopenharmony_ci devid = e->devid; 144662306a36Sopenharmony_ci devid_to = e->ext >> 8; 144762306a36Sopenharmony_ci set_dev_entry_from_acpi(iommu, devid , e->flags, 0); 144862306a36Sopenharmony_ci set_dev_entry_from_acpi(iommu, devid_to, e->flags, 0); 144962306a36Sopenharmony_ci pci_seg->alias_table[devid] = devid_to; 145062306a36Sopenharmony_ci break; 145162306a36Sopenharmony_ci case IVHD_DEV_ALIAS_RANGE: 145262306a36Sopenharmony_ci 145362306a36Sopenharmony_ci DUMP_printk(" DEV_ALIAS_RANGE\t\t " 145462306a36Sopenharmony_ci "devid: %04x:%02x:%02x.%x flags: %02x " 145562306a36Sopenharmony_ci "devid_to: %04x:%02x:%02x.%x\n", 145662306a36Sopenharmony_ci seg_id, PCI_BUS_NUM(e->devid), 145762306a36Sopenharmony_ci PCI_SLOT(e->devid), 145862306a36Sopenharmony_ci PCI_FUNC(e->devid), 145962306a36Sopenharmony_ci e->flags, 146062306a36Sopenharmony_ci seg_id, PCI_BUS_NUM(e->ext >> 8), 146162306a36Sopenharmony_ci PCI_SLOT(e->ext >> 8), 146262306a36Sopenharmony_ci PCI_FUNC(e->ext >> 8)); 146362306a36Sopenharmony_ci 146462306a36Sopenharmony_ci devid_start = e->devid; 146562306a36Sopenharmony_ci flags = e->flags; 146662306a36Sopenharmony_ci devid_to = e->ext >> 8; 146762306a36Sopenharmony_ci ext_flags = 0; 146862306a36Sopenharmony_ci alias = true; 146962306a36Sopenharmony_ci break; 147062306a36Sopenharmony_ci case IVHD_DEV_EXT_SELECT: 147162306a36Sopenharmony_ci 147262306a36Sopenharmony_ci DUMP_printk(" DEV_EXT_SELECT\t\t devid: %04x:%02x:%02x.%x " 147362306a36Sopenharmony_ci "flags: %02x ext: %08x\n", 147462306a36Sopenharmony_ci seg_id, PCI_BUS_NUM(e->devid), 147562306a36Sopenharmony_ci PCI_SLOT(e->devid), 147662306a36Sopenharmony_ci PCI_FUNC(e->devid), 147762306a36Sopenharmony_ci e->flags, e->ext); 147862306a36Sopenharmony_ci 147962306a36Sopenharmony_ci devid = e->devid; 148062306a36Sopenharmony_ci set_dev_entry_from_acpi(iommu, devid, e->flags, 148162306a36Sopenharmony_ci e->ext); 148262306a36Sopenharmony_ci break; 148362306a36Sopenharmony_ci case IVHD_DEV_EXT_SELECT_RANGE: 148462306a36Sopenharmony_ci 148562306a36Sopenharmony_ci DUMP_printk(" DEV_EXT_SELECT_RANGE\t devid: " 148662306a36Sopenharmony_ci "%04x:%02x:%02x.%x flags: %02x ext: %08x\n", 148762306a36Sopenharmony_ci seg_id, PCI_BUS_NUM(e->devid), 148862306a36Sopenharmony_ci PCI_SLOT(e->devid), 148962306a36Sopenharmony_ci PCI_FUNC(e->devid), 149062306a36Sopenharmony_ci e->flags, e->ext); 149162306a36Sopenharmony_ci 149262306a36Sopenharmony_ci devid_start = e->devid; 149362306a36Sopenharmony_ci flags = e->flags; 149462306a36Sopenharmony_ci ext_flags = e->ext; 149562306a36Sopenharmony_ci alias = false; 149662306a36Sopenharmony_ci break; 149762306a36Sopenharmony_ci case IVHD_DEV_RANGE_END: 149862306a36Sopenharmony_ci 149962306a36Sopenharmony_ci DUMP_printk(" DEV_RANGE_END\t\t devid: %04x:%02x:%02x.%x\n", 150062306a36Sopenharmony_ci seg_id, PCI_BUS_NUM(e->devid), 150162306a36Sopenharmony_ci PCI_SLOT(e->devid), 150262306a36Sopenharmony_ci PCI_FUNC(e->devid)); 150362306a36Sopenharmony_ci 150462306a36Sopenharmony_ci devid = e->devid; 150562306a36Sopenharmony_ci for (dev_i = devid_start; dev_i <= devid; ++dev_i) { 150662306a36Sopenharmony_ci if (alias) { 150762306a36Sopenharmony_ci pci_seg->alias_table[dev_i] = devid_to; 150862306a36Sopenharmony_ci set_dev_entry_from_acpi(iommu, 150962306a36Sopenharmony_ci devid_to, flags, ext_flags); 151062306a36Sopenharmony_ci } 151162306a36Sopenharmony_ci set_dev_entry_from_acpi(iommu, dev_i, 151262306a36Sopenharmony_ci flags, ext_flags); 151362306a36Sopenharmony_ci } 151462306a36Sopenharmony_ci break; 151562306a36Sopenharmony_ci case IVHD_DEV_SPECIAL: { 151662306a36Sopenharmony_ci u8 handle, type; 151762306a36Sopenharmony_ci const char *var; 151862306a36Sopenharmony_ci u32 devid; 151962306a36Sopenharmony_ci int ret; 152062306a36Sopenharmony_ci 152162306a36Sopenharmony_ci handle = e->ext & 0xff; 152262306a36Sopenharmony_ci devid = PCI_SEG_DEVID_TO_SBDF(seg_id, (e->ext >> 8)); 152362306a36Sopenharmony_ci type = (e->ext >> 24) & 0xff; 152462306a36Sopenharmony_ci 152562306a36Sopenharmony_ci if (type == IVHD_SPECIAL_IOAPIC) 152662306a36Sopenharmony_ci var = "IOAPIC"; 152762306a36Sopenharmony_ci else if (type == IVHD_SPECIAL_HPET) 152862306a36Sopenharmony_ci var = "HPET"; 152962306a36Sopenharmony_ci else 153062306a36Sopenharmony_ci var = "UNKNOWN"; 153162306a36Sopenharmony_ci 153262306a36Sopenharmony_ci DUMP_printk(" DEV_SPECIAL(%s[%d])\t\tdevid: %04x:%02x:%02x.%x\n", 153362306a36Sopenharmony_ci var, (int)handle, 153462306a36Sopenharmony_ci seg_id, PCI_BUS_NUM(devid), 153562306a36Sopenharmony_ci PCI_SLOT(devid), 153662306a36Sopenharmony_ci PCI_FUNC(devid)); 153762306a36Sopenharmony_ci 153862306a36Sopenharmony_ci ret = add_special_device(type, handle, &devid, false); 153962306a36Sopenharmony_ci if (ret) 154062306a36Sopenharmony_ci return ret; 154162306a36Sopenharmony_ci 154262306a36Sopenharmony_ci /* 154362306a36Sopenharmony_ci * add_special_device might update the devid in case a 154462306a36Sopenharmony_ci * command-line override is present. So call 154562306a36Sopenharmony_ci * set_dev_entry_from_acpi after add_special_device. 154662306a36Sopenharmony_ci */ 154762306a36Sopenharmony_ci set_dev_entry_from_acpi(iommu, devid, e->flags, 0); 154862306a36Sopenharmony_ci 154962306a36Sopenharmony_ci break; 155062306a36Sopenharmony_ci } 155162306a36Sopenharmony_ci case IVHD_DEV_ACPI_HID: { 155262306a36Sopenharmony_ci u32 devid; 155362306a36Sopenharmony_ci u8 hid[ACPIHID_HID_LEN]; 155462306a36Sopenharmony_ci u8 uid[ACPIHID_UID_LEN]; 155562306a36Sopenharmony_ci int ret; 155662306a36Sopenharmony_ci 155762306a36Sopenharmony_ci if (h->type != 0x40) { 155862306a36Sopenharmony_ci pr_err(FW_BUG "Invalid IVHD device type %#x\n", 155962306a36Sopenharmony_ci e->type); 156062306a36Sopenharmony_ci break; 156162306a36Sopenharmony_ci } 156262306a36Sopenharmony_ci 156362306a36Sopenharmony_ci BUILD_BUG_ON(sizeof(e->ext_hid) != ACPIHID_HID_LEN - 1); 156462306a36Sopenharmony_ci memcpy(hid, &e->ext_hid, ACPIHID_HID_LEN - 1); 156562306a36Sopenharmony_ci hid[ACPIHID_HID_LEN - 1] = '\0'; 156662306a36Sopenharmony_ci 156762306a36Sopenharmony_ci if (!(*hid)) { 156862306a36Sopenharmony_ci pr_err(FW_BUG "Invalid HID.\n"); 156962306a36Sopenharmony_ci break; 157062306a36Sopenharmony_ci } 157162306a36Sopenharmony_ci 157262306a36Sopenharmony_ci uid[0] = '\0'; 157362306a36Sopenharmony_ci switch (e->uidf) { 157462306a36Sopenharmony_ci case UID_NOT_PRESENT: 157562306a36Sopenharmony_ci 157662306a36Sopenharmony_ci if (e->uidl != 0) 157762306a36Sopenharmony_ci pr_warn(FW_BUG "Invalid UID length.\n"); 157862306a36Sopenharmony_ci 157962306a36Sopenharmony_ci break; 158062306a36Sopenharmony_ci case UID_IS_INTEGER: 158162306a36Sopenharmony_ci 158262306a36Sopenharmony_ci sprintf(uid, "%d", e->uid); 158362306a36Sopenharmony_ci 158462306a36Sopenharmony_ci break; 158562306a36Sopenharmony_ci case UID_IS_CHARACTER: 158662306a36Sopenharmony_ci 158762306a36Sopenharmony_ci memcpy(uid, &e->uid, e->uidl); 158862306a36Sopenharmony_ci uid[e->uidl] = '\0'; 158962306a36Sopenharmony_ci 159062306a36Sopenharmony_ci break; 159162306a36Sopenharmony_ci default: 159262306a36Sopenharmony_ci break; 159362306a36Sopenharmony_ci } 159462306a36Sopenharmony_ci 159562306a36Sopenharmony_ci devid = PCI_SEG_DEVID_TO_SBDF(seg_id, e->devid); 159662306a36Sopenharmony_ci DUMP_printk(" DEV_ACPI_HID(%s[%s])\t\tdevid: %04x:%02x:%02x.%x\n", 159762306a36Sopenharmony_ci hid, uid, seg_id, 159862306a36Sopenharmony_ci PCI_BUS_NUM(devid), 159962306a36Sopenharmony_ci PCI_SLOT(devid), 160062306a36Sopenharmony_ci PCI_FUNC(devid)); 160162306a36Sopenharmony_ci 160262306a36Sopenharmony_ci flags = e->flags; 160362306a36Sopenharmony_ci 160462306a36Sopenharmony_ci ret = add_acpi_hid_device(hid, uid, &devid, false); 160562306a36Sopenharmony_ci if (ret) 160662306a36Sopenharmony_ci return ret; 160762306a36Sopenharmony_ci 160862306a36Sopenharmony_ci /* 160962306a36Sopenharmony_ci * add_special_device might update the devid in case a 161062306a36Sopenharmony_ci * command-line override is present. So call 161162306a36Sopenharmony_ci * set_dev_entry_from_acpi after add_special_device. 161262306a36Sopenharmony_ci */ 161362306a36Sopenharmony_ci set_dev_entry_from_acpi(iommu, devid, e->flags, 0); 161462306a36Sopenharmony_ci 161562306a36Sopenharmony_ci break; 161662306a36Sopenharmony_ci } 161762306a36Sopenharmony_ci default: 161862306a36Sopenharmony_ci break; 161962306a36Sopenharmony_ci } 162062306a36Sopenharmony_ci 162162306a36Sopenharmony_ci p += ivhd_entry_length(p); 162262306a36Sopenharmony_ci } 162362306a36Sopenharmony_ci 162462306a36Sopenharmony_ci return 0; 162562306a36Sopenharmony_ci} 162662306a36Sopenharmony_ci 162762306a36Sopenharmony_ci/* Allocate PCI segment data structure */ 162862306a36Sopenharmony_cistatic struct amd_iommu_pci_seg *__init alloc_pci_segment(u16 id, 162962306a36Sopenharmony_ci struct acpi_table_header *ivrs_base) 163062306a36Sopenharmony_ci{ 163162306a36Sopenharmony_ci struct amd_iommu_pci_seg *pci_seg; 163262306a36Sopenharmony_ci int last_bdf; 163362306a36Sopenharmony_ci 163462306a36Sopenharmony_ci /* 163562306a36Sopenharmony_ci * First parse ACPI tables to find the largest Bus/Dev/Func we need to 163662306a36Sopenharmony_ci * handle in this PCI segment. Upon this information the shared data 163762306a36Sopenharmony_ci * structures for the PCI segments in the system will be allocated. 163862306a36Sopenharmony_ci */ 163962306a36Sopenharmony_ci last_bdf = find_last_devid_acpi(ivrs_base, id); 164062306a36Sopenharmony_ci if (last_bdf < 0) 164162306a36Sopenharmony_ci return NULL; 164262306a36Sopenharmony_ci 164362306a36Sopenharmony_ci pci_seg = kzalloc(sizeof(struct amd_iommu_pci_seg), GFP_KERNEL); 164462306a36Sopenharmony_ci if (pci_seg == NULL) 164562306a36Sopenharmony_ci return NULL; 164662306a36Sopenharmony_ci 164762306a36Sopenharmony_ci pci_seg->last_bdf = last_bdf; 164862306a36Sopenharmony_ci DUMP_printk("PCI segment : 0x%0x, last bdf : 0x%04x\n", id, last_bdf); 164962306a36Sopenharmony_ci pci_seg->dev_table_size = tbl_size(DEV_TABLE_ENTRY_SIZE, last_bdf); 165062306a36Sopenharmony_ci pci_seg->alias_table_size = tbl_size(ALIAS_TABLE_ENTRY_SIZE, last_bdf); 165162306a36Sopenharmony_ci pci_seg->rlookup_table_size = tbl_size(RLOOKUP_TABLE_ENTRY_SIZE, last_bdf); 165262306a36Sopenharmony_ci 165362306a36Sopenharmony_ci pci_seg->id = id; 165462306a36Sopenharmony_ci init_llist_head(&pci_seg->dev_data_list); 165562306a36Sopenharmony_ci INIT_LIST_HEAD(&pci_seg->unity_map); 165662306a36Sopenharmony_ci list_add_tail(&pci_seg->list, &amd_iommu_pci_seg_list); 165762306a36Sopenharmony_ci 165862306a36Sopenharmony_ci if (alloc_dev_table(pci_seg)) 165962306a36Sopenharmony_ci return NULL; 166062306a36Sopenharmony_ci if (alloc_alias_table(pci_seg)) 166162306a36Sopenharmony_ci return NULL; 166262306a36Sopenharmony_ci if (alloc_rlookup_table(pci_seg)) 166362306a36Sopenharmony_ci return NULL; 166462306a36Sopenharmony_ci 166562306a36Sopenharmony_ci return pci_seg; 166662306a36Sopenharmony_ci} 166762306a36Sopenharmony_ci 166862306a36Sopenharmony_cistatic struct amd_iommu_pci_seg *__init get_pci_segment(u16 id, 166962306a36Sopenharmony_ci struct acpi_table_header *ivrs_base) 167062306a36Sopenharmony_ci{ 167162306a36Sopenharmony_ci struct amd_iommu_pci_seg *pci_seg; 167262306a36Sopenharmony_ci 167362306a36Sopenharmony_ci for_each_pci_segment(pci_seg) { 167462306a36Sopenharmony_ci if (pci_seg->id == id) 167562306a36Sopenharmony_ci return pci_seg; 167662306a36Sopenharmony_ci } 167762306a36Sopenharmony_ci 167862306a36Sopenharmony_ci return alloc_pci_segment(id, ivrs_base); 167962306a36Sopenharmony_ci} 168062306a36Sopenharmony_ci 168162306a36Sopenharmony_cistatic void __init free_pci_segments(void) 168262306a36Sopenharmony_ci{ 168362306a36Sopenharmony_ci struct amd_iommu_pci_seg *pci_seg, *next; 168462306a36Sopenharmony_ci 168562306a36Sopenharmony_ci for_each_pci_segment_safe(pci_seg, next) { 168662306a36Sopenharmony_ci list_del(&pci_seg->list); 168762306a36Sopenharmony_ci free_irq_lookup_table(pci_seg); 168862306a36Sopenharmony_ci free_rlookup_table(pci_seg); 168962306a36Sopenharmony_ci free_alias_table(pci_seg); 169062306a36Sopenharmony_ci free_dev_table(pci_seg); 169162306a36Sopenharmony_ci kfree(pci_seg); 169262306a36Sopenharmony_ci } 169362306a36Sopenharmony_ci} 169462306a36Sopenharmony_ci 169562306a36Sopenharmony_cistatic void __init free_iommu_one(struct amd_iommu *iommu) 169662306a36Sopenharmony_ci{ 169762306a36Sopenharmony_ci free_cwwb_sem(iommu); 169862306a36Sopenharmony_ci free_command_buffer(iommu); 169962306a36Sopenharmony_ci free_event_buffer(iommu); 170062306a36Sopenharmony_ci free_ppr_log(iommu); 170162306a36Sopenharmony_ci free_ga_log(iommu); 170262306a36Sopenharmony_ci iommu_unmap_mmio_space(iommu); 170362306a36Sopenharmony_ci} 170462306a36Sopenharmony_ci 170562306a36Sopenharmony_cistatic void __init free_iommu_all(void) 170662306a36Sopenharmony_ci{ 170762306a36Sopenharmony_ci struct amd_iommu *iommu, *next; 170862306a36Sopenharmony_ci 170962306a36Sopenharmony_ci for_each_iommu_safe(iommu, next) { 171062306a36Sopenharmony_ci list_del(&iommu->list); 171162306a36Sopenharmony_ci free_iommu_one(iommu); 171262306a36Sopenharmony_ci kfree(iommu); 171362306a36Sopenharmony_ci } 171462306a36Sopenharmony_ci} 171562306a36Sopenharmony_ci 171662306a36Sopenharmony_ci/* 171762306a36Sopenharmony_ci * Family15h Model 10h-1fh erratum 746 (IOMMU Logging May Stall Translations) 171862306a36Sopenharmony_ci * Workaround: 171962306a36Sopenharmony_ci * BIOS should disable L2B micellaneous clock gating by setting 172062306a36Sopenharmony_ci * L2_L2B_CK_GATE_CONTROL[CKGateL2BMiscDisable](D0F2xF4_x90[2]) = 1b 172162306a36Sopenharmony_ci */ 172262306a36Sopenharmony_cistatic void amd_iommu_erratum_746_workaround(struct amd_iommu *iommu) 172362306a36Sopenharmony_ci{ 172462306a36Sopenharmony_ci u32 value; 172562306a36Sopenharmony_ci 172662306a36Sopenharmony_ci if ((boot_cpu_data.x86 != 0x15) || 172762306a36Sopenharmony_ci (boot_cpu_data.x86_model < 0x10) || 172862306a36Sopenharmony_ci (boot_cpu_data.x86_model > 0x1f)) 172962306a36Sopenharmony_ci return; 173062306a36Sopenharmony_ci 173162306a36Sopenharmony_ci pci_write_config_dword(iommu->dev, 0xf0, 0x90); 173262306a36Sopenharmony_ci pci_read_config_dword(iommu->dev, 0xf4, &value); 173362306a36Sopenharmony_ci 173462306a36Sopenharmony_ci if (value & BIT(2)) 173562306a36Sopenharmony_ci return; 173662306a36Sopenharmony_ci 173762306a36Sopenharmony_ci /* Select NB indirect register 0x90 and enable writing */ 173862306a36Sopenharmony_ci pci_write_config_dword(iommu->dev, 0xf0, 0x90 | (1 << 8)); 173962306a36Sopenharmony_ci 174062306a36Sopenharmony_ci pci_write_config_dword(iommu->dev, 0xf4, value | 0x4); 174162306a36Sopenharmony_ci pci_info(iommu->dev, "Applying erratum 746 workaround\n"); 174262306a36Sopenharmony_ci 174362306a36Sopenharmony_ci /* Clear the enable writing bit */ 174462306a36Sopenharmony_ci pci_write_config_dword(iommu->dev, 0xf0, 0x90); 174562306a36Sopenharmony_ci} 174662306a36Sopenharmony_ci 174762306a36Sopenharmony_ci/* 174862306a36Sopenharmony_ci * Family15h Model 30h-3fh (IOMMU Mishandles ATS Write Permission) 174962306a36Sopenharmony_ci * Workaround: 175062306a36Sopenharmony_ci * BIOS should enable ATS write permission check by setting 175162306a36Sopenharmony_ci * L2_DEBUG_3[AtsIgnoreIWDis](D0F2xF4_x47[0]) = 1b 175262306a36Sopenharmony_ci */ 175362306a36Sopenharmony_cistatic void amd_iommu_ats_write_check_workaround(struct amd_iommu *iommu) 175462306a36Sopenharmony_ci{ 175562306a36Sopenharmony_ci u32 value; 175662306a36Sopenharmony_ci 175762306a36Sopenharmony_ci if ((boot_cpu_data.x86 != 0x15) || 175862306a36Sopenharmony_ci (boot_cpu_data.x86_model < 0x30) || 175962306a36Sopenharmony_ci (boot_cpu_data.x86_model > 0x3f)) 176062306a36Sopenharmony_ci return; 176162306a36Sopenharmony_ci 176262306a36Sopenharmony_ci /* Test L2_DEBUG_3[AtsIgnoreIWDis] == 1 */ 176362306a36Sopenharmony_ci value = iommu_read_l2(iommu, 0x47); 176462306a36Sopenharmony_ci 176562306a36Sopenharmony_ci if (value & BIT(0)) 176662306a36Sopenharmony_ci return; 176762306a36Sopenharmony_ci 176862306a36Sopenharmony_ci /* Set L2_DEBUG_3[AtsIgnoreIWDis] = 1 */ 176962306a36Sopenharmony_ci iommu_write_l2(iommu, 0x47, value | BIT(0)); 177062306a36Sopenharmony_ci 177162306a36Sopenharmony_ci pci_info(iommu->dev, "Applying ATS write check workaround\n"); 177262306a36Sopenharmony_ci} 177362306a36Sopenharmony_ci 177462306a36Sopenharmony_ci/* 177562306a36Sopenharmony_ci * This function glues the initialization function for one IOMMU 177662306a36Sopenharmony_ci * together and also allocates the command buffer and programs the 177762306a36Sopenharmony_ci * hardware. It does NOT enable the IOMMU. This is done afterwards. 177862306a36Sopenharmony_ci */ 177962306a36Sopenharmony_cistatic int __init init_iommu_one(struct amd_iommu *iommu, struct ivhd_header *h, 178062306a36Sopenharmony_ci struct acpi_table_header *ivrs_base) 178162306a36Sopenharmony_ci{ 178262306a36Sopenharmony_ci struct amd_iommu_pci_seg *pci_seg; 178362306a36Sopenharmony_ci 178462306a36Sopenharmony_ci pci_seg = get_pci_segment(h->pci_seg, ivrs_base); 178562306a36Sopenharmony_ci if (pci_seg == NULL) 178662306a36Sopenharmony_ci return -ENOMEM; 178762306a36Sopenharmony_ci iommu->pci_seg = pci_seg; 178862306a36Sopenharmony_ci 178962306a36Sopenharmony_ci raw_spin_lock_init(&iommu->lock); 179062306a36Sopenharmony_ci atomic64_set(&iommu->cmd_sem_val, 0); 179162306a36Sopenharmony_ci 179262306a36Sopenharmony_ci /* Add IOMMU to internal data structures */ 179362306a36Sopenharmony_ci list_add_tail(&iommu->list, &amd_iommu_list); 179462306a36Sopenharmony_ci iommu->index = amd_iommus_present++; 179562306a36Sopenharmony_ci 179662306a36Sopenharmony_ci if (unlikely(iommu->index >= MAX_IOMMUS)) { 179762306a36Sopenharmony_ci WARN(1, "System has more IOMMUs than supported by this driver\n"); 179862306a36Sopenharmony_ci return -ENOSYS; 179962306a36Sopenharmony_ci } 180062306a36Sopenharmony_ci 180162306a36Sopenharmony_ci /* Index is fine - add IOMMU to the array */ 180262306a36Sopenharmony_ci amd_iommus[iommu->index] = iommu; 180362306a36Sopenharmony_ci 180462306a36Sopenharmony_ci /* 180562306a36Sopenharmony_ci * Copy data from ACPI table entry to the iommu struct 180662306a36Sopenharmony_ci */ 180762306a36Sopenharmony_ci iommu->devid = h->devid; 180862306a36Sopenharmony_ci iommu->cap_ptr = h->cap_ptr; 180962306a36Sopenharmony_ci iommu->mmio_phys = h->mmio_phys; 181062306a36Sopenharmony_ci 181162306a36Sopenharmony_ci switch (h->type) { 181262306a36Sopenharmony_ci case 0x10: 181362306a36Sopenharmony_ci /* Check if IVHD EFR contains proper max banks/counters */ 181462306a36Sopenharmony_ci if ((h->efr_attr != 0) && 181562306a36Sopenharmony_ci ((h->efr_attr & (0xF << 13)) != 0) && 181662306a36Sopenharmony_ci ((h->efr_attr & (0x3F << 17)) != 0)) 181762306a36Sopenharmony_ci iommu->mmio_phys_end = MMIO_REG_END_OFFSET; 181862306a36Sopenharmony_ci else 181962306a36Sopenharmony_ci iommu->mmio_phys_end = MMIO_CNTR_CONF_OFFSET; 182062306a36Sopenharmony_ci 182162306a36Sopenharmony_ci /* 182262306a36Sopenharmony_ci * Note: GA (128-bit IRTE) mode requires cmpxchg16b supports. 182362306a36Sopenharmony_ci * GAM also requires GA mode. Therefore, we need to 182462306a36Sopenharmony_ci * check cmpxchg16b support before enabling it. 182562306a36Sopenharmony_ci */ 182662306a36Sopenharmony_ci if (!boot_cpu_has(X86_FEATURE_CX16) || 182762306a36Sopenharmony_ci ((h->efr_attr & (0x1 << IOMMU_FEAT_GASUP_SHIFT)) == 0)) 182862306a36Sopenharmony_ci amd_iommu_guest_ir = AMD_IOMMU_GUEST_IR_LEGACY; 182962306a36Sopenharmony_ci break; 183062306a36Sopenharmony_ci case 0x11: 183162306a36Sopenharmony_ci case 0x40: 183262306a36Sopenharmony_ci if (h->efr_reg & (1 << 9)) 183362306a36Sopenharmony_ci iommu->mmio_phys_end = MMIO_REG_END_OFFSET; 183462306a36Sopenharmony_ci else 183562306a36Sopenharmony_ci iommu->mmio_phys_end = MMIO_CNTR_CONF_OFFSET; 183662306a36Sopenharmony_ci 183762306a36Sopenharmony_ci /* 183862306a36Sopenharmony_ci * Note: GA (128-bit IRTE) mode requires cmpxchg16b supports. 183962306a36Sopenharmony_ci * XT, GAM also requires GA mode. Therefore, we need to 184062306a36Sopenharmony_ci * check cmpxchg16b support before enabling them. 184162306a36Sopenharmony_ci */ 184262306a36Sopenharmony_ci if (!boot_cpu_has(X86_FEATURE_CX16) || 184362306a36Sopenharmony_ci ((h->efr_reg & (0x1 << IOMMU_EFR_GASUP_SHIFT)) == 0)) { 184462306a36Sopenharmony_ci amd_iommu_guest_ir = AMD_IOMMU_GUEST_IR_LEGACY; 184562306a36Sopenharmony_ci break; 184662306a36Sopenharmony_ci } 184762306a36Sopenharmony_ci 184862306a36Sopenharmony_ci if (h->efr_reg & BIT(IOMMU_EFR_XTSUP_SHIFT)) 184962306a36Sopenharmony_ci amd_iommu_xt_mode = IRQ_REMAP_X2APIC_MODE; 185062306a36Sopenharmony_ci 185162306a36Sopenharmony_ci early_iommu_features_init(iommu, h); 185262306a36Sopenharmony_ci 185362306a36Sopenharmony_ci break; 185462306a36Sopenharmony_ci default: 185562306a36Sopenharmony_ci return -EINVAL; 185662306a36Sopenharmony_ci } 185762306a36Sopenharmony_ci 185862306a36Sopenharmony_ci iommu->mmio_base = iommu_map_mmio_space(iommu->mmio_phys, 185962306a36Sopenharmony_ci iommu->mmio_phys_end); 186062306a36Sopenharmony_ci if (!iommu->mmio_base) 186162306a36Sopenharmony_ci return -ENOMEM; 186262306a36Sopenharmony_ci 186362306a36Sopenharmony_ci return init_iommu_from_acpi(iommu, h); 186462306a36Sopenharmony_ci} 186562306a36Sopenharmony_ci 186662306a36Sopenharmony_cistatic int __init init_iommu_one_late(struct amd_iommu *iommu) 186762306a36Sopenharmony_ci{ 186862306a36Sopenharmony_ci int ret; 186962306a36Sopenharmony_ci 187062306a36Sopenharmony_ci if (alloc_cwwb_sem(iommu)) 187162306a36Sopenharmony_ci return -ENOMEM; 187262306a36Sopenharmony_ci 187362306a36Sopenharmony_ci if (alloc_command_buffer(iommu)) 187462306a36Sopenharmony_ci return -ENOMEM; 187562306a36Sopenharmony_ci 187662306a36Sopenharmony_ci if (alloc_event_buffer(iommu)) 187762306a36Sopenharmony_ci return -ENOMEM; 187862306a36Sopenharmony_ci 187962306a36Sopenharmony_ci iommu->int_enabled = false; 188062306a36Sopenharmony_ci 188162306a36Sopenharmony_ci init_translation_status(iommu); 188262306a36Sopenharmony_ci if (translation_pre_enabled(iommu) && !is_kdump_kernel()) { 188362306a36Sopenharmony_ci iommu_disable(iommu); 188462306a36Sopenharmony_ci clear_translation_pre_enabled(iommu); 188562306a36Sopenharmony_ci pr_warn("Translation was enabled for IOMMU:%d but we are not in kdump mode\n", 188662306a36Sopenharmony_ci iommu->index); 188762306a36Sopenharmony_ci } 188862306a36Sopenharmony_ci if (amd_iommu_pre_enabled) 188962306a36Sopenharmony_ci amd_iommu_pre_enabled = translation_pre_enabled(iommu); 189062306a36Sopenharmony_ci 189162306a36Sopenharmony_ci if (amd_iommu_irq_remap) { 189262306a36Sopenharmony_ci ret = amd_iommu_create_irq_domain(iommu); 189362306a36Sopenharmony_ci if (ret) 189462306a36Sopenharmony_ci return ret; 189562306a36Sopenharmony_ci } 189662306a36Sopenharmony_ci 189762306a36Sopenharmony_ci /* 189862306a36Sopenharmony_ci * Make sure IOMMU is not considered to translate itself. The IVRS 189962306a36Sopenharmony_ci * table tells us so, but this is a lie! 190062306a36Sopenharmony_ci */ 190162306a36Sopenharmony_ci iommu->pci_seg->rlookup_table[iommu->devid] = NULL; 190262306a36Sopenharmony_ci 190362306a36Sopenharmony_ci return 0; 190462306a36Sopenharmony_ci} 190562306a36Sopenharmony_ci 190662306a36Sopenharmony_ci/** 190762306a36Sopenharmony_ci * get_highest_supported_ivhd_type - Look up the appropriate IVHD type 190862306a36Sopenharmony_ci * @ivrs: Pointer to the IVRS header 190962306a36Sopenharmony_ci * 191062306a36Sopenharmony_ci * This function search through all IVDB of the maximum supported IVHD 191162306a36Sopenharmony_ci */ 191262306a36Sopenharmony_cistatic u8 get_highest_supported_ivhd_type(struct acpi_table_header *ivrs) 191362306a36Sopenharmony_ci{ 191462306a36Sopenharmony_ci u8 *base = (u8 *)ivrs; 191562306a36Sopenharmony_ci struct ivhd_header *ivhd = (struct ivhd_header *) 191662306a36Sopenharmony_ci (base + IVRS_HEADER_LENGTH); 191762306a36Sopenharmony_ci u8 last_type = ivhd->type; 191862306a36Sopenharmony_ci u16 devid = ivhd->devid; 191962306a36Sopenharmony_ci 192062306a36Sopenharmony_ci while (((u8 *)ivhd - base < ivrs->length) && 192162306a36Sopenharmony_ci (ivhd->type <= ACPI_IVHD_TYPE_MAX_SUPPORTED)) { 192262306a36Sopenharmony_ci u8 *p = (u8 *) ivhd; 192362306a36Sopenharmony_ci 192462306a36Sopenharmony_ci if (ivhd->devid == devid) 192562306a36Sopenharmony_ci last_type = ivhd->type; 192662306a36Sopenharmony_ci ivhd = (struct ivhd_header *)(p + ivhd->length); 192762306a36Sopenharmony_ci } 192862306a36Sopenharmony_ci 192962306a36Sopenharmony_ci return last_type; 193062306a36Sopenharmony_ci} 193162306a36Sopenharmony_ci 193262306a36Sopenharmony_ci/* 193362306a36Sopenharmony_ci * Iterates over all IOMMU entries in the ACPI table, allocates the 193462306a36Sopenharmony_ci * IOMMU structure and initializes it with init_iommu_one() 193562306a36Sopenharmony_ci */ 193662306a36Sopenharmony_cistatic int __init init_iommu_all(struct acpi_table_header *table) 193762306a36Sopenharmony_ci{ 193862306a36Sopenharmony_ci u8 *p = (u8 *)table, *end = (u8 *)table; 193962306a36Sopenharmony_ci struct ivhd_header *h; 194062306a36Sopenharmony_ci struct amd_iommu *iommu; 194162306a36Sopenharmony_ci int ret; 194262306a36Sopenharmony_ci 194362306a36Sopenharmony_ci end += table->length; 194462306a36Sopenharmony_ci p += IVRS_HEADER_LENGTH; 194562306a36Sopenharmony_ci 194662306a36Sopenharmony_ci /* Phase 1: Process all IVHD blocks */ 194762306a36Sopenharmony_ci while (p < end) { 194862306a36Sopenharmony_ci h = (struct ivhd_header *)p; 194962306a36Sopenharmony_ci if (*p == amd_iommu_target_ivhd_type) { 195062306a36Sopenharmony_ci 195162306a36Sopenharmony_ci DUMP_printk("device: %04x:%02x:%02x.%01x cap: %04x " 195262306a36Sopenharmony_ci "flags: %01x info %04x\n", 195362306a36Sopenharmony_ci h->pci_seg, PCI_BUS_NUM(h->devid), 195462306a36Sopenharmony_ci PCI_SLOT(h->devid), PCI_FUNC(h->devid), 195562306a36Sopenharmony_ci h->cap_ptr, h->flags, h->info); 195662306a36Sopenharmony_ci DUMP_printk(" mmio-addr: %016llx\n", 195762306a36Sopenharmony_ci h->mmio_phys); 195862306a36Sopenharmony_ci 195962306a36Sopenharmony_ci iommu = kzalloc(sizeof(struct amd_iommu), GFP_KERNEL); 196062306a36Sopenharmony_ci if (iommu == NULL) 196162306a36Sopenharmony_ci return -ENOMEM; 196262306a36Sopenharmony_ci 196362306a36Sopenharmony_ci ret = init_iommu_one(iommu, h, table); 196462306a36Sopenharmony_ci if (ret) 196562306a36Sopenharmony_ci return ret; 196662306a36Sopenharmony_ci } 196762306a36Sopenharmony_ci p += h->length; 196862306a36Sopenharmony_ci 196962306a36Sopenharmony_ci } 197062306a36Sopenharmony_ci WARN_ON(p != end); 197162306a36Sopenharmony_ci 197262306a36Sopenharmony_ci /* Phase 2 : Early feature support check */ 197362306a36Sopenharmony_ci get_global_efr(); 197462306a36Sopenharmony_ci 197562306a36Sopenharmony_ci /* Phase 3 : Enabling IOMMU features */ 197662306a36Sopenharmony_ci for_each_iommu(iommu) { 197762306a36Sopenharmony_ci ret = init_iommu_one_late(iommu); 197862306a36Sopenharmony_ci if (ret) 197962306a36Sopenharmony_ci return ret; 198062306a36Sopenharmony_ci } 198162306a36Sopenharmony_ci 198262306a36Sopenharmony_ci return 0; 198362306a36Sopenharmony_ci} 198462306a36Sopenharmony_ci 198562306a36Sopenharmony_cistatic void init_iommu_perf_ctr(struct amd_iommu *iommu) 198662306a36Sopenharmony_ci{ 198762306a36Sopenharmony_ci u64 val; 198862306a36Sopenharmony_ci struct pci_dev *pdev = iommu->dev; 198962306a36Sopenharmony_ci 199062306a36Sopenharmony_ci if (!iommu_feature(iommu, FEATURE_PC)) 199162306a36Sopenharmony_ci return; 199262306a36Sopenharmony_ci 199362306a36Sopenharmony_ci amd_iommu_pc_present = true; 199462306a36Sopenharmony_ci 199562306a36Sopenharmony_ci pci_info(pdev, "IOMMU performance counters supported\n"); 199662306a36Sopenharmony_ci 199762306a36Sopenharmony_ci val = readl(iommu->mmio_base + MMIO_CNTR_CONF_OFFSET); 199862306a36Sopenharmony_ci iommu->max_banks = (u8) ((val >> 12) & 0x3f); 199962306a36Sopenharmony_ci iommu->max_counters = (u8) ((val >> 7) & 0xf); 200062306a36Sopenharmony_ci 200162306a36Sopenharmony_ci return; 200262306a36Sopenharmony_ci} 200362306a36Sopenharmony_ci 200462306a36Sopenharmony_cistatic ssize_t amd_iommu_show_cap(struct device *dev, 200562306a36Sopenharmony_ci struct device_attribute *attr, 200662306a36Sopenharmony_ci char *buf) 200762306a36Sopenharmony_ci{ 200862306a36Sopenharmony_ci struct amd_iommu *iommu = dev_to_amd_iommu(dev); 200962306a36Sopenharmony_ci return sysfs_emit(buf, "%x\n", iommu->cap); 201062306a36Sopenharmony_ci} 201162306a36Sopenharmony_cistatic DEVICE_ATTR(cap, S_IRUGO, amd_iommu_show_cap, NULL); 201262306a36Sopenharmony_ci 201362306a36Sopenharmony_cistatic ssize_t amd_iommu_show_features(struct device *dev, 201462306a36Sopenharmony_ci struct device_attribute *attr, 201562306a36Sopenharmony_ci char *buf) 201662306a36Sopenharmony_ci{ 201762306a36Sopenharmony_ci struct amd_iommu *iommu = dev_to_amd_iommu(dev); 201862306a36Sopenharmony_ci return sysfs_emit(buf, "%llx:%llx\n", iommu->features2, iommu->features); 201962306a36Sopenharmony_ci} 202062306a36Sopenharmony_cistatic DEVICE_ATTR(features, S_IRUGO, amd_iommu_show_features, NULL); 202162306a36Sopenharmony_ci 202262306a36Sopenharmony_cistatic struct attribute *amd_iommu_attrs[] = { 202362306a36Sopenharmony_ci &dev_attr_cap.attr, 202462306a36Sopenharmony_ci &dev_attr_features.attr, 202562306a36Sopenharmony_ci NULL, 202662306a36Sopenharmony_ci}; 202762306a36Sopenharmony_ci 202862306a36Sopenharmony_cistatic struct attribute_group amd_iommu_group = { 202962306a36Sopenharmony_ci .name = "amd-iommu", 203062306a36Sopenharmony_ci .attrs = amd_iommu_attrs, 203162306a36Sopenharmony_ci}; 203262306a36Sopenharmony_ci 203362306a36Sopenharmony_cistatic const struct attribute_group *amd_iommu_groups[] = { 203462306a36Sopenharmony_ci &amd_iommu_group, 203562306a36Sopenharmony_ci NULL, 203662306a36Sopenharmony_ci}; 203762306a36Sopenharmony_ci 203862306a36Sopenharmony_ci/* 203962306a36Sopenharmony_ci * Note: IVHD 0x11 and 0x40 also contains exact copy 204062306a36Sopenharmony_ci * of the IOMMU Extended Feature Register [MMIO Offset 0030h]. 204162306a36Sopenharmony_ci * Default to EFR in IVHD since it is available sooner (i.e. before PCI init). 204262306a36Sopenharmony_ci */ 204362306a36Sopenharmony_cistatic void __init late_iommu_features_init(struct amd_iommu *iommu) 204462306a36Sopenharmony_ci{ 204562306a36Sopenharmony_ci u64 features, features2; 204662306a36Sopenharmony_ci 204762306a36Sopenharmony_ci if (!(iommu->cap & (1 << IOMMU_CAP_EFR))) 204862306a36Sopenharmony_ci return; 204962306a36Sopenharmony_ci 205062306a36Sopenharmony_ci /* read extended feature bits */ 205162306a36Sopenharmony_ci features = readq(iommu->mmio_base + MMIO_EXT_FEATURES); 205262306a36Sopenharmony_ci features2 = readq(iommu->mmio_base + MMIO_EXT_FEATURES2); 205362306a36Sopenharmony_ci 205462306a36Sopenharmony_ci if (!iommu->features) { 205562306a36Sopenharmony_ci iommu->features = features; 205662306a36Sopenharmony_ci iommu->features2 = features2; 205762306a36Sopenharmony_ci return; 205862306a36Sopenharmony_ci } 205962306a36Sopenharmony_ci 206062306a36Sopenharmony_ci /* 206162306a36Sopenharmony_ci * Sanity check and warn if EFR values from 206262306a36Sopenharmony_ci * IVHD and MMIO conflict. 206362306a36Sopenharmony_ci */ 206462306a36Sopenharmony_ci if (features != iommu->features || 206562306a36Sopenharmony_ci features2 != iommu->features2) { 206662306a36Sopenharmony_ci pr_warn(FW_WARN 206762306a36Sopenharmony_ci "EFR mismatch. Use IVHD EFR (%#llx : %#llx), EFR2 (%#llx : %#llx).\n", 206862306a36Sopenharmony_ci features, iommu->features, 206962306a36Sopenharmony_ci features2, iommu->features2); 207062306a36Sopenharmony_ci } 207162306a36Sopenharmony_ci} 207262306a36Sopenharmony_ci 207362306a36Sopenharmony_cistatic int __init iommu_init_pci(struct amd_iommu *iommu) 207462306a36Sopenharmony_ci{ 207562306a36Sopenharmony_ci int cap_ptr = iommu->cap_ptr; 207662306a36Sopenharmony_ci int ret; 207762306a36Sopenharmony_ci 207862306a36Sopenharmony_ci iommu->dev = pci_get_domain_bus_and_slot(iommu->pci_seg->id, 207962306a36Sopenharmony_ci PCI_BUS_NUM(iommu->devid), 208062306a36Sopenharmony_ci iommu->devid & 0xff); 208162306a36Sopenharmony_ci if (!iommu->dev) 208262306a36Sopenharmony_ci return -ENODEV; 208362306a36Sopenharmony_ci 208462306a36Sopenharmony_ci /* Prevent binding other PCI device drivers to IOMMU devices */ 208562306a36Sopenharmony_ci iommu->dev->match_driver = false; 208662306a36Sopenharmony_ci 208762306a36Sopenharmony_ci /* ACPI _PRT won't have an IRQ for IOMMU */ 208862306a36Sopenharmony_ci iommu->dev->irq_managed = 1; 208962306a36Sopenharmony_ci 209062306a36Sopenharmony_ci pci_read_config_dword(iommu->dev, cap_ptr + MMIO_CAP_HDR_OFFSET, 209162306a36Sopenharmony_ci &iommu->cap); 209262306a36Sopenharmony_ci 209362306a36Sopenharmony_ci if (!(iommu->cap & (1 << IOMMU_CAP_IOTLB))) 209462306a36Sopenharmony_ci amd_iommu_iotlb_sup = false; 209562306a36Sopenharmony_ci 209662306a36Sopenharmony_ci late_iommu_features_init(iommu); 209762306a36Sopenharmony_ci 209862306a36Sopenharmony_ci if (iommu_feature(iommu, FEATURE_GT)) { 209962306a36Sopenharmony_ci int glxval; 210062306a36Sopenharmony_ci u32 max_pasid; 210162306a36Sopenharmony_ci u64 pasmax; 210262306a36Sopenharmony_ci 210362306a36Sopenharmony_ci pasmax = iommu->features & FEATURE_PASID_MASK; 210462306a36Sopenharmony_ci pasmax >>= FEATURE_PASID_SHIFT; 210562306a36Sopenharmony_ci max_pasid = (1 << (pasmax + 1)) - 1; 210662306a36Sopenharmony_ci 210762306a36Sopenharmony_ci amd_iommu_max_pasid = min(amd_iommu_max_pasid, max_pasid); 210862306a36Sopenharmony_ci 210962306a36Sopenharmony_ci BUG_ON(amd_iommu_max_pasid & ~PASID_MASK); 211062306a36Sopenharmony_ci 211162306a36Sopenharmony_ci glxval = iommu->features & FEATURE_GLXVAL_MASK; 211262306a36Sopenharmony_ci glxval >>= FEATURE_GLXVAL_SHIFT; 211362306a36Sopenharmony_ci 211462306a36Sopenharmony_ci if (amd_iommu_max_glx_val == -1) 211562306a36Sopenharmony_ci amd_iommu_max_glx_val = glxval; 211662306a36Sopenharmony_ci else 211762306a36Sopenharmony_ci amd_iommu_max_glx_val = min(amd_iommu_max_glx_val, glxval); 211862306a36Sopenharmony_ci } 211962306a36Sopenharmony_ci 212062306a36Sopenharmony_ci if (iommu_feature(iommu, FEATURE_GT) && 212162306a36Sopenharmony_ci iommu_feature(iommu, FEATURE_PPR)) { 212262306a36Sopenharmony_ci iommu->is_iommu_v2 = true; 212362306a36Sopenharmony_ci amd_iommu_v2_present = true; 212462306a36Sopenharmony_ci } 212562306a36Sopenharmony_ci 212662306a36Sopenharmony_ci if (iommu_feature(iommu, FEATURE_PPR) && alloc_ppr_log(iommu)) 212762306a36Sopenharmony_ci return -ENOMEM; 212862306a36Sopenharmony_ci 212962306a36Sopenharmony_ci if (iommu->cap & (1UL << IOMMU_CAP_NPCACHE)) { 213062306a36Sopenharmony_ci pr_info("Using strict mode due to virtualization\n"); 213162306a36Sopenharmony_ci iommu_set_dma_strict(); 213262306a36Sopenharmony_ci amd_iommu_np_cache = true; 213362306a36Sopenharmony_ci } 213462306a36Sopenharmony_ci 213562306a36Sopenharmony_ci init_iommu_perf_ctr(iommu); 213662306a36Sopenharmony_ci 213762306a36Sopenharmony_ci if (amd_iommu_pgtable == AMD_IOMMU_V2) { 213862306a36Sopenharmony_ci if (!iommu_feature(iommu, FEATURE_GIOSUP) || 213962306a36Sopenharmony_ci !iommu_feature(iommu, FEATURE_GT)) { 214062306a36Sopenharmony_ci pr_warn("Cannot enable v2 page table for DMA-API. Fallback to v1.\n"); 214162306a36Sopenharmony_ci amd_iommu_pgtable = AMD_IOMMU_V1; 214262306a36Sopenharmony_ci } else if (iommu_default_passthrough()) { 214362306a36Sopenharmony_ci pr_warn("V2 page table doesn't support passthrough mode. Fallback to v1.\n"); 214462306a36Sopenharmony_ci amd_iommu_pgtable = AMD_IOMMU_V1; 214562306a36Sopenharmony_ci } 214662306a36Sopenharmony_ci } 214762306a36Sopenharmony_ci 214862306a36Sopenharmony_ci if (is_rd890_iommu(iommu->dev)) { 214962306a36Sopenharmony_ci int i, j; 215062306a36Sopenharmony_ci 215162306a36Sopenharmony_ci iommu->root_pdev = 215262306a36Sopenharmony_ci pci_get_domain_bus_and_slot(iommu->pci_seg->id, 215362306a36Sopenharmony_ci iommu->dev->bus->number, 215462306a36Sopenharmony_ci PCI_DEVFN(0, 0)); 215562306a36Sopenharmony_ci 215662306a36Sopenharmony_ci /* 215762306a36Sopenharmony_ci * Some rd890 systems may not be fully reconfigured by the 215862306a36Sopenharmony_ci * BIOS, so it's necessary for us to store this information so 215962306a36Sopenharmony_ci * it can be reprogrammed on resume 216062306a36Sopenharmony_ci */ 216162306a36Sopenharmony_ci pci_read_config_dword(iommu->dev, iommu->cap_ptr + 4, 216262306a36Sopenharmony_ci &iommu->stored_addr_lo); 216362306a36Sopenharmony_ci pci_read_config_dword(iommu->dev, iommu->cap_ptr + 8, 216462306a36Sopenharmony_ci &iommu->stored_addr_hi); 216562306a36Sopenharmony_ci 216662306a36Sopenharmony_ci /* Low bit locks writes to configuration space */ 216762306a36Sopenharmony_ci iommu->stored_addr_lo &= ~1; 216862306a36Sopenharmony_ci 216962306a36Sopenharmony_ci for (i = 0; i < 6; i++) 217062306a36Sopenharmony_ci for (j = 0; j < 0x12; j++) 217162306a36Sopenharmony_ci iommu->stored_l1[i][j] = iommu_read_l1(iommu, i, j); 217262306a36Sopenharmony_ci 217362306a36Sopenharmony_ci for (i = 0; i < 0x83; i++) 217462306a36Sopenharmony_ci iommu->stored_l2[i] = iommu_read_l2(iommu, i); 217562306a36Sopenharmony_ci } 217662306a36Sopenharmony_ci 217762306a36Sopenharmony_ci amd_iommu_erratum_746_workaround(iommu); 217862306a36Sopenharmony_ci amd_iommu_ats_write_check_workaround(iommu); 217962306a36Sopenharmony_ci 218062306a36Sopenharmony_ci ret = iommu_device_sysfs_add(&iommu->iommu, &iommu->dev->dev, 218162306a36Sopenharmony_ci amd_iommu_groups, "ivhd%d", iommu->index); 218262306a36Sopenharmony_ci if (ret) 218362306a36Sopenharmony_ci return ret; 218462306a36Sopenharmony_ci 218562306a36Sopenharmony_ci iommu_device_register(&iommu->iommu, &amd_iommu_ops, NULL); 218662306a36Sopenharmony_ci 218762306a36Sopenharmony_ci return pci_enable_device(iommu->dev); 218862306a36Sopenharmony_ci} 218962306a36Sopenharmony_ci 219062306a36Sopenharmony_cistatic void print_iommu_info(void) 219162306a36Sopenharmony_ci{ 219262306a36Sopenharmony_ci static const char * const feat_str[] = { 219362306a36Sopenharmony_ci "PreF", "PPR", "X2APIC", "NX", "GT", "[5]", 219462306a36Sopenharmony_ci "IA", "GA", "HE", "PC" 219562306a36Sopenharmony_ci }; 219662306a36Sopenharmony_ci struct amd_iommu *iommu; 219762306a36Sopenharmony_ci 219862306a36Sopenharmony_ci for_each_iommu(iommu) { 219962306a36Sopenharmony_ci struct pci_dev *pdev = iommu->dev; 220062306a36Sopenharmony_ci int i; 220162306a36Sopenharmony_ci 220262306a36Sopenharmony_ci pci_info(pdev, "Found IOMMU cap 0x%x\n", iommu->cap_ptr); 220362306a36Sopenharmony_ci 220462306a36Sopenharmony_ci if (iommu->cap & (1 << IOMMU_CAP_EFR)) { 220562306a36Sopenharmony_ci pr_info("Extended features (%#llx, %#llx):", iommu->features, iommu->features2); 220662306a36Sopenharmony_ci 220762306a36Sopenharmony_ci for (i = 0; i < ARRAY_SIZE(feat_str); ++i) { 220862306a36Sopenharmony_ci if (iommu_feature(iommu, (1ULL << i))) 220962306a36Sopenharmony_ci pr_cont(" %s", feat_str[i]); 221062306a36Sopenharmony_ci } 221162306a36Sopenharmony_ci 221262306a36Sopenharmony_ci if (iommu->features & FEATURE_GAM_VAPIC) 221362306a36Sopenharmony_ci pr_cont(" GA_vAPIC"); 221462306a36Sopenharmony_ci 221562306a36Sopenharmony_ci if (iommu->features & FEATURE_SNP) 221662306a36Sopenharmony_ci pr_cont(" SNP"); 221762306a36Sopenharmony_ci 221862306a36Sopenharmony_ci pr_cont("\n"); 221962306a36Sopenharmony_ci } 222062306a36Sopenharmony_ci } 222162306a36Sopenharmony_ci if (irq_remapping_enabled) { 222262306a36Sopenharmony_ci pr_info("Interrupt remapping enabled\n"); 222362306a36Sopenharmony_ci if (amd_iommu_xt_mode == IRQ_REMAP_X2APIC_MODE) 222462306a36Sopenharmony_ci pr_info("X2APIC enabled\n"); 222562306a36Sopenharmony_ci } 222662306a36Sopenharmony_ci if (amd_iommu_pgtable == AMD_IOMMU_V2) { 222762306a36Sopenharmony_ci pr_info("V2 page table enabled (Paging mode : %d level)\n", 222862306a36Sopenharmony_ci amd_iommu_gpt_level); 222962306a36Sopenharmony_ci } 223062306a36Sopenharmony_ci} 223162306a36Sopenharmony_ci 223262306a36Sopenharmony_cistatic int __init amd_iommu_init_pci(void) 223362306a36Sopenharmony_ci{ 223462306a36Sopenharmony_ci struct amd_iommu *iommu; 223562306a36Sopenharmony_ci struct amd_iommu_pci_seg *pci_seg; 223662306a36Sopenharmony_ci int ret; 223762306a36Sopenharmony_ci 223862306a36Sopenharmony_ci for_each_iommu(iommu) { 223962306a36Sopenharmony_ci ret = iommu_init_pci(iommu); 224062306a36Sopenharmony_ci if (ret) { 224162306a36Sopenharmony_ci pr_err("IOMMU%d: Failed to initialize IOMMU Hardware (error=%d)!\n", 224262306a36Sopenharmony_ci iommu->index, ret); 224362306a36Sopenharmony_ci goto out; 224462306a36Sopenharmony_ci } 224562306a36Sopenharmony_ci /* Need to setup range after PCI init */ 224662306a36Sopenharmony_ci iommu_set_cwwb_range(iommu); 224762306a36Sopenharmony_ci } 224862306a36Sopenharmony_ci 224962306a36Sopenharmony_ci /* 225062306a36Sopenharmony_ci * Order is important here to make sure any unity map requirements are 225162306a36Sopenharmony_ci * fulfilled. The unity mappings are created and written to the device 225262306a36Sopenharmony_ci * table during the iommu_init_pci() call. 225362306a36Sopenharmony_ci * 225462306a36Sopenharmony_ci * After that we call init_device_table_dma() to make sure any 225562306a36Sopenharmony_ci * uninitialized DTE will block DMA, and in the end we flush the caches 225662306a36Sopenharmony_ci * of all IOMMUs to make sure the changes to the device table are 225762306a36Sopenharmony_ci * active. 225862306a36Sopenharmony_ci */ 225962306a36Sopenharmony_ci for_each_pci_segment(pci_seg) 226062306a36Sopenharmony_ci init_device_table_dma(pci_seg); 226162306a36Sopenharmony_ci 226262306a36Sopenharmony_ci for_each_iommu(iommu) 226362306a36Sopenharmony_ci iommu_flush_all_caches(iommu); 226462306a36Sopenharmony_ci 226562306a36Sopenharmony_ci print_iommu_info(); 226662306a36Sopenharmony_ci 226762306a36Sopenharmony_ciout: 226862306a36Sopenharmony_ci return ret; 226962306a36Sopenharmony_ci} 227062306a36Sopenharmony_ci 227162306a36Sopenharmony_ci/**************************************************************************** 227262306a36Sopenharmony_ci * 227362306a36Sopenharmony_ci * The following functions initialize the MSI interrupts for all IOMMUs 227462306a36Sopenharmony_ci * in the system. It's a bit challenging because there could be multiple 227562306a36Sopenharmony_ci * IOMMUs per PCI BDF but we can call pci_enable_msi(x) only once per 227662306a36Sopenharmony_ci * pci_dev. 227762306a36Sopenharmony_ci * 227862306a36Sopenharmony_ci ****************************************************************************/ 227962306a36Sopenharmony_ci 228062306a36Sopenharmony_cistatic int iommu_setup_msi(struct amd_iommu *iommu) 228162306a36Sopenharmony_ci{ 228262306a36Sopenharmony_ci int r; 228362306a36Sopenharmony_ci 228462306a36Sopenharmony_ci r = pci_enable_msi(iommu->dev); 228562306a36Sopenharmony_ci if (r) 228662306a36Sopenharmony_ci return r; 228762306a36Sopenharmony_ci 228862306a36Sopenharmony_ci r = request_threaded_irq(iommu->dev->irq, 228962306a36Sopenharmony_ci amd_iommu_int_handler, 229062306a36Sopenharmony_ci amd_iommu_int_thread, 229162306a36Sopenharmony_ci 0, "AMD-Vi", 229262306a36Sopenharmony_ci iommu); 229362306a36Sopenharmony_ci 229462306a36Sopenharmony_ci if (r) { 229562306a36Sopenharmony_ci pci_disable_msi(iommu->dev); 229662306a36Sopenharmony_ci return r; 229762306a36Sopenharmony_ci } 229862306a36Sopenharmony_ci 229962306a36Sopenharmony_ci return 0; 230062306a36Sopenharmony_ci} 230162306a36Sopenharmony_ci 230262306a36Sopenharmony_ciunion intcapxt { 230362306a36Sopenharmony_ci u64 capxt; 230462306a36Sopenharmony_ci struct { 230562306a36Sopenharmony_ci u64 reserved_0 : 2, 230662306a36Sopenharmony_ci dest_mode_logical : 1, 230762306a36Sopenharmony_ci reserved_1 : 5, 230862306a36Sopenharmony_ci destid_0_23 : 24, 230962306a36Sopenharmony_ci vector : 8, 231062306a36Sopenharmony_ci reserved_2 : 16, 231162306a36Sopenharmony_ci destid_24_31 : 8; 231262306a36Sopenharmony_ci }; 231362306a36Sopenharmony_ci} __attribute__ ((packed)); 231462306a36Sopenharmony_ci 231562306a36Sopenharmony_ci 231662306a36Sopenharmony_cistatic struct irq_chip intcapxt_controller; 231762306a36Sopenharmony_ci 231862306a36Sopenharmony_cistatic int intcapxt_irqdomain_activate(struct irq_domain *domain, 231962306a36Sopenharmony_ci struct irq_data *irqd, bool reserve) 232062306a36Sopenharmony_ci{ 232162306a36Sopenharmony_ci return 0; 232262306a36Sopenharmony_ci} 232362306a36Sopenharmony_ci 232462306a36Sopenharmony_cistatic void intcapxt_irqdomain_deactivate(struct irq_domain *domain, 232562306a36Sopenharmony_ci struct irq_data *irqd) 232662306a36Sopenharmony_ci{ 232762306a36Sopenharmony_ci} 232862306a36Sopenharmony_ci 232962306a36Sopenharmony_ci 233062306a36Sopenharmony_cistatic int intcapxt_irqdomain_alloc(struct irq_domain *domain, unsigned int virq, 233162306a36Sopenharmony_ci unsigned int nr_irqs, void *arg) 233262306a36Sopenharmony_ci{ 233362306a36Sopenharmony_ci struct irq_alloc_info *info = arg; 233462306a36Sopenharmony_ci int i, ret; 233562306a36Sopenharmony_ci 233662306a36Sopenharmony_ci if (!info || info->type != X86_IRQ_ALLOC_TYPE_AMDVI) 233762306a36Sopenharmony_ci return -EINVAL; 233862306a36Sopenharmony_ci 233962306a36Sopenharmony_ci ret = irq_domain_alloc_irqs_parent(domain, virq, nr_irqs, arg); 234062306a36Sopenharmony_ci if (ret < 0) 234162306a36Sopenharmony_ci return ret; 234262306a36Sopenharmony_ci 234362306a36Sopenharmony_ci for (i = virq; i < virq + nr_irqs; i++) { 234462306a36Sopenharmony_ci struct irq_data *irqd = irq_domain_get_irq_data(domain, i); 234562306a36Sopenharmony_ci 234662306a36Sopenharmony_ci irqd->chip = &intcapxt_controller; 234762306a36Sopenharmony_ci irqd->hwirq = info->hwirq; 234862306a36Sopenharmony_ci irqd->chip_data = info->data; 234962306a36Sopenharmony_ci __irq_set_handler(i, handle_edge_irq, 0, "edge"); 235062306a36Sopenharmony_ci } 235162306a36Sopenharmony_ci 235262306a36Sopenharmony_ci return ret; 235362306a36Sopenharmony_ci} 235462306a36Sopenharmony_ci 235562306a36Sopenharmony_cistatic void intcapxt_irqdomain_free(struct irq_domain *domain, unsigned int virq, 235662306a36Sopenharmony_ci unsigned int nr_irqs) 235762306a36Sopenharmony_ci{ 235862306a36Sopenharmony_ci irq_domain_free_irqs_top(domain, virq, nr_irqs); 235962306a36Sopenharmony_ci} 236062306a36Sopenharmony_ci 236162306a36Sopenharmony_ci 236262306a36Sopenharmony_cistatic void intcapxt_unmask_irq(struct irq_data *irqd) 236362306a36Sopenharmony_ci{ 236462306a36Sopenharmony_ci struct amd_iommu *iommu = irqd->chip_data; 236562306a36Sopenharmony_ci struct irq_cfg *cfg = irqd_cfg(irqd); 236662306a36Sopenharmony_ci union intcapxt xt; 236762306a36Sopenharmony_ci 236862306a36Sopenharmony_ci xt.capxt = 0ULL; 236962306a36Sopenharmony_ci xt.dest_mode_logical = apic->dest_mode_logical; 237062306a36Sopenharmony_ci xt.vector = cfg->vector; 237162306a36Sopenharmony_ci xt.destid_0_23 = cfg->dest_apicid & GENMASK(23, 0); 237262306a36Sopenharmony_ci xt.destid_24_31 = cfg->dest_apicid >> 24; 237362306a36Sopenharmony_ci 237462306a36Sopenharmony_ci writeq(xt.capxt, iommu->mmio_base + irqd->hwirq); 237562306a36Sopenharmony_ci} 237662306a36Sopenharmony_ci 237762306a36Sopenharmony_cistatic void intcapxt_mask_irq(struct irq_data *irqd) 237862306a36Sopenharmony_ci{ 237962306a36Sopenharmony_ci struct amd_iommu *iommu = irqd->chip_data; 238062306a36Sopenharmony_ci 238162306a36Sopenharmony_ci writeq(0, iommu->mmio_base + irqd->hwirq); 238262306a36Sopenharmony_ci} 238362306a36Sopenharmony_ci 238462306a36Sopenharmony_ci 238562306a36Sopenharmony_cistatic int intcapxt_set_affinity(struct irq_data *irqd, 238662306a36Sopenharmony_ci const struct cpumask *mask, bool force) 238762306a36Sopenharmony_ci{ 238862306a36Sopenharmony_ci struct irq_data *parent = irqd->parent_data; 238962306a36Sopenharmony_ci int ret; 239062306a36Sopenharmony_ci 239162306a36Sopenharmony_ci ret = parent->chip->irq_set_affinity(parent, mask, force); 239262306a36Sopenharmony_ci if (ret < 0 || ret == IRQ_SET_MASK_OK_DONE) 239362306a36Sopenharmony_ci return ret; 239462306a36Sopenharmony_ci return 0; 239562306a36Sopenharmony_ci} 239662306a36Sopenharmony_ci 239762306a36Sopenharmony_cistatic int intcapxt_set_wake(struct irq_data *irqd, unsigned int on) 239862306a36Sopenharmony_ci{ 239962306a36Sopenharmony_ci return on ? -EOPNOTSUPP : 0; 240062306a36Sopenharmony_ci} 240162306a36Sopenharmony_ci 240262306a36Sopenharmony_cistatic struct irq_chip intcapxt_controller = { 240362306a36Sopenharmony_ci .name = "IOMMU-MSI", 240462306a36Sopenharmony_ci .irq_unmask = intcapxt_unmask_irq, 240562306a36Sopenharmony_ci .irq_mask = intcapxt_mask_irq, 240662306a36Sopenharmony_ci .irq_ack = irq_chip_ack_parent, 240762306a36Sopenharmony_ci .irq_retrigger = irq_chip_retrigger_hierarchy, 240862306a36Sopenharmony_ci .irq_set_affinity = intcapxt_set_affinity, 240962306a36Sopenharmony_ci .irq_set_wake = intcapxt_set_wake, 241062306a36Sopenharmony_ci .flags = IRQCHIP_MASK_ON_SUSPEND, 241162306a36Sopenharmony_ci}; 241262306a36Sopenharmony_ci 241362306a36Sopenharmony_cistatic const struct irq_domain_ops intcapxt_domain_ops = { 241462306a36Sopenharmony_ci .alloc = intcapxt_irqdomain_alloc, 241562306a36Sopenharmony_ci .free = intcapxt_irqdomain_free, 241662306a36Sopenharmony_ci .activate = intcapxt_irqdomain_activate, 241762306a36Sopenharmony_ci .deactivate = intcapxt_irqdomain_deactivate, 241862306a36Sopenharmony_ci}; 241962306a36Sopenharmony_ci 242062306a36Sopenharmony_ci 242162306a36Sopenharmony_cistatic struct irq_domain *iommu_irqdomain; 242262306a36Sopenharmony_ci 242362306a36Sopenharmony_cistatic struct irq_domain *iommu_get_irqdomain(void) 242462306a36Sopenharmony_ci{ 242562306a36Sopenharmony_ci struct fwnode_handle *fn; 242662306a36Sopenharmony_ci 242762306a36Sopenharmony_ci /* No need for locking here (yet) as the init is single-threaded */ 242862306a36Sopenharmony_ci if (iommu_irqdomain) 242962306a36Sopenharmony_ci return iommu_irqdomain; 243062306a36Sopenharmony_ci 243162306a36Sopenharmony_ci fn = irq_domain_alloc_named_fwnode("AMD-Vi-MSI"); 243262306a36Sopenharmony_ci if (!fn) 243362306a36Sopenharmony_ci return NULL; 243462306a36Sopenharmony_ci 243562306a36Sopenharmony_ci iommu_irqdomain = irq_domain_create_hierarchy(x86_vector_domain, 0, 0, 243662306a36Sopenharmony_ci fn, &intcapxt_domain_ops, 243762306a36Sopenharmony_ci NULL); 243862306a36Sopenharmony_ci if (!iommu_irqdomain) 243962306a36Sopenharmony_ci irq_domain_free_fwnode(fn); 244062306a36Sopenharmony_ci 244162306a36Sopenharmony_ci return iommu_irqdomain; 244262306a36Sopenharmony_ci} 244362306a36Sopenharmony_ci 244462306a36Sopenharmony_cistatic int __iommu_setup_intcapxt(struct amd_iommu *iommu, const char *devname, 244562306a36Sopenharmony_ci int hwirq, irq_handler_t thread_fn) 244662306a36Sopenharmony_ci{ 244762306a36Sopenharmony_ci struct irq_domain *domain; 244862306a36Sopenharmony_ci struct irq_alloc_info info; 244962306a36Sopenharmony_ci int irq, ret; 245062306a36Sopenharmony_ci int node = dev_to_node(&iommu->dev->dev); 245162306a36Sopenharmony_ci 245262306a36Sopenharmony_ci domain = iommu_get_irqdomain(); 245362306a36Sopenharmony_ci if (!domain) 245462306a36Sopenharmony_ci return -ENXIO; 245562306a36Sopenharmony_ci 245662306a36Sopenharmony_ci init_irq_alloc_info(&info, NULL); 245762306a36Sopenharmony_ci info.type = X86_IRQ_ALLOC_TYPE_AMDVI; 245862306a36Sopenharmony_ci info.data = iommu; 245962306a36Sopenharmony_ci info.hwirq = hwirq; 246062306a36Sopenharmony_ci 246162306a36Sopenharmony_ci irq = irq_domain_alloc_irqs(domain, 1, node, &info); 246262306a36Sopenharmony_ci if (irq < 0) { 246362306a36Sopenharmony_ci irq_domain_remove(domain); 246462306a36Sopenharmony_ci return irq; 246562306a36Sopenharmony_ci } 246662306a36Sopenharmony_ci 246762306a36Sopenharmony_ci ret = request_threaded_irq(irq, amd_iommu_int_handler, 246862306a36Sopenharmony_ci thread_fn, 0, devname, iommu); 246962306a36Sopenharmony_ci if (ret) { 247062306a36Sopenharmony_ci irq_domain_free_irqs(irq, 1); 247162306a36Sopenharmony_ci irq_domain_remove(domain); 247262306a36Sopenharmony_ci return ret; 247362306a36Sopenharmony_ci } 247462306a36Sopenharmony_ci 247562306a36Sopenharmony_ci return 0; 247662306a36Sopenharmony_ci} 247762306a36Sopenharmony_ci 247862306a36Sopenharmony_cistatic int iommu_setup_intcapxt(struct amd_iommu *iommu) 247962306a36Sopenharmony_ci{ 248062306a36Sopenharmony_ci int ret; 248162306a36Sopenharmony_ci 248262306a36Sopenharmony_ci snprintf(iommu->evt_irq_name, sizeof(iommu->evt_irq_name), 248362306a36Sopenharmony_ci "AMD-Vi%d-Evt", iommu->index); 248462306a36Sopenharmony_ci ret = __iommu_setup_intcapxt(iommu, iommu->evt_irq_name, 248562306a36Sopenharmony_ci MMIO_INTCAPXT_EVT_OFFSET, 248662306a36Sopenharmony_ci amd_iommu_int_thread_evtlog); 248762306a36Sopenharmony_ci if (ret) 248862306a36Sopenharmony_ci return ret; 248962306a36Sopenharmony_ci 249062306a36Sopenharmony_ci snprintf(iommu->ppr_irq_name, sizeof(iommu->ppr_irq_name), 249162306a36Sopenharmony_ci "AMD-Vi%d-PPR", iommu->index); 249262306a36Sopenharmony_ci ret = __iommu_setup_intcapxt(iommu, iommu->ppr_irq_name, 249362306a36Sopenharmony_ci MMIO_INTCAPXT_PPR_OFFSET, 249462306a36Sopenharmony_ci amd_iommu_int_thread_pprlog); 249562306a36Sopenharmony_ci if (ret) 249662306a36Sopenharmony_ci return ret; 249762306a36Sopenharmony_ci 249862306a36Sopenharmony_ci#ifdef CONFIG_IRQ_REMAP 249962306a36Sopenharmony_ci snprintf(iommu->ga_irq_name, sizeof(iommu->ga_irq_name), 250062306a36Sopenharmony_ci "AMD-Vi%d-GA", iommu->index); 250162306a36Sopenharmony_ci ret = __iommu_setup_intcapxt(iommu, iommu->ga_irq_name, 250262306a36Sopenharmony_ci MMIO_INTCAPXT_GALOG_OFFSET, 250362306a36Sopenharmony_ci amd_iommu_int_thread_galog); 250462306a36Sopenharmony_ci#endif 250562306a36Sopenharmony_ci 250662306a36Sopenharmony_ci return ret; 250762306a36Sopenharmony_ci} 250862306a36Sopenharmony_ci 250962306a36Sopenharmony_cistatic int iommu_init_irq(struct amd_iommu *iommu) 251062306a36Sopenharmony_ci{ 251162306a36Sopenharmony_ci int ret; 251262306a36Sopenharmony_ci 251362306a36Sopenharmony_ci if (iommu->int_enabled) 251462306a36Sopenharmony_ci goto enable_faults; 251562306a36Sopenharmony_ci 251662306a36Sopenharmony_ci if (amd_iommu_xt_mode == IRQ_REMAP_X2APIC_MODE) 251762306a36Sopenharmony_ci ret = iommu_setup_intcapxt(iommu); 251862306a36Sopenharmony_ci else if (iommu->dev->msi_cap) 251962306a36Sopenharmony_ci ret = iommu_setup_msi(iommu); 252062306a36Sopenharmony_ci else 252162306a36Sopenharmony_ci ret = -ENODEV; 252262306a36Sopenharmony_ci 252362306a36Sopenharmony_ci if (ret) 252462306a36Sopenharmony_ci return ret; 252562306a36Sopenharmony_ci 252662306a36Sopenharmony_ci iommu->int_enabled = true; 252762306a36Sopenharmony_cienable_faults: 252862306a36Sopenharmony_ci 252962306a36Sopenharmony_ci if (amd_iommu_xt_mode == IRQ_REMAP_X2APIC_MODE) 253062306a36Sopenharmony_ci iommu_feature_enable(iommu, CONTROL_INTCAPXT_EN); 253162306a36Sopenharmony_ci 253262306a36Sopenharmony_ci iommu_feature_enable(iommu, CONTROL_EVT_INT_EN); 253362306a36Sopenharmony_ci 253462306a36Sopenharmony_ci return 0; 253562306a36Sopenharmony_ci} 253662306a36Sopenharmony_ci 253762306a36Sopenharmony_ci/**************************************************************************** 253862306a36Sopenharmony_ci * 253962306a36Sopenharmony_ci * The next functions belong to the third pass of parsing the ACPI 254062306a36Sopenharmony_ci * table. In this last pass the memory mapping requirements are 254162306a36Sopenharmony_ci * gathered (like exclusion and unity mapping ranges). 254262306a36Sopenharmony_ci * 254362306a36Sopenharmony_ci ****************************************************************************/ 254462306a36Sopenharmony_ci 254562306a36Sopenharmony_cistatic void __init free_unity_maps(void) 254662306a36Sopenharmony_ci{ 254762306a36Sopenharmony_ci struct unity_map_entry *entry, *next; 254862306a36Sopenharmony_ci struct amd_iommu_pci_seg *p, *pci_seg; 254962306a36Sopenharmony_ci 255062306a36Sopenharmony_ci for_each_pci_segment_safe(pci_seg, p) { 255162306a36Sopenharmony_ci list_for_each_entry_safe(entry, next, &pci_seg->unity_map, list) { 255262306a36Sopenharmony_ci list_del(&entry->list); 255362306a36Sopenharmony_ci kfree(entry); 255462306a36Sopenharmony_ci } 255562306a36Sopenharmony_ci } 255662306a36Sopenharmony_ci} 255762306a36Sopenharmony_ci 255862306a36Sopenharmony_ci/* called for unity map ACPI definition */ 255962306a36Sopenharmony_cistatic int __init init_unity_map_range(struct ivmd_header *m, 256062306a36Sopenharmony_ci struct acpi_table_header *ivrs_base) 256162306a36Sopenharmony_ci{ 256262306a36Sopenharmony_ci struct unity_map_entry *e = NULL; 256362306a36Sopenharmony_ci struct amd_iommu_pci_seg *pci_seg; 256462306a36Sopenharmony_ci char *s; 256562306a36Sopenharmony_ci 256662306a36Sopenharmony_ci pci_seg = get_pci_segment(m->pci_seg, ivrs_base); 256762306a36Sopenharmony_ci if (pci_seg == NULL) 256862306a36Sopenharmony_ci return -ENOMEM; 256962306a36Sopenharmony_ci 257062306a36Sopenharmony_ci e = kzalloc(sizeof(*e), GFP_KERNEL); 257162306a36Sopenharmony_ci if (e == NULL) 257262306a36Sopenharmony_ci return -ENOMEM; 257362306a36Sopenharmony_ci 257462306a36Sopenharmony_ci switch (m->type) { 257562306a36Sopenharmony_ci default: 257662306a36Sopenharmony_ci kfree(e); 257762306a36Sopenharmony_ci return 0; 257862306a36Sopenharmony_ci case ACPI_IVMD_TYPE: 257962306a36Sopenharmony_ci s = "IVMD_TYPEi\t\t\t"; 258062306a36Sopenharmony_ci e->devid_start = e->devid_end = m->devid; 258162306a36Sopenharmony_ci break; 258262306a36Sopenharmony_ci case ACPI_IVMD_TYPE_ALL: 258362306a36Sopenharmony_ci s = "IVMD_TYPE_ALL\t\t"; 258462306a36Sopenharmony_ci e->devid_start = 0; 258562306a36Sopenharmony_ci e->devid_end = pci_seg->last_bdf; 258662306a36Sopenharmony_ci break; 258762306a36Sopenharmony_ci case ACPI_IVMD_TYPE_RANGE: 258862306a36Sopenharmony_ci s = "IVMD_TYPE_RANGE\t\t"; 258962306a36Sopenharmony_ci e->devid_start = m->devid; 259062306a36Sopenharmony_ci e->devid_end = m->aux; 259162306a36Sopenharmony_ci break; 259262306a36Sopenharmony_ci } 259362306a36Sopenharmony_ci e->address_start = PAGE_ALIGN(m->range_start); 259462306a36Sopenharmony_ci e->address_end = e->address_start + PAGE_ALIGN(m->range_length); 259562306a36Sopenharmony_ci e->prot = m->flags >> 1; 259662306a36Sopenharmony_ci 259762306a36Sopenharmony_ci /* 259862306a36Sopenharmony_ci * Treat per-device exclusion ranges as r/w unity-mapped regions 259962306a36Sopenharmony_ci * since some buggy BIOSes might lead to the overwritten exclusion 260062306a36Sopenharmony_ci * range (exclusion_start and exclusion_length members). This 260162306a36Sopenharmony_ci * happens when there are multiple exclusion ranges (IVMD entries) 260262306a36Sopenharmony_ci * defined in ACPI table. 260362306a36Sopenharmony_ci */ 260462306a36Sopenharmony_ci if (m->flags & IVMD_FLAG_EXCL_RANGE) 260562306a36Sopenharmony_ci e->prot = (IVMD_FLAG_IW | IVMD_FLAG_IR) >> 1; 260662306a36Sopenharmony_ci 260762306a36Sopenharmony_ci DUMP_printk("%s devid_start: %04x:%02x:%02x.%x devid_end: " 260862306a36Sopenharmony_ci "%04x:%02x:%02x.%x range_start: %016llx range_end: %016llx" 260962306a36Sopenharmony_ci " flags: %x\n", s, m->pci_seg, 261062306a36Sopenharmony_ci PCI_BUS_NUM(e->devid_start), PCI_SLOT(e->devid_start), 261162306a36Sopenharmony_ci PCI_FUNC(e->devid_start), m->pci_seg, 261262306a36Sopenharmony_ci PCI_BUS_NUM(e->devid_end), 261362306a36Sopenharmony_ci PCI_SLOT(e->devid_end), PCI_FUNC(e->devid_end), 261462306a36Sopenharmony_ci e->address_start, e->address_end, m->flags); 261562306a36Sopenharmony_ci 261662306a36Sopenharmony_ci list_add_tail(&e->list, &pci_seg->unity_map); 261762306a36Sopenharmony_ci 261862306a36Sopenharmony_ci return 0; 261962306a36Sopenharmony_ci} 262062306a36Sopenharmony_ci 262162306a36Sopenharmony_ci/* iterates over all memory definitions we find in the ACPI table */ 262262306a36Sopenharmony_cistatic int __init init_memory_definitions(struct acpi_table_header *table) 262362306a36Sopenharmony_ci{ 262462306a36Sopenharmony_ci u8 *p = (u8 *)table, *end = (u8 *)table; 262562306a36Sopenharmony_ci struct ivmd_header *m; 262662306a36Sopenharmony_ci 262762306a36Sopenharmony_ci end += table->length; 262862306a36Sopenharmony_ci p += IVRS_HEADER_LENGTH; 262962306a36Sopenharmony_ci 263062306a36Sopenharmony_ci while (p < end) { 263162306a36Sopenharmony_ci m = (struct ivmd_header *)p; 263262306a36Sopenharmony_ci if (m->flags & (IVMD_FLAG_UNITY_MAP | IVMD_FLAG_EXCL_RANGE)) 263362306a36Sopenharmony_ci init_unity_map_range(m, table); 263462306a36Sopenharmony_ci 263562306a36Sopenharmony_ci p += m->length; 263662306a36Sopenharmony_ci } 263762306a36Sopenharmony_ci 263862306a36Sopenharmony_ci return 0; 263962306a36Sopenharmony_ci} 264062306a36Sopenharmony_ci 264162306a36Sopenharmony_ci/* 264262306a36Sopenharmony_ci * Init the device table to not allow DMA access for devices 264362306a36Sopenharmony_ci */ 264462306a36Sopenharmony_cistatic void init_device_table_dma(struct amd_iommu_pci_seg *pci_seg) 264562306a36Sopenharmony_ci{ 264662306a36Sopenharmony_ci u32 devid; 264762306a36Sopenharmony_ci struct dev_table_entry *dev_table = pci_seg->dev_table; 264862306a36Sopenharmony_ci 264962306a36Sopenharmony_ci if (dev_table == NULL) 265062306a36Sopenharmony_ci return; 265162306a36Sopenharmony_ci 265262306a36Sopenharmony_ci for (devid = 0; devid <= pci_seg->last_bdf; ++devid) { 265362306a36Sopenharmony_ci __set_dev_entry_bit(dev_table, devid, DEV_ENTRY_VALID); 265462306a36Sopenharmony_ci if (!amd_iommu_snp_en) 265562306a36Sopenharmony_ci __set_dev_entry_bit(dev_table, devid, DEV_ENTRY_TRANSLATION); 265662306a36Sopenharmony_ci } 265762306a36Sopenharmony_ci} 265862306a36Sopenharmony_ci 265962306a36Sopenharmony_cistatic void __init uninit_device_table_dma(struct amd_iommu_pci_seg *pci_seg) 266062306a36Sopenharmony_ci{ 266162306a36Sopenharmony_ci u32 devid; 266262306a36Sopenharmony_ci struct dev_table_entry *dev_table = pci_seg->dev_table; 266362306a36Sopenharmony_ci 266462306a36Sopenharmony_ci if (dev_table == NULL) 266562306a36Sopenharmony_ci return; 266662306a36Sopenharmony_ci 266762306a36Sopenharmony_ci for (devid = 0; devid <= pci_seg->last_bdf; ++devid) { 266862306a36Sopenharmony_ci dev_table[devid].data[0] = 0ULL; 266962306a36Sopenharmony_ci dev_table[devid].data[1] = 0ULL; 267062306a36Sopenharmony_ci } 267162306a36Sopenharmony_ci} 267262306a36Sopenharmony_ci 267362306a36Sopenharmony_cistatic void init_device_table(void) 267462306a36Sopenharmony_ci{ 267562306a36Sopenharmony_ci struct amd_iommu_pci_seg *pci_seg; 267662306a36Sopenharmony_ci u32 devid; 267762306a36Sopenharmony_ci 267862306a36Sopenharmony_ci if (!amd_iommu_irq_remap) 267962306a36Sopenharmony_ci return; 268062306a36Sopenharmony_ci 268162306a36Sopenharmony_ci for_each_pci_segment(pci_seg) { 268262306a36Sopenharmony_ci for (devid = 0; devid <= pci_seg->last_bdf; ++devid) 268362306a36Sopenharmony_ci __set_dev_entry_bit(pci_seg->dev_table, 268462306a36Sopenharmony_ci devid, DEV_ENTRY_IRQ_TBL_EN); 268562306a36Sopenharmony_ci } 268662306a36Sopenharmony_ci} 268762306a36Sopenharmony_ci 268862306a36Sopenharmony_cistatic void iommu_init_flags(struct amd_iommu *iommu) 268962306a36Sopenharmony_ci{ 269062306a36Sopenharmony_ci iommu->acpi_flags & IVHD_FLAG_HT_TUN_EN_MASK ? 269162306a36Sopenharmony_ci iommu_feature_enable(iommu, CONTROL_HT_TUN_EN) : 269262306a36Sopenharmony_ci iommu_feature_disable(iommu, CONTROL_HT_TUN_EN); 269362306a36Sopenharmony_ci 269462306a36Sopenharmony_ci iommu->acpi_flags & IVHD_FLAG_PASSPW_EN_MASK ? 269562306a36Sopenharmony_ci iommu_feature_enable(iommu, CONTROL_PASSPW_EN) : 269662306a36Sopenharmony_ci iommu_feature_disable(iommu, CONTROL_PASSPW_EN); 269762306a36Sopenharmony_ci 269862306a36Sopenharmony_ci iommu->acpi_flags & IVHD_FLAG_RESPASSPW_EN_MASK ? 269962306a36Sopenharmony_ci iommu_feature_enable(iommu, CONTROL_RESPASSPW_EN) : 270062306a36Sopenharmony_ci iommu_feature_disable(iommu, CONTROL_RESPASSPW_EN); 270162306a36Sopenharmony_ci 270262306a36Sopenharmony_ci iommu->acpi_flags & IVHD_FLAG_ISOC_EN_MASK ? 270362306a36Sopenharmony_ci iommu_feature_enable(iommu, CONTROL_ISOC_EN) : 270462306a36Sopenharmony_ci iommu_feature_disable(iommu, CONTROL_ISOC_EN); 270562306a36Sopenharmony_ci 270662306a36Sopenharmony_ci /* 270762306a36Sopenharmony_ci * make IOMMU memory accesses cache coherent 270862306a36Sopenharmony_ci */ 270962306a36Sopenharmony_ci iommu_feature_enable(iommu, CONTROL_COHERENT_EN); 271062306a36Sopenharmony_ci 271162306a36Sopenharmony_ci /* Set IOTLB invalidation timeout to 1s */ 271262306a36Sopenharmony_ci iommu_set_inv_tlb_timeout(iommu, CTRL_INV_TO_1S); 271362306a36Sopenharmony_ci} 271462306a36Sopenharmony_ci 271562306a36Sopenharmony_cistatic void iommu_apply_resume_quirks(struct amd_iommu *iommu) 271662306a36Sopenharmony_ci{ 271762306a36Sopenharmony_ci int i, j; 271862306a36Sopenharmony_ci u32 ioc_feature_control; 271962306a36Sopenharmony_ci struct pci_dev *pdev = iommu->root_pdev; 272062306a36Sopenharmony_ci 272162306a36Sopenharmony_ci /* RD890 BIOSes may not have completely reconfigured the iommu */ 272262306a36Sopenharmony_ci if (!is_rd890_iommu(iommu->dev) || !pdev) 272362306a36Sopenharmony_ci return; 272462306a36Sopenharmony_ci 272562306a36Sopenharmony_ci /* 272662306a36Sopenharmony_ci * First, we need to ensure that the iommu is enabled. This is 272762306a36Sopenharmony_ci * controlled by a register in the northbridge 272862306a36Sopenharmony_ci */ 272962306a36Sopenharmony_ci 273062306a36Sopenharmony_ci /* Select Northbridge indirect register 0x75 and enable writing */ 273162306a36Sopenharmony_ci pci_write_config_dword(pdev, 0x60, 0x75 | (1 << 7)); 273262306a36Sopenharmony_ci pci_read_config_dword(pdev, 0x64, &ioc_feature_control); 273362306a36Sopenharmony_ci 273462306a36Sopenharmony_ci /* Enable the iommu */ 273562306a36Sopenharmony_ci if (!(ioc_feature_control & 0x1)) 273662306a36Sopenharmony_ci pci_write_config_dword(pdev, 0x64, ioc_feature_control | 1); 273762306a36Sopenharmony_ci 273862306a36Sopenharmony_ci /* Restore the iommu BAR */ 273962306a36Sopenharmony_ci pci_write_config_dword(iommu->dev, iommu->cap_ptr + 4, 274062306a36Sopenharmony_ci iommu->stored_addr_lo); 274162306a36Sopenharmony_ci pci_write_config_dword(iommu->dev, iommu->cap_ptr + 8, 274262306a36Sopenharmony_ci iommu->stored_addr_hi); 274362306a36Sopenharmony_ci 274462306a36Sopenharmony_ci /* Restore the l1 indirect regs for each of the 6 l1s */ 274562306a36Sopenharmony_ci for (i = 0; i < 6; i++) 274662306a36Sopenharmony_ci for (j = 0; j < 0x12; j++) 274762306a36Sopenharmony_ci iommu_write_l1(iommu, i, j, iommu->stored_l1[i][j]); 274862306a36Sopenharmony_ci 274962306a36Sopenharmony_ci /* Restore the l2 indirect regs */ 275062306a36Sopenharmony_ci for (i = 0; i < 0x83; i++) 275162306a36Sopenharmony_ci iommu_write_l2(iommu, i, iommu->stored_l2[i]); 275262306a36Sopenharmony_ci 275362306a36Sopenharmony_ci /* Lock PCI setup registers */ 275462306a36Sopenharmony_ci pci_write_config_dword(iommu->dev, iommu->cap_ptr + 4, 275562306a36Sopenharmony_ci iommu->stored_addr_lo | 1); 275662306a36Sopenharmony_ci} 275762306a36Sopenharmony_ci 275862306a36Sopenharmony_cistatic void iommu_enable_ga(struct amd_iommu *iommu) 275962306a36Sopenharmony_ci{ 276062306a36Sopenharmony_ci#ifdef CONFIG_IRQ_REMAP 276162306a36Sopenharmony_ci switch (amd_iommu_guest_ir) { 276262306a36Sopenharmony_ci case AMD_IOMMU_GUEST_IR_VAPIC: 276362306a36Sopenharmony_ci case AMD_IOMMU_GUEST_IR_LEGACY_GA: 276462306a36Sopenharmony_ci iommu_feature_enable(iommu, CONTROL_GA_EN); 276562306a36Sopenharmony_ci iommu->irte_ops = &irte_128_ops; 276662306a36Sopenharmony_ci break; 276762306a36Sopenharmony_ci default: 276862306a36Sopenharmony_ci iommu->irte_ops = &irte_32_ops; 276962306a36Sopenharmony_ci break; 277062306a36Sopenharmony_ci } 277162306a36Sopenharmony_ci#endif 277262306a36Sopenharmony_ci} 277362306a36Sopenharmony_ci 277462306a36Sopenharmony_cistatic void iommu_disable_irtcachedis(struct amd_iommu *iommu) 277562306a36Sopenharmony_ci{ 277662306a36Sopenharmony_ci iommu_feature_disable(iommu, CONTROL_IRTCACHEDIS); 277762306a36Sopenharmony_ci} 277862306a36Sopenharmony_ci 277962306a36Sopenharmony_cistatic void iommu_enable_irtcachedis(struct amd_iommu *iommu) 278062306a36Sopenharmony_ci{ 278162306a36Sopenharmony_ci u64 ctrl; 278262306a36Sopenharmony_ci 278362306a36Sopenharmony_ci if (!amd_iommu_irtcachedis) 278462306a36Sopenharmony_ci return; 278562306a36Sopenharmony_ci 278662306a36Sopenharmony_ci /* 278762306a36Sopenharmony_ci * Note: 278862306a36Sopenharmony_ci * The support for IRTCacheDis feature is dertermined by 278962306a36Sopenharmony_ci * checking if the bit is writable. 279062306a36Sopenharmony_ci */ 279162306a36Sopenharmony_ci iommu_feature_enable(iommu, CONTROL_IRTCACHEDIS); 279262306a36Sopenharmony_ci ctrl = readq(iommu->mmio_base + MMIO_CONTROL_OFFSET); 279362306a36Sopenharmony_ci ctrl &= (1ULL << CONTROL_IRTCACHEDIS); 279462306a36Sopenharmony_ci if (ctrl) 279562306a36Sopenharmony_ci iommu->irtcachedis_enabled = true; 279662306a36Sopenharmony_ci pr_info("iommu%d (%#06x) : IRT cache is %s\n", 279762306a36Sopenharmony_ci iommu->index, iommu->devid, 279862306a36Sopenharmony_ci iommu->irtcachedis_enabled ? "disabled" : "enabled"); 279962306a36Sopenharmony_ci} 280062306a36Sopenharmony_ci 280162306a36Sopenharmony_cistatic void early_enable_iommu(struct amd_iommu *iommu) 280262306a36Sopenharmony_ci{ 280362306a36Sopenharmony_ci iommu_disable(iommu); 280462306a36Sopenharmony_ci iommu_init_flags(iommu); 280562306a36Sopenharmony_ci iommu_set_device_table(iommu); 280662306a36Sopenharmony_ci iommu_enable_command_buffer(iommu); 280762306a36Sopenharmony_ci iommu_enable_event_buffer(iommu); 280862306a36Sopenharmony_ci iommu_set_exclusion_range(iommu); 280962306a36Sopenharmony_ci iommu_enable_ga(iommu); 281062306a36Sopenharmony_ci iommu_enable_xt(iommu); 281162306a36Sopenharmony_ci iommu_enable_irtcachedis(iommu); 281262306a36Sopenharmony_ci iommu_enable(iommu); 281362306a36Sopenharmony_ci iommu_flush_all_caches(iommu); 281462306a36Sopenharmony_ci} 281562306a36Sopenharmony_ci 281662306a36Sopenharmony_ci/* 281762306a36Sopenharmony_ci * This function finally enables all IOMMUs found in the system after 281862306a36Sopenharmony_ci * they have been initialized. 281962306a36Sopenharmony_ci * 282062306a36Sopenharmony_ci * Or if in kdump kernel and IOMMUs are all pre-enabled, try to copy 282162306a36Sopenharmony_ci * the old content of device table entries. Not this case or copy failed, 282262306a36Sopenharmony_ci * just continue as normal kernel does. 282362306a36Sopenharmony_ci */ 282462306a36Sopenharmony_cistatic void early_enable_iommus(void) 282562306a36Sopenharmony_ci{ 282662306a36Sopenharmony_ci struct amd_iommu *iommu; 282762306a36Sopenharmony_ci struct amd_iommu_pci_seg *pci_seg; 282862306a36Sopenharmony_ci 282962306a36Sopenharmony_ci if (!copy_device_table()) { 283062306a36Sopenharmony_ci /* 283162306a36Sopenharmony_ci * If come here because of failure in copying device table from old 283262306a36Sopenharmony_ci * kernel with all IOMMUs enabled, print error message and try to 283362306a36Sopenharmony_ci * free allocated old_dev_tbl_cpy. 283462306a36Sopenharmony_ci */ 283562306a36Sopenharmony_ci if (amd_iommu_pre_enabled) 283662306a36Sopenharmony_ci pr_err("Failed to copy DEV table from previous kernel.\n"); 283762306a36Sopenharmony_ci 283862306a36Sopenharmony_ci for_each_pci_segment(pci_seg) { 283962306a36Sopenharmony_ci if (pci_seg->old_dev_tbl_cpy != NULL) { 284062306a36Sopenharmony_ci free_pages((unsigned long)pci_seg->old_dev_tbl_cpy, 284162306a36Sopenharmony_ci get_order(pci_seg->dev_table_size)); 284262306a36Sopenharmony_ci pci_seg->old_dev_tbl_cpy = NULL; 284362306a36Sopenharmony_ci } 284462306a36Sopenharmony_ci } 284562306a36Sopenharmony_ci 284662306a36Sopenharmony_ci for_each_iommu(iommu) { 284762306a36Sopenharmony_ci clear_translation_pre_enabled(iommu); 284862306a36Sopenharmony_ci early_enable_iommu(iommu); 284962306a36Sopenharmony_ci } 285062306a36Sopenharmony_ci } else { 285162306a36Sopenharmony_ci pr_info("Copied DEV table from previous kernel.\n"); 285262306a36Sopenharmony_ci 285362306a36Sopenharmony_ci for_each_pci_segment(pci_seg) { 285462306a36Sopenharmony_ci free_pages((unsigned long)pci_seg->dev_table, 285562306a36Sopenharmony_ci get_order(pci_seg->dev_table_size)); 285662306a36Sopenharmony_ci pci_seg->dev_table = pci_seg->old_dev_tbl_cpy; 285762306a36Sopenharmony_ci } 285862306a36Sopenharmony_ci 285962306a36Sopenharmony_ci for_each_iommu(iommu) { 286062306a36Sopenharmony_ci iommu_disable_command_buffer(iommu); 286162306a36Sopenharmony_ci iommu_disable_event_buffer(iommu); 286262306a36Sopenharmony_ci iommu_disable_irtcachedis(iommu); 286362306a36Sopenharmony_ci iommu_enable_command_buffer(iommu); 286462306a36Sopenharmony_ci iommu_enable_event_buffer(iommu); 286562306a36Sopenharmony_ci iommu_enable_ga(iommu); 286662306a36Sopenharmony_ci iommu_enable_xt(iommu); 286762306a36Sopenharmony_ci iommu_enable_irtcachedis(iommu); 286862306a36Sopenharmony_ci iommu_set_device_table(iommu); 286962306a36Sopenharmony_ci iommu_flush_all_caches(iommu); 287062306a36Sopenharmony_ci } 287162306a36Sopenharmony_ci } 287262306a36Sopenharmony_ci} 287362306a36Sopenharmony_ci 287462306a36Sopenharmony_cistatic void enable_iommus_v2(void) 287562306a36Sopenharmony_ci{ 287662306a36Sopenharmony_ci struct amd_iommu *iommu; 287762306a36Sopenharmony_ci 287862306a36Sopenharmony_ci for_each_iommu(iommu) { 287962306a36Sopenharmony_ci iommu_enable_ppr_log(iommu); 288062306a36Sopenharmony_ci iommu_enable_gt(iommu); 288162306a36Sopenharmony_ci } 288262306a36Sopenharmony_ci} 288362306a36Sopenharmony_ci 288462306a36Sopenharmony_cistatic void enable_iommus_vapic(void) 288562306a36Sopenharmony_ci{ 288662306a36Sopenharmony_ci#ifdef CONFIG_IRQ_REMAP 288762306a36Sopenharmony_ci u32 status, i; 288862306a36Sopenharmony_ci struct amd_iommu *iommu; 288962306a36Sopenharmony_ci 289062306a36Sopenharmony_ci for_each_iommu(iommu) { 289162306a36Sopenharmony_ci /* 289262306a36Sopenharmony_ci * Disable GALog if already running. It could have been enabled 289362306a36Sopenharmony_ci * in the previous boot before kdump. 289462306a36Sopenharmony_ci */ 289562306a36Sopenharmony_ci status = readl(iommu->mmio_base + MMIO_STATUS_OFFSET); 289662306a36Sopenharmony_ci if (!(status & MMIO_STATUS_GALOG_RUN_MASK)) 289762306a36Sopenharmony_ci continue; 289862306a36Sopenharmony_ci 289962306a36Sopenharmony_ci iommu_feature_disable(iommu, CONTROL_GALOG_EN); 290062306a36Sopenharmony_ci iommu_feature_disable(iommu, CONTROL_GAINT_EN); 290162306a36Sopenharmony_ci 290262306a36Sopenharmony_ci /* 290362306a36Sopenharmony_ci * Need to set and poll check the GALOGRun bit to zero before 290462306a36Sopenharmony_ci * we can set/ modify GA Log registers safely. 290562306a36Sopenharmony_ci */ 290662306a36Sopenharmony_ci for (i = 0; i < LOOP_TIMEOUT; ++i) { 290762306a36Sopenharmony_ci status = readl(iommu->mmio_base + MMIO_STATUS_OFFSET); 290862306a36Sopenharmony_ci if (!(status & MMIO_STATUS_GALOG_RUN_MASK)) 290962306a36Sopenharmony_ci break; 291062306a36Sopenharmony_ci udelay(10); 291162306a36Sopenharmony_ci } 291262306a36Sopenharmony_ci 291362306a36Sopenharmony_ci if (WARN_ON(i >= LOOP_TIMEOUT)) 291462306a36Sopenharmony_ci return; 291562306a36Sopenharmony_ci } 291662306a36Sopenharmony_ci 291762306a36Sopenharmony_ci if (AMD_IOMMU_GUEST_IR_VAPIC(amd_iommu_guest_ir) && 291862306a36Sopenharmony_ci !check_feature_on_all_iommus(FEATURE_GAM_VAPIC)) { 291962306a36Sopenharmony_ci amd_iommu_guest_ir = AMD_IOMMU_GUEST_IR_LEGACY_GA; 292062306a36Sopenharmony_ci return; 292162306a36Sopenharmony_ci } 292262306a36Sopenharmony_ci 292362306a36Sopenharmony_ci if (amd_iommu_snp_en && 292462306a36Sopenharmony_ci !FEATURE_SNPAVICSUP_GAM(amd_iommu_efr2)) { 292562306a36Sopenharmony_ci pr_warn("Force to disable Virtual APIC due to SNP\n"); 292662306a36Sopenharmony_ci amd_iommu_guest_ir = AMD_IOMMU_GUEST_IR_LEGACY_GA; 292762306a36Sopenharmony_ci return; 292862306a36Sopenharmony_ci } 292962306a36Sopenharmony_ci 293062306a36Sopenharmony_ci /* Enabling GAM and SNPAVIC support */ 293162306a36Sopenharmony_ci for_each_iommu(iommu) { 293262306a36Sopenharmony_ci if (iommu_init_ga_log(iommu) || 293362306a36Sopenharmony_ci iommu_ga_log_enable(iommu)) 293462306a36Sopenharmony_ci return; 293562306a36Sopenharmony_ci 293662306a36Sopenharmony_ci iommu_feature_enable(iommu, CONTROL_GAM_EN); 293762306a36Sopenharmony_ci if (amd_iommu_snp_en) 293862306a36Sopenharmony_ci iommu_feature_enable(iommu, CONTROL_SNPAVIC_EN); 293962306a36Sopenharmony_ci } 294062306a36Sopenharmony_ci 294162306a36Sopenharmony_ci amd_iommu_irq_ops.capability |= (1 << IRQ_POSTING_CAP); 294262306a36Sopenharmony_ci pr_info("Virtual APIC enabled\n"); 294362306a36Sopenharmony_ci#endif 294462306a36Sopenharmony_ci} 294562306a36Sopenharmony_ci 294662306a36Sopenharmony_cistatic void enable_iommus(void) 294762306a36Sopenharmony_ci{ 294862306a36Sopenharmony_ci early_enable_iommus(); 294962306a36Sopenharmony_ci} 295062306a36Sopenharmony_ci 295162306a36Sopenharmony_cistatic void disable_iommus(void) 295262306a36Sopenharmony_ci{ 295362306a36Sopenharmony_ci struct amd_iommu *iommu; 295462306a36Sopenharmony_ci 295562306a36Sopenharmony_ci for_each_iommu(iommu) 295662306a36Sopenharmony_ci iommu_disable(iommu); 295762306a36Sopenharmony_ci 295862306a36Sopenharmony_ci#ifdef CONFIG_IRQ_REMAP 295962306a36Sopenharmony_ci if (AMD_IOMMU_GUEST_IR_VAPIC(amd_iommu_guest_ir)) 296062306a36Sopenharmony_ci amd_iommu_irq_ops.capability &= ~(1 << IRQ_POSTING_CAP); 296162306a36Sopenharmony_ci#endif 296262306a36Sopenharmony_ci} 296362306a36Sopenharmony_ci 296462306a36Sopenharmony_ci/* 296562306a36Sopenharmony_ci * Suspend/Resume support 296662306a36Sopenharmony_ci * disable suspend until real resume implemented 296762306a36Sopenharmony_ci */ 296862306a36Sopenharmony_ci 296962306a36Sopenharmony_cistatic void amd_iommu_resume(void) 297062306a36Sopenharmony_ci{ 297162306a36Sopenharmony_ci struct amd_iommu *iommu; 297262306a36Sopenharmony_ci 297362306a36Sopenharmony_ci for_each_iommu(iommu) 297462306a36Sopenharmony_ci iommu_apply_resume_quirks(iommu); 297562306a36Sopenharmony_ci 297662306a36Sopenharmony_ci /* re-load the hardware */ 297762306a36Sopenharmony_ci enable_iommus(); 297862306a36Sopenharmony_ci 297962306a36Sopenharmony_ci amd_iommu_enable_interrupts(); 298062306a36Sopenharmony_ci} 298162306a36Sopenharmony_ci 298262306a36Sopenharmony_cistatic int amd_iommu_suspend(void) 298362306a36Sopenharmony_ci{ 298462306a36Sopenharmony_ci /* disable IOMMUs to go out of the way for BIOS */ 298562306a36Sopenharmony_ci disable_iommus(); 298662306a36Sopenharmony_ci 298762306a36Sopenharmony_ci return 0; 298862306a36Sopenharmony_ci} 298962306a36Sopenharmony_ci 299062306a36Sopenharmony_cistatic struct syscore_ops amd_iommu_syscore_ops = { 299162306a36Sopenharmony_ci .suspend = amd_iommu_suspend, 299262306a36Sopenharmony_ci .resume = amd_iommu_resume, 299362306a36Sopenharmony_ci}; 299462306a36Sopenharmony_ci 299562306a36Sopenharmony_cistatic void __init free_iommu_resources(void) 299662306a36Sopenharmony_ci{ 299762306a36Sopenharmony_ci kmem_cache_destroy(amd_iommu_irq_cache); 299862306a36Sopenharmony_ci amd_iommu_irq_cache = NULL; 299962306a36Sopenharmony_ci 300062306a36Sopenharmony_ci free_iommu_all(); 300162306a36Sopenharmony_ci free_pci_segments(); 300262306a36Sopenharmony_ci} 300362306a36Sopenharmony_ci 300462306a36Sopenharmony_ci/* SB IOAPIC is always on this device in AMD systems */ 300562306a36Sopenharmony_ci#define IOAPIC_SB_DEVID ((0x00 << 8) | PCI_DEVFN(0x14, 0)) 300662306a36Sopenharmony_ci 300762306a36Sopenharmony_cistatic bool __init check_ioapic_information(void) 300862306a36Sopenharmony_ci{ 300962306a36Sopenharmony_ci const char *fw_bug = FW_BUG; 301062306a36Sopenharmony_ci bool ret, has_sb_ioapic; 301162306a36Sopenharmony_ci int idx; 301262306a36Sopenharmony_ci 301362306a36Sopenharmony_ci has_sb_ioapic = false; 301462306a36Sopenharmony_ci ret = false; 301562306a36Sopenharmony_ci 301662306a36Sopenharmony_ci /* 301762306a36Sopenharmony_ci * If we have map overrides on the kernel command line the 301862306a36Sopenharmony_ci * messages in this function might not describe firmware bugs 301962306a36Sopenharmony_ci * anymore - so be careful 302062306a36Sopenharmony_ci */ 302162306a36Sopenharmony_ci if (cmdline_maps) 302262306a36Sopenharmony_ci fw_bug = ""; 302362306a36Sopenharmony_ci 302462306a36Sopenharmony_ci for (idx = 0; idx < nr_ioapics; idx++) { 302562306a36Sopenharmony_ci int devid, id = mpc_ioapic_id(idx); 302662306a36Sopenharmony_ci 302762306a36Sopenharmony_ci devid = get_ioapic_devid(id); 302862306a36Sopenharmony_ci if (devid < 0) { 302962306a36Sopenharmony_ci pr_err("%s: IOAPIC[%d] not in IVRS table\n", 303062306a36Sopenharmony_ci fw_bug, id); 303162306a36Sopenharmony_ci ret = false; 303262306a36Sopenharmony_ci } else if (devid == IOAPIC_SB_DEVID) { 303362306a36Sopenharmony_ci has_sb_ioapic = true; 303462306a36Sopenharmony_ci ret = true; 303562306a36Sopenharmony_ci } 303662306a36Sopenharmony_ci } 303762306a36Sopenharmony_ci 303862306a36Sopenharmony_ci if (!has_sb_ioapic) { 303962306a36Sopenharmony_ci /* 304062306a36Sopenharmony_ci * We expect the SB IOAPIC to be listed in the IVRS 304162306a36Sopenharmony_ci * table. The system timer is connected to the SB IOAPIC 304262306a36Sopenharmony_ci * and if we don't have it in the list the system will 304362306a36Sopenharmony_ci * panic at boot time. This situation usually happens 304462306a36Sopenharmony_ci * when the BIOS is buggy and provides us the wrong 304562306a36Sopenharmony_ci * device id for the IOAPIC in the system. 304662306a36Sopenharmony_ci */ 304762306a36Sopenharmony_ci pr_err("%s: No southbridge IOAPIC found\n", fw_bug); 304862306a36Sopenharmony_ci } 304962306a36Sopenharmony_ci 305062306a36Sopenharmony_ci if (!ret) 305162306a36Sopenharmony_ci pr_err("Disabling interrupt remapping\n"); 305262306a36Sopenharmony_ci 305362306a36Sopenharmony_ci return ret; 305462306a36Sopenharmony_ci} 305562306a36Sopenharmony_ci 305662306a36Sopenharmony_cistatic void __init free_dma_resources(void) 305762306a36Sopenharmony_ci{ 305862306a36Sopenharmony_ci free_pages((unsigned long)amd_iommu_pd_alloc_bitmap, 305962306a36Sopenharmony_ci get_order(MAX_DOMAIN_ID/8)); 306062306a36Sopenharmony_ci amd_iommu_pd_alloc_bitmap = NULL; 306162306a36Sopenharmony_ci 306262306a36Sopenharmony_ci free_unity_maps(); 306362306a36Sopenharmony_ci} 306462306a36Sopenharmony_ci 306562306a36Sopenharmony_cistatic void __init ivinfo_init(void *ivrs) 306662306a36Sopenharmony_ci{ 306762306a36Sopenharmony_ci amd_iommu_ivinfo = *((u32 *)(ivrs + IOMMU_IVINFO_OFFSET)); 306862306a36Sopenharmony_ci} 306962306a36Sopenharmony_ci 307062306a36Sopenharmony_ci/* 307162306a36Sopenharmony_ci * This is the hardware init function for AMD IOMMU in the system. 307262306a36Sopenharmony_ci * This function is called either from amd_iommu_init or from the interrupt 307362306a36Sopenharmony_ci * remapping setup code. 307462306a36Sopenharmony_ci * 307562306a36Sopenharmony_ci * This function basically parses the ACPI table for AMD IOMMU (IVRS) 307662306a36Sopenharmony_ci * four times: 307762306a36Sopenharmony_ci * 307862306a36Sopenharmony_ci * 1 pass) Discover the most comprehensive IVHD type to use. 307962306a36Sopenharmony_ci * 308062306a36Sopenharmony_ci * 2 pass) Find the highest PCI device id the driver has to handle. 308162306a36Sopenharmony_ci * Upon this information the size of the data structures is 308262306a36Sopenharmony_ci * determined that needs to be allocated. 308362306a36Sopenharmony_ci * 308462306a36Sopenharmony_ci * 3 pass) Initialize the data structures just allocated with the 308562306a36Sopenharmony_ci * information in the ACPI table about available AMD IOMMUs 308662306a36Sopenharmony_ci * in the system. It also maps the PCI devices in the 308762306a36Sopenharmony_ci * system to specific IOMMUs 308862306a36Sopenharmony_ci * 308962306a36Sopenharmony_ci * 4 pass) After the basic data structures are allocated and 309062306a36Sopenharmony_ci * initialized we update them with information about memory 309162306a36Sopenharmony_ci * remapping requirements parsed out of the ACPI table in 309262306a36Sopenharmony_ci * this last pass. 309362306a36Sopenharmony_ci * 309462306a36Sopenharmony_ci * After everything is set up the IOMMUs are enabled and the necessary 309562306a36Sopenharmony_ci * hotplug and suspend notifiers are registered. 309662306a36Sopenharmony_ci */ 309762306a36Sopenharmony_cistatic int __init early_amd_iommu_init(void) 309862306a36Sopenharmony_ci{ 309962306a36Sopenharmony_ci struct acpi_table_header *ivrs_base; 310062306a36Sopenharmony_ci int remap_cache_sz, ret; 310162306a36Sopenharmony_ci acpi_status status; 310262306a36Sopenharmony_ci 310362306a36Sopenharmony_ci if (!amd_iommu_detected) 310462306a36Sopenharmony_ci return -ENODEV; 310562306a36Sopenharmony_ci 310662306a36Sopenharmony_ci status = acpi_get_table("IVRS", 0, &ivrs_base); 310762306a36Sopenharmony_ci if (status == AE_NOT_FOUND) 310862306a36Sopenharmony_ci return -ENODEV; 310962306a36Sopenharmony_ci else if (ACPI_FAILURE(status)) { 311062306a36Sopenharmony_ci const char *err = acpi_format_exception(status); 311162306a36Sopenharmony_ci pr_err("IVRS table error: %s\n", err); 311262306a36Sopenharmony_ci return -EINVAL; 311362306a36Sopenharmony_ci } 311462306a36Sopenharmony_ci 311562306a36Sopenharmony_ci /* 311662306a36Sopenharmony_ci * Validate checksum here so we don't need to do it when 311762306a36Sopenharmony_ci * we actually parse the table 311862306a36Sopenharmony_ci */ 311962306a36Sopenharmony_ci ret = check_ivrs_checksum(ivrs_base); 312062306a36Sopenharmony_ci if (ret) 312162306a36Sopenharmony_ci goto out; 312262306a36Sopenharmony_ci 312362306a36Sopenharmony_ci ivinfo_init(ivrs_base); 312462306a36Sopenharmony_ci 312562306a36Sopenharmony_ci amd_iommu_target_ivhd_type = get_highest_supported_ivhd_type(ivrs_base); 312662306a36Sopenharmony_ci DUMP_printk("Using IVHD type %#x\n", amd_iommu_target_ivhd_type); 312762306a36Sopenharmony_ci 312862306a36Sopenharmony_ci /* Device table - directly used by all IOMMUs */ 312962306a36Sopenharmony_ci ret = -ENOMEM; 313062306a36Sopenharmony_ci 313162306a36Sopenharmony_ci amd_iommu_pd_alloc_bitmap = (void *)__get_free_pages( 313262306a36Sopenharmony_ci GFP_KERNEL | __GFP_ZERO, 313362306a36Sopenharmony_ci get_order(MAX_DOMAIN_ID/8)); 313462306a36Sopenharmony_ci if (amd_iommu_pd_alloc_bitmap == NULL) 313562306a36Sopenharmony_ci goto out; 313662306a36Sopenharmony_ci 313762306a36Sopenharmony_ci /* 313862306a36Sopenharmony_ci * never allocate domain 0 because its used as the non-allocated and 313962306a36Sopenharmony_ci * error value placeholder 314062306a36Sopenharmony_ci */ 314162306a36Sopenharmony_ci __set_bit(0, amd_iommu_pd_alloc_bitmap); 314262306a36Sopenharmony_ci 314362306a36Sopenharmony_ci /* 314462306a36Sopenharmony_ci * now the data structures are allocated and basically initialized 314562306a36Sopenharmony_ci * start the real acpi table scan 314662306a36Sopenharmony_ci */ 314762306a36Sopenharmony_ci ret = init_iommu_all(ivrs_base); 314862306a36Sopenharmony_ci if (ret) 314962306a36Sopenharmony_ci goto out; 315062306a36Sopenharmony_ci 315162306a36Sopenharmony_ci /* 5 level guest page table */ 315262306a36Sopenharmony_ci if (cpu_feature_enabled(X86_FEATURE_LA57) && 315362306a36Sopenharmony_ci check_feature_gpt_level() == GUEST_PGTABLE_5_LEVEL) 315462306a36Sopenharmony_ci amd_iommu_gpt_level = PAGE_MODE_5_LEVEL; 315562306a36Sopenharmony_ci 315662306a36Sopenharmony_ci /* Disable any previously enabled IOMMUs */ 315762306a36Sopenharmony_ci if (!is_kdump_kernel() || amd_iommu_disabled) 315862306a36Sopenharmony_ci disable_iommus(); 315962306a36Sopenharmony_ci 316062306a36Sopenharmony_ci if (amd_iommu_irq_remap) 316162306a36Sopenharmony_ci amd_iommu_irq_remap = check_ioapic_information(); 316262306a36Sopenharmony_ci 316362306a36Sopenharmony_ci if (amd_iommu_irq_remap) { 316462306a36Sopenharmony_ci struct amd_iommu_pci_seg *pci_seg; 316562306a36Sopenharmony_ci /* 316662306a36Sopenharmony_ci * Interrupt remapping enabled, create kmem_cache for the 316762306a36Sopenharmony_ci * remapping tables. 316862306a36Sopenharmony_ci */ 316962306a36Sopenharmony_ci ret = -ENOMEM; 317062306a36Sopenharmony_ci if (!AMD_IOMMU_GUEST_IR_GA(amd_iommu_guest_ir)) 317162306a36Sopenharmony_ci remap_cache_sz = MAX_IRQS_PER_TABLE * sizeof(u32); 317262306a36Sopenharmony_ci else 317362306a36Sopenharmony_ci remap_cache_sz = MAX_IRQS_PER_TABLE * (sizeof(u64) * 2); 317462306a36Sopenharmony_ci amd_iommu_irq_cache = kmem_cache_create("irq_remap_cache", 317562306a36Sopenharmony_ci remap_cache_sz, 317662306a36Sopenharmony_ci DTE_INTTAB_ALIGNMENT, 317762306a36Sopenharmony_ci 0, NULL); 317862306a36Sopenharmony_ci if (!amd_iommu_irq_cache) 317962306a36Sopenharmony_ci goto out; 318062306a36Sopenharmony_ci 318162306a36Sopenharmony_ci for_each_pci_segment(pci_seg) { 318262306a36Sopenharmony_ci if (alloc_irq_lookup_table(pci_seg)) 318362306a36Sopenharmony_ci goto out; 318462306a36Sopenharmony_ci } 318562306a36Sopenharmony_ci } 318662306a36Sopenharmony_ci 318762306a36Sopenharmony_ci ret = init_memory_definitions(ivrs_base); 318862306a36Sopenharmony_ci if (ret) 318962306a36Sopenharmony_ci goto out; 319062306a36Sopenharmony_ci 319162306a36Sopenharmony_ci /* init the device table */ 319262306a36Sopenharmony_ci init_device_table(); 319362306a36Sopenharmony_ci 319462306a36Sopenharmony_ciout: 319562306a36Sopenharmony_ci /* Don't leak any ACPI memory */ 319662306a36Sopenharmony_ci acpi_put_table(ivrs_base); 319762306a36Sopenharmony_ci 319862306a36Sopenharmony_ci return ret; 319962306a36Sopenharmony_ci} 320062306a36Sopenharmony_ci 320162306a36Sopenharmony_cistatic int amd_iommu_enable_interrupts(void) 320262306a36Sopenharmony_ci{ 320362306a36Sopenharmony_ci struct amd_iommu *iommu; 320462306a36Sopenharmony_ci int ret = 0; 320562306a36Sopenharmony_ci 320662306a36Sopenharmony_ci for_each_iommu(iommu) { 320762306a36Sopenharmony_ci ret = iommu_init_irq(iommu); 320862306a36Sopenharmony_ci if (ret) 320962306a36Sopenharmony_ci goto out; 321062306a36Sopenharmony_ci } 321162306a36Sopenharmony_ci 321262306a36Sopenharmony_ci /* 321362306a36Sopenharmony_ci * Interrupt handler is ready to process interrupts. Enable 321462306a36Sopenharmony_ci * PPR and GA log interrupt for all IOMMUs. 321562306a36Sopenharmony_ci */ 321662306a36Sopenharmony_ci enable_iommus_vapic(); 321762306a36Sopenharmony_ci enable_iommus_v2(); 321862306a36Sopenharmony_ci 321962306a36Sopenharmony_ciout: 322062306a36Sopenharmony_ci return ret; 322162306a36Sopenharmony_ci} 322262306a36Sopenharmony_ci 322362306a36Sopenharmony_cistatic bool __init detect_ivrs(void) 322462306a36Sopenharmony_ci{ 322562306a36Sopenharmony_ci struct acpi_table_header *ivrs_base; 322662306a36Sopenharmony_ci acpi_status status; 322762306a36Sopenharmony_ci int i; 322862306a36Sopenharmony_ci 322962306a36Sopenharmony_ci status = acpi_get_table("IVRS", 0, &ivrs_base); 323062306a36Sopenharmony_ci if (status == AE_NOT_FOUND) 323162306a36Sopenharmony_ci return false; 323262306a36Sopenharmony_ci else if (ACPI_FAILURE(status)) { 323362306a36Sopenharmony_ci const char *err = acpi_format_exception(status); 323462306a36Sopenharmony_ci pr_err("IVRS table error: %s\n", err); 323562306a36Sopenharmony_ci return false; 323662306a36Sopenharmony_ci } 323762306a36Sopenharmony_ci 323862306a36Sopenharmony_ci acpi_put_table(ivrs_base); 323962306a36Sopenharmony_ci 324062306a36Sopenharmony_ci if (amd_iommu_force_enable) 324162306a36Sopenharmony_ci goto out; 324262306a36Sopenharmony_ci 324362306a36Sopenharmony_ci /* Don't use IOMMU if there is Stoney Ridge graphics */ 324462306a36Sopenharmony_ci for (i = 0; i < 32; i++) { 324562306a36Sopenharmony_ci u32 pci_id; 324662306a36Sopenharmony_ci 324762306a36Sopenharmony_ci pci_id = read_pci_config(0, i, 0, 0); 324862306a36Sopenharmony_ci if ((pci_id & 0xffff) == 0x1002 && (pci_id >> 16) == 0x98e4) { 324962306a36Sopenharmony_ci pr_info("Disable IOMMU on Stoney Ridge\n"); 325062306a36Sopenharmony_ci return false; 325162306a36Sopenharmony_ci } 325262306a36Sopenharmony_ci } 325362306a36Sopenharmony_ci 325462306a36Sopenharmony_ciout: 325562306a36Sopenharmony_ci /* Make sure ACS will be enabled during PCI probe */ 325662306a36Sopenharmony_ci pci_request_acs(); 325762306a36Sopenharmony_ci 325862306a36Sopenharmony_ci return true; 325962306a36Sopenharmony_ci} 326062306a36Sopenharmony_ci 326162306a36Sopenharmony_ci/**************************************************************************** 326262306a36Sopenharmony_ci * 326362306a36Sopenharmony_ci * AMD IOMMU Initialization State Machine 326462306a36Sopenharmony_ci * 326562306a36Sopenharmony_ci ****************************************************************************/ 326662306a36Sopenharmony_ci 326762306a36Sopenharmony_cistatic int __init state_next(void) 326862306a36Sopenharmony_ci{ 326962306a36Sopenharmony_ci int ret = 0; 327062306a36Sopenharmony_ci 327162306a36Sopenharmony_ci switch (init_state) { 327262306a36Sopenharmony_ci case IOMMU_START_STATE: 327362306a36Sopenharmony_ci if (!detect_ivrs()) { 327462306a36Sopenharmony_ci init_state = IOMMU_NOT_FOUND; 327562306a36Sopenharmony_ci ret = -ENODEV; 327662306a36Sopenharmony_ci } else { 327762306a36Sopenharmony_ci init_state = IOMMU_IVRS_DETECTED; 327862306a36Sopenharmony_ci } 327962306a36Sopenharmony_ci break; 328062306a36Sopenharmony_ci case IOMMU_IVRS_DETECTED: 328162306a36Sopenharmony_ci if (amd_iommu_disabled) { 328262306a36Sopenharmony_ci init_state = IOMMU_CMDLINE_DISABLED; 328362306a36Sopenharmony_ci ret = -EINVAL; 328462306a36Sopenharmony_ci } else { 328562306a36Sopenharmony_ci ret = early_amd_iommu_init(); 328662306a36Sopenharmony_ci init_state = ret ? IOMMU_INIT_ERROR : IOMMU_ACPI_FINISHED; 328762306a36Sopenharmony_ci } 328862306a36Sopenharmony_ci break; 328962306a36Sopenharmony_ci case IOMMU_ACPI_FINISHED: 329062306a36Sopenharmony_ci early_enable_iommus(); 329162306a36Sopenharmony_ci x86_platform.iommu_shutdown = disable_iommus; 329262306a36Sopenharmony_ci init_state = IOMMU_ENABLED; 329362306a36Sopenharmony_ci break; 329462306a36Sopenharmony_ci case IOMMU_ENABLED: 329562306a36Sopenharmony_ci register_syscore_ops(&amd_iommu_syscore_ops); 329662306a36Sopenharmony_ci ret = amd_iommu_init_pci(); 329762306a36Sopenharmony_ci init_state = ret ? IOMMU_INIT_ERROR : IOMMU_PCI_INIT; 329862306a36Sopenharmony_ci break; 329962306a36Sopenharmony_ci case IOMMU_PCI_INIT: 330062306a36Sopenharmony_ci ret = amd_iommu_enable_interrupts(); 330162306a36Sopenharmony_ci init_state = ret ? IOMMU_INIT_ERROR : IOMMU_INTERRUPTS_EN; 330262306a36Sopenharmony_ci break; 330362306a36Sopenharmony_ci case IOMMU_INTERRUPTS_EN: 330462306a36Sopenharmony_ci init_state = IOMMU_INITIALIZED; 330562306a36Sopenharmony_ci break; 330662306a36Sopenharmony_ci case IOMMU_INITIALIZED: 330762306a36Sopenharmony_ci /* Nothing to do */ 330862306a36Sopenharmony_ci break; 330962306a36Sopenharmony_ci case IOMMU_NOT_FOUND: 331062306a36Sopenharmony_ci case IOMMU_INIT_ERROR: 331162306a36Sopenharmony_ci case IOMMU_CMDLINE_DISABLED: 331262306a36Sopenharmony_ci /* Error states => do nothing */ 331362306a36Sopenharmony_ci ret = -EINVAL; 331462306a36Sopenharmony_ci break; 331562306a36Sopenharmony_ci default: 331662306a36Sopenharmony_ci /* Unknown state */ 331762306a36Sopenharmony_ci BUG(); 331862306a36Sopenharmony_ci } 331962306a36Sopenharmony_ci 332062306a36Sopenharmony_ci if (ret) { 332162306a36Sopenharmony_ci free_dma_resources(); 332262306a36Sopenharmony_ci if (!irq_remapping_enabled) { 332362306a36Sopenharmony_ci disable_iommus(); 332462306a36Sopenharmony_ci free_iommu_resources(); 332562306a36Sopenharmony_ci } else { 332662306a36Sopenharmony_ci struct amd_iommu *iommu; 332762306a36Sopenharmony_ci struct amd_iommu_pci_seg *pci_seg; 332862306a36Sopenharmony_ci 332962306a36Sopenharmony_ci for_each_pci_segment(pci_seg) 333062306a36Sopenharmony_ci uninit_device_table_dma(pci_seg); 333162306a36Sopenharmony_ci 333262306a36Sopenharmony_ci for_each_iommu(iommu) 333362306a36Sopenharmony_ci iommu_flush_all_caches(iommu); 333462306a36Sopenharmony_ci } 333562306a36Sopenharmony_ci } 333662306a36Sopenharmony_ci return ret; 333762306a36Sopenharmony_ci} 333862306a36Sopenharmony_ci 333962306a36Sopenharmony_cistatic int __init iommu_go_to_state(enum iommu_init_state state) 334062306a36Sopenharmony_ci{ 334162306a36Sopenharmony_ci int ret = -EINVAL; 334262306a36Sopenharmony_ci 334362306a36Sopenharmony_ci while (init_state != state) { 334462306a36Sopenharmony_ci if (init_state == IOMMU_NOT_FOUND || 334562306a36Sopenharmony_ci init_state == IOMMU_INIT_ERROR || 334662306a36Sopenharmony_ci init_state == IOMMU_CMDLINE_DISABLED) 334762306a36Sopenharmony_ci break; 334862306a36Sopenharmony_ci ret = state_next(); 334962306a36Sopenharmony_ci } 335062306a36Sopenharmony_ci 335162306a36Sopenharmony_ci return ret; 335262306a36Sopenharmony_ci} 335362306a36Sopenharmony_ci 335462306a36Sopenharmony_ci#ifdef CONFIG_IRQ_REMAP 335562306a36Sopenharmony_ciint __init amd_iommu_prepare(void) 335662306a36Sopenharmony_ci{ 335762306a36Sopenharmony_ci int ret; 335862306a36Sopenharmony_ci 335962306a36Sopenharmony_ci amd_iommu_irq_remap = true; 336062306a36Sopenharmony_ci 336162306a36Sopenharmony_ci ret = iommu_go_to_state(IOMMU_ACPI_FINISHED); 336262306a36Sopenharmony_ci if (ret) { 336362306a36Sopenharmony_ci amd_iommu_irq_remap = false; 336462306a36Sopenharmony_ci return ret; 336562306a36Sopenharmony_ci } 336662306a36Sopenharmony_ci 336762306a36Sopenharmony_ci return amd_iommu_irq_remap ? 0 : -ENODEV; 336862306a36Sopenharmony_ci} 336962306a36Sopenharmony_ci 337062306a36Sopenharmony_ciint __init amd_iommu_enable(void) 337162306a36Sopenharmony_ci{ 337262306a36Sopenharmony_ci int ret; 337362306a36Sopenharmony_ci 337462306a36Sopenharmony_ci ret = iommu_go_to_state(IOMMU_ENABLED); 337562306a36Sopenharmony_ci if (ret) 337662306a36Sopenharmony_ci return ret; 337762306a36Sopenharmony_ci 337862306a36Sopenharmony_ci irq_remapping_enabled = 1; 337962306a36Sopenharmony_ci return amd_iommu_xt_mode; 338062306a36Sopenharmony_ci} 338162306a36Sopenharmony_ci 338262306a36Sopenharmony_civoid amd_iommu_disable(void) 338362306a36Sopenharmony_ci{ 338462306a36Sopenharmony_ci amd_iommu_suspend(); 338562306a36Sopenharmony_ci} 338662306a36Sopenharmony_ci 338762306a36Sopenharmony_ciint amd_iommu_reenable(int mode) 338862306a36Sopenharmony_ci{ 338962306a36Sopenharmony_ci amd_iommu_resume(); 339062306a36Sopenharmony_ci 339162306a36Sopenharmony_ci return 0; 339262306a36Sopenharmony_ci} 339362306a36Sopenharmony_ci 339462306a36Sopenharmony_ciint __init amd_iommu_enable_faulting(void) 339562306a36Sopenharmony_ci{ 339662306a36Sopenharmony_ci /* We enable MSI later when PCI is initialized */ 339762306a36Sopenharmony_ci return 0; 339862306a36Sopenharmony_ci} 339962306a36Sopenharmony_ci#endif 340062306a36Sopenharmony_ci 340162306a36Sopenharmony_ci/* 340262306a36Sopenharmony_ci * This is the core init function for AMD IOMMU hardware in the system. 340362306a36Sopenharmony_ci * This function is called from the generic x86 DMA layer initialization 340462306a36Sopenharmony_ci * code. 340562306a36Sopenharmony_ci */ 340662306a36Sopenharmony_cistatic int __init amd_iommu_init(void) 340762306a36Sopenharmony_ci{ 340862306a36Sopenharmony_ci struct amd_iommu *iommu; 340962306a36Sopenharmony_ci int ret; 341062306a36Sopenharmony_ci 341162306a36Sopenharmony_ci ret = iommu_go_to_state(IOMMU_INITIALIZED); 341262306a36Sopenharmony_ci#ifdef CONFIG_GART_IOMMU 341362306a36Sopenharmony_ci if (ret && list_empty(&amd_iommu_list)) { 341462306a36Sopenharmony_ci /* 341562306a36Sopenharmony_ci * We failed to initialize the AMD IOMMU - try fallback 341662306a36Sopenharmony_ci * to GART if possible. 341762306a36Sopenharmony_ci */ 341862306a36Sopenharmony_ci gart_iommu_init(); 341962306a36Sopenharmony_ci } 342062306a36Sopenharmony_ci#endif 342162306a36Sopenharmony_ci 342262306a36Sopenharmony_ci for_each_iommu(iommu) 342362306a36Sopenharmony_ci amd_iommu_debugfs_setup(iommu); 342462306a36Sopenharmony_ci 342562306a36Sopenharmony_ci return ret; 342662306a36Sopenharmony_ci} 342762306a36Sopenharmony_ci 342862306a36Sopenharmony_cistatic bool amd_iommu_sme_check(void) 342962306a36Sopenharmony_ci{ 343062306a36Sopenharmony_ci if (!cc_platform_has(CC_ATTR_HOST_MEM_ENCRYPT) || 343162306a36Sopenharmony_ci (boot_cpu_data.x86 != 0x17)) 343262306a36Sopenharmony_ci return true; 343362306a36Sopenharmony_ci 343462306a36Sopenharmony_ci /* For Fam17h, a specific level of support is required */ 343562306a36Sopenharmony_ci if (boot_cpu_data.microcode >= 0x08001205) 343662306a36Sopenharmony_ci return true; 343762306a36Sopenharmony_ci 343862306a36Sopenharmony_ci if ((boot_cpu_data.microcode >= 0x08001126) && 343962306a36Sopenharmony_ci (boot_cpu_data.microcode <= 0x080011ff)) 344062306a36Sopenharmony_ci return true; 344162306a36Sopenharmony_ci 344262306a36Sopenharmony_ci pr_notice("IOMMU not currently supported when SME is active\n"); 344362306a36Sopenharmony_ci 344462306a36Sopenharmony_ci return false; 344562306a36Sopenharmony_ci} 344662306a36Sopenharmony_ci 344762306a36Sopenharmony_ci/**************************************************************************** 344862306a36Sopenharmony_ci * 344962306a36Sopenharmony_ci * Early detect code. This code runs at IOMMU detection time in the DMA 345062306a36Sopenharmony_ci * layer. It just looks if there is an IVRS ACPI table to detect AMD 345162306a36Sopenharmony_ci * IOMMUs 345262306a36Sopenharmony_ci * 345362306a36Sopenharmony_ci ****************************************************************************/ 345462306a36Sopenharmony_ciint __init amd_iommu_detect(void) 345562306a36Sopenharmony_ci{ 345662306a36Sopenharmony_ci int ret; 345762306a36Sopenharmony_ci 345862306a36Sopenharmony_ci if (no_iommu || (iommu_detected && !gart_iommu_aperture)) 345962306a36Sopenharmony_ci return -ENODEV; 346062306a36Sopenharmony_ci 346162306a36Sopenharmony_ci if (!amd_iommu_sme_check()) 346262306a36Sopenharmony_ci return -ENODEV; 346362306a36Sopenharmony_ci 346462306a36Sopenharmony_ci ret = iommu_go_to_state(IOMMU_IVRS_DETECTED); 346562306a36Sopenharmony_ci if (ret) 346662306a36Sopenharmony_ci return ret; 346762306a36Sopenharmony_ci 346862306a36Sopenharmony_ci amd_iommu_detected = true; 346962306a36Sopenharmony_ci iommu_detected = 1; 347062306a36Sopenharmony_ci x86_init.iommu.iommu_init = amd_iommu_init; 347162306a36Sopenharmony_ci 347262306a36Sopenharmony_ci return 1; 347362306a36Sopenharmony_ci} 347462306a36Sopenharmony_ci 347562306a36Sopenharmony_ci/**************************************************************************** 347662306a36Sopenharmony_ci * 347762306a36Sopenharmony_ci * Parsing functions for the AMD IOMMU specific kernel command line 347862306a36Sopenharmony_ci * options. 347962306a36Sopenharmony_ci * 348062306a36Sopenharmony_ci ****************************************************************************/ 348162306a36Sopenharmony_ci 348262306a36Sopenharmony_cistatic int __init parse_amd_iommu_dump(char *str) 348362306a36Sopenharmony_ci{ 348462306a36Sopenharmony_ci amd_iommu_dump = true; 348562306a36Sopenharmony_ci 348662306a36Sopenharmony_ci return 1; 348762306a36Sopenharmony_ci} 348862306a36Sopenharmony_ci 348962306a36Sopenharmony_cistatic int __init parse_amd_iommu_intr(char *str) 349062306a36Sopenharmony_ci{ 349162306a36Sopenharmony_ci for (; *str; ++str) { 349262306a36Sopenharmony_ci if (strncmp(str, "legacy", 6) == 0) { 349362306a36Sopenharmony_ci amd_iommu_guest_ir = AMD_IOMMU_GUEST_IR_LEGACY_GA; 349462306a36Sopenharmony_ci break; 349562306a36Sopenharmony_ci } 349662306a36Sopenharmony_ci if (strncmp(str, "vapic", 5) == 0) { 349762306a36Sopenharmony_ci amd_iommu_guest_ir = AMD_IOMMU_GUEST_IR_VAPIC; 349862306a36Sopenharmony_ci break; 349962306a36Sopenharmony_ci } 350062306a36Sopenharmony_ci } 350162306a36Sopenharmony_ci return 1; 350262306a36Sopenharmony_ci} 350362306a36Sopenharmony_ci 350462306a36Sopenharmony_cistatic int __init parse_amd_iommu_options(char *str) 350562306a36Sopenharmony_ci{ 350662306a36Sopenharmony_ci if (!str) 350762306a36Sopenharmony_ci return -EINVAL; 350862306a36Sopenharmony_ci 350962306a36Sopenharmony_ci while (*str) { 351062306a36Sopenharmony_ci if (strncmp(str, "fullflush", 9) == 0) { 351162306a36Sopenharmony_ci pr_warn("amd_iommu=fullflush deprecated; use iommu.strict=1 instead\n"); 351262306a36Sopenharmony_ci iommu_set_dma_strict(); 351362306a36Sopenharmony_ci } else if (strncmp(str, "force_enable", 12) == 0) { 351462306a36Sopenharmony_ci amd_iommu_force_enable = true; 351562306a36Sopenharmony_ci } else if (strncmp(str, "off", 3) == 0) { 351662306a36Sopenharmony_ci amd_iommu_disabled = true; 351762306a36Sopenharmony_ci } else if (strncmp(str, "force_isolation", 15) == 0) { 351862306a36Sopenharmony_ci amd_iommu_force_isolation = true; 351962306a36Sopenharmony_ci } else if (strncmp(str, "pgtbl_v1", 8) == 0) { 352062306a36Sopenharmony_ci amd_iommu_pgtable = AMD_IOMMU_V1; 352162306a36Sopenharmony_ci } else if (strncmp(str, "pgtbl_v2", 8) == 0) { 352262306a36Sopenharmony_ci amd_iommu_pgtable = AMD_IOMMU_V2; 352362306a36Sopenharmony_ci } else if (strncmp(str, "irtcachedis", 11) == 0) { 352462306a36Sopenharmony_ci amd_iommu_irtcachedis = true; 352562306a36Sopenharmony_ci } else { 352662306a36Sopenharmony_ci pr_notice("Unknown option - '%s'\n", str); 352762306a36Sopenharmony_ci } 352862306a36Sopenharmony_ci 352962306a36Sopenharmony_ci str += strcspn(str, ","); 353062306a36Sopenharmony_ci while (*str == ',') 353162306a36Sopenharmony_ci str++; 353262306a36Sopenharmony_ci } 353362306a36Sopenharmony_ci 353462306a36Sopenharmony_ci return 1; 353562306a36Sopenharmony_ci} 353662306a36Sopenharmony_ci 353762306a36Sopenharmony_cistatic int __init parse_ivrs_ioapic(char *str) 353862306a36Sopenharmony_ci{ 353962306a36Sopenharmony_ci u32 seg = 0, bus, dev, fn; 354062306a36Sopenharmony_ci int id, i; 354162306a36Sopenharmony_ci u32 devid; 354262306a36Sopenharmony_ci 354362306a36Sopenharmony_ci if (sscanf(str, "=%d@%x:%x.%x", &id, &bus, &dev, &fn) == 4 || 354462306a36Sopenharmony_ci sscanf(str, "=%d@%x:%x:%x.%x", &id, &seg, &bus, &dev, &fn) == 5) 354562306a36Sopenharmony_ci goto found; 354662306a36Sopenharmony_ci 354762306a36Sopenharmony_ci if (sscanf(str, "[%d]=%x:%x.%x", &id, &bus, &dev, &fn) == 4 || 354862306a36Sopenharmony_ci sscanf(str, "[%d]=%x:%x:%x.%x", &id, &seg, &bus, &dev, &fn) == 5) { 354962306a36Sopenharmony_ci pr_warn("ivrs_ioapic%s option format deprecated; use ivrs_ioapic=%d@%04x:%02x:%02x.%d instead\n", 355062306a36Sopenharmony_ci str, id, seg, bus, dev, fn); 355162306a36Sopenharmony_ci goto found; 355262306a36Sopenharmony_ci } 355362306a36Sopenharmony_ci 355462306a36Sopenharmony_ci pr_err("Invalid command line: ivrs_ioapic%s\n", str); 355562306a36Sopenharmony_ci return 1; 355662306a36Sopenharmony_ci 355762306a36Sopenharmony_cifound: 355862306a36Sopenharmony_ci if (early_ioapic_map_size == EARLY_MAP_SIZE) { 355962306a36Sopenharmony_ci pr_err("Early IOAPIC map overflow - ignoring ivrs_ioapic%s\n", 356062306a36Sopenharmony_ci str); 356162306a36Sopenharmony_ci return 1; 356262306a36Sopenharmony_ci } 356362306a36Sopenharmony_ci 356462306a36Sopenharmony_ci devid = IVRS_GET_SBDF_ID(seg, bus, dev, fn); 356562306a36Sopenharmony_ci 356662306a36Sopenharmony_ci cmdline_maps = true; 356762306a36Sopenharmony_ci i = early_ioapic_map_size++; 356862306a36Sopenharmony_ci early_ioapic_map[i].id = id; 356962306a36Sopenharmony_ci early_ioapic_map[i].devid = devid; 357062306a36Sopenharmony_ci early_ioapic_map[i].cmd_line = true; 357162306a36Sopenharmony_ci 357262306a36Sopenharmony_ci return 1; 357362306a36Sopenharmony_ci} 357462306a36Sopenharmony_ci 357562306a36Sopenharmony_cistatic int __init parse_ivrs_hpet(char *str) 357662306a36Sopenharmony_ci{ 357762306a36Sopenharmony_ci u32 seg = 0, bus, dev, fn; 357862306a36Sopenharmony_ci int id, i; 357962306a36Sopenharmony_ci u32 devid; 358062306a36Sopenharmony_ci 358162306a36Sopenharmony_ci if (sscanf(str, "=%d@%x:%x.%x", &id, &bus, &dev, &fn) == 4 || 358262306a36Sopenharmony_ci sscanf(str, "=%d@%x:%x:%x.%x", &id, &seg, &bus, &dev, &fn) == 5) 358362306a36Sopenharmony_ci goto found; 358462306a36Sopenharmony_ci 358562306a36Sopenharmony_ci if (sscanf(str, "[%d]=%x:%x.%x", &id, &bus, &dev, &fn) == 4 || 358662306a36Sopenharmony_ci sscanf(str, "[%d]=%x:%x:%x.%x", &id, &seg, &bus, &dev, &fn) == 5) { 358762306a36Sopenharmony_ci pr_warn("ivrs_hpet%s option format deprecated; use ivrs_hpet=%d@%04x:%02x:%02x.%d instead\n", 358862306a36Sopenharmony_ci str, id, seg, bus, dev, fn); 358962306a36Sopenharmony_ci goto found; 359062306a36Sopenharmony_ci } 359162306a36Sopenharmony_ci 359262306a36Sopenharmony_ci pr_err("Invalid command line: ivrs_hpet%s\n", str); 359362306a36Sopenharmony_ci return 1; 359462306a36Sopenharmony_ci 359562306a36Sopenharmony_cifound: 359662306a36Sopenharmony_ci if (early_hpet_map_size == EARLY_MAP_SIZE) { 359762306a36Sopenharmony_ci pr_err("Early HPET map overflow - ignoring ivrs_hpet%s\n", 359862306a36Sopenharmony_ci str); 359962306a36Sopenharmony_ci return 1; 360062306a36Sopenharmony_ci } 360162306a36Sopenharmony_ci 360262306a36Sopenharmony_ci devid = IVRS_GET_SBDF_ID(seg, bus, dev, fn); 360362306a36Sopenharmony_ci 360462306a36Sopenharmony_ci cmdline_maps = true; 360562306a36Sopenharmony_ci i = early_hpet_map_size++; 360662306a36Sopenharmony_ci early_hpet_map[i].id = id; 360762306a36Sopenharmony_ci early_hpet_map[i].devid = devid; 360862306a36Sopenharmony_ci early_hpet_map[i].cmd_line = true; 360962306a36Sopenharmony_ci 361062306a36Sopenharmony_ci return 1; 361162306a36Sopenharmony_ci} 361262306a36Sopenharmony_ci 361362306a36Sopenharmony_ci#define ACPIID_LEN (ACPIHID_UID_LEN + ACPIHID_HID_LEN) 361462306a36Sopenharmony_ci 361562306a36Sopenharmony_cistatic int __init parse_ivrs_acpihid(char *str) 361662306a36Sopenharmony_ci{ 361762306a36Sopenharmony_ci u32 seg = 0, bus, dev, fn; 361862306a36Sopenharmony_ci char *hid, *uid, *p, *addr; 361962306a36Sopenharmony_ci char acpiid[ACPIID_LEN] = {0}; 362062306a36Sopenharmony_ci int i; 362162306a36Sopenharmony_ci 362262306a36Sopenharmony_ci addr = strchr(str, '@'); 362362306a36Sopenharmony_ci if (!addr) { 362462306a36Sopenharmony_ci addr = strchr(str, '='); 362562306a36Sopenharmony_ci if (!addr) 362662306a36Sopenharmony_ci goto not_found; 362762306a36Sopenharmony_ci 362862306a36Sopenharmony_ci ++addr; 362962306a36Sopenharmony_ci 363062306a36Sopenharmony_ci if (strlen(addr) > ACPIID_LEN) 363162306a36Sopenharmony_ci goto not_found; 363262306a36Sopenharmony_ci 363362306a36Sopenharmony_ci if (sscanf(str, "[%x:%x.%x]=%s", &bus, &dev, &fn, acpiid) == 4 || 363462306a36Sopenharmony_ci sscanf(str, "[%x:%x:%x.%x]=%s", &seg, &bus, &dev, &fn, acpiid) == 5) { 363562306a36Sopenharmony_ci pr_warn("ivrs_acpihid%s option format deprecated; use ivrs_acpihid=%s@%04x:%02x:%02x.%d instead\n", 363662306a36Sopenharmony_ci str, acpiid, seg, bus, dev, fn); 363762306a36Sopenharmony_ci goto found; 363862306a36Sopenharmony_ci } 363962306a36Sopenharmony_ci goto not_found; 364062306a36Sopenharmony_ci } 364162306a36Sopenharmony_ci 364262306a36Sopenharmony_ci /* We have the '@', make it the terminator to get just the acpiid */ 364362306a36Sopenharmony_ci *addr++ = 0; 364462306a36Sopenharmony_ci 364562306a36Sopenharmony_ci if (strlen(str) > ACPIID_LEN + 1) 364662306a36Sopenharmony_ci goto not_found; 364762306a36Sopenharmony_ci 364862306a36Sopenharmony_ci if (sscanf(str, "=%s", acpiid) != 1) 364962306a36Sopenharmony_ci goto not_found; 365062306a36Sopenharmony_ci 365162306a36Sopenharmony_ci if (sscanf(addr, "%x:%x.%x", &bus, &dev, &fn) == 3 || 365262306a36Sopenharmony_ci sscanf(addr, "%x:%x:%x.%x", &seg, &bus, &dev, &fn) == 4) 365362306a36Sopenharmony_ci goto found; 365462306a36Sopenharmony_ci 365562306a36Sopenharmony_cinot_found: 365662306a36Sopenharmony_ci pr_err("Invalid command line: ivrs_acpihid%s\n", str); 365762306a36Sopenharmony_ci return 1; 365862306a36Sopenharmony_ci 365962306a36Sopenharmony_cifound: 366062306a36Sopenharmony_ci p = acpiid; 366162306a36Sopenharmony_ci hid = strsep(&p, ":"); 366262306a36Sopenharmony_ci uid = p; 366362306a36Sopenharmony_ci 366462306a36Sopenharmony_ci if (!hid || !(*hid) || !uid) { 366562306a36Sopenharmony_ci pr_err("Invalid command line: hid or uid\n"); 366662306a36Sopenharmony_ci return 1; 366762306a36Sopenharmony_ci } 366862306a36Sopenharmony_ci 366962306a36Sopenharmony_ci /* 367062306a36Sopenharmony_ci * Ignore leading zeroes after ':', so e.g., AMDI0095:00 367162306a36Sopenharmony_ci * will match AMDI0095:0 in the second strcmp in acpi_dev_hid_uid_match 367262306a36Sopenharmony_ci */ 367362306a36Sopenharmony_ci while (*uid == '0' && *(uid + 1)) 367462306a36Sopenharmony_ci uid++; 367562306a36Sopenharmony_ci 367662306a36Sopenharmony_ci i = early_acpihid_map_size++; 367762306a36Sopenharmony_ci memcpy(early_acpihid_map[i].hid, hid, strlen(hid)); 367862306a36Sopenharmony_ci memcpy(early_acpihid_map[i].uid, uid, strlen(uid)); 367962306a36Sopenharmony_ci early_acpihid_map[i].devid = IVRS_GET_SBDF_ID(seg, bus, dev, fn); 368062306a36Sopenharmony_ci early_acpihid_map[i].cmd_line = true; 368162306a36Sopenharmony_ci 368262306a36Sopenharmony_ci return 1; 368362306a36Sopenharmony_ci} 368462306a36Sopenharmony_ci 368562306a36Sopenharmony_ci__setup("amd_iommu_dump", parse_amd_iommu_dump); 368662306a36Sopenharmony_ci__setup("amd_iommu=", parse_amd_iommu_options); 368762306a36Sopenharmony_ci__setup("amd_iommu_intr=", parse_amd_iommu_intr); 368862306a36Sopenharmony_ci__setup("ivrs_ioapic", parse_ivrs_ioapic); 368962306a36Sopenharmony_ci__setup("ivrs_hpet", parse_ivrs_hpet); 369062306a36Sopenharmony_ci__setup("ivrs_acpihid", parse_ivrs_acpihid); 369162306a36Sopenharmony_ci 369262306a36Sopenharmony_cibool amd_iommu_v2_supported(void) 369362306a36Sopenharmony_ci{ 369462306a36Sopenharmony_ci /* CPU page table size should match IOMMU guest page table size */ 369562306a36Sopenharmony_ci if (cpu_feature_enabled(X86_FEATURE_LA57) && 369662306a36Sopenharmony_ci amd_iommu_gpt_level != PAGE_MODE_5_LEVEL) 369762306a36Sopenharmony_ci return false; 369862306a36Sopenharmony_ci 369962306a36Sopenharmony_ci /* 370062306a36Sopenharmony_ci * Since DTE[Mode]=0 is prohibited on SNP-enabled system 370162306a36Sopenharmony_ci * (i.e. EFR[SNPSup]=1), IOMMUv2 page table cannot be used without 370262306a36Sopenharmony_ci * setting up IOMMUv1 page table. 370362306a36Sopenharmony_ci */ 370462306a36Sopenharmony_ci return amd_iommu_v2_present && !amd_iommu_snp_en; 370562306a36Sopenharmony_ci} 370662306a36Sopenharmony_ciEXPORT_SYMBOL(amd_iommu_v2_supported); 370762306a36Sopenharmony_ci 370862306a36Sopenharmony_cistruct amd_iommu *get_amd_iommu(unsigned int idx) 370962306a36Sopenharmony_ci{ 371062306a36Sopenharmony_ci unsigned int i = 0; 371162306a36Sopenharmony_ci struct amd_iommu *iommu; 371262306a36Sopenharmony_ci 371362306a36Sopenharmony_ci for_each_iommu(iommu) 371462306a36Sopenharmony_ci if (i++ == idx) 371562306a36Sopenharmony_ci return iommu; 371662306a36Sopenharmony_ci return NULL; 371762306a36Sopenharmony_ci} 371862306a36Sopenharmony_ci 371962306a36Sopenharmony_ci/**************************************************************************** 372062306a36Sopenharmony_ci * 372162306a36Sopenharmony_ci * IOMMU EFR Performance Counter support functionality. This code allows 372262306a36Sopenharmony_ci * access to the IOMMU PC functionality. 372362306a36Sopenharmony_ci * 372462306a36Sopenharmony_ci ****************************************************************************/ 372562306a36Sopenharmony_ci 372662306a36Sopenharmony_ciu8 amd_iommu_pc_get_max_banks(unsigned int idx) 372762306a36Sopenharmony_ci{ 372862306a36Sopenharmony_ci struct amd_iommu *iommu = get_amd_iommu(idx); 372962306a36Sopenharmony_ci 373062306a36Sopenharmony_ci if (iommu) 373162306a36Sopenharmony_ci return iommu->max_banks; 373262306a36Sopenharmony_ci 373362306a36Sopenharmony_ci return 0; 373462306a36Sopenharmony_ci} 373562306a36Sopenharmony_ciEXPORT_SYMBOL(amd_iommu_pc_get_max_banks); 373662306a36Sopenharmony_ci 373762306a36Sopenharmony_cibool amd_iommu_pc_supported(void) 373862306a36Sopenharmony_ci{ 373962306a36Sopenharmony_ci return amd_iommu_pc_present; 374062306a36Sopenharmony_ci} 374162306a36Sopenharmony_ciEXPORT_SYMBOL(amd_iommu_pc_supported); 374262306a36Sopenharmony_ci 374362306a36Sopenharmony_ciu8 amd_iommu_pc_get_max_counters(unsigned int idx) 374462306a36Sopenharmony_ci{ 374562306a36Sopenharmony_ci struct amd_iommu *iommu = get_amd_iommu(idx); 374662306a36Sopenharmony_ci 374762306a36Sopenharmony_ci if (iommu) 374862306a36Sopenharmony_ci return iommu->max_counters; 374962306a36Sopenharmony_ci 375062306a36Sopenharmony_ci return 0; 375162306a36Sopenharmony_ci} 375262306a36Sopenharmony_ciEXPORT_SYMBOL(amd_iommu_pc_get_max_counters); 375362306a36Sopenharmony_ci 375462306a36Sopenharmony_cistatic int iommu_pc_get_set_reg(struct amd_iommu *iommu, u8 bank, u8 cntr, 375562306a36Sopenharmony_ci u8 fxn, u64 *value, bool is_write) 375662306a36Sopenharmony_ci{ 375762306a36Sopenharmony_ci u32 offset; 375862306a36Sopenharmony_ci u32 max_offset_lim; 375962306a36Sopenharmony_ci 376062306a36Sopenharmony_ci /* Make sure the IOMMU PC resource is available */ 376162306a36Sopenharmony_ci if (!amd_iommu_pc_present) 376262306a36Sopenharmony_ci return -ENODEV; 376362306a36Sopenharmony_ci 376462306a36Sopenharmony_ci /* Check for valid iommu and pc register indexing */ 376562306a36Sopenharmony_ci if (WARN_ON(!iommu || (fxn > 0x28) || (fxn & 7))) 376662306a36Sopenharmony_ci return -ENODEV; 376762306a36Sopenharmony_ci 376862306a36Sopenharmony_ci offset = (u32)(((0x40 | bank) << 12) | (cntr << 8) | fxn); 376962306a36Sopenharmony_ci 377062306a36Sopenharmony_ci /* Limit the offset to the hw defined mmio region aperture */ 377162306a36Sopenharmony_ci max_offset_lim = (u32)(((0x40 | iommu->max_banks) << 12) | 377262306a36Sopenharmony_ci (iommu->max_counters << 8) | 0x28); 377362306a36Sopenharmony_ci if ((offset < MMIO_CNTR_REG_OFFSET) || 377462306a36Sopenharmony_ci (offset > max_offset_lim)) 377562306a36Sopenharmony_ci return -EINVAL; 377662306a36Sopenharmony_ci 377762306a36Sopenharmony_ci if (is_write) { 377862306a36Sopenharmony_ci u64 val = *value & GENMASK_ULL(47, 0); 377962306a36Sopenharmony_ci 378062306a36Sopenharmony_ci writel((u32)val, iommu->mmio_base + offset); 378162306a36Sopenharmony_ci writel((val >> 32), iommu->mmio_base + offset + 4); 378262306a36Sopenharmony_ci } else { 378362306a36Sopenharmony_ci *value = readl(iommu->mmio_base + offset + 4); 378462306a36Sopenharmony_ci *value <<= 32; 378562306a36Sopenharmony_ci *value |= readl(iommu->mmio_base + offset); 378662306a36Sopenharmony_ci *value &= GENMASK_ULL(47, 0); 378762306a36Sopenharmony_ci } 378862306a36Sopenharmony_ci 378962306a36Sopenharmony_ci return 0; 379062306a36Sopenharmony_ci} 379162306a36Sopenharmony_ci 379262306a36Sopenharmony_ciint amd_iommu_pc_get_reg(struct amd_iommu *iommu, u8 bank, u8 cntr, u8 fxn, u64 *value) 379362306a36Sopenharmony_ci{ 379462306a36Sopenharmony_ci if (!iommu) 379562306a36Sopenharmony_ci return -EINVAL; 379662306a36Sopenharmony_ci 379762306a36Sopenharmony_ci return iommu_pc_get_set_reg(iommu, bank, cntr, fxn, value, false); 379862306a36Sopenharmony_ci} 379962306a36Sopenharmony_ci 380062306a36Sopenharmony_ciint amd_iommu_pc_set_reg(struct amd_iommu *iommu, u8 bank, u8 cntr, u8 fxn, u64 *value) 380162306a36Sopenharmony_ci{ 380262306a36Sopenharmony_ci if (!iommu) 380362306a36Sopenharmony_ci return -EINVAL; 380462306a36Sopenharmony_ci 380562306a36Sopenharmony_ci return iommu_pc_get_set_reg(iommu, bank, cntr, fxn, value, true); 380662306a36Sopenharmony_ci} 380762306a36Sopenharmony_ci 380862306a36Sopenharmony_ci#ifdef CONFIG_AMD_MEM_ENCRYPT 380962306a36Sopenharmony_ciint amd_iommu_snp_enable(void) 381062306a36Sopenharmony_ci{ 381162306a36Sopenharmony_ci /* 381262306a36Sopenharmony_ci * The SNP support requires that IOMMU must be enabled, and is 381362306a36Sopenharmony_ci * not configured in the passthrough mode. 381462306a36Sopenharmony_ci */ 381562306a36Sopenharmony_ci if (no_iommu || iommu_default_passthrough()) { 381662306a36Sopenharmony_ci pr_err("SNP: IOMMU is disabled or configured in passthrough mode, SNP cannot be supported"); 381762306a36Sopenharmony_ci return -EINVAL; 381862306a36Sopenharmony_ci } 381962306a36Sopenharmony_ci 382062306a36Sopenharmony_ci /* 382162306a36Sopenharmony_ci * Prevent enabling SNP after IOMMU_ENABLED state because this process 382262306a36Sopenharmony_ci * affect how IOMMU driver sets up data structures and configures 382362306a36Sopenharmony_ci * IOMMU hardware. 382462306a36Sopenharmony_ci */ 382562306a36Sopenharmony_ci if (init_state > IOMMU_ENABLED) { 382662306a36Sopenharmony_ci pr_err("SNP: Too late to enable SNP for IOMMU.\n"); 382762306a36Sopenharmony_ci return -EINVAL; 382862306a36Sopenharmony_ci } 382962306a36Sopenharmony_ci 383062306a36Sopenharmony_ci amd_iommu_snp_en = check_feature_on_all_iommus(FEATURE_SNP); 383162306a36Sopenharmony_ci if (!amd_iommu_snp_en) 383262306a36Sopenharmony_ci return -EINVAL; 383362306a36Sopenharmony_ci 383462306a36Sopenharmony_ci pr_info("SNP enabled\n"); 383562306a36Sopenharmony_ci 383662306a36Sopenharmony_ci /* Enforce IOMMU v1 pagetable when SNP is enabled. */ 383762306a36Sopenharmony_ci if (amd_iommu_pgtable != AMD_IOMMU_V1) { 383862306a36Sopenharmony_ci pr_warn("Force to using AMD IOMMU v1 page table due to SNP\n"); 383962306a36Sopenharmony_ci amd_iommu_pgtable = AMD_IOMMU_V1; 384062306a36Sopenharmony_ci } 384162306a36Sopenharmony_ci 384262306a36Sopenharmony_ci return 0; 384362306a36Sopenharmony_ci} 384462306a36Sopenharmony_ci#endif 3845