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