提交 49d11527 编写于 作者: J Joerg Roedel

Merge branches 'iommu/fixes', 'arm/mediatek', 'arm/smmu', 'arm/exynos',...

Merge branches 'iommu/fixes', 'arm/mediatek', 'arm/smmu', 'arm/exynos', 'unisoc', 'x86/vt-d', 'x86/amd' and 'core' into next
...@@ -1869,13 +1869,6 @@ ...@@ -1869,13 +1869,6 @@
bypassed by not enabling DMAR with this option. In bypassed by not enabling DMAR with this option. In
this case, gfx device will use physical address for this case, gfx device will use physical address for
DMA. DMA.
forcedac [X86-64]
With this option iommu will not optimize to look
for io virtual address below 32-bit forcing dual
address cycle on pci bus for cards supporting greater
than 32-bit addressing. The default is to look
for translation below 32-bit and if not available
then look in the higher range.
strict [Default Off] strict [Default Off]
With this option on every unmap_single operation will With this option on every unmap_single operation will
result in a hardware IOTLB flush operation as opposed result in a hardware IOTLB flush operation as opposed
...@@ -1964,6 +1957,14 @@ ...@@ -1964,6 +1957,14 @@
nobypass [PPC/POWERNV] nobypass [PPC/POWERNV]
Disable IOMMU bypass, using IOMMU for PCI devices. Disable IOMMU bypass, using IOMMU for PCI devices.
iommu.forcedac= [ARM64, X86] Control IOVA allocation for PCI devices.
Format: { "0" | "1" }
0 - Try to allocate a 32-bit DMA address first, before
falling back to the full range if needed.
1 - Allocate directly from the full usable range,
forcing Dual Address Cycle for PCI cards supporting
greater than 32-bit addressing.
iommu.strict= [ARM64] Configure TLB invalidation behaviour iommu.strict= [ARM64] Configure TLB invalidation behaviour
Format: { "0" | "1" } Format: { "0" | "1" }
0 - Lazy mode. 0 - Lazy mode.
......
...@@ -34,6 +34,7 @@ properties: ...@@ -34,6 +34,7 @@ properties:
items: items:
- enum: - enum:
- qcom,sc7180-smmu-500 - qcom,sc7180-smmu-500
- qcom,sc7280-smmu-500
- qcom,sc8180x-smmu-500 - qcom,sc8180x-smmu-500
- qcom,sdm845-smmu-500 - qcom,sdm845-smmu-500
- qcom,sm8150-smmu-500 - qcom,sm8150-smmu-500
......
# SPDX-License-Identifier: (GPL-2.0-only OR BSD-2-Clause)
# Copyright 2020 Unisoc Inc.
%YAML 1.2
---
$id: http://devicetree.org/schemas/iommu/sprd,iommu.yaml#
$schema: http://devicetree.org/meta-schemas/core.yaml#
title: Unisoc IOMMU and Multi-media MMU
maintainers:
- Chunyan Zhang <zhang.lyra@gmail.com>
properties:
compatible:
enum:
- sprd,iommu-v1
"#iommu-cells":
const: 0
description:
Unisoc IOMMUs are all single-master IOMMU devices, therefore no
additional information needs to associate with its master device.
Please refer to the generic bindings document for more details,
Documentation/devicetree/bindings/iommu/iommu.txt
reg:
maxItems: 1
clocks:
description:
Reference to a gate clock phandle, since access to some of IOMMUs are
controlled by gate clock, but this is not required.
required:
- compatible
- reg
- "#iommu-cells"
additionalProperties: false
examples:
- |
iommu_disp: iommu@63000800 {
compatible = "sprd,iommu-v1";
reg = <0x63000800 0x80>;
#iommu-cells = <0>;
};
- |
iommu_jpg: iommu@62300300 {
compatible = "sprd,iommu-v1";
reg = <0x62300300 0x80>;
#iommu-cells = <0>;
clocks = <&mm_gate 1>;
};
...
...@@ -7,6 +7,8 @@ ...@@ -7,6 +7,8 @@
#ifndef __FSL_PAMU_STASH_H #ifndef __FSL_PAMU_STASH_H
#define __FSL_PAMU_STASH_H #define __FSL_PAMU_STASH_H
struct iommu_domain;
/* cache stash targets */ /* cache stash targets */
enum pamu_stash_target { enum pamu_stash_target {
PAMU_ATTR_CACHE_L1 = 1, PAMU_ATTR_CACHE_L1 = 1,
...@@ -14,14 +16,6 @@ enum pamu_stash_target { ...@@ -14,14 +16,6 @@ enum pamu_stash_target {
PAMU_ATTR_CACHE_L3, PAMU_ATTR_CACHE_L3,
}; };
/* int fsl_pamu_configure_l1_stash(struct iommu_domain *domain, u32 cpu);
* This attribute allows configuring stashig specific parameters
* in the PAMU hardware.
*/
struct pamu_stash_attribute {
u32 cpu; /* cpu number */
u32 cache; /* cache to stash to: L1,L2,L3 */
};
#endif /* __FSL_PAMU_STASH_H */ #endif /* __FSL_PAMU_STASH_H */
...@@ -14,6 +14,7 @@ ...@@ -14,6 +14,7 @@
#include <linux/init.h> #include <linux/init.h>
#include <linux/cpumask.h> #include <linux/cpumask.h>
#include <linux/slab.h> #include <linux/slab.h>
#include <linux/amd-iommu.h>
#include "../perf_event.h" #include "../perf_event.h"
#include "iommu.h" #include "iommu.h"
......
...@@ -21,23 +21,4 @@ ...@@ -21,23 +21,4 @@
#define PC_MAX_SPEC_BNKS 64 #define PC_MAX_SPEC_BNKS 64
#define PC_MAX_SPEC_CNTRS 16 #define PC_MAX_SPEC_CNTRS 16
struct amd_iommu;
/* amd_iommu_init.c external support functions */
extern int amd_iommu_get_num_iommus(void);
extern bool amd_iommu_pc_supported(void);
extern u8 amd_iommu_pc_get_max_banks(unsigned int idx);
extern u8 amd_iommu_pc_get_max_counters(unsigned int idx);
extern int amd_iommu_pc_set_reg(struct amd_iommu *iommu, u8 bank, u8 cntr,
u8 fxn, u64 *value);
extern int amd_iommu_pc_get_reg(struct amd_iommu *iommu, u8 bank, u8 cntr,
u8 fxn, u64 *value);
extern struct amd_iommu *get_amd_iommu(int idx);
#endif /*_PERF_EVENT_AMD_IOMMU_H_*/ #endif /*_PERF_EVENT_AMD_IOMMU_H_*/
...@@ -968,15 +968,16 @@ static int iort_pci_iommu_init(struct pci_dev *pdev, u16 alias, void *data) ...@@ -968,15 +968,16 @@ static int iort_pci_iommu_init(struct pci_dev *pdev, u16 alias, void *data)
static void iort_named_component_init(struct device *dev, static void iort_named_component_init(struct device *dev,
struct acpi_iort_node *node) struct acpi_iort_node *node)
{ {
struct property_entry props[2] = {};
struct acpi_iort_named_component *nc; struct acpi_iort_named_component *nc;
struct iommu_fwspec *fwspec = dev_iommu_fwspec_get(dev);
if (!fwspec)
return;
nc = (struct acpi_iort_named_component *)node->node_data; nc = (struct acpi_iort_named_component *)node->node_data;
fwspec->num_pasid_bits = FIELD_GET(ACPI_IORT_NC_PASID_BITS, props[0] = PROPERTY_ENTRY_U32("pasid-num-bits",
nc->node_flags); FIELD_GET(ACPI_IORT_NC_PASID_BITS,
nc->node_flags));
if (device_add_properties(dev, props))
dev_warn(dev, "Could not add device properties\n");
} }
static int iort_nc_iommu_map(struct device *dev, struct acpi_iort_node *node) static int iort_nc_iommu_map(struct device *dev, struct acpi_iort_node *node)
......
...@@ -329,10 +329,6 @@ int kfd_iommu_resume(struct kfd_dev *kfd) ...@@ -329,10 +329,6 @@ int kfd_iommu_resume(struct kfd_dev *kfd)
return 0; return 0;
} }
extern bool amd_iommu_pc_supported(void);
extern u8 amd_iommu_pc_get_max_banks(u16 devid);
extern u8 amd_iommu_pc_get_max_counters(u16 devid);
/** kfd_iommu_add_perf_counters - Add IOMMU performance counters to topology /** kfd_iommu_add_perf_counters - Add IOMMU performance counters to topology
*/ */
int kfd_iommu_add_perf_counters(struct kfd_topology_device *kdev) int kfd_iommu_add_perf_counters(struct kfd_topology_device *kdev)
......
...@@ -188,10 +188,7 @@ int adreno_zap_shader_load(struct msm_gpu *gpu, u32 pasid) ...@@ -188,10 +188,7 @@ int adreno_zap_shader_load(struct msm_gpu *gpu, u32 pasid)
void adreno_set_llc_attributes(struct iommu_domain *iommu) void adreno_set_llc_attributes(struct iommu_domain *iommu)
{ {
struct io_pgtable_domain_attr pgtbl_cfg; iommu_set_pgtable_quirks(iommu, IO_PGTABLE_QUIRK_ARM_OUTER_WBWA);
pgtbl_cfg.quirks = IO_PGTABLE_QUIRK_ARM_OUTER_WBWA;
iommu_domain_set_attr(iommu, DOMAIN_ATTR_IO_PGTABLE_CFG, &pgtbl_cfg);
} }
struct msm_gem_address_space * struct msm_gem_address_space *
......
...@@ -349,7 +349,7 @@ config S390_AP_IOMMU ...@@ -349,7 +349,7 @@ config S390_AP_IOMMU
is not implemented as it is not necessary for VFIO. is not implemented as it is not necessary for VFIO.
config MTK_IOMMU config MTK_IOMMU
bool "MTK IOMMU Support" tristate "MediaTek IOMMU Support"
depends on ARCH_MEDIATEK || COMPILE_TEST depends on ARCH_MEDIATEK || COMPILE_TEST
select ARM_DMA_USE_IOMMU select ARM_DMA_USE_IOMMU
select IOMMU_API select IOMMU_API
...@@ -364,7 +364,7 @@ config MTK_IOMMU ...@@ -364,7 +364,7 @@ config MTK_IOMMU
If unsure, say N here. If unsure, say N here.
config MTK_IOMMU_V1 config MTK_IOMMU_V1
bool "MTK IOMMU Version 1 (M4U gen1) Support" tristate "MediaTek IOMMU Version 1 (M4U gen1) Support"
depends on ARM depends on ARM
depends on ARCH_MEDIATEK || COMPILE_TEST depends on ARCH_MEDIATEK || COMPILE_TEST
select ARM_DMA_USE_IOMMU select ARM_DMA_USE_IOMMU
...@@ -408,4 +408,16 @@ config VIRTIO_IOMMU ...@@ -408,4 +408,16 @@ config VIRTIO_IOMMU
Say Y here if you intend to run this kernel as a guest. Say Y here if you intend to run this kernel as a guest.
config SPRD_IOMMU
tristate "Unisoc IOMMU Support"
depends on ARCH_SPRD || COMPILE_TEST
select IOMMU_API
help
Support for IOMMU on Unisoc's SoCs, this IOMMU can be used by
Unisoc's multimedia devices, such as display, Image codec(jpeg)
and a few signal processors, including VSP(video), GSP(graphic),
ISP(image), and CPP(camera pixel processor), etc.
Say Y here if you want to use the multimedia devices listed above.
endif # IOMMU_SUPPORT endif # IOMMU_SUPPORT
...@@ -27,4 +27,5 @@ obj-$(CONFIG_FSL_PAMU) += fsl_pamu.o fsl_pamu_domain.o ...@@ -27,4 +27,5 @@ obj-$(CONFIG_FSL_PAMU) += fsl_pamu.o fsl_pamu_domain.o
obj-$(CONFIG_S390_IOMMU) += s390-iommu.o obj-$(CONFIG_S390_IOMMU) += s390-iommu.o
obj-$(CONFIG_HYPERV_IOMMU) += hyperv-iommu.o obj-$(CONFIG_HYPERV_IOMMU) += hyperv-iommu.o
obj-$(CONFIG_VIRTIO_IOMMU) += virtio-iommu.o obj-$(CONFIG_VIRTIO_IOMMU) += virtio-iommu.o
obj-$(CONFIG_IOMMU_SVA_LIB) += iommu-sva-lib.o obj-$(CONFIG_IOMMU_SVA_LIB) += iommu-sva-lib.o io-pgfault.o
obj-$(CONFIG_SPRD_IOMMU) += sprd-iommu.o
...@@ -11,7 +11,6 @@ ...@@ -11,7 +11,6 @@
#include "amd_iommu_types.h" #include "amd_iommu_types.h"
extern int amd_iommu_get_num_iommus(void);
extern int amd_iommu_init_dma_ops(void); extern int amd_iommu_init_dma_ops(void);
extern int amd_iommu_init_passthrough(void); extern int amd_iommu_init_passthrough(void);
extern irqreturn_t amd_iommu_int_thread(int irq, void *data); extern irqreturn_t amd_iommu_int_thread(int irq, void *data);
...@@ -65,7 +64,6 @@ extern int amd_iommu_flush_tlb(struct iommu_domain *dom, u32 pasid); ...@@ -65,7 +64,6 @@ extern int amd_iommu_flush_tlb(struct iommu_domain *dom, u32 pasid);
extern int amd_iommu_domain_set_gcr3(struct iommu_domain *dom, u32 pasid, extern int amd_iommu_domain_set_gcr3(struct iommu_domain *dom, u32 pasid,
unsigned long cr3); unsigned long cr3);
extern int amd_iommu_domain_clear_gcr3(struct iommu_domain *dom, u32 pasid); extern int amd_iommu_domain_clear_gcr3(struct iommu_domain *dom, u32 pasid);
extern struct iommu_domain *amd_iommu_get_v2_domain(struct pci_dev *pdev);
#ifdef CONFIG_IRQ_REMAP #ifdef CONFIG_IRQ_REMAP
extern int amd_iommu_create_irq_domain(struct amd_iommu *iommu); extern int amd_iommu_create_irq_domain(struct amd_iommu *iommu);
......
...@@ -693,7 +693,6 @@ struct iommu_dev_data { ...@@ -693,7 +693,6 @@ struct iommu_dev_data {
} ats; /* ATS state */ } ats; /* ATS state */
bool pri_tlp; /* PASID TLB required for bool pri_tlp; /* PASID TLB required for
PPR completions */ PPR completions */
u32 errata; /* Bitmap for errata to apply */
bool use_vapic; /* Enable device to use vapic mode */ bool use_vapic; /* Enable device to use vapic mode */
bool defer_attach; bool defer_attach;
......
...@@ -12,7 +12,6 @@ ...@@ -12,7 +12,6 @@
#include <linux/acpi.h> #include <linux/acpi.h>
#include <linux/list.h> #include <linux/list.h>
#include <linux/bitmap.h> #include <linux/bitmap.h>
#include <linux/delay.h>
#include <linux/slab.h> #include <linux/slab.h>
#include <linux/syscore_ops.h> #include <linux/syscore_ops.h>
#include <linux/interrupt.h> #include <linux/interrupt.h>
...@@ -208,7 +207,6 @@ u16 *amd_iommu_alias_table; ...@@ -208,7 +207,6 @@ u16 *amd_iommu_alias_table;
* for a specific device. It is also indexed by the PCI device id. * for a specific device. It is also indexed by the PCI device id.
*/ */
struct amd_iommu **amd_iommu_rlookup_table; struct amd_iommu **amd_iommu_rlookup_table;
EXPORT_SYMBOL(amd_iommu_rlookup_table);
/* /*
* This table is used to find the irq remapping table for a given device id * This table is used to find the irq remapping table for a given device id
...@@ -257,8 +255,6 @@ static enum iommu_init_state init_state = IOMMU_START_STATE; ...@@ -257,8 +255,6 @@ static enum iommu_init_state init_state = IOMMU_START_STATE;
static int amd_iommu_enable_interrupts(void); static int amd_iommu_enable_interrupts(void);
static int __init iommu_go_to_state(enum iommu_init_state state); static int __init iommu_go_to_state(enum iommu_init_state state);
static void init_device_table_dma(void); static void init_device_table_dma(void);
static int iommu_pc_get_set_reg(struct amd_iommu *iommu, u8 bank, u8 cntr,
u8 fxn, u64 *value, bool is_write);
static bool amd_iommu_pre_enabled = true; static bool amd_iommu_pre_enabled = true;
...@@ -268,7 +264,6 @@ bool translation_pre_enabled(struct amd_iommu *iommu) ...@@ -268,7 +264,6 @@ bool translation_pre_enabled(struct amd_iommu *iommu)
{ {
return (iommu->flags & AMD_IOMMU_FLAG_TRANS_PRE_ENABLED); return (iommu->flags & AMD_IOMMU_FLAG_TRANS_PRE_ENABLED);
} }
EXPORT_SYMBOL(translation_pre_enabled);
static void clear_translation_pre_enabled(struct amd_iommu *iommu) static void clear_translation_pre_enabled(struct amd_iommu *iommu)
{ {
...@@ -1717,53 +1712,16 @@ static int __init init_iommu_all(struct acpi_table_header *table) ...@@ -1717,53 +1712,16 @@ static int __init init_iommu_all(struct acpi_table_header *table)
return 0; return 0;
} }
static void __init init_iommu_perf_ctr(struct amd_iommu *iommu) static void init_iommu_perf_ctr(struct amd_iommu *iommu)
{ {
int retry; u64 val;
struct pci_dev *pdev = iommu->dev; struct pci_dev *pdev = iommu->dev;
u64 val = 0xabcd, val2 = 0, save_reg, save_src;
if (!iommu_feature(iommu, FEATURE_PC)) if (!iommu_feature(iommu, FEATURE_PC))
return; return;
amd_iommu_pc_present = true; amd_iommu_pc_present = true;
/* save the value to restore, if writable */
if (iommu_pc_get_set_reg(iommu, 0, 0, 0, &save_reg, false) ||
iommu_pc_get_set_reg(iommu, 0, 0, 8, &save_src, false))
goto pc_false;
/*
* Disable power gating by programing the performance counter
* source to 20 (i.e. counts the reads and writes from/to IOMMU
* Reserved Register [MMIO Offset 1FF8h] that are ignored.),
* which never get incremented during this init phase.
* (Note: The event is also deprecated.)
*/
val = 20;
if (iommu_pc_get_set_reg(iommu, 0, 0, 8, &val, true))
goto pc_false;
/* Check if the performance counters can be written to */
val = 0xabcd;
for (retry = 5; retry; retry--) {
if (iommu_pc_get_set_reg(iommu, 0, 0, 0, &val, true) ||
iommu_pc_get_set_reg(iommu, 0, 0, 0, &val2, false) ||
val2)
break;
/* Wait about 20 msec for power gating to disable and retry. */
msleep(20);
}
/* restore */
if (iommu_pc_get_set_reg(iommu, 0, 0, 0, &save_reg, true) ||
iommu_pc_get_set_reg(iommu, 0, 0, 8, &save_src, true))
goto pc_false;
if (val != val2)
goto pc_false;
pci_info(pdev, "IOMMU performance counters supported\n"); pci_info(pdev, "IOMMU performance counters supported\n");
val = readl(iommu->mmio_base + MMIO_CNTR_CONF_OFFSET); val = readl(iommu->mmio_base + MMIO_CNTR_CONF_OFFSET);
...@@ -1771,11 +1729,6 @@ static void __init init_iommu_perf_ctr(struct amd_iommu *iommu) ...@@ -1771,11 +1729,6 @@ static void __init init_iommu_perf_ctr(struct amd_iommu *iommu)
iommu->max_counters = (u8) ((val >> 7) & 0xf); iommu->max_counters = (u8) ((val >> 7) & 0xf);
return; return;
pc_false:
pci_err(pdev, "Unable to read/write to IOMMU perf counter.\n");
amd_iommu_pc_present = false;
return;
} }
static ssize_t amd_iommu_show_cap(struct device *dev, static ssize_t amd_iommu_show_cap(struct device *dev,
...@@ -1837,7 +1790,7 @@ static void __init late_iommu_features_init(struct amd_iommu *iommu) ...@@ -1837,7 +1790,7 @@ static void __init late_iommu_features_init(struct amd_iommu *iommu)
* IVHD and MMIO conflict. * IVHD and MMIO conflict.
*/ */
if (features != iommu->features) if (features != iommu->features)
pr_warn(FW_WARN "EFR mismatch. Use IVHD EFR (%#llx : %#llx\n).", pr_warn(FW_WARN "EFR mismatch. Use IVHD EFR (%#llx : %#llx).\n",
features, iommu->features); features, iommu->features);
} }
...@@ -3277,7 +3230,6 @@ struct amd_iommu *get_amd_iommu(unsigned int idx) ...@@ -3277,7 +3230,6 @@ struct amd_iommu *get_amd_iommu(unsigned int idx)
return iommu; return iommu;
return NULL; return NULL;
} }
EXPORT_SYMBOL(get_amd_iommu);
/**************************************************************************** /****************************************************************************
* *
...@@ -3359,7 +3311,6 @@ int amd_iommu_pc_get_reg(struct amd_iommu *iommu, u8 bank, u8 cntr, u8 fxn, u64 ...@@ -3359,7 +3311,6 @@ int amd_iommu_pc_get_reg(struct amd_iommu *iommu, u8 bank, u8 cntr, u8 fxn, u64
return iommu_pc_get_set_reg(iommu, bank, cntr, fxn, value, false); return iommu_pc_get_set_reg(iommu, bank, cntr, fxn, value, false);
} }
EXPORT_SYMBOL(amd_iommu_pc_get_reg);
int amd_iommu_pc_set_reg(struct amd_iommu *iommu, u8 bank, u8 cntr, u8 fxn, u64 *value) int amd_iommu_pc_set_reg(struct amd_iommu *iommu, u8 bank, u8 cntr, u8 fxn, u64 *value)
{ {
...@@ -3368,4 +3319,3 @@ int amd_iommu_pc_set_reg(struct amd_iommu *iommu, u8 bank, u8 cntr, u8 fxn, u64 ...@@ -3368,4 +3319,3 @@ int amd_iommu_pc_set_reg(struct amd_iommu *iommu, u8 bank, u8 cntr, u8 fxn, u64
return iommu_pc_get_set_reg(iommu, bank, cntr, fxn, value, true); return iommu_pc_get_set_reg(iommu, bank, cntr, fxn, value, true);
} }
EXPORT_SYMBOL(amd_iommu_pc_set_reg);
...@@ -290,15 +290,6 @@ static bool pci_iommuv2_capable(struct pci_dev *pdev) ...@@ -290,15 +290,6 @@ static bool pci_iommuv2_capable(struct pci_dev *pdev)
return true; return true;
} }
static bool pdev_pri_erratum(struct pci_dev *pdev, u32 erratum)
{
struct iommu_dev_data *dev_data;
dev_data = dev_iommu_priv_get(&pdev->dev);
return dev_data->errata & (1 << erratum) ? true : false;
}
/* /*
* This function checks if the driver got a valid device from the caller to * This function checks if the driver got a valid device from the caller to
* avoid dereferencing invalid pointers. * avoid dereferencing invalid pointers.
...@@ -861,33 +852,58 @@ static void build_inv_dte(struct iommu_cmd *cmd, u16 devid) ...@@ -861,33 +852,58 @@ static void build_inv_dte(struct iommu_cmd *cmd, u16 devid)
CMD_SET_TYPE(cmd, CMD_INV_DEV_ENTRY); CMD_SET_TYPE(cmd, CMD_INV_DEV_ENTRY);
} }
static void build_inv_iommu_pages(struct iommu_cmd *cmd, u64 address, /*
size_t size, u16 domid, int pde) * Builds an invalidation address which is suitable for one page or multiple
* pages. Sets the size bit (S) as needed is more than one page is flushed.
*/
static inline u64 build_inv_address(u64 address, size_t size)
{ {
u64 pages; u64 pages, end, msb_diff;
bool s;
pages = iommu_num_pages(address, size, PAGE_SIZE); pages = iommu_num_pages(address, size, PAGE_SIZE);
s = false;
if (pages > 1) { if (pages == 1)
return address & PAGE_MASK;
end = address + size - 1;
/*
* msb_diff would hold the index of the most significant bit that
* flipped between the start and end.
*/
msb_diff = fls64(end ^ address) - 1;
/*
* Bits 63:52 are sign extended. If for some reason bit 51 is different
* between the start and the end, invalidate everything.
*/
if (unlikely(msb_diff > 51)) {
address = CMD_INV_IOMMU_ALL_PAGES_ADDRESS;
} else {
/* /*
* If we have to flush more than one page, flush all * The msb-bit must be clear on the address. Just set all the
* TLB entries for this domain * lower bits.
*/ */
address = CMD_INV_IOMMU_ALL_PAGES_ADDRESS; address |= 1ull << (msb_diff - 1);
s = true;
} }
/* Clear bits 11:0 */
address &= PAGE_MASK; address &= PAGE_MASK;
/* Set the size bit - we flush more than one 4kb page */
return address | CMD_INV_IOMMU_PAGES_SIZE_MASK;
}
static void build_inv_iommu_pages(struct iommu_cmd *cmd, u64 address,
size_t size, u16 domid, int pde)
{
u64 inv_address = build_inv_address(address, size);
memset(cmd, 0, sizeof(*cmd)); memset(cmd, 0, sizeof(*cmd));
cmd->data[1] |= domid; cmd->data[1] |= domid;
cmd->data[2] = lower_32_bits(address); cmd->data[2] = lower_32_bits(inv_address);
cmd->data[3] = upper_32_bits(address); cmd->data[3] = upper_32_bits(inv_address);
CMD_SET_TYPE(cmd, CMD_INV_IOMMU_PAGES); CMD_SET_TYPE(cmd, CMD_INV_IOMMU_PAGES);
if (s) /* size bit - we flush more than one 4kb page */
cmd->data[2] |= CMD_INV_IOMMU_PAGES_SIZE_MASK;
if (pde) /* PDE bit - we want to flush everything, not only the PTEs */ if (pde) /* PDE bit - we want to flush everything, not only the PTEs */
cmd->data[2] |= CMD_INV_IOMMU_PAGES_PDE_MASK; cmd->data[2] |= CMD_INV_IOMMU_PAGES_PDE_MASK;
} }
...@@ -895,32 +911,15 @@ static void build_inv_iommu_pages(struct iommu_cmd *cmd, u64 address, ...@@ -895,32 +911,15 @@ static void build_inv_iommu_pages(struct iommu_cmd *cmd, u64 address,
static void build_inv_iotlb_pages(struct iommu_cmd *cmd, u16 devid, int qdep, static void build_inv_iotlb_pages(struct iommu_cmd *cmd, u16 devid, int qdep,
u64 address, size_t size) u64 address, size_t size)
{ {
u64 pages; u64 inv_address = build_inv_address(address, size);
bool s;
pages = iommu_num_pages(address, size, PAGE_SIZE);
s = false;
if (pages > 1) {
/*
* If we have to flush more than one page, flush all
* TLB entries for this domain
*/
address = CMD_INV_IOMMU_ALL_PAGES_ADDRESS;
s = true;
}
address &= PAGE_MASK;
memset(cmd, 0, sizeof(*cmd)); memset(cmd, 0, sizeof(*cmd));
cmd->data[0] = devid; cmd->data[0] = devid;
cmd->data[0] |= (qdep & 0xff) << 24; cmd->data[0] |= (qdep & 0xff) << 24;
cmd->data[1] = devid; cmd->data[1] = devid;
cmd->data[2] = lower_32_bits(address); cmd->data[2] = lower_32_bits(inv_address);
cmd->data[3] = upper_32_bits(address); cmd->data[3] = upper_32_bits(inv_address);
CMD_SET_TYPE(cmd, CMD_INV_IOTLB_PAGES); CMD_SET_TYPE(cmd, CMD_INV_IOTLB_PAGES);
if (s)
cmd->data[2] |= CMD_INV_IOMMU_PAGES_SIZE_MASK;
} }
static void build_inv_iommu_pasid(struct iommu_cmd *cmd, u16 domid, u32 pasid, static void build_inv_iommu_pasid(struct iommu_cmd *cmd, u16 domid, u32 pasid,
...@@ -1531,33 +1530,9 @@ static void pdev_iommuv2_disable(struct pci_dev *pdev) ...@@ -1531,33 +1530,9 @@ static void pdev_iommuv2_disable(struct pci_dev *pdev)
pci_disable_pasid(pdev); pci_disable_pasid(pdev);
} }
/* FIXME: Change generic reset-function to do the same */
static int pri_reset_while_enabled(struct pci_dev *pdev)
{
u16 control;
int pos;
pos = pci_find_ext_capability(pdev, PCI_EXT_CAP_ID_PRI);
if (!pos)
return -EINVAL;
pci_read_config_word(pdev, pos + PCI_PRI_CTRL, &control);
control |= PCI_PRI_CTRL_RESET;
pci_write_config_word(pdev, pos + PCI_PRI_CTRL, control);
return 0;
}
static int pdev_iommuv2_enable(struct pci_dev *pdev) static int pdev_iommuv2_enable(struct pci_dev *pdev)
{ {
bool reset_enable; int ret;
int reqs, ret;
/* FIXME: Hardcode number of outstanding requests for now */
reqs = 32;
if (pdev_pri_erratum(pdev, AMD_PRI_DEV_ERRATUM_LIMIT_REQ_ONE))
reqs = 1;
reset_enable = pdev_pri_erratum(pdev, AMD_PRI_DEV_ERRATUM_ENABLE_RESET);
/* Only allow access to user-accessible pages */ /* Only allow access to user-accessible pages */
ret = pci_enable_pasid(pdev, 0); ret = pci_enable_pasid(pdev, 0);
...@@ -1570,16 +1545,11 @@ static int pdev_iommuv2_enable(struct pci_dev *pdev) ...@@ -1570,16 +1545,11 @@ static int pdev_iommuv2_enable(struct pci_dev *pdev)
goto out_err; goto out_err;
/* Enable PRI */ /* Enable PRI */
ret = pci_enable_pri(pdev, reqs); /* FIXME: Hardcode number of outstanding requests for now */
ret = pci_enable_pri(pdev, 32);
if (ret) if (ret)
goto out_err; goto out_err;
if (reset_enable) {
ret = pri_reset_while_enabled(pdev);
if (ret)
goto out_err;
}
ret = pci_enable_ats(pdev, PAGE_SHIFT); ret = pci_enable_ats(pdev, PAGE_SHIFT);
if (ret) if (ret)
goto out_err; goto out_err;
...@@ -1715,9 +1685,6 @@ static struct iommu_device *amd_iommu_probe_device(struct device *dev) ...@@ -1715,9 +1685,6 @@ static struct iommu_device *amd_iommu_probe_device(struct device *dev)
return ERR_PTR(-ENODEV); return ERR_PTR(-ENODEV);
devid = get_device_id(dev); devid = get_device_id(dev);
if (devid < 0)
return ERR_PTR(devid);
iommu = amd_iommu_rlookup_table[devid]; iommu = amd_iommu_rlookup_table[devid];
if (dev_iommu_priv_get(dev)) if (dev_iommu_priv_get(dev))
...@@ -1771,26 +1738,6 @@ static struct iommu_group *amd_iommu_device_group(struct device *dev) ...@@ -1771,26 +1738,6 @@ static struct iommu_group *amd_iommu_device_group(struct device *dev)
return acpihid_device_group(dev); return acpihid_device_group(dev);
} }
static int amd_iommu_domain_get_attr(struct iommu_domain *domain,
enum iommu_attr attr, void *data)
{
switch (domain->type) {
case IOMMU_DOMAIN_UNMANAGED:
return -ENODEV;
case IOMMU_DOMAIN_DMA:
switch (attr) {
case DOMAIN_ATTR_DMA_USE_FLUSH_QUEUE:
*(int *)data = !amd_iommu_unmap_flush;
return 0;
default:
return -ENODEV;
}
break;
default:
return -EINVAL;
}
}
/***************************************************************************** /*****************************************************************************
* *
* The next functions belong to the dma_ops mapping/unmapping code. * The next functions belong to the dma_ops mapping/unmapping code.
...@@ -1855,7 +1802,7 @@ int __init amd_iommu_init_dma_ops(void) ...@@ -1855,7 +1802,7 @@ int __init amd_iommu_init_dma_ops(void)
pr_info("IO/TLB flush on unmap enabled\n"); pr_info("IO/TLB flush on unmap enabled\n");
else else
pr_info("Lazy IO/TLB flushing enabled\n"); pr_info("Lazy IO/TLB flushing enabled\n");
iommu_set_dma_strict(amd_iommu_unmap_flush);
return 0; return 0;
} }
...@@ -2019,16 +1966,12 @@ static void amd_iommu_detach_device(struct iommu_domain *dom, ...@@ -2019,16 +1966,12 @@ static void amd_iommu_detach_device(struct iommu_domain *dom,
struct device *dev) struct device *dev)
{ {
struct iommu_dev_data *dev_data = dev_iommu_priv_get(dev); struct iommu_dev_data *dev_data = dev_iommu_priv_get(dev);
int devid = get_device_id(dev);
struct amd_iommu *iommu; struct amd_iommu *iommu;
int devid;
if (!check_device(dev)) if (!check_device(dev))
return; return;
devid = get_device_id(dev);
if (devid < 0)
return;
if (dev_data->domain != NULL) if (dev_data->domain != NULL)
detach_device(dev); detach_device(dev);
...@@ -2257,7 +2200,6 @@ const struct iommu_ops amd_iommu_ops = { ...@@ -2257,7 +2200,6 @@ const struct iommu_ops amd_iommu_ops = {
.release_device = amd_iommu_release_device, .release_device = amd_iommu_release_device,
.probe_finalize = amd_iommu_probe_finalize, .probe_finalize = amd_iommu_probe_finalize,
.device_group = amd_iommu_device_group, .device_group = amd_iommu_device_group,
.domain_get_attr = amd_iommu_domain_get_attr,
.get_resv_regions = amd_iommu_get_resv_regions, .get_resv_regions = amd_iommu_get_resv_regions,
.put_resv_regions = generic_iommu_put_resv_regions, .put_resv_regions = generic_iommu_put_resv_regions,
.is_attach_deferred = amd_iommu_is_attach_deferred, .is_attach_deferred = amd_iommu_is_attach_deferred,
...@@ -2310,9 +2252,6 @@ int amd_iommu_domain_enable_v2(struct iommu_domain *dom, int pasids) ...@@ -2310,9 +2252,6 @@ int amd_iommu_domain_enable_v2(struct iommu_domain *dom, int pasids)
unsigned long flags; unsigned long flags;
int levels, ret; int levels, ret;
if (pasids <= 0 || pasids > (PASID_MASK + 1))
return -EINVAL;
/* Number of GCR3 table levels required */ /* Number of GCR3 table levels required */
for (levels = 0; (pasids - 1) & ~0x1ff; pasids >>= 9) for (levels = 0; (pasids - 1) & ~0x1ff; pasids >>= 9)
levels += 1; levels += 1;
...@@ -2563,52 +2502,6 @@ int amd_iommu_complete_ppr(struct pci_dev *pdev, u32 pasid, ...@@ -2563,52 +2502,6 @@ int amd_iommu_complete_ppr(struct pci_dev *pdev, u32 pasid,
} }
EXPORT_SYMBOL(amd_iommu_complete_ppr); EXPORT_SYMBOL(amd_iommu_complete_ppr);
struct iommu_domain *amd_iommu_get_v2_domain(struct pci_dev *pdev)
{
struct protection_domain *pdomain;
struct iommu_dev_data *dev_data;
struct device *dev = &pdev->dev;
struct iommu_domain *io_domain;
if (!check_device(dev))
return NULL;
dev_data = dev_iommu_priv_get(&pdev->dev);
pdomain = dev_data->domain;
io_domain = iommu_get_domain_for_dev(dev);
if (pdomain == NULL && dev_data->defer_attach) {
dev_data->defer_attach = false;
pdomain = to_pdomain(io_domain);
attach_device(dev, pdomain);
}
if (pdomain == NULL)
return NULL;
if (io_domain->type != IOMMU_DOMAIN_DMA)
return NULL;
/* Only return IOMMUv2 domains */
if (!(pdomain->flags & PD_IOMMUV2_MASK))
return NULL;
return &pdomain->domain;
}
EXPORT_SYMBOL(amd_iommu_get_v2_domain);
void amd_iommu_enable_device_erratum(struct pci_dev *pdev, u32 erratum)
{
struct iommu_dev_data *dev_data;
if (!amd_iommu_v2_supported())
return;
dev_data = dev_iommu_priv_get(&pdev->dev);
dev_data->errata |= (1 << erratum);
}
EXPORT_SYMBOL(amd_iommu_enable_device_erratum);
int amd_iommu_device_info(struct pci_dev *pdev, int amd_iommu_device_info(struct pci_dev *pdev,
struct amd_iommu_device_info *info) struct amd_iommu_device_info *info)
{ {
......
...@@ -245,8 +245,6 @@ static int arm_smmu_cmdq_build_cmd(u64 *cmd, struct arm_smmu_cmdq_ent *ent) ...@@ -245,8 +245,6 @@ static int arm_smmu_cmdq_build_cmd(u64 *cmd, struct arm_smmu_cmdq_ent *ent)
break; break;
case CMDQ_OP_PREFETCH_CFG: case CMDQ_OP_PREFETCH_CFG:
cmd[0] |= FIELD_PREP(CMDQ_PREFETCH_0_SID, ent->prefetch.sid); cmd[0] |= FIELD_PREP(CMDQ_PREFETCH_0_SID, ent->prefetch.sid);
cmd[1] |= FIELD_PREP(CMDQ_PREFETCH_1_SIZE, ent->prefetch.size);
cmd[1] |= ent->prefetch.addr & CMDQ_PREFETCH_1_ADDR_MASK;
break; break;
case CMDQ_OP_CFGI_CD: case CMDQ_OP_CFGI_CD:
cmd[0] |= FIELD_PREP(CMDQ_CFGI_0_SSID, ent->cfgi.ssid); cmd[0] |= FIELD_PREP(CMDQ_CFGI_0_SSID, ent->cfgi.ssid);
...@@ -909,8 +907,8 @@ static void arm_smmu_sync_cd(struct arm_smmu_domain *smmu_domain, ...@@ -909,8 +907,8 @@ static void arm_smmu_sync_cd(struct arm_smmu_domain *smmu_domain,
spin_lock_irqsave(&smmu_domain->devices_lock, flags); spin_lock_irqsave(&smmu_domain->devices_lock, flags);
list_for_each_entry(master, &smmu_domain->devices, domain_head) { list_for_each_entry(master, &smmu_domain->devices, domain_head) {
for (i = 0; i < master->num_sids; i++) { for (i = 0; i < master->num_streams; i++) {
cmd.cfgi.sid = master->sids[i]; cmd.cfgi.sid = master->streams[i].id;
arm_smmu_cmdq_batch_add(smmu, &cmds, &cmd); arm_smmu_cmdq_batch_add(smmu, &cmds, &cmd);
} }
} }
...@@ -1355,6 +1353,29 @@ static int arm_smmu_init_l2_strtab(struct arm_smmu_device *smmu, u32 sid) ...@@ -1355,6 +1353,29 @@ static int arm_smmu_init_l2_strtab(struct arm_smmu_device *smmu, u32 sid)
return 0; return 0;
} }
__maybe_unused
static struct arm_smmu_master *
arm_smmu_find_master(struct arm_smmu_device *smmu, u32 sid)
{
struct rb_node *node;
struct arm_smmu_stream *stream;
lockdep_assert_held(&smmu->streams_mutex);
node = smmu->streams.rb_node;
while (node) {
stream = rb_entry(node, struct arm_smmu_stream, node);
if (stream->id < sid)
node = node->rb_right;
else if (stream->id > sid)
node = node->rb_left;
else
return stream->master;
}
return NULL;
}
/* IRQ and event handlers */ /* IRQ and event handlers */
static irqreturn_t arm_smmu_evtq_thread(int irq, void *dev) static irqreturn_t arm_smmu_evtq_thread(int irq, void *dev)
{ {
...@@ -1588,8 +1609,8 @@ static int arm_smmu_atc_inv_master(struct arm_smmu_master *master) ...@@ -1588,8 +1609,8 @@ static int arm_smmu_atc_inv_master(struct arm_smmu_master *master)
arm_smmu_atc_inv_to_cmd(0, 0, 0, &cmd); arm_smmu_atc_inv_to_cmd(0, 0, 0, &cmd);
for (i = 0; i < master->num_sids; i++) { for (i = 0; i < master->num_streams; i++) {
cmd.atc.sid = master->sids[i]; cmd.atc.sid = master->streams[i].id;
arm_smmu_cmdq_issue_cmd(master->smmu, &cmd); arm_smmu_cmdq_issue_cmd(master->smmu, &cmd);
} }
...@@ -1632,8 +1653,8 @@ int arm_smmu_atc_inv_domain(struct arm_smmu_domain *smmu_domain, int ssid, ...@@ -1632,8 +1653,8 @@ int arm_smmu_atc_inv_domain(struct arm_smmu_domain *smmu_domain, int ssid,
if (!master->ats_enabled) if (!master->ats_enabled)
continue; continue;
for (i = 0; i < master->num_sids; i++) { for (i = 0; i < master->num_streams; i++) {
cmd.atc.sid = master->sids[i]; cmd.atc.sid = master->streams[i].id;
arm_smmu_cmdq_batch_add(smmu_domain->smmu, &cmds, &cmd); arm_smmu_cmdq_batch_add(smmu_domain->smmu, &cmds, &cmd);
} }
} }
...@@ -2017,7 +2038,7 @@ static int arm_smmu_domain_finalise(struct iommu_domain *domain, ...@@ -2017,7 +2038,7 @@ static int arm_smmu_domain_finalise(struct iommu_domain *domain,
.iommu_dev = smmu->dev, .iommu_dev = smmu->dev,
}; };
if (smmu_domain->non_strict) if (!iommu_get_dma_strict(domain))
pgtbl_cfg.quirks |= IO_PGTABLE_QUIRK_NON_STRICT; pgtbl_cfg.quirks |= IO_PGTABLE_QUIRK_NON_STRICT;
pgtbl_ops = alloc_io_pgtable_ops(fmt, &pgtbl_cfg, smmu_domain); pgtbl_ops = alloc_io_pgtable_ops(fmt, &pgtbl_cfg, smmu_domain);
...@@ -2065,13 +2086,13 @@ static void arm_smmu_install_ste_for_dev(struct arm_smmu_master *master) ...@@ -2065,13 +2086,13 @@ static void arm_smmu_install_ste_for_dev(struct arm_smmu_master *master)
int i, j; int i, j;
struct arm_smmu_device *smmu = master->smmu; struct arm_smmu_device *smmu = master->smmu;
for (i = 0; i < master->num_sids; ++i) { for (i = 0; i < master->num_streams; ++i) {
u32 sid = master->sids[i]; u32 sid = master->streams[i].id;
__le64 *step = arm_smmu_get_step_for_sid(smmu, sid); __le64 *step = arm_smmu_get_step_for_sid(smmu, sid);
/* Bridged PCI devices may end up with duplicated IDs */ /* Bridged PCI devices may end up with duplicated IDs */
for (j = 0; j < i; j++) for (j = 0; j < i; j++)
if (master->sids[j] == sid) if (master->streams[j].id == sid)
break; break;
if (j < i) if (j < i)
continue; continue;
...@@ -2305,6 +2326,9 @@ static void arm_smmu_iotlb_sync(struct iommu_domain *domain, ...@@ -2305,6 +2326,9 @@ static void arm_smmu_iotlb_sync(struct iommu_domain *domain,
{ {
struct arm_smmu_domain *smmu_domain = to_smmu_domain(domain); struct arm_smmu_domain *smmu_domain = to_smmu_domain(domain);
if (!gather->pgsize)
return;
arm_smmu_tlb_inv_range_domain(gather->start, arm_smmu_tlb_inv_range_domain(gather->start,
gather->end - gather->start + 1, gather->end - gather->start + 1,
gather->pgsize, true, smmu_domain); gather->pgsize, true, smmu_domain);
...@@ -2345,11 +2369,101 @@ static bool arm_smmu_sid_in_range(struct arm_smmu_device *smmu, u32 sid) ...@@ -2345,11 +2369,101 @@ static bool arm_smmu_sid_in_range(struct arm_smmu_device *smmu, u32 sid)
return sid < limit; return sid < limit;
} }
static int arm_smmu_insert_master(struct arm_smmu_device *smmu,
struct arm_smmu_master *master)
{
int i;
int ret = 0;
struct arm_smmu_stream *new_stream, *cur_stream;
struct rb_node **new_node, *parent_node = NULL;
struct iommu_fwspec *fwspec = dev_iommu_fwspec_get(master->dev);
master->streams = kcalloc(fwspec->num_ids, sizeof(*master->streams),
GFP_KERNEL);
if (!master->streams)
return -ENOMEM;
master->num_streams = fwspec->num_ids;
mutex_lock(&smmu->streams_mutex);
for (i = 0; i < fwspec->num_ids; i++) {
u32 sid = fwspec->ids[i];
new_stream = &master->streams[i];
new_stream->id = sid;
new_stream->master = master;
/*
* Check the SIDs are in range of the SMMU and our stream table
*/
if (!arm_smmu_sid_in_range(smmu, sid)) {
ret = -ERANGE;
break;
}
/* Ensure l2 strtab is initialised */
if (smmu->features & ARM_SMMU_FEAT_2_LVL_STRTAB) {
ret = arm_smmu_init_l2_strtab(smmu, sid);
if (ret)
break;
}
/* Insert into SID tree */
new_node = &(smmu->streams.rb_node);
while (*new_node) {
cur_stream = rb_entry(*new_node, struct arm_smmu_stream,
node);
parent_node = *new_node;
if (cur_stream->id > new_stream->id) {
new_node = &((*new_node)->rb_left);
} else if (cur_stream->id < new_stream->id) {
new_node = &((*new_node)->rb_right);
} else {
dev_warn(master->dev,
"stream %u already in tree\n",
cur_stream->id);
ret = -EINVAL;
break;
}
}
if (ret)
break;
rb_link_node(&new_stream->node, parent_node, new_node);
rb_insert_color(&new_stream->node, &smmu->streams);
}
if (ret) {
for (i--; i >= 0; i--)
rb_erase(&master->streams[i].node, &smmu->streams);
kfree(master->streams);
}
mutex_unlock(&smmu->streams_mutex);
return ret;
}
static void arm_smmu_remove_master(struct arm_smmu_master *master)
{
int i;
struct arm_smmu_device *smmu = master->smmu;
struct iommu_fwspec *fwspec = dev_iommu_fwspec_get(master->dev);
if (!smmu || !master->streams)
return;
mutex_lock(&smmu->streams_mutex);
for (i = 0; i < fwspec->num_ids; i++)
rb_erase(&master->streams[i].node, &smmu->streams);
mutex_unlock(&smmu->streams_mutex);
kfree(master->streams);
}
static struct iommu_ops arm_smmu_ops; static struct iommu_ops arm_smmu_ops;
static struct iommu_device *arm_smmu_probe_device(struct device *dev) static struct iommu_device *arm_smmu_probe_device(struct device *dev)
{ {
int i, ret; int ret;
struct arm_smmu_device *smmu; struct arm_smmu_device *smmu;
struct arm_smmu_master *master; struct arm_smmu_master *master;
struct iommu_fwspec *fwspec = dev_iommu_fwspec_get(dev); struct iommu_fwspec *fwspec = dev_iommu_fwspec_get(dev);
...@@ -2370,29 +2484,15 @@ static struct iommu_device *arm_smmu_probe_device(struct device *dev) ...@@ -2370,29 +2484,15 @@ static struct iommu_device *arm_smmu_probe_device(struct device *dev)
master->dev = dev; master->dev = dev;
master->smmu = smmu; master->smmu = smmu;
master->sids = fwspec->ids;
master->num_sids = fwspec->num_ids;
INIT_LIST_HEAD(&master->bonds); INIT_LIST_HEAD(&master->bonds);
dev_iommu_priv_set(dev, master); dev_iommu_priv_set(dev, master);
/* Check the SIDs are in range of the SMMU and our stream table */ ret = arm_smmu_insert_master(smmu, master);
for (i = 0; i < master->num_sids; i++) { if (ret)
u32 sid = master->sids[i]; goto err_free_master;
if (!arm_smmu_sid_in_range(smmu, sid)) {
ret = -ERANGE;
goto err_free_master;
}
/* Ensure l2 strtab is initialised */
if (smmu->features & ARM_SMMU_FEAT_2_LVL_STRTAB) {
ret = arm_smmu_init_l2_strtab(smmu, sid);
if (ret)
goto err_free_master;
}
}
master->ssid_bits = min(smmu->ssid_bits, fwspec->num_pasid_bits); device_property_read_u32(dev, "pasid-num-bits", &master->ssid_bits);
master->ssid_bits = min(smmu->ssid_bits, master->ssid_bits);
/* /*
* Note that PASID must be enabled before, and disabled after ATS: * Note that PASID must be enabled before, and disabled after ATS:
...@@ -2428,6 +2528,7 @@ static void arm_smmu_release_device(struct device *dev) ...@@ -2428,6 +2528,7 @@ static void arm_smmu_release_device(struct device *dev)
WARN_ON(arm_smmu_master_sva_enabled(master)); WARN_ON(arm_smmu_master_sva_enabled(master));
arm_smmu_detach_dev(master); arm_smmu_detach_dev(master);
arm_smmu_disable_pasid(master); arm_smmu_disable_pasid(master);
arm_smmu_remove_master(master);
kfree(master); kfree(master);
iommu_fwspec_free(dev); iommu_fwspec_free(dev);
} }
...@@ -2449,76 +2550,18 @@ static struct iommu_group *arm_smmu_device_group(struct device *dev) ...@@ -2449,76 +2550,18 @@ static struct iommu_group *arm_smmu_device_group(struct device *dev)
return group; return group;
} }
static int arm_smmu_domain_get_attr(struct iommu_domain *domain, static int arm_smmu_enable_nesting(struct iommu_domain *domain)
enum iommu_attr attr, void *data)
{ {
struct arm_smmu_domain *smmu_domain = to_smmu_domain(domain); struct arm_smmu_domain *smmu_domain = to_smmu_domain(domain);
switch (domain->type) {
case IOMMU_DOMAIN_UNMANAGED:
switch (attr) {
case DOMAIN_ATTR_NESTING:
*(int *)data = (smmu_domain->stage == ARM_SMMU_DOMAIN_NESTED);
return 0;
default:
return -ENODEV;
}
break;
case IOMMU_DOMAIN_DMA:
switch (attr) {
case DOMAIN_ATTR_DMA_USE_FLUSH_QUEUE:
*(int *)data = smmu_domain->non_strict;
return 0;
default:
return -ENODEV;
}
break;
default:
return -EINVAL;
}
}
static int arm_smmu_domain_set_attr(struct iommu_domain *domain,
enum iommu_attr attr, void *data)
{
int ret = 0; int ret = 0;
struct arm_smmu_domain *smmu_domain = to_smmu_domain(domain);
mutex_lock(&smmu_domain->init_mutex); mutex_lock(&smmu_domain->init_mutex);
if (smmu_domain->smmu)
switch (domain->type) { ret = -EPERM;
case IOMMU_DOMAIN_UNMANAGED: else
switch (attr) { smmu_domain->stage = ARM_SMMU_DOMAIN_NESTED;
case DOMAIN_ATTR_NESTING:
if (smmu_domain->smmu) {
ret = -EPERM;
goto out_unlock;
}
if (*(int *)data)
smmu_domain->stage = ARM_SMMU_DOMAIN_NESTED;
else
smmu_domain->stage = ARM_SMMU_DOMAIN_S1;
break;
default:
ret = -ENODEV;
}
break;
case IOMMU_DOMAIN_DMA:
switch(attr) {
case DOMAIN_ATTR_DMA_USE_FLUSH_QUEUE:
smmu_domain->non_strict = *(int *)data;
break;
default:
ret = -ENODEV;
}
break;
default:
ret = -EINVAL;
}
out_unlock:
mutex_unlock(&smmu_domain->init_mutex); mutex_unlock(&smmu_domain->init_mutex);
return ret; return ret;
} }
...@@ -2619,8 +2662,7 @@ static struct iommu_ops arm_smmu_ops = { ...@@ -2619,8 +2662,7 @@ static struct iommu_ops arm_smmu_ops = {
.probe_device = arm_smmu_probe_device, .probe_device = arm_smmu_probe_device,
.release_device = arm_smmu_release_device, .release_device = arm_smmu_release_device,
.device_group = arm_smmu_device_group, .device_group = arm_smmu_device_group,
.domain_get_attr = arm_smmu_domain_get_attr, .enable_nesting = arm_smmu_enable_nesting,
.domain_set_attr = arm_smmu_domain_set_attr,
.of_xlate = arm_smmu_of_xlate, .of_xlate = arm_smmu_of_xlate,
.get_resv_regions = arm_smmu_get_resv_regions, .get_resv_regions = arm_smmu_get_resv_regions,
.put_resv_regions = generic_iommu_put_resv_regions, .put_resv_regions = generic_iommu_put_resv_regions,
...@@ -2851,6 +2893,9 @@ static int arm_smmu_init_structures(struct arm_smmu_device *smmu) ...@@ -2851,6 +2893,9 @@ static int arm_smmu_init_structures(struct arm_smmu_device *smmu)
{ {
int ret; int ret;
mutex_init(&smmu->streams_mutex);
smmu->streams = RB_ROOT;
ret = arm_smmu_init_queues(smmu); ret = arm_smmu_init_queues(smmu);
if (ret) if (ret)
return ret; return ret;
......
...@@ -115,7 +115,7 @@ ...@@ -115,7 +115,7 @@
#define GERROR_PRIQ_ABT_ERR (1 << 3) #define GERROR_PRIQ_ABT_ERR (1 << 3)
#define GERROR_EVTQ_ABT_ERR (1 << 2) #define GERROR_EVTQ_ABT_ERR (1 << 2)
#define GERROR_CMDQ_ERR (1 << 0) #define GERROR_CMDQ_ERR (1 << 0)
#define GERROR_ERR_MASK 0xfd #define GERROR_ERR_MASK 0x1fd
#define ARM_SMMU_GERRORN 0x64 #define ARM_SMMU_GERRORN 0x64
...@@ -410,8 +410,6 @@ struct arm_smmu_cmdq_ent { ...@@ -410,8 +410,6 @@ struct arm_smmu_cmdq_ent {
#define CMDQ_OP_PREFETCH_CFG 0x1 #define CMDQ_OP_PREFETCH_CFG 0x1
struct { struct {
u32 sid; u32 sid;
u8 size;
u64 addr;
} prefetch; } prefetch;
#define CMDQ_OP_CFGI_STE 0x3 #define CMDQ_OP_CFGI_STE 0x3
...@@ -639,6 +637,15 @@ struct arm_smmu_device { ...@@ -639,6 +637,15 @@ struct arm_smmu_device {
/* IOMMU core code handle */ /* IOMMU core code handle */
struct iommu_device iommu; struct iommu_device iommu;
struct rb_root streams;
struct mutex streams_mutex;
};
struct arm_smmu_stream {
u32 id;
struct arm_smmu_master *master;
struct rb_node node;
}; };
/* SMMU private data for each master */ /* SMMU private data for each master */
...@@ -647,8 +654,8 @@ struct arm_smmu_master { ...@@ -647,8 +654,8 @@ struct arm_smmu_master {
struct device *dev; struct device *dev;
struct arm_smmu_domain *domain; struct arm_smmu_domain *domain;
struct list_head domain_head; struct list_head domain_head;
u32 *sids; struct arm_smmu_stream *streams;
unsigned int num_sids; unsigned int num_streams;
bool ats_enabled; bool ats_enabled;
bool sva_enabled; bool sva_enabled;
struct list_head bonds; struct list_head bonds;
...@@ -668,7 +675,6 @@ struct arm_smmu_domain { ...@@ -668,7 +675,6 @@ struct arm_smmu_domain {
struct mutex init_mutex; /* Protects smmu pointer */ struct mutex init_mutex; /* Protects smmu pointer */
struct io_pgtable_ops *pgtbl_ops; struct io_pgtable_ops *pgtbl_ops;
bool non_strict;
atomic_t nr_ats_masters; atomic_t nr_ats_masters;
enum arm_smmu_domain_stage stage; enum arm_smmu_domain_stage stage;
......
...@@ -761,14 +761,17 @@ static int arm_smmu_init_domain_context(struct iommu_domain *domain, ...@@ -761,14 +761,17 @@ static int arm_smmu_init_domain_context(struct iommu_domain *domain,
.iommu_dev = smmu->dev, .iommu_dev = smmu->dev,
}; };
if (!iommu_get_dma_strict(domain))
pgtbl_cfg.quirks |= IO_PGTABLE_QUIRK_NON_STRICT;
if (smmu->impl && smmu->impl->init_context) { if (smmu->impl && smmu->impl->init_context) {
ret = smmu->impl->init_context(smmu_domain, &pgtbl_cfg, dev); ret = smmu->impl->init_context(smmu_domain, &pgtbl_cfg, dev);
if (ret) if (ret)
goto out_clear_smmu; goto out_clear_smmu;
} }
if (smmu_domain->pgtbl_cfg.quirks) if (smmu_domain->pgtbl_quirks)
pgtbl_cfg.quirks |= smmu_domain->pgtbl_cfg.quirks; pgtbl_cfg.quirks |= smmu_domain->pgtbl_quirks;
pgtbl_ops = alloc_io_pgtable_ops(fmt, &pgtbl_cfg, smmu_domain); pgtbl_ops = alloc_io_pgtable_ops(fmt, &pgtbl_cfg, smmu_domain);
if (!pgtbl_ops) { if (!pgtbl_ops) {
...@@ -1481,98 +1484,34 @@ static struct iommu_group *arm_smmu_device_group(struct device *dev) ...@@ -1481,98 +1484,34 @@ static struct iommu_group *arm_smmu_device_group(struct device *dev)
return group; return group;
} }
static int arm_smmu_domain_get_attr(struct iommu_domain *domain, static int arm_smmu_enable_nesting(struct iommu_domain *domain)
enum iommu_attr attr, void *data)
{ {
struct arm_smmu_domain *smmu_domain = to_smmu_domain(domain); struct arm_smmu_domain *smmu_domain = to_smmu_domain(domain);
int ret = 0;
switch(domain->type) { mutex_lock(&smmu_domain->init_mutex);
case IOMMU_DOMAIN_UNMANAGED: if (smmu_domain->smmu)
switch (attr) { ret = -EPERM;
case DOMAIN_ATTR_NESTING: else
*(int *)data = (smmu_domain->stage == ARM_SMMU_DOMAIN_NESTED); smmu_domain->stage = ARM_SMMU_DOMAIN_NESTED;
return 0; mutex_unlock(&smmu_domain->init_mutex);
case DOMAIN_ATTR_IO_PGTABLE_CFG: {
struct io_pgtable_domain_attr *pgtbl_cfg = data; return ret;
*pgtbl_cfg = smmu_domain->pgtbl_cfg;
return 0;
}
default:
return -ENODEV;
}
break;
case IOMMU_DOMAIN_DMA:
switch (attr) {
case DOMAIN_ATTR_DMA_USE_FLUSH_QUEUE: {
bool non_strict = smmu_domain->pgtbl_cfg.quirks &
IO_PGTABLE_QUIRK_NON_STRICT;
*(int *)data = non_strict;
return 0;
}
default:
return -ENODEV;
}
break;
default:
return -EINVAL;
}
} }
static int arm_smmu_domain_set_attr(struct iommu_domain *domain, static int arm_smmu_set_pgtable_quirks(struct iommu_domain *domain,
enum iommu_attr attr, void *data) unsigned long quirks)
{ {
int ret = 0;
struct arm_smmu_domain *smmu_domain = to_smmu_domain(domain); struct arm_smmu_domain *smmu_domain = to_smmu_domain(domain);
int ret = 0;
mutex_lock(&smmu_domain->init_mutex); mutex_lock(&smmu_domain->init_mutex);
if (smmu_domain->smmu)
switch(domain->type) { ret = -EPERM;
case IOMMU_DOMAIN_UNMANAGED: else
switch (attr) { smmu_domain->pgtbl_quirks = quirks;
case DOMAIN_ATTR_NESTING:
if (smmu_domain->smmu) {
ret = -EPERM;
goto out_unlock;
}
if (*(int *)data)
smmu_domain->stage = ARM_SMMU_DOMAIN_NESTED;
else
smmu_domain->stage = ARM_SMMU_DOMAIN_S1;
break;
case DOMAIN_ATTR_IO_PGTABLE_CFG: {
struct io_pgtable_domain_attr *pgtbl_cfg = data;
if (smmu_domain->smmu) {
ret = -EPERM;
goto out_unlock;
}
smmu_domain->pgtbl_cfg = *pgtbl_cfg;
break;
}
default:
ret = -ENODEV;
}
break;
case IOMMU_DOMAIN_DMA:
switch (attr) {
case DOMAIN_ATTR_DMA_USE_FLUSH_QUEUE:
if (*(int *)data)
smmu_domain->pgtbl_cfg.quirks |= IO_PGTABLE_QUIRK_NON_STRICT;
else
smmu_domain->pgtbl_cfg.quirks &= ~IO_PGTABLE_QUIRK_NON_STRICT;
break;
default:
ret = -ENODEV;
}
break;
default:
ret = -EINVAL;
}
out_unlock:
mutex_unlock(&smmu_domain->init_mutex); mutex_unlock(&smmu_domain->init_mutex);
return ret; return ret;
} }
...@@ -1631,8 +1570,8 @@ static struct iommu_ops arm_smmu_ops = { ...@@ -1631,8 +1570,8 @@ static struct iommu_ops arm_smmu_ops = {
.probe_device = arm_smmu_probe_device, .probe_device = arm_smmu_probe_device,
.release_device = arm_smmu_release_device, .release_device = arm_smmu_release_device,
.device_group = arm_smmu_device_group, .device_group = arm_smmu_device_group,
.domain_get_attr = arm_smmu_domain_get_attr, .enable_nesting = arm_smmu_enable_nesting,
.domain_set_attr = arm_smmu_domain_set_attr, .set_pgtable_quirks = arm_smmu_set_pgtable_quirks,
.of_xlate = arm_smmu_of_xlate, .of_xlate = arm_smmu_of_xlate,
.get_resv_regions = arm_smmu_get_resv_regions, .get_resv_regions = arm_smmu_get_resv_regions,
.put_resv_regions = generic_iommu_put_resv_regions, .put_resv_regions = generic_iommu_put_resv_regions,
......
...@@ -364,7 +364,7 @@ enum arm_smmu_domain_stage { ...@@ -364,7 +364,7 @@ enum arm_smmu_domain_stage {
struct arm_smmu_domain { struct arm_smmu_domain {
struct arm_smmu_device *smmu; struct arm_smmu_device *smmu;
struct io_pgtable_ops *pgtbl_ops; struct io_pgtable_ops *pgtbl_ops;
struct io_pgtable_domain_attr pgtbl_cfg; unsigned long pgtbl_quirks;
const struct iommu_flush_ops *flush_ops; const struct iommu_flush_ops *flush_ops;
struct arm_smmu_cfg cfg; struct arm_smmu_cfg cfg;
enum arm_smmu_domain_stage stage; enum arm_smmu_domain_stage stage;
......
...@@ -52,15 +52,17 @@ struct iommu_dma_cookie { ...@@ -52,15 +52,17 @@ struct iommu_dma_cookie {
}; };
static DEFINE_STATIC_KEY_FALSE(iommu_deferred_attach_enabled); static DEFINE_STATIC_KEY_FALSE(iommu_deferred_attach_enabled);
bool iommu_dma_forcedac __read_mostly;
void iommu_dma_free_cpu_cached_iovas(unsigned int cpu, static int __init iommu_dma_forcedac_setup(char *str)
struct iommu_domain *domain)
{ {
struct iommu_dma_cookie *cookie = domain->iova_cookie; int ret = kstrtobool(str, &iommu_dma_forcedac);
struct iova_domain *iovad = &cookie->iovad;
free_cpu_cached_iovas(cpu, iovad); if (!ret && iommu_dma_forcedac)
pr_info("Forcing DAC for PCI devices\n");
return ret;
} }
early_param("iommu.forcedac", iommu_dma_forcedac_setup);
static void iommu_dma_entry_dtor(unsigned long data) static void iommu_dma_entry_dtor(unsigned long data)
{ {
...@@ -304,10 +306,7 @@ static void iommu_dma_flush_iotlb_all(struct iova_domain *iovad) ...@@ -304,10 +306,7 @@ static void iommu_dma_flush_iotlb_all(struct iova_domain *iovad)
cookie = container_of(iovad, struct iommu_dma_cookie, iovad); cookie = container_of(iovad, struct iommu_dma_cookie, iovad);
domain = cookie->fq_domain; domain = cookie->fq_domain;
/*
* The IOMMU driver supporting DOMAIN_ATTR_DMA_USE_FLUSH_QUEUE
* implies that ops->flush_iotlb_all must be non-NULL.
*/
domain->ops->flush_iotlb_all(domain); domain->ops->flush_iotlb_all(domain);
} }
...@@ -334,7 +333,6 @@ static int iommu_dma_init_domain(struct iommu_domain *domain, dma_addr_t base, ...@@ -334,7 +333,6 @@ static int iommu_dma_init_domain(struct iommu_domain *domain, dma_addr_t base,
struct iommu_dma_cookie *cookie = domain->iova_cookie; struct iommu_dma_cookie *cookie = domain->iova_cookie;
unsigned long order, base_pfn; unsigned long order, base_pfn;
struct iova_domain *iovad; struct iova_domain *iovad;
int attr;
if (!cookie || cookie->type != IOMMU_DMA_IOVA_COOKIE) if (!cookie || cookie->type != IOMMU_DMA_IOVA_COOKIE)
return -EINVAL; return -EINVAL;
...@@ -371,8 +369,7 @@ static int iommu_dma_init_domain(struct iommu_domain *domain, dma_addr_t base, ...@@ -371,8 +369,7 @@ static int iommu_dma_init_domain(struct iommu_domain *domain, dma_addr_t base,
init_iova_domain(iovad, 1UL << order, base_pfn); init_iova_domain(iovad, 1UL << order, base_pfn);
if (!cookie->fq_domain && (!dev || !dev_is_untrusted(dev)) && if (!cookie->fq_domain && (!dev || !dev_is_untrusted(dev)) &&
!iommu_domain_get_attr(domain, DOMAIN_ATTR_DMA_USE_FLUSH_QUEUE, &attr) && domain->ops->flush_iotlb_all && !iommu_get_dma_strict(domain)) {
attr) {
if (init_iova_flush_queue(iovad, iommu_dma_flush_iotlb_all, if (init_iova_flush_queue(iovad, iommu_dma_flush_iotlb_all,
iommu_dma_entry_dtor)) iommu_dma_entry_dtor))
pr_warn("iova flush queue initialization failed\n"); pr_warn("iova flush queue initialization failed\n");
...@@ -444,7 +441,7 @@ static dma_addr_t iommu_dma_alloc_iova(struct iommu_domain *domain, ...@@ -444,7 +441,7 @@ static dma_addr_t iommu_dma_alloc_iova(struct iommu_domain *domain,
dma_limit = min(dma_limit, (u64)domain->geometry.aperture_end); dma_limit = min(dma_limit, (u64)domain->geometry.aperture_end);
/* Try to get PCI devices a SAC address */ /* Try to get PCI devices a SAC address */
if (dma_limit > DMA_BIT_MASK(32) && dev_is_pci(dev)) if (dma_limit > DMA_BIT_MASK(32) && !iommu_dma_forcedac && dev_is_pci(dev))
iova = alloc_iova_fast(iovad, iova_len, iova = alloc_iova_fast(iovad, iova_len,
DMA_BIT_MASK(32) >> shift, false); DMA_BIT_MASK(32) >> shift, false);
......
...@@ -407,7 +407,7 @@ static irqreturn_t exynos_sysmmu_irq(int irq, void *dev_id) ...@@ -407,7 +407,7 @@ static irqreturn_t exynos_sysmmu_irq(int irq, void *dev_id)
struct sysmmu_drvdata *data = dev_id; struct sysmmu_drvdata *data = dev_id;
const struct sysmmu_fault_info *finfo; const struct sysmmu_fault_info *finfo;
unsigned int i, n, itype; unsigned int i, n, itype;
sysmmu_iova_t fault_addr = -1; sysmmu_iova_t fault_addr;
unsigned short reg_status, reg_clear; unsigned short reg_status, reg_clear;
int ret = -ENOSYS; int ret = -ENOSYS;
......
...@@ -63,19 +63,6 @@ static const struct of_device_id l3_device_ids[] = { ...@@ -63,19 +63,6 @@ static const struct of_device_id l3_device_ids[] = {
/* maximum subwindows permitted per liodn */ /* maximum subwindows permitted per liodn */
static u32 max_subwindow_count; static u32 max_subwindow_count;
/* Pool for fspi allocation */
static struct gen_pool *spaace_pool;
/**
* pamu_get_max_subwin_cnt() - Return the maximum supported
* subwindow count per liodn.
*
*/
u32 pamu_get_max_subwin_cnt(void)
{
return max_subwindow_count;
}
/** /**
* pamu_get_ppaace() - Return the primary PACCE * pamu_get_ppaace() - Return the primary PACCE
* @liodn: liodn PAACT index for desired PAACE * @liodn: liodn PAACT index for desired PAACE
...@@ -155,13 +142,6 @@ static unsigned int map_addrspace_size_to_wse(phys_addr_t addrspace_size) ...@@ -155,13 +142,6 @@ static unsigned int map_addrspace_size_to_wse(phys_addr_t addrspace_size)
return fls64(addrspace_size) - 2; return fls64(addrspace_size) - 2;
} }
/* Derive the PAACE window count encoding for the subwindow count */
static unsigned int map_subwindow_cnt_to_wce(u32 subwindow_cnt)
{
/* window count is 2^(WCE+1) bytes */
return __ffs(subwindow_cnt) - 1;
}
/* /*
* Set the PAACE type as primary and set the coherency required domain * Set the PAACE type as primary and set the coherency required domain
* attribute * attribute
...@@ -174,89 +154,11 @@ static void pamu_init_ppaace(struct paace *ppaace) ...@@ -174,89 +154,11 @@ static void pamu_init_ppaace(struct paace *ppaace)
PAACE_M_COHERENCE_REQ); PAACE_M_COHERENCE_REQ);
} }
/*
* Set the PAACE type as secondary and set the coherency required domain
* attribute.
*/
static void pamu_init_spaace(struct paace *spaace)
{
set_bf(spaace->addr_bitfields, PAACE_AF_PT, PAACE_PT_SECONDARY);
set_bf(spaace->domain_attr.to_host.coherency_required, PAACE_DA_HOST_CR,
PAACE_M_COHERENCE_REQ);
}
/*
* Return the spaace (corresponding to the secondary window index)
* for a particular ppaace.
*/
static struct paace *pamu_get_spaace(struct paace *paace, u32 wnum)
{
u32 subwin_cnt;
struct paace *spaace = NULL;
subwin_cnt = 1UL << (get_bf(paace->impl_attr, PAACE_IA_WCE) + 1);
if (wnum < subwin_cnt)
spaace = &spaact[paace->fspi + wnum];
else
pr_debug("secondary paace out of bounds\n");
return spaace;
}
/**
* pamu_get_fspi_and_allocate() - Allocates fspi index and reserves subwindows
* required for primary PAACE in the secondary
* PAACE table.
* @subwin_cnt: Number of subwindows to be reserved.
*
* A PPAACE entry may have a number of associated subwindows. A subwindow
* corresponds to a SPAACE entry in the SPAACT table. Each PAACE entry stores
* the index (fspi) of the first SPAACE entry in the SPAACT table. This
* function returns the index of the first SPAACE entry. The remaining
* SPAACE entries are reserved contiguously from that index.
*
* Returns a valid fspi index in the range of 0 - SPAACE_NUMBER_ENTRIES on success.
* If no SPAACE entry is available or the allocator can not reserve the required
* number of contiguous entries function returns ULONG_MAX indicating a failure.
*
*/
static unsigned long pamu_get_fspi_and_allocate(u32 subwin_cnt)
{
unsigned long spaace_addr;
spaace_addr = gen_pool_alloc(spaace_pool, subwin_cnt * sizeof(struct paace));
if (!spaace_addr)
return ULONG_MAX;
return (spaace_addr - (unsigned long)spaact) / (sizeof(struct paace));
}
/* Release the subwindows reserved for a particular LIODN */
void pamu_free_subwins(int liodn)
{
struct paace *ppaace;
u32 subwin_cnt, size;
ppaace = pamu_get_ppaace(liodn);
if (!ppaace) {
pr_debug("Invalid liodn entry\n");
return;
}
if (get_bf(ppaace->addr_bitfields, PPAACE_AF_MW)) {
subwin_cnt = 1UL << (get_bf(ppaace->impl_attr, PAACE_IA_WCE) + 1);
size = (subwin_cnt - 1) * sizeof(struct paace);
gen_pool_free(spaace_pool, (unsigned long)&spaact[ppaace->fspi], size);
set_bf(ppaace->addr_bitfields, PPAACE_AF_MW, 0);
}
}
/* /*
* Function used for updating stash destination for the coressponding * Function used for updating stash destination for the coressponding
* LIODN. * LIODN.
*/ */
int pamu_update_paace_stash(int liodn, u32 subwin, u32 value) int pamu_update_paace_stash(int liodn, u32 value)
{ {
struct paace *paace; struct paace *paace;
...@@ -265,11 +167,6 @@ int pamu_update_paace_stash(int liodn, u32 subwin, u32 value) ...@@ -265,11 +167,6 @@ int pamu_update_paace_stash(int liodn, u32 subwin, u32 value)
pr_debug("Invalid liodn entry\n"); pr_debug("Invalid liodn entry\n");
return -ENOENT; return -ENOENT;
} }
if (subwin) {
paace = pamu_get_spaace(paace, subwin - 1);
if (!paace)
return -ENOENT;
}
set_bf(paace->impl_attr, PAACE_IA_CID, value); set_bf(paace->impl_attr, PAACE_IA_CID, value);
mb(); mb();
...@@ -277,65 +174,20 @@ int pamu_update_paace_stash(int liodn, u32 subwin, u32 value) ...@@ -277,65 +174,20 @@ int pamu_update_paace_stash(int liodn, u32 subwin, u32 value)
return 0; return 0;
} }
/* Disable a subwindow corresponding to the LIODN */
int pamu_disable_spaace(int liodn, u32 subwin)
{
struct paace *paace;
paace = pamu_get_ppaace(liodn);
if (!paace) {
pr_debug("Invalid liodn entry\n");
return -ENOENT;
}
if (subwin) {
paace = pamu_get_spaace(paace, subwin - 1);
if (!paace)
return -ENOENT;
set_bf(paace->addr_bitfields, PAACE_AF_V, PAACE_V_INVALID);
} else {
set_bf(paace->addr_bitfields, PAACE_AF_AP,
PAACE_AP_PERMS_DENIED);
}
mb();
return 0;
}
/** /**
* pamu_config_paace() - Sets up PPAACE entry for specified liodn * pamu_config_paace() - Sets up PPAACE entry for specified liodn
* *
* @liodn: Logical IO device number * @liodn: Logical IO device number
* @win_addr: starting address of DSA window
* @win-size: size of DSA window
* @omi: Operation mapping index -- if ~omi == 0 then omi not defined * @omi: Operation mapping index -- if ~omi == 0 then omi not defined
* @rpn: real (true physical) page number
* @stashid: cache stash id for associated cpu -- if ~stashid == 0 then * @stashid: cache stash id for associated cpu -- if ~stashid == 0 then
* stashid not defined * stashid not defined
* @snoopid: snoop id for hardware coherency -- if ~snoopid == 0 then
* snoopid not defined
* @subwin_cnt: number of sub-windows
* @prot: window permissions * @prot: window permissions
* *
* Returns 0 upon success else error code < 0 returned * Returns 0 upon success else error code < 0 returned
*/ */
int pamu_config_ppaace(int liodn, phys_addr_t win_addr, phys_addr_t win_size, int pamu_config_ppaace(int liodn, u32 omi, u32 stashid, int prot)
u32 omi, unsigned long rpn, u32 snoopid, u32 stashid,
u32 subwin_cnt, int prot)
{ {
struct paace *ppaace; struct paace *ppaace;
unsigned long fspi;
if ((win_size & (win_size - 1)) || win_size < PAMU_PAGE_SIZE) {
pr_debug("window size too small or not a power of two %pa\n",
&win_size);
return -EINVAL;
}
if (win_addr & (win_size - 1)) {
pr_debug("window address is not aligned with window size\n");
return -EINVAL;
}
ppaace = pamu_get_ppaace(liodn); ppaace = pamu_get_ppaace(liodn);
if (!ppaace) if (!ppaace)
...@@ -343,13 +195,12 @@ int pamu_config_ppaace(int liodn, phys_addr_t win_addr, phys_addr_t win_size, ...@@ -343,13 +195,12 @@ int pamu_config_ppaace(int liodn, phys_addr_t win_addr, phys_addr_t win_size,
/* window size is 2^(WSE+1) bytes */ /* window size is 2^(WSE+1) bytes */
set_bf(ppaace->addr_bitfields, PPAACE_AF_WSE, set_bf(ppaace->addr_bitfields, PPAACE_AF_WSE,
map_addrspace_size_to_wse(win_size)); map_addrspace_size_to_wse(1ULL << 36));
pamu_init_ppaace(ppaace); pamu_init_ppaace(ppaace);
ppaace->wbah = win_addr >> (PAMU_PAGE_SHIFT + 20); ppaace->wbah = 0;
set_bf(ppaace->addr_bitfields, PPAACE_AF_WBAL, set_bf(ppaace->addr_bitfields, PPAACE_AF_WBAL, 0);
(win_addr >> PAMU_PAGE_SHIFT));
/* set up operation mapping if it's configured */ /* set up operation mapping if it's configured */
if (omi < OME_NUMBER_ENTRIES) { if (omi < OME_NUMBER_ENTRIES) {
...@@ -364,120 +215,12 @@ int pamu_config_ppaace(int liodn, phys_addr_t win_addr, phys_addr_t win_size, ...@@ -364,120 +215,12 @@ int pamu_config_ppaace(int liodn, phys_addr_t win_addr, phys_addr_t win_size,
if (~stashid != 0) if (~stashid != 0)
set_bf(ppaace->impl_attr, PAACE_IA_CID, stashid); set_bf(ppaace->impl_attr, PAACE_IA_CID, stashid);
/* configure snoop id */ set_bf(ppaace->impl_attr, PAACE_IA_ATM, PAACE_ATM_WINDOW_XLATE);
if (~snoopid != 0) ppaace->twbah = 0;
ppaace->domain_attr.to_host.snpid = snoopid; set_bf(ppaace->win_bitfields, PAACE_WIN_TWBAL, 0);
set_bf(ppaace->addr_bitfields, PAACE_AF_AP, prot);
if (subwin_cnt) { set_bf(ppaace->impl_attr, PAACE_IA_WCE, 0);
/* The first entry is in the primary PAACE instead */ set_bf(ppaace->addr_bitfields, PPAACE_AF_MW, 0);
fspi = pamu_get_fspi_and_allocate(subwin_cnt - 1);
if (fspi == ULONG_MAX) {
pr_debug("spaace indexes exhausted\n");
return -EINVAL;
}
/* window count is 2^(WCE+1) bytes */
set_bf(ppaace->impl_attr, PAACE_IA_WCE,
map_subwindow_cnt_to_wce(subwin_cnt));
set_bf(ppaace->addr_bitfields, PPAACE_AF_MW, 0x1);
ppaace->fspi = fspi;
} else {
set_bf(ppaace->impl_attr, PAACE_IA_ATM, PAACE_ATM_WINDOW_XLATE);
ppaace->twbah = rpn >> 20;
set_bf(ppaace->win_bitfields, PAACE_WIN_TWBAL, rpn);
set_bf(ppaace->addr_bitfields, PAACE_AF_AP, prot);
set_bf(ppaace->impl_attr, PAACE_IA_WCE, 0);
set_bf(ppaace->addr_bitfields, PPAACE_AF_MW, 0);
}
mb();
return 0;
}
/**
* pamu_config_spaace() - Sets up SPAACE entry for specified subwindow
*
* @liodn: Logical IO device number
* @subwin_cnt: number of sub-windows associated with dma-window
* @subwin: subwindow index
* @subwin_size: size of subwindow
* @omi: Operation mapping index
* @rpn: real (true physical) page number
* @snoopid: snoop id for hardware coherency -- if ~snoopid == 0 then
* snoopid not defined
* @stashid: cache stash id for associated cpu
* @enable: enable/disable subwindow after reconfiguration
* @prot: sub window permissions
*
* Returns 0 upon success else error code < 0 returned
*/
int pamu_config_spaace(int liodn, u32 subwin_cnt, u32 subwin,
phys_addr_t subwin_size, u32 omi, unsigned long rpn,
u32 snoopid, u32 stashid, int enable, int prot)
{
struct paace *paace;
/* setup sub-windows */
if (!subwin_cnt) {
pr_debug("Invalid subwindow count\n");
return -EINVAL;
}
paace = pamu_get_ppaace(liodn);
if (subwin > 0 && subwin < subwin_cnt && paace) {
paace = pamu_get_spaace(paace, subwin - 1);
if (paace && !(paace->addr_bitfields & PAACE_V_VALID)) {
pamu_init_spaace(paace);
set_bf(paace->addr_bitfields, SPAACE_AF_LIODN, liodn);
}
}
if (!paace) {
pr_debug("Invalid liodn entry\n");
return -ENOENT;
}
if ((subwin_size & (subwin_size - 1)) || subwin_size < PAMU_PAGE_SIZE) {
pr_debug("subwindow size out of range, or not a power of 2\n");
return -EINVAL;
}
if (rpn == ULONG_MAX) {
pr_debug("real page number out of range\n");
return -EINVAL;
}
/* window size is 2^(WSE+1) bytes */
set_bf(paace->win_bitfields, PAACE_WIN_SWSE,
map_addrspace_size_to_wse(subwin_size));
set_bf(paace->impl_attr, PAACE_IA_ATM, PAACE_ATM_WINDOW_XLATE);
paace->twbah = rpn >> 20;
set_bf(paace->win_bitfields, PAACE_WIN_TWBAL, rpn);
set_bf(paace->addr_bitfields, PAACE_AF_AP, prot);
/* configure snoop id */
if (~snoopid != 0)
paace->domain_attr.to_host.snpid = snoopid;
/* set up operation mapping if it's configured */
if (omi < OME_NUMBER_ENTRIES) {
set_bf(paace->impl_attr, PAACE_IA_OTM, PAACE_OTM_INDEXED);
paace->op_encode.index_ot.omi = omi;
} else if (~omi != 0) {
pr_debug("bad operation mapping index: %d\n", omi);
return -EINVAL;
}
if (~stashid != 0)
set_bf(paace->impl_attr, PAACE_IA_CID, stashid);
smp_wmb();
if (enable)
set_bf(paace->addr_bitfields, PAACE_AF_V, PAACE_V_VALID);
mb(); mb();
return 0; return 0;
...@@ -1129,17 +872,6 @@ static int fsl_pamu_probe(struct platform_device *pdev) ...@@ -1129,17 +872,6 @@ static int fsl_pamu_probe(struct platform_device *pdev)
spaact_phys = virt_to_phys(spaact); spaact_phys = virt_to_phys(spaact);
omt_phys = virt_to_phys(omt); omt_phys = virt_to_phys(omt);
spaace_pool = gen_pool_create(ilog2(sizeof(struct paace)), -1);
if (!spaace_pool) {
ret = -ENOMEM;
dev_err(dev, "Failed to allocate spaace gen pool\n");
goto error;
}
ret = gen_pool_add(spaace_pool, (unsigned long)spaact, SPAACT_SIZE, -1);
if (ret)
goto error_genpool;
pamubypenr = in_be32(&guts_regs->pamubypenr); pamubypenr = in_be32(&guts_regs->pamubypenr);
for (pamu_reg_off = 0, pamu_counter = 0x80000000; pamu_reg_off < size; for (pamu_reg_off = 0, pamu_counter = 0x80000000; pamu_reg_off < size;
...@@ -1167,9 +899,6 @@ static int fsl_pamu_probe(struct platform_device *pdev) ...@@ -1167,9 +899,6 @@ static int fsl_pamu_probe(struct platform_device *pdev)
return 0; return 0;
error_genpool:
gen_pool_destroy(spaace_pool);
error: error:
if (irq != NO_IRQ) if (irq != NO_IRQ)
free_irq(irq, data); free_irq(irq, data);
......
...@@ -383,18 +383,10 @@ struct ome { ...@@ -383,18 +383,10 @@ struct ome {
int pamu_domain_init(void); int pamu_domain_init(void);
int pamu_enable_liodn(int liodn); int pamu_enable_liodn(int liodn);
int pamu_disable_liodn(int liodn); int pamu_disable_liodn(int liodn);
void pamu_free_subwins(int liodn); int pamu_config_ppaace(int liodn, u32 omi, uint32_t stashid, int prot);
int pamu_config_ppaace(int liodn, phys_addr_t win_addr, phys_addr_t win_size,
u32 omi, unsigned long rpn, u32 snoopid, uint32_t stashid,
u32 subwin_cnt, int prot);
int pamu_config_spaace(int liodn, u32 subwin_cnt, u32 subwin_addr,
phys_addr_t subwin_size, u32 omi, unsigned long rpn,
uint32_t snoopid, u32 stashid, int enable, int prot);
u32 get_stash_id(u32 stash_dest_hint, u32 vcpu); u32 get_stash_id(u32 stash_dest_hint, u32 vcpu);
void get_ome_index(u32 *omi_index, struct device *dev); void get_ome_index(u32 *omi_index, struct device *dev);
int pamu_update_paace_stash(int liodn, u32 subwin, u32 value); int pamu_update_paace_stash(int liodn, u32 value);
int pamu_disable_spaace(int liodn, u32 subwin);
u32 pamu_get_max_subwin_cnt(void);
#endif /* __FSL_PAMU_H */ #endif /* __FSL_PAMU_H */
此差异已折叠。
...@@ -9,56 +9,10 @@ ...@@ -9,56 +9,10 @@
#include "fsl_pamu.h" #include "fsl_pamu.h"
struct dma_window {
phys_addr_t paddr;
u64 size;
int valid;
int prot;
};
struct fsl_dma_domain { struct fsl_dma_domain {
/*
* Indicates the geometry size for the domain.
* This would be set when the geometry is
* configured for the domain.
*/
dma_addr_t geom_size;
/*
* Number of windows assocaited with this domain.
* During domain initialization, it is set to the
* the maximum number of subwindows allowed for a LIODN.
* Minimum value for this is 1 indicating a single PAMU
* window, without any sub windows. Value can be set/
* queried by set_attr/get_attr API for DOMAIN_ATTR_WINDOWS.
* Value can only be set once the geometry has been configured.
*/
u32 win_cnt;
/*
* win_arr contains information of the configured
* windows for a domain. This is allocated only
* when the number of windows for the domain are
* set.
*/
struct dma_window *win_arr;
/* list of devices associated with the domain */ /* list of devices associated with the domain */
struct list_head devices; struct list_head devices;
/* dma_domain states:
* mapped - A particular mapping has been created
* within the configured geometry.
* enabled - DMA has been enabled for the given
* domain. This translates to setting of the
* valid bit for the primary PAACE in the PAMU
* PAACT table. Domain geometry should be set and
* it must have a valid mapping before DMA can be
* enabled for it.
*
*/
int mapped;
int enabled;
/* stash_id obtained from the stash attribute details */
u32 stash_id; u32 stash_id;
struct pamu_stash_attribute dma_stash;
u32 snoop_id;
struct iommu_domain iommu_domain; struct iommu_domain iommu_domain;
spinlock_t domain_lock; spinlock_t domain_lock;
}; };
......
...@@ -1205,6 +1205,63 @@ static inline void reclaim_free_desc(struct q_inval *qi) ...@@ -1205,6 +1205,63 @@ static inline void reclaim_free_desc(struct q_inval *qi)
} }
} }
static const char *qi_type_string(u8 type)
{
switch (type) {
case QI_CC_TYPE:
return "Context-cache Invalidation";
case QI_IOTLB_TYPE:
return "IOTLB Invalidation";
case QI_DIOTLB_TYPE:
return "Device-TLB Invalidation";
case QI_IEC_TYPE:
return "Interrupt Entry Cache Invalidation";
case QI_IWD_TYPE:
return "Invalidation Wait";
case QI_EIOTLB_TYPE:
return "PASID-based IOTLB Invalidation";
case QI_PC_TYPE:
return "PASID-cache Invalidation";
case QI_DEIOTLB_TYPE:
return "PASID-based Device-TLB Invalidation";
case QI_PGRP_RESP_TYPE:
return "Page Group Response";
default:
return "UNKNOWN";
}
}
static void qi_dump_fault(struct intel_iommu *iommu, u32 fault)
{
unsigned int head = dmar_readl(iommu->reg + DMAR_IQH_REG);
u64 iqe_err = dmar_readq(iommu->reg + DMAR_IQER_REG);
struct qi_desc *desc = iommu->qi->desc + head;
if (fault & DMA_FSTS_IQE)
pr_err("VT-d detected Invalidation Queue Error: Reason %llx",
DMAR_IQER_REG_IQEI(iqe_err));
if (fault & DMA_FSTS_ITE)
pr_err("VT-d detected Invalidation Time-out Error: SID %llx",
DMAR_IQER_REG_ITESID(iqe_err));
if (fault & DMA_FSTS_ICE)
pr_err("VT-d detected Invalidation Completion Error: SID %llx",
DMAR_IQER_REG_ICESID(iqe_err));
pr_err("QI HEAD: %s qw0 = 0x%llx, qw1 = 0x%llx\n",
qi_type_string(desc->qw0 & 0xf),
(unsigned long long)desc->qw0,
(unsigned long long)desc->qw1);
head = ((head >> qi_shift(iommu)) + QI_LENGTH - 1) % QI_LENGTH;
head <<= qi_shift(iommu);
desc = iommu->qi->desc + head;
pr_err("QI PRIOR: %s qw0 = 0x%llx, qw1 = 0x%llx\n",
qi_type_string(desc->qw0 & 0xf),
(unsigned long long)desc->qw0,
(unsigned long long)desc->qw1);
}
static int qi_check_fault(struct intel_iommu *iommu, int index, int wait_index) static int qi_check_fault(struct intel_iommu *iommu, int index, int wait_index)
{ {
u32 fault; u32 fault;
...@@ -1216,6 +1273,8 @@ static int qi_check_fault(struct intel_iommu *iommu, int index, int wait_index) ...@@ -1216,6 +1273,8 @@ static int qi_check_fault(struct intel_iommu *iommu, int index, int wait_index)
return -EAGAIN; return -EAGAIN;
fault = readl(iommu->reg + DMAR_FSTS_REG); fault = readl(iommu->reg + DMAR_FSTS_REG);
if (fault & (DMA_FSTS_IQE | DMA_FSTS_ITE | DMA_FSTS_ICE))
qi_dump_fault(iommu, fault);
/* /*
* If IQE happens, the head points to the descriptor associated * If IQE happens, the head points to the descriptor associated
...@@ -1232,12 +1291,10 @@ static int qi_check_fault(struct intel_iommu *iommu, int index, int wait_index) ...@@ -1232,12 +1291,10 @@ static int qi_check_fault(struct intel_iommu *iommu, int index, int wait_index)
* used by software as private data. We won't print * used by software as private data. We won't print
* out these two qw's for security consideration. * out these two qw's for security consideration.
*/ */
pr_err("VT-d detected invalid descriptor: qw0 = %llx, qw1 = %llx\n",
(unsigned long long)desc->qw0,
(unsigned long long)desc->qw1);
memcpy(desc, qi->desc + (wait_index << shift), memcpy(desc, qi->desc + (wait_index << shift),
1 << shift); 1 << shift);
writel(DMA_FSTS_IQE, iommu->reg + DMAR_FSTS_REG); writel(DMA_FSTS_IQE, iommu->reg + DMAR_FSTS_REG);
pr_info("Invalidation Queue Error (IQE) cleared\n");
return -EINVAL; return -EINVAL;
} }
} }
...@@ -1254,6 +1311,7 @@ static int qi_check_fault(struct intel_iommu *iommu, int index, int wait_index) ...@@ -1254,6 +1311,7 @@ static int qi_check_fault(struct intel_iommu *iommu, int index, int wait_index)
tail = ((tail >> shift) - 1 + QI_LENGTH) % QI_LENGTH; tail = ((tail >> shift) - 1 + QI_LENGTH) % QI_LENGTH;
writel(DMA_FSTS_ITE, iommu->reg + DMAR_FSTS_REG); writel(DMA_FSTS_ITE, iommu->reg + DMAR_FSTS_REG);
pr_info("Invalidation Time-out Error (ITE) cleared\n");
do { do {
if (qi->desc_status[head] == QI_IN_USE) if (qi->desc_status[head] == QI_IN_USE)
...@@ -1265,8 +1323,10 @@ static int qi_check_fault(struct intel_iommu *iommu, int index, int wait_index) ...@@ -1265,8 +1323,10 @@ static int qi_check_fault(struct intel_iommu *iommu, int index, int wait_index)
return -EAGAIN; return -EAGAIN;
} }
if (fault & DMA_FSTS_ICE) if (fault & DMA_FSTS_ICE) {
writel(DMA_FSTS_ICE, iommu->reg + DMAR_FSTS_REG); writel(DMA_FSTS_ICE, iommu->reg + DMAR_FSTS_REG);
pr_info("Invalidation Completion Error (ICE) cleared\n");
}
return 0; return 0;
} }
......
...@@ -360,7 +360,6 @@ int intel_iommu_enabled = 0; ...@@ -360,7 +360,6 @@ int intel_iommu_enabled = 0;
EXPORT_SYMBOL_GPL(intel_iommu_enabled); EXPORT_SYMBOL_GPL(intel_iommu_enabled);
static int dmar_map_gfx = 1; static int dmar_map_gfx = 1;
static int dmar_forcedac;
static int intel_iommu_strict; static int intel_iommu_strict;
static int intel_iommu_superpage = 1; static int intel_iommu_superpage = 1;
static int iommu_identity_mapping; static int iommu_identity_mapping;
...@@ -451,8 +450,8 @@ static int __init intel_iommu_setup(char *str) ...@@ -451,8 +450,8 @@ static int __init intel_iommu_setup(char *str)
dmar_map_gfx = 0; dmar_map_gfx = 0;
pr_info("Disable GFX device mapping\n"); pr_info("Disable GFX device mapping\n");
} else if (!strncmp(str, "forcedac", 8)) { } else if (!strncmp(str, "forcedac", 8)) {
pr_info("Forcing DAC for PCI devices\n"); pr_warn("intel_iommu=forcedac deprecated; use iommu.forcedac instead\n");
dmar_forcedac = 1; iommu_dma_forcedac = true;
} else if (!strncmp(str, "strict", 6)) { } else if (!strncmp(str, "strict", 6)) {
pr_info("Disable batched IOTLB flush\n"); pr_info("Disable batched IOTLB flush\n");
intel_iommu_strict = 1; intel_iommu_strict = 1;
...@@ -658,7 +657,14 @@ static int domain_update_iommu_snooping(struct intel_iommu *skip) ...@@ -658,7 +657,14 @@ static int domain_update_iommu_snooping(struct intel_iommu *skip)
rcu_read_lock(); rcu_read_lock();
for_each_active_iommu(iommu, drhd) { for_each_active_iommu(iommu, drhd) {
if (iommu != skip) { if (iommu != skip) {
if (!ecap_sc_support(iommu->ecap)) { /*
* If the hardware is operating in the scalable mode,
* the snooping control is always supported since we
* always set PASID-table-entry.PGSNP bit if the domain
* is managed outside (UNMANAGED).
*/
if (!sm_supported(iommu) &&
!ecap_sc_support(iommu->ecap)) {
ret = 0; ret = 0;
break; break;
} }
...@@ -1340,6 +1346,11 @@ static void iommu_set_root_entry(struct intel_iommu *iommu) ...@@ -1340,6 +1346,11 @@ static void iommu_set_root_entry(struct intel_iommu *iommu)
readl, (sts & DMA_GSTS_RTPS), sts); readl, (sts & DMA_GSTS_RTPS), sts);
raw_spin_unlock_irqrestore(&iommu->register_lock, flag); raw_spin_unlock_irqrestore(&iommu->register_lock, flag);
iommu->flush.flush_context(iommu, 0, 0, 0, DMA_CCMD_GLOBAL_INVL);
if (sm_supported(iommu))
qi_flush_pasid_cache(iommu, 0, QI_PC_GLOBAL, 0);
iommu->flush.flush_iotlb(iommu, 0, 0, 0, DMA_TLB_GLOBAL_FLUSH);
} }
void iommu_flush_write_buffer(struct intel_iommu *iommu) void iommu_flush_write_buffer(struct intel_iommu *iommu)
...@@ -2289,6 +2300,41 @@ static inline int hardware_largepage_caps(struct dmar_domain *domain, ...@@ -2289,6 +2300,41 @@ static inline int hardware_largepage_caps(struct dmar_domain *domain,
return level; return level;
} }
/*
* Ensure that old small page tables are removed to make room for superpage(s).
* We're going to add new large pages, so make sure we don't remove their parent
* tables. The IOTLB/devTLBs should be flushed if any PDE/PTEs are cleared.
*/
static void switch_to_super_page(struct dmar_domain *domain,
unsigned long start_pfn,
unsigned long end_pfn, int level)
{
unsigned long lvl_pages = lvl_to_nr_pages(level);
struct dma_pte *pte = NULL;
int i;
while (start_pfn <= end_pfn) {
if (!pte)
pte = pfn_to_dma_pte(domain, start_pfn, &level);
if (dma_pte_present(pte)) {
dma_pte_free_pagetable(domain, start_pfn,
start_pfn + lvl_pages - 1,
level + 1);
for_each_domain_iommu(i, domain)
iommu_flush_iotlb_psi(g_iommus[i], domain,
start_pfn, lvl_pages,
0, 0);
}
pte++;
start_pfn += lvl_pages;
if (first_pte_in_page(pte))
pte = NULL;
}
}
static int static int
__domain_mapping(struct dmar_domain *domain, unsigned long iov_pfn, __domain_mapping(struct dmar_domain *domain, unsigned long iov_pfn,
unsigned long phys_pfn, unsigned long nr_pages, int prot) unsigned long phys_pfn, unsigned long nr_pages, int prot)
...@@ -2305,8 +2351,9 @@ __domain_mapping(struct dmar_domain *domain, unsigned long iov_pfn, ...@@ -2305,8 +2351,9 @@ __domain_mapping(struct dmar_domain *domain, unsigned long iov_pfn,
return -EINVAL; return -EINVAL;
attr = prot & (DMA_PTE_READ | DMA_PTE_WRITE | DMA_PTE_SNP); attr = prot & (DMA_PTE_READ | DMA_PTE_WRITE | DMA_PTE_SNP);
attr |= DMA_FL_PTE_PRESENT;
if (domain_use_first_level(domain)) { if (domain_use_first_level(domain)) {
attr |= DMA_FL_PTE_PRESENT | DMA_FL_PTE_XD | DMA_FL_PTE_US; attr |= DMA_FL_PTE_XD | DMA_FL_PTE_US;
if (domain->domain.type == IOMMU_DOMAIN_DMA) { if (domain->domain.type == IOMMU_DOMAIN_DMA) {
attr |= DMA_FL_PTE_ACCESS; attr |= DMA_FL_PTE_ACCESS;
...@@ -2329,22 +2376,11 @@ __domain_mapping(struct dmar_domain *domain, unsigned long iov_pfn, ...@@ -2329,22 +2376,11 @@ __domain_mapping(struct dmar_domain *domain, unsigned long iov_pfn,
return -ENOMEM; return -ENOMEM;
/* It is large page*/ /* It is large page*/
if (largepage_lvl > 1) { if (largepage_lvl > 1) {
unsigned long nr_superpages, end_pfn; unsigned long end_pfn;
pteval |= DMA_PTE_LARGE_PAGE; pteval |= DMA_PTE_LARGE_PAGE;
lvl_pages = lvl_to_nr_pages(largepage_lvl); end_pfn = ((iov_pfn + nr_pages) & level_mask(largepage_lvl)) - 1;
switch_to_super_page(domain, iov_pfn, end_pfn, largepage_lvl);
nr_superpages = nr_pages / lvl_pages;
end_pfn = iov_pfn + nr_superpages * lvl_pages - 1;
/*
* Ensure that old small page tables are
* removed to make room for superpage(s).
* We're adding new large pages, so make sure
* we don't remove their parent tables.
*/
dma_pte_free_pagetable(domain, iov_pfn, end_pfn,
largepage_lvl + 1);
} else { } else {
pteval &= ~(uint64_t)DMA_PTE_LARGE_PAGE; pteval &= ~(uint64_t)DMA_PTE_LARGE_PAGE;
} }
...@@ -2422,6 +2458,10 @@ static void domain_context_clear_one(struct intel_iommu *iommu, u8 bus, u8 devfn ...@@ -2422,6 +2458,10 @@ static void domain_context_clear_one(struct intel_iommu *iommu, u8 bus, u8 devfn
(((u16)bus) << 8) | devfn, (((u16)bus) << 8) | devfn,
DMA_CCMD_MASK_NOBIT, DMA_CCMD_MASK_NOBIT,
DMA_CCMD_DEVICE_INVL); DMA_CCMD_DEVICE_INVL);
if (sm_supported(iommu))
qi_flush_pasid_cache(iommu, did_old, QI_PC_ALL_PASIDS, 0);
iommu->flush.flush_iotlb(iommu, iommu->flush.flush_iotlb(iommu,
did_old, did_old,
0, 0,
...@@ -2505,6 +2545,9 @@ static int domain_setup_first_level(struct intel_iommu *iommu, ...@@ -2505,6 +2545,9 @@ static int domain_setup_first_level(struct intel_iommu *iommu,
flags |= (level == 5) ? PASID_FLAG_FL5LP : 0; flags |= (level == 5) ? PASID_FLAG_FL5LP : 0;
if (domain->domain.type == IOMMU_DOMAIN_UNMANAGED)
flags |= PASID_FLAG_PAGE_SNOOP;
return intel_pasid_setup_first_level(iommu, dev, (pgd_t *)pgd, pasid, return intel_pasid_setup_first_level(iommu, dev, (pgd_t *)pgd, pasid,
domain->iommu_did[iommu->seq_id], domain->iommu_did[iommu->seq_id],
flags); flags);
...@@ -3267,8 +3310,6 @@ static int __init init_dmars(void) ...@@ -3267,8 +3310,6 @@ static int __init init_dmars(void)
register_pasid_allocator(iommu); register_pasid_allocator(iommu);
#endif #endif
iommu_set_root_entry(iommu); iommu_set_root_entry(iommu);
iommu->flush.flush_context(iommu, 0, 0, 0, DMA_CCMD_GLOBAL_INVL);
iommu->flush.flush_iotlb(iommu, 0, 0, 0, DMA_TLB_GLOBAL_FLUSH);
} }
#ifdef CONFIG_INTEL_IOMMU_BROKEN_GFX_WA #ifdef CONFIG_INTEL_IOMMU_BROKEN_GFX_WA
...@@ -3458,12 +3499,7 @@ static int init_iommu_hw(void) ...@@ -3458,12 +3499,7 @@ static int init_iommu_hw(void)
} }
iommu_flush_write_buffer(iommu); iommu_flush_write_buffer(iommu);
iommu_set_root_entry(iommu); iommu_set_root_entry(iommu);
iommu->flush.flush_context(iommu, 0, 0, 0,
DMA_CCMD_GLOBAL_INVL);
iommu->flush.flush_iotlb(iommu, 0, 0, 0, DMA_TLB_GLOBAL_FLUSH);
iommu_enable_translation(iommu); iommu_enable_translation(iommu);
iommu_disable_protect_mem_regions(iommu); iommu_disable_protect_mem_regions(iommu);
} }
...@@ -3846,8 +3882,6 @@ static int intel_iommu_add(struct dmar_drhd_unit *dmaru) ...@@ -3846,8 +3882,6 @@ static int intel_iommu_add(struct dmar_drhd_unit *dmaru)
goto disable_iommu; goto disable_iommu;
iommu_set_root_entry(iommu); iommu_set_root_entry(iommu);
iommu->flush.flush_context(iommu, 0, 0, 0, DMA_CCMD_GLOBAL_INVL);
iommu->flush.flush_iotlb(iommu, 0, 0, 0, DMA_TLB_GLOBAL_FLUSH);
iommu_enable_translation(iommu); iommu_enable_translation(iommu);
iommu_disable_protect_mem_regions(iommu); iommu_disable_protect_mem_regions(iommu);
...@@ -4065,35 +4099,6 @@ static struct notifier_block intel_iommu_memory_nb = { ...@@ -4065,35 +4099,6 @@ static struct notifier_block intel_iommu_memory_nb = {
.priority = 0 .priority = 0
}; };
static void free_all_cpu_cached_iovas(unsigned int cpu)
{
int i;
for (i = 0; i < g_num_of_iommus; i++) {
struct intel_iommu *iommu = g_iommus[i];
struct dmar_domain *domain;
int did;
if (!iommu)
continue;
for (did = 0; did < cap_ndoms(iommu->cap); did++) {
domain = get_iommu_domain(iommu, (u16)did);
if (!domain || domain->domain.type != IOMMU_DOMAIN_DMA)
continue;
iommu_dma_free_cpu_cached_iovas(cpu, &domain->domain);
}
}
}
static int intel_iommu_cpu_dead(unsigned int cpu)
{
free_all_cpu_cached_iovas(cpu);
return 0;
}
static void intel_disable_iommus(void) static void intel_disable_iommus(void)
{ {
struct intel_iommu *iommu = NULL; struct intel_iommu *iommu = NULL;
...@@ -4377,6 +4382,17 @@ int __init intel_iommu_init(void) ...@@ -4377,6 +4382,17 @@ int __init intel_iommu_init(void)
down_read(&dmar_global_lock); down_read(&dmar_global_lock);
for_each_active_iommu(iommu, drhd) { for_each_active_iommu(iommu, drhd) {
/*
* The flush queue implementation does not perform
* page-selective invalidations that are required for efficient
* TLB flushes in virtual environments. The benefit of batching
* is likely to be much lower than the overhead of synchronizing
* the virtual and physical IOMMU page-tables.
*/
if (!intel_iommu_strict && cap_caching_mode(iommu->cap)) {
pr_warn("IOMMU batching is disabled due to virtualization");
intel_iommu_strict = 1;
}
iommu_device_sysfs_add(&iommu->iommu, NULL, iommu_device_sysfs_add(&iommu->iommu, NULL,
intel_iommu_groups, intel_iommu_groups,
"%s", iommu->name); "%s", iommu->name);
...@@ -4385,11 +4401,10 @@ int __init intel_iommu_init(void) ...@@ -4385,11 +4401,10 @@ int __init intel_iommu_init(void)
} }
up_read(&dmar_global_lock); up_read(&dmar_global_lock);
iommu_set_dma_strict(intel_iommu_strict);
bus_set_iommu(&pci_bus_type, &intel_iommu_ops); bus_set_iommu(&pci_bus_type, &intel_iommu_ops);
if (si_domain && !hw_pass_through) if (si_domain && !hw_pass_through)
register_memory_notifier(&intel_iommu_memory_nb); register_memory_notifier(&intel_iommu_memory_nb);
cpuhp_setup_state(CPUHP_IOMMU_INTEL_DEAD, "iommu/intel:dead", NULL,
intel_iommu_cpu_dead);
down_read(&dmar_global_lock); down_read(&dmar_global_lock);
if (probe_acpi_namespace_devices()) if (probe_acpi_namespace_devices())
...@@ -5343,6 +5358,8 @@ static int siov_find_pci_dvsec(struct pci_dev *pdev) ...@@ -5343,6 +5358,8 @@ static int siov_find_pci_dvsec(struct pci_dev *pdev)
static bool static bool
intel_iommu_dev_has_feat(struct device *dev, enum iommu_dev_features feat) intel_iommu_dev_has_feat(struct device *dev, enum iommu_dev_features feat)
{ {
struct device_domain_info *info = get_domain_info(dev);
if (feat == IOMMU_DEV_FEAT_AUX) { if (feat == IOMMU_DEV_FEAT_AUX) {
int ret; int ret;
...@@ -5357,13 +5374,13 @@ intel_iommu_dev_has_feat(struct device *dev, enum iommu_dev_features feat) ...@@ -5357,13 +5374,13 @@ intel_iommu_dev_has_feat(struct device *dev, enum iommu_dev_features feat)
return !!siov_find_pci_dvsec(to_pci_dev(dev)); return !!siov_find_pci_dvsec(to_pci_dev(dev));
} }
if (feat == IOMMU_DEV_FEAT_SVA) { if (feat == IOMMU_DEV_FEAT_IOPF)
struct device_domain_info *info = get_domain_info(dev); return info && info->pri_supported;
if (feat == IOMMU_DEV_FEAT_SVA)
return info && (info->iommu->flags & VTD_FLAG_SVM_CAPABLE) && return info && (info->iommu->flags & VTD_FLAG_SVM_CAPABLE) &&
info->pasid_supported && info->pri_supported && info->pasid_supported && info->pri_supported &&
info->ats_supported; info->ats_supported;
}
return false; return false;
} }
...@@ -5374,12 +5391,18 @@ intel_iommu_dev_enable_feat(struct device *dev, enum iommu_dev_features feat) ...@@ -5374,12 +5391,18 @@ intel_iommu_dev_enable_feat(struct device *dev, enum iommu_dev_features feat)
if (feat == IOMMU_DEV_FEAT_AUX) if (feat == IOMMU_DEV_FEAT_AUX)
return intel_iommu_enable_auxd(dev); return intel_iommu_enable_auxd(dev);
if (feat == IOMMU_DEV_FEAT_IOPF)
return intel_iommu_dev_has_feat(dev, feat) ? 0 : -ENODEV;
if (feat == IOMMU_DEV_FEAT_SVA) { if (feat == IOMMU_DEV_FEAT_SVA) {
struct device_domain_info *info = get_domain_info(dev); struct device_domain_info *info = get_domain_info(dev);
if (!info) if (!info)
return -EINVAL; return -EINVAL;
if (!info->pasid_enabled || !info->pri_enabled || !info->ats_enabled)
return -EINVAL;
if (info->iommu->flags & VTD_FLAG_SVM_CAPABLE) if (info->iommu->flags & VTD_FLAG_SVM_CAPABLE)
return 0; return 0;
} }
...@@ -5423,87 +5446,23 @@ static bool intel_iommu_is_attach_deferred(struct iommu_domain *domain, ...@@ -5423,87 +5446,23 @@ static bool intel_iommu_is_attach_deferred(struct iommu_domain *domain,
} }
static int static int
intel_iommu_domain_set_attr(struct iommu_domain *domain, intel_iommu_enable_nesting(struct iommu_domain *domain)
enum iommu_attr attr, void *data)
{ {
struct dmar_domain *dmar_domain = to_dmar_domain(domain); struct dmar_domain *dmar_domain = to_dmar_domain(domain);
unsigned long flags; unsigned long flags;
int ret = 0; int ret = -ENODEV;
if (domain->type != IOMMU_DOMAIN_UNMANAGED)
return -EINVAL;
switch (attr) { spin_lock_irqsave(&device_domain_lock, flags);
case DOMAIN_ATTR_NESTING: if (nested_mode_support() && list_empty(&dmar_domain->devices)) {
spin_lock_irqsave(&device_domain_lock, flags); dmar_domain->flags |= DOMAIN_FLAG_NESTING_MODE;
if (nested_mode_support() && dmar_domain->flags &= ~DOMAIN_FLAG_USE_FIRST_LEVEL;
list_empty(&dmar_domain->devices)) { ret = 0;
dmar_domain->flags |= DOMAIN_FLAG_NESTING_MODE;
dmar_domain->flags &= ~DOMAIN_FLAG_USE_FIRST_LEVEL;
} else {
ret = -ENODEV;
}
spin_unlock_irqrestore(&device_domain_lock, flags);
break;
default:
ret = -EINVAL;
break;
} }
spin_unlock_irqrestore(&device_domain_lock, flags);
return ret; return ret;
} }
static bool domain_use_flush_queue(void)
{
struct dmar_drhd_unit *drhd;
struct intel_iommu *iommu;
bool r = true;
if (intel_iommu_strict)
return false;
/*
* The flush queue implementation does not perform page-selective
* invalidations that are required for efficient TLB flushes in virtual
* environments. The benefit of batching is likely to be much lower than
* the overhead of synchronizing the virtual and physical IOMMU
* page-tables.
*/
rcu_read_lock();
for_each_active_iommu(iommu, drhd) {
if (!cap_caching_mode(iommu->cap))
continue;
pr_warn_once("IOMMU batching is disabled due to virtualization");
r = false;
break;
}
rcu_read_unlock();
return r;
}
static int
intel_iommu_domain_get_attr(struct iommu_domain *domain,
enum iommu_attr attr, void *data)
{
switch (domain->type) {
case IOMMU_DOMAIN_UNMANAGED:
return -ENODEV;
case IOMMU_DOMAIN_DMA:
switch (attr) {
case DOMAIN_ATTR_DMA_USE_FLUSH_QUEUE:
*(int *)data = domain_use_flush_queue();
return 0;
default:
return -ENODEV;
}
break;
default:
return -EINVAL;
}
}
/* /*
* Check that the device does not live on an external facing PCI port that is * Check that the device does not live on an external facing PCI port that is
* marked as untrusted. Such devices should not be able to apply quirks and * marked as untrusted. Such devices should not be able to apply quirks and
...@@ -5576,8 +5535,7 @@ const struct iommu_ops intel_iommu_ops = { ...@@ -5576,8 +5535,7 @@ const struct iommu_ops intel_iommu_ops = {
.capable = intel_iommu_capable, .capable = intel_iommu_capable,
.domain_alloc = intel_iommu_domain_alloc, .domain_alloc = intel_iommu_domain_alloc,
.domain_free = intel_iommu_domain_free, .domain_free = intel_iommu_domain_free,
.domain_get_attr = intel_iommu_domain_get_attr, .enable_nesting = intel_iommu_enable_nesting,
.domain_set_attr = intel_iommu_domain_set_attr,
.attach_dev = intel_iommu_attach_device, .attach_dev = intel_iommu_attach_device,
.detach_dev = intel_iommu_detach_device, .detach_dev = intel_iommu_detach_device,
.aux_attach_dev = intel_iommu_aux_attach_device, .aux_attach_dev = intel_iommu_aux_attach_device,
......
...@@ -736,7 +736,7 @@ static int __init intel_prepare_irq_remapping(void) ...@@ -736,7 +736,7 @@ static int __init intel_prepare_irq_remapping(void)
return -ENODEV; return -ENODEV;
if (intel_cap_audit(CAP_AUDIT_STATIC_IRQR, NULL)) if (intel_cap_audit(CAP_AUDIT_STATIC_IRQR, NULL))
goto error; return -ENODEV;
if (!dmar_ir_support()) if (!dmar_ir_support())
return -ENODEV; return -ENODEV;
......
...@@ -24,7 +24,6 @@ ...@@ -24,7 +24,6 @@
/* /*
* Intel IOMMU system wide PASID name space: * Intel IOMMU system wide PASID name space:
*/ */
static DEFINE_SPINLOCK(pasid_lock);
u32 intel_pasid_max_id = PASID_MAX; u32 intel_pasid_max_id = PASID_MAX;
int vcmd_alloc_pasid(struct intel_iommu *iommu, u32 *pasid) int vcmd_alloc_pasid(struct intel_iommu *iommu, u32 *pasid)
...@@ -231,7 +230,7 @@ struct pasid_table *intel_pasid_get_table(struct device *dev) ...@@ -231,7 +230,7 @@ struct pasid_table *intel_pasid_get_table(struct device *dev)
return info->pasid_table; return info->pasid_table;
} }
int intel_pasid_get_dev_max_id(struct device *dev) static int intel_pasid_get_dev_max_id(struct device *dev)
{ {
struct device_domain_info *info; struct device_domain_info *info;
...@@ -242,7 +241,7 @@ int intel_pasid_get_dev_max_id(struct device *dev) ...@@ -242,7 +241,7 @@ int intel_pasid_get_dev_max_id(struct device *dev)
return info->pasid_table->max_pasid; return info->pasid_table->max_pasid;
} }
struct pasid_entry *intel_pasid_get_entry(struct device *dev, u32 pasid) static struct pasid_entry *intel_pasid_get_entry(struct device *dev, u32 pasid)
{ {
struct device_domain_info *info; struct device_domain_info *info;
struct pasid_table *pasid_table; struct pasid_table *pasid_table;
...@@ -259,19 +258,25 @@ struct pasid_entry *intel_pasid_get_entry(struct device *dev, u32 pasid) ...@@ -259,19 +258,25 @@ struct pasid_entry *intel_pasid_get_entry(struct device *dev, u32 pasid)
dir_index = pasid >> PASID_PDE_SHIFT; dir_index = pasid >> PASID_PDE_SHIFT;
index = pasid & PASID_PTE_MASK; index = pasid & PASID_PTE_MASK;
spin_lock(&pasid_lock); retry:
entries = get_pasid_table_from_pde(&dir[dir_index]); entries = get_pasid_table_from_pde(&dir[dir_index]);
if (!entries) { if (!entries) {
entries = alloc_pgtable_page(info->iommu->node); entries = alloc_pgtable_page(info->iommu->node);
if (!entries) { if (!entries)
spin_unlock(&pasid_lock);
return NULL; return NULL;
}
WRITE_ONCE(dir[dir_index].val, /*
(u64)virt_to_phys(entries) | PASID_PTE_PRESENT); * The pasid directory table entry won't be freed after
* allocation. No worry about the race with free and
* clear. However, this entry might be populated by others
* while we are preparing it. Use theirs with a retry.
*/
if (cmpxchg64(&dir[dir_index].val, 0ULL,
(u64)virt_to_phys(entries) | PASID_PTE_PRESENT)) {
free_pgtable_page(entries);
goto retry;
}
} }
spin_unlock(&pasid_lock);
return &entries[index]; return &entries[index];
} }
...@@ -393,6 +398,15 @@ static inline void pasid_set_sre(struct pasid_entry *pe) ...@@ -393,6 +398,15 @@ static inline void pasid_set_sre(struct pasid_entry *pe)
pasid_set_bits(&pe->val[2], 1 << 0, 1); pasid_set_bits(&pe->val[2], 1 << 0, 1);
} }
/*
* Setup the WPE(Write Protect Enable) field (Bit 132) of a
* scalable mode PASID entry.
*/
static inline void pasid_set_wpe(struct pasid_entry *pe)
{
pasid_set_bits(&pe->val[2], 1 << 4, 1 << 4);
}
/* /*
* Setup the P(Present) field (Bit 0) of a scalable mode PASID * Setup the P(Present) field (Bit 0) of a scalable mode PASID
* entry. * entry.
...@@ -411,6 +425,16 @@ static inline void pasid_set_page_snoop(struct pasid_entry *pe, bool value) ...@@ -411,6 +425,16 @@ static inline void pasid_set_page_snoop(struct pasid_entry *pe, bool value)
pasid_set_bits(&pe->val[1], 1 << 23, value << 23); pasid_set_bits(&pe->val[1], 1 << 23, value << 23);
} }
/*
* Setup the Page Snoop (PGSNP) field (Bit 88) of a scalable mode
* PASID entry.
*/
static inline void
pasid_set_pgsnp(struct pasid_entry *pe)
{
pasid_set_bits(&pe->val[1], 1ULL << 24, 1ULL << 24);
}
/* /*
* Setup the First Level Page table Pointer field (Bit 140~191) * Setup the First Level Page table Pointer field (Bit 140~191)
* of a scalable mode PASID entry. * of a scalable mode PASID entry.
...@@ -493,6 +517,9 @@ void intel_pasid_tear_down_entry(struct intel_iommu *iommu, struct device *dev, ...@@ -493,6 +517,9 @@ void intel_pasid_tear_down_entry(struct intel_iommu *iommu, struct device *dev,
if (WARN_ON(!pte)) if (WARN_ON(!pte))
return; return;
if (!(pte->val[0] & PASID_PTE_PRESENT))
return;
did = pasid_get_domain_id(pte); did = pasid_get_domain_id(pte);
intel_pasid_clear_entry(dev, pasid, fault_ignore); intel_pasid_clear_entry(dev, pasid, fault_ignore);
...@@ -522,6 +549,22 @@ static void pasid_flush_caches(struct intel_iommu *iommu, ...@@ -522,6 +549,22 @@ static void pasid_flush_caches(struct intel_iommu *iommu,
} }
} }
static inline int pasid_enable_wpe(struct pasid_entry *pte)
{
#ifdef CONFIG_X86
unsigned long cr0 = read_cr0();
/* CR0.WP is normally set but just to be sure */
if (unlikely(!(cr0 & X86_CR0_WP))) {
pr_err_ratelimited("No CPU write protect!\n");
return -EINVAL;
}
#endif
pasid_set_wpe(pte);
return 0;
};
/* /*
* Set up the scalable mode pasid table entry for first only * Set up the scalable mode pasid table entry for first only
* translation type. * translation type.
...@@ -553,6 +596,9 @@ int intel_pasid_setup_first_level(struct intel_iommu *iommu, ...@@ -553,6 +596,9 @@ int intel_pasid_setup_first_level(struct intel_iommu *iommu,
return -EINVAL; return -EINVAL;
} }
pasid_set_sre(pte); pasid_set_sre(pte);
if (pasid_enable_wpe(pte))
return -EINVAL;
} }
if (flags & PASID_FLAG_FL5LP) { if (flags & PASID_FLAG_FL5LP) {
...@@ -565,6 +611,9 @@ int intel_pasid_setup_first_level(struct intel_iommu *iommu, ...@@ -565,6 +611,9 @@ int intel_pasid_setup_first_level(struct intel_iommu *iommu,
} }
} }
if (flags & PASID_FLAG_PAGE_SNOOP)
pasid_set_pgsnp(pte);
pasid_set_domain_id(pte, did); pasid_set_domain_id(pte, did);
pasid_set_address_width(pte, iommu->agaw); pasid_set_address_width(pte, iommu->agaw);
pasid_set_page_snoop(pte, !!ecap_smpwc(iommu->ecap)); pasid_set_page_snoop(pte, !!ecap_smpwc(iommu->ecap));
...@@ -643,6 +692,9 @@ int intel_pasid_setup_second_level(struct intel_iommu *iommu, ...@@ -643,6 +692,9 @@ int intel_pasid_setup_second_level(struct intel_iommu *iommu,
pasid_set_fault_enable(pte); pasid_set_fault_enable(pte);
pasid_set_page_snoop(pte, !!ecap_smpwc(iommu->ecap)); pasid_set_page_snoop(pte, !!ecap_smpwc(iommu->ecap));
if (domain->domain.type == IOMMU_DOMAIN_UNMANAGED)
pasid_set_pgsnp(pte);
/* /*
* Since it is a second level only translation setup, we should * Since it is a second level only translation setup, we should
* set SRE bit as well (addresses are expected to be GPAs). * set SRE bit as well (addresses are expected to be GPAs).
...@@ -706,6 +758,9 @@ intel_pasid_setup_bind_data(struct intel_iommu *iommu, struct pasid_entry *pte, ...@@ -706,6 +758,9 @@ intel_pasid_setup_bind_data(struct intel_iommu *iommu, struct pasid_entry *pte,
return -EINVAL; return -EINVAL;
} }
pasid_set_sre(pte); pasid_set_sre(pte);
/* Enable write protect WP if guest requested */
if (pasid_data->flags & IOMMU_SVA_VTD_GPASID_WPE)
pasid_set_wpe(pte);
} }
if (pasid_data->flags & IOMMU_SVA_VTD_GPASID_EAFE) { if (pasid_data->flags & IOMMU_SVA_VTD_GPASID_EAFE) {
......
...@@ -48,6 +48,7 @@ ...@@ -48,6 +48,7 @@
*/ */
#define PASID_FLAG_SUPERVISOR_MODE BIT(0) #define PASID_FLAG_SUPERVISOR_MODE BIT(0)
#define PASID_FLAG_NESTED BIT(1) #define PASID_FLAG_NESTED BIT(1)
#define PASID_FLAG_PAGE_SNOOP BIT(2)
/* /*
* The PASID_FLAG_FL5LP flag Indicates using 5-level paging for first- * The PASID_FLAG_FL5LP flag Indicates using 5-level paging for first-
...@@ -99,14 +100,9 @@ static inline bool pasid_pte_is_present(struct pasid_entry *pte) ...@@ -99,14 +100,9 @@ static inline bool pasid_pte_is_present(struct pasid_entry *pte)
} }
extern unsigned int intel_pasid_max_id; extern unsigned int intel_pasid_max_id;
int intel_pasid_alloc_id(void *ptr, int start, int end, gfp_t gfp);
void intel_pasid_free_id(u32 pasid);
void *intel_pasid_lookup_id(u32 pasid);
int intel_pasid_alloc_table(struct device *dev); int intel_pasid_alloc_table(struct device *dev);
void intel_pasid_free_table(struct device *dev); void intel_pasid_free_table(struct device *dev);
struct pasid_table *intel_pasid_get_table(struct device *dev); struct pasid_table *intel_pasid_get_table(struct device *dev);
int intel_pasid_get_dev_max_id(struct device *dev);
struct pasid_entry *intel_pasid_get_entry(struct device *dev, u32 pasid);
int intel_pasid_setup_first_level(struct intel_iommu *iommu, int intel_pasid_setup_first_level(struct intel_iommu *iommu,
struct device *dev, pgd_t *pgd, struct device *dev, pgd_t *pgd,
u32 pasid, u16 did, int flags); u32 pasid, u16 did, int flags);
......
...@@ -462,13 +462,12 @@ static void load_pasid(struct mm_struct *mm, u32 pasid) ...@@ -462,13 +462,12 @@ static void load_pasid(struct mm_struct *mm, u32 pasid)
/* Caller must hold pasid_mutex, mm reference */ /* Caller must hold pasid_mutex, mm reference */
static int static int
intel_svm_bind_mm(struct device *dev, unsigned int flags, intel_svm_bind_mm(struct device *dev, unsigned int flags,
struct svm_dev_ops *ops,
struct mm_struct *mm, struct intel_svm_dev **sd) struct mm_struct *mm, struct intel_svm_dev **sd)
{ {
struct intel_iommu *iommu = device_to_iommu(dev, NULL, NULL); struct intel_iommu *iommu = device_to_iommu(dev, NULL, NULL);
struct intel_svm *svm = NULL, *t;
struct device_domain_info *info; struct device_domain_info *info;
struct intel_svm_dev *sdev; struct intel_svm_dev *sdev;
struct intel_svm *svm = NULL;
unsigned long iflags; unsigned long iflags;
int pasid_max; int pasid_max;
int ret; int ret;
...@@ -494,34 +493,26 @@ intel_svm_bind_mm(struct device *dev, unsigned int flags, ...@@ -494,34 +493,26 @@ intel_svm_bind_mm(struct device *dev, unsigned int flags,
} }
} }
if (!(flags & SVM_FLAG_PRIVATE_PASID)) { list_for_each_entry(t, &global_svm_list, list) {
struct intel_svm *t; if (t->mm != mm)
continue;
list_for_each_entry(t, &global_svm_list, list) {
if (t->mm != mm || (t->flags & SVM_FLAG_PRIVATE_PASID))
continue;
svm = t;
if (svm->pasid >= pasid_max) {
dev_warn(dev,
"Limited PASID width. Cannot use existing PASID %d\n",
svm->pasid);
ret = -ENOSPC;
goto out;
}
/* Find the matching device in svm list */ svm = t;
for_each_svm_dev(sdev, svm, dev) { if (svm->pasid >= pasid_max) {
if (sdev->ops != ops) { dev_warn(dev,
ret = -EBUSY; "Limited PASID width. Cannot use existing PASID %d\n",
goto out; svm->pasid);
} ret = -ENOSPC;
sdev->users++; goto out;
goto success; }
}
break; /* Find the matching device in svm list */
for_each_svm_dev(sdev, svm, dev) {
sdev->users++;
goto success;
} }
break;
} }
sdev = kzalloc(sizeof(*sdev), GFP_KERNEL); sdev = kzalloc(sizeof(*sdev), GFP_KERNEL);
...@@ -550,7 +541,6 @@ intel_svm_bind_mm(struct device *dev, unsigned int flags, ...@@ -550,7 +541,6 @@ intel_svm_bind_mm(struct device *dev, unsigned int flags,
/* Finish the setup now we know we're keeping it */ /* Finish the setup now we know we're keeping it */
sdev->users = 1; sdev->users = 1;
sdev->ops = ops;
init_rcu_head(&sdev->rcu); init_rcu_head(&sdev->rcu);
if (!svm) { if (!svm) {
...@@ -862,7 +852,7 @@ intel_svm_prq_report(struct device *dev, struct page_req_dsc *desc) ...@@ -862,7 +852,7 @@ intel_svm_prq_report(struct device *dev, struct page_req_dsc *desc)
/* Fill in event data for device specific processing */ /* Fill in event data for device specific processing */
memset(&event, 0, sizeof(struct iommu_fault_event)); memset(&event, 0, sizeof(struct iommu_fault_event));
event.fault.type = IOMMU_FAULT_PAGE_REQ; event.fault.type = IOMMU_FAULT_PAGE_REQ;
event.fault.prm.addr = desc->addr; event.fault.prm.addr = (u64)desc->addr << VTD_PAGE_SHIFT;
event.fault.prm.pasid = desc->pasid; event.fault.prm.pasid = desc->pasid;
event.fault.prm.grpid = desc->prg_index; event.fault.prm.grpid = desc->prg_index;
event.fault.prm.perm = prq_to_iommu_prot(desc); event.fault.prm.perm = prq_to_iommu_prot(desc);
...@@ -895,6 +885,7 @@ static irqreturn_t prq_event_thread(int irq, void *d) ...@@ -895,6 +885,7 @@ static irqreturn_t prq_event_thread(int irq, void *d)
struct intel_iommu *iommu = d; struct intel_iommu *iommu = d;
struct intel_svm *svm = NULL; struct intel_svm *svm = NULL;
int head, tail, handled = 0; int head, tail, handled = 0;
unsigned int flags = 0;
/* Clear PPR bit before reading head/tail registers, to /* Clear PPR bit before reading head/tail registers, to
* ensure that we get a new interrupt if needed. */ * ensure that we get a new interrupt if needed. */
...@@ -920,7 +911,17 @@ static irqreturn_t prq_event_thread(int irq, void *d) ...@@ -920,7 +911,17 @@ static irqreturn_t prq_event_thread(int irq, void *d)
((unsigned long long *)req)[1]); ((unsigned long long *)req)[1]);
goto no_pasid; goto no_pasid;
} }
/* We shall not receive page request for supervisor SVM */
if (req->pm_req && (req->rd_req | req->wr_req)) {
pr_err("Unexpected page request in Privilege Mode");
/* No need to find the matching sdev as for bad_req */
goto no_pasid;
}
/* DMA read with exec requeset is not supported. */
if (req->exe_req && req->rd_req) {
pr_err("Execution request not supported\n");
goto no_pasid;
}
if (!svm || svm->pasid != req->pasid) { if (!svm || svm->pasid != req->pasid) {
rcu_read_lock(); rcu_read_lock();
svm = ioasid_find(NULL, req->pasid, NULL); svm = ioasid_find(NULL, req->pasid, NULL);
...@@ -982,9 +983,11 @@ static irqreturn_t prq_event_thread(int irq, void *d) ...@@ -982,9 +983,11 @@ static irqreturn_t prq_event_thread(int irq, void *d)
if (access_error(vma, req)) if (access_error(vma, req))
goto invalid; goto invalid;
ret = handle_mm_fault(vma, address, flags = FAULT_FLAG_USER | FAULT_FLAG_REMOTE;
req->wr_req ? FAULT_FLAG_WRITE : 0, if (req->wr_req)
NULL); flags |= FAULT_FLAG_WRITE;
ret = handle_mm_fault(vma, address, flags, NULL);
if (ret & VM_FAULT_ERROR) if (ret & VM_FAULT_ERROR)
goto invalid; goto invalid;
...@@ -993,13 +996,6 @@ static irqreturn_t prq_event_thread(int irq, void *d) ...@@ -993,13 +996,6 @@ static irqreturn_t prq_event_thread(int irq, void *d)
mmap_read_unlock(svm->mm); mmap_read_unlock(svm->mm);
mmput(svm->mm); mmput(svm->mm);
bad_req: bad_req:
WARN_ON(!sdev);
if (sdev && sdev->ops && sdev->ops->fault_cb) {
int rwxp = (req->rd_req << 3) | (req->wr_req << 2) |
(req->exe_req << 1) | (req->pm_req);
sdev->ops->fault_cb(sdev->dev, req->pasid, req->addr,
req->priv_data, rwxp, result);
}
/* We get here in the error case where the PASID lookup failed, /* We get here in the error case where the PASID lookup failed,
and these can be NULL. Do not use them below this point! */ and these can be NULL. Do not use them below this point! */
sdev = NULL; sdev = NULL;
...@@ -1021,12 +1017,12 @@ static irqreturn_t prq_event_thread(int irq, void *d) ...@@ -1021,12 +1017,12 @@ static irqreturn_t prq_event_thread(int irq, void *d)
QI_PGRP_RESP_TYPE; QI_PGRP_RESP_TYPE;
resp.qw1 = QI_PGRP_IDX(req->prg_index) | resp.qw1 = QI_PGRP_IDX(req->prg_index) |
QI_PGRP_LPIG(req->lpig); QI_PGRP_LPIG(req->lpig);
resp.qw2 = 0;
resp.qw3 = 0;
if (req->priv_data_present) if (req->priv_data_present)
memcpy(&resp.qw2, req->priv_data, memcpy(&resp.qw2, req->priv_data,
sizeof(req->priv_data)); sizeof(req->priv_data));
resp.qw2 = 0;
resp.qw3 = 0;
qi_submit_sync(iommu, &resp, 1, 0); qi_submit_sync(iommu, &resp, 1, 0);
} }
prq_advance: prq_advance:
...@@ -1074,7 +1070,7 @@ intel_svm_bind(struct device *dev, struct mm_struct *mm, void *drvdata) ...@@ -1074,7 +1070,7 @@ intel_svm_bind(struct device *dev, struct mm_struct *mm, void *drvdata)
if (drvdata) if (drvdata)
flags = *(unsigned int *)drvdata; flags = *(unsigned int *)drvdata;
mutex_lock(&pasid_mutex); mutex_lock(&pasid_mutex);
ret = intel_svm_bind_mm(dev, flags, NULL, mm, &sdev); ret = intel_svm_bind_mm(dev, flags, mm, &sdev);
if (ret) if (ret)
sva = ERR_PTR(ret); sva = ERR_PTR(ret);
else if (sdev) else if (sdev)
......
// SPDX-License-Identifier: GPL-2.0
/*
* Handle device page faults
*
* Copyright (C) 2020 ARM Ltd.
*/
#include <linux/iommu.h>
#include <linux/list.h>
#include <linux/sched/mm.h>
#include <linux/slab.h>
#include <linux/workqueue.h>
#include "iommu-sva-lib.h"
/**
* struct iopf_queue - IO Page Fault queue
* @wq: the fault workqueue
* @devices: devices attached to this queue
* @lock: protects the device list
*/
struct iopf_queue {
struct workqueue_struct *wq;
struct list_head devices;
struct mutex lock;
};
/**
* struct iopf_device_param - IO Page Fault data attached to a device
* @dev: the device that owns this param
* @queue: IOPF queue
* @queue_list: index into queue->devices
* @partial: faults that are part of a Page Request Group for which the last
* request hasn't been submitted yet.
*/
struct iopf_device_param {
struct device *dev;
struct iopf_queue *queue;
struct list_head queue_list;
struct list_head partial;
};
struct iopf_fault {
struct iommu_fault fault;
struct list_head list;
};
struct iopf_group {
struct iopf_fault last_fault;
struct list_head faults;
struct work_struct work;
struct device *dev;
};
static int iopf_complete_group(struct device *dev, struct iopf_fault *iopf,
enum iommu_page_response_code status)
{
struct iommu_page_response resp = {
.version = IOMMU_PAGE_RESP_VERSION_1,
.pasid = iopf->fault.prm.pasid,
.grpid = iopf->fault.prm.grpid,
.code = status,
};
if ((iopf->fault.prm.flags & IOMMU_FAULT_PAGE_REQUEST_PASID_VALID) &&
(iopf->fault.prm.flags & IOMMU_FAULT_PAGE_RESPONSE_NEEDS_PASID))
resp.flags = IOMMU_PAGE_RESP_PASID_VALID;
return iommu_page_response(dev, &resp);
}
static enum iommu_page_response_code
iopf_handle_single(struct iopf_fault *iopf)
{
vm_fault_t ret;
struct mm_struct *mm;
struct vm_area_struct *vma;
unsigned int access_flags = 0;
unsigned int fault_flags = FAULT_FLAG_REMOTE;
struct iommu_fault_page_request *prm = &iopf->fault.prm;
enum iommu_page_response_code status = IOMMU_PAGE_RESP_INVALID;
if (!(prm->flags & IOMMU_FAULT_PAGE_REQUEST_PASID_VALID))
return status;
mm = iommu_sva_find(prm->pasid);
if (IS_ERR_OR_NULL(mm))
return status;
mmap_read_lock(mm);
vma = find_extend_vma(mm, prm->addr);
if (!vma)
/* Unmapped area */
goto out_put_mm;
if (prm->perm & IOMMU_FAULT_PERM_READ)
access_flags |= VM_READ;
if (prm->perm & IOMMU_FAULT_PERM_WRITE) {
access_flags |= VM_WRITE;
fault_flags |= FAULT_FLAG_WRITE;
}
if (prm->perm & IOMMU_FAULT_PERM_EXEC) {
access_flags |= VM_EXEC;
fault_flags |= FAULT_FLAG_INSTRUCTION;
}
if (!(prm->perm & IOMMU_FAULT_PERM_PRIV))
fault_flags |= FAULT_FLAG_USER;
if (access_flags & ~vma->vm_flags)
/* Access fault */
goto out_put_mm;
ret = handle_mm_fault(vma, prm->addr, fault_flags, NULL);
status = ret & VM_FAULT_ERROR ? IOMMU_PAGE_RESP_INVALID :
IOMMU_PAGE_RESP_SUCCESS;
out_put_mm:
mmap_read_unlock(mm);
mmput(mm);
return status;
}
static void iopf_handle_group(struct work_struct *work)
{
struct iopf_group *group;
struct iopf_fault *iopf, *next;
enum iommu_page_response_code status = IOMMU_PAGE_RESP_SUCCESS;
group = container_of(work, struct iopf_group, work);
list_for_each_entry_safe(iopf, next, &group->faults, list) {
/*
* For the moment, errors are sticky: don't handle subsequent
* faults in the group if there is an error.
*/
if (status == IOMMU_PAGE_RESP_SUCCESS)
status = iopf_handle_single(iopf);
if (!(iopf->fault.prm.flags &
IOMMU_FAULT_PAGE_REQUEST_LAST_PAGE))
kfree(iopf);
}
iopf_complete_group(group->dev, &group->last_fault, status);
kfree(group);
}
/**
* iommu_queue_iopf - IO Page Fault handler
* @fault: fault event
* @cookie: struct device, passed to iommu_register_device_fault_handler.
*
* Add a fault to the device workqueue, to be handled by mm.
*
* This module doesn't handle PCI PASID Stop Marker; IOMMU drivers must discard
* them before reporting faults. A PASID Stop Marker (LRW = 0b100) doesn't
* expect a response. It may be generated when disabling a PASID (issuing a
* PASID stop request) by some PCI devices.
*
* The PASID stop request is issued by the device driver before unbind(). Once
* it completes, no page request is generated for this PASID anymore and
* outstanding ones have been pushed to the IOMMU (as per PCIe 4.0r1.0 - 6.20.1
* and 10.4.1.2 - Managing PASID TLP Prefix Usage). Some PCI devices will wait
* for all outstanding page requests to come back with a response before
* completing the PASID stop request. Others do not wait for page responses, and
* instead issue this Stop Marker that tells us when the PASID can be
* reallocated.
*
* It is safe to discard the Stop Marker because it is an optimization.
* a. Page requests, which are posted requests, have been flushed to the IOMMU
* when the stop request completes.
* b. The IOMMU driver flushes all fault queues on unbind() before freeing the
* PASID.
*
* So even though the Stop Marker might be issued by the device *after* the stop
* request completes, outstanding faults will have been dealt with by the time
* the PASID is freed.
*
* Return: 0 on success and <0 on error.
*/
int iommu_queue_iopf(struct iommu_fault *fault, void *cookie)
{
int ret;
struct iopf_group *group;
struct iopf_fault *iopf, *next;
struct iopf_device_param *iopf_param;
struct device *dev = cookie;
struct dev_iommu *param = dev->iommu;
lockdep_assert_held(&param->lock);
if (fault->type != IOMMU_FAULT_PAGE_REQ)
/* Not a recoverable page fault */
return -EOPNOTSUPP;
/*
* As long as we're holding param->lock, the queue can't be unlinked
* from the device and therefore cannot disappear.
*/
iopf_param = param->iopf_param;
if (!iopf_param)
return -ENODEV;
if (!(fault->prm.flags & IOMMU_FAULT_PAGE_REQUEST_LAST_PAGE)) {
iopf = kzalloc(sizeof(*iopf), GFP_KERNEL);
if (!iopf)
return -ENOMEM;
iopf->fault = *fault;
/* Non-last request of a group. Postpone until the last one */
list_add(&iopf->list, &iopf_param->partial);
return 0;
}
group = kzalloc(sizeof(*group), GFP_KERNEL);
if (!group) {
/*
* The caller will send a response to the hardware. But we do
* need to clean up before leaving, otherwise partial faults
* will be stuck.
*/
ret = -ENOMEM;
goto cleanup_partial;
}
group->dev = dev;
group->last_fault.fault = *fault;
INIT_LIST_HEAD(&group->faults);
list_add(&group->last_fault.list, &group->faults);
INIT_WORK(&group->work, iopf_handle_group);
/* See if we have partial faults for this group */
list_for_each_entry_safe(iopf, next, &iopf_param->partial, list) {
if (iopf->fault.prm.grpid == fault->prm.grpid)
/* Insert *before* the last fault */
list_move(&iopf->list, &group->faults);
}
queue_work(iopf_param->queue->wq, &group->work);
return 0;
cleanup_partial:
list_for_each_entry_safe(iopf, next, &iopf_param->partial, list) {
if (iopf->fault.prm.grpid == fault->prm.grpid) {
list_del(&iopf->list);
kfree(iopf);
}
}
return ret;
}
EXPORT_SYMBOL_GPL(iommu_queue_iopf);
/**
* iopf_queue_flush_dev - Ensure that all queued faults have been processed
* @dev: the endpoint whose faults need to be flushed.
*
* The IOMMU driver calls this before releasing a PASID, to ensure that all
* pending faults for this PASID have been handled, and won't hit the address
* space of the next process that uses this PASID. The driver must make sure
* that no new fault is added to the queue. In particular it must flush its
* low-level queue before calling this function.
*
* Return: 0 on success and <0 on error.
*/
int iopf_queue_flush_dev(struct device *dev)
{
int ret = 0;
struct iopf_device_param *iopf_param;
struct dev_iommu *param = dev->iommu;
if (!param)
return -ENODEV;
mutex_lock(&param->lock);
iopf_param = param->iopf_param;
if (iopf_param)
flush_workqueue(iopf_param->queue->wq);
else
ret = -ENODEV;
mutex_unlock(&param->lock);
return ret;
}
EXPORT_SYMBOL_GPL(iopf_queue_flush_dev);
/**
* iopf_queue_discard_partial - Remove all pending partial fault
* @queue: the queue whose partial faults need to be discarded
*
* When the hardware queue overflows, last page faults in a group may have been
* lost and the IOMMU driver calls this to discard all partial faults. The
* driver shouldn't be adding new faults to this queue concurrently.
*
* Return: 0 on success and <0 on error.
*/
int iopf_queue_discard_partial(struct iopf_queue *queue)
{
struct iopf_fault *iopf, *next;
struct iopf_device_param *iopf_param;
if (!queue)
return -EINVAL;
mutex_lock(&queue->lock);
list_for_each_entry(iopf_param, &queue->devices, queue_list) {
list_for_each_entry_safe(iopf, next, &iopf_param->partial,
list) {
list_del(&iopf->list);
kfree(iopf);
}
}
mutex_unlock(&queue->lock);
return 0;
}
EXPORT_SYMBOL_GPL(iopf_queue_discard_partial);
/**
* iopf_queue_add_device - Add producer to the fault queue
* @queue: IOPF queue
* @dev: device to add
*
* Return: 0 on success and <0 on error.
*/
int iopf_queue_add_device(struct iopf_queue *queue, struct device *dev)
{
int ret = -EBUSY;
struct iopf_device_param *iopf_param;
struct dev_iommu *param = dev->iommu;
if (!param)
return -ENODEV;
iopf_param = kzalloc(sizeof(*iopf_param), GFP_KERNEL);
if (!iopf_param)
return -ENOMEM;
INIT_LIST_HEAD(&iopf_param->partial);
iopf_param->queue = queue;
iopf_param->dev = dev;
mutex_lock(&queue->lock);
mutex_lock(&param->lock);
if (!param->iopf_param) {
list_add(&iopf_param->queue_list, &queue->devices);
param->iopf_param = iopf_param;
ret = 0;
}
mutex_unlock(&param->lock);
mutex_unlock(&queue->lock);
if (ret)
kfree(iopf_param);
return ret;
}
EXPORT_SYMBOL_GPL(iopf_queue_add_device);
/**
* iopf_queue_remove_device - Remove producer from fault queue
* @queue: IOPF queue
* @dev: device to remove
*
* Caller makes sure that no more faults are reported for this device.
*
* Return: 0 on success and <0 on error.
*/
int iopf_queue_remove_device(struct iopf_queue *queue, struct device *dev)
{
int ret = -EINVAL;
struct iopf_fault *iopf, *next;
struct iopf_device_param *iopf_param;
struct dev_iommu *param = dev->iommu;
if (!param || !queue)
return -EINVAL;
mutex_lock(&queue->lock);
mutex_lock(&param->lock);
iopf_param = param->iopf_param;
if (iopf_param && iopf_param->queue == queue) {
list_del(&iopf_param->queue_list);
param->iopf_param = NULL;
ret = 0;
}
mutex_unlock(&param->lock);
mutex_unlock(&queue->lock);
if (ret)
return ret;
/* Just in case some faults are still stuck */
list_for_each_entry_safe(iopf, next, &iopf_param->partial, list)
kfree(iopf);
kfree(iopf_param);
return 0;
}
EXPORT_SYMBOL_GPL(iopf_queue_remove_device);
/**
* iopf_queue_alloc - Allocate and initialize a fault queue
* @name: a unique string identifying the queue (for workqueue)
*
* Return: the queue on success and NULL on error.
*/
struct iopf_queue *iopf_queue_alloc(const char *name)
{
struct iopf_queue *queue;
queue = kzalloc(sizeof(*queue), GFP_KERNEL);
if (!queue)
return NULL;
/*
* The WQ is unordered because the low-level handler enqueues faults by
* group. PRI requests within a group have to be ordered, but once
* that's dealt with, the high-level function can handle groups out of
* order.
*/
queue->wq = alloc_workqueue("iopf_queue/%s", WQ_UNBOUND, 0, name);
if (!queue->wq) {
kfree(queue);
return NULL;
}
INIT_LIST_HEAD(&queue->devices);
mutex_init(&queue->lock);
return queue;
}
EXPORT_SYMBOL_GPL(iopf_queue_alloc);
/**
* iopf_queue_free - Free IOPF queue
* @queue: queue to free
*
* Counterpart to iopf_queue_alloc(). The driver must not be queuing faults or
* adding/removing devices on this queue anymore.
*/
void iopf_queue_free(struct iopf_queue *queue)
{
struct iopf_device_param *iopf_param, *next;
if (!queue)
return;
list_for_each_entry_safe(iopf_param, next, &queue->devices, queue_list)
iopf_queue_remove_device(queue, iopf_param->dev);
destroy_workqueue(queue->wq);
kfree(queue);
}
EXPORT_SYMBOL_GPL(iopf_queue_free);
...@@ -12,4 +12,57 @@ int iommu_sva_alloc_pasid(struct mm_struct *mm, ioasid_t min, ioasid_t max); ...@@ -12,4 +12,57 @@ int iommu_sva_alloc_pasid(struct mm_struct *mm, ioasid_t min, ioasid_t max);
void iommu_sva_free_pasid(struct mm_struct *mm); void iommu_sva_free_pasid(struct mm_struct *mm);
struct mm_struct *iommu_sva_find(ioasid_t pasid); struct mm_struct *iommu_sva_find(ioasid_t pasid);
/* I/O Page fault */
struct device;
struct iommu_fault;
struct iopf_queue;
#ifdef CONFIG_IOMMU_SVA_LIB
int iommu_queue_iopf(struct iommu_fault *fault, void *cookie);
int iopf_queue_add_device(struct iopf_queue *queue, struct device *dev);
int iopf_queue_remove_device(struct iopf_queue *queue,
struct device *dev);
int iopf_queue_flush_dev(struct device *dev);
struct iopf_queue *iopf_queue_alloc(const char *name);
void iopf_queue_free(struct iopf_queue *queue);
int iopf_queue_discard_partial(struct iopf_queue *queue);
#else /* CONFIG_IOMMU_SVA_LIB */
static inline int iommu_queue_iopf(struct iommu_fault *fault, void *cookie)
{
return -ENODEV;
}
static inline int iopf_queue_add_device(struct iopf_queue *queue,
struct device *dev)
{
return -ENODEV;
}
static inline int iopf_queue_remove_device(struct iopf_queue *queue,
struct device *dev)
{
return -ENODEV;
}
static inline int iopf_queue_flush_dev(struct device *dev)
{
return -ENODEV;
}
static inline struct iopf_queue *iopf_queue_alloc(const char *name)
{
return NULL;
}
static inline void iopf_queue_free(struct iopf_queue *queue)
{
}
static inline int iopf_queue_discard_partial(struct iopf_queue *queue)
{
return -ENODEV;
}
#endif /* CONFIG_IOMMU_SVA_LIB */
#endif /* _IOMMU_SVA_LIB_H */ #endif /* _IOMMU_SVA_LIB_H */
...@@ -69,16 +69,7 @@ static const char * const iommu_group_resv_type_string[] = { ...@@ -69,16 +69,7 @@ static const char * const iommu_group_resv_type_string[] = {
}; };
#define IOMMU_CMD_LINE_DMA_API BIT(0) #define IOMMU_CMD_LINE_DMA_API BIT(0)
#define IOMMU_CMD_LINE_STRICT BIT(1)
static void iommu_set_cmd_line_dma_api(void)
{
iommu_cmd_line |= IOMMU_CMD_LINE_DMA_API;
}
static bool iommu_cmd_line_dma_api(void)
{
return !!(iommu_cmd_line & IOMMU_CMD_LINE_DMA_API);
}
static int iommu_alloc_default_domain(struct iommu_group *group, static int iommu_alloc_default_domain(struct iommu_group *group,
struct device *dev); struct device *dev);
...@@ -130,9 +121,7 @@ static const char *iommu_domain_type_str(unsigned int t) ...@@ -130,9 +121,7 @@ static const char *iommu_domain_type_str(unsigned int t)
static int __init iommu_subsys_init(void) static int __init iommu_subsys_init(void)
{ {
bool cmd_line = iommu_cmd_line_dma_api(); if (!(iommu_cmd_line & IOMMU_CMD_LINE_DMA_API)) {
if (!cmd_line) {
if (IS_ENABLED(CONFIG_IOMMU_DEFAULT_PASSTHROUGH)) if (IS_ENABLED(CONFIG_IOMMU_DEFAULT_PASSTHROUGH))
iommu_set_default_passthrough(false); iommu_set_default_passthrough(false);
else else
...@@ -146,7 +135,8 @@ static int __init iommu_subsys_init(void) ...@@ -146,7 +135,8 @@ static int __init iommu_subsys_init(void)
pr_info("Default domain type: %s %s\n", pr_info("Default domain type: %s %s\n",
iommu_domain_type_str(iommu_def_domain_type), iommu_domain_type_str(iommu_def_domain_type),
cmd_line ? "(set via kernel command line)" : ""); (iommu_cmd_line & IOMMU_CMD_LINE_DMA_API) ?
"(set via kernel command line)" : "");
return 0; return 0;
} }
...@@ -329,10 +319,29 @@ early_param("iommu.passthrough", iommu_set_def_domain_type); ...@@ -329,10 +319,29 @@ early_param("iommu.passthrough", iommu_set_def_domain_type);
static int __init iommu_dma_setup(char *str) static int __init iommu_dma_setup(char *str)
{ {
return kstrtobool(str, &iommu_dma_strict); int ret = kstrtobool(str, &iommu_dma_strict);
if (!ret)
iommu_cmd_line |= IOMMU_CMD_LINE_STRICT;
return ret;
} }
early_param("iommu.strict", iommu_dma_setup); early_param("iommu.strict", iommu_dma_setup);
void iommu_set_dma_strict(bool strict)
{
if (strict || !(iommu_cmd_line & IOMMU_CMD_LINE_STRICT))
iommu_dma_strict = strict;
}
bool iommu_get_dma_strict(struct iommu_domain *domain)
{
/* only allow lazy flushing for DMA domains */
if (domain->type == IOMMU_DOMAIN_DMA)
return iommu_dma_strict;
return true;
}
EXPORT_SYMBOL_GPL(iommu_get_dma_strict);
static ssize_t iommu_group_attr_show(struct kobject *kobj, static ssize_t iommu_group_attr_show(struct kobject *kobj,
struct attribute *__attr, char *buf) struct attribute *__attr, char *buf)
{ {
...@@ -1511,14 +1520,6 @@ static int iommu_group_alloc_default_domain(struct bus_type *bus, ...@@ -1511,14 +1520,6 @@ static int iommu_group_alloc_default_domain(struct bus_type *bus,
group->default_domain = dom; group->default_domain = dom;
if (!group->domain) if (!group->domain)
group->domain = dom; group->domain = dom;
if (!iommu_dma_strict) {
int attr = 1;
iommu_domain_set_attr(dom,
DOMAIN_ATTR_DMA_USE_FLUSH_QUEUE,
&attr);
}
return 0; return 0;
} }
...@@ -2610,17 +2611,6 @@ size_t iommu_map_sg_atomic(struct iommu_domain *domain, unsigned long iova, ...@@ -2610,17 +2611,6 @@ size_t iommu_map_sg_atomic(struct iommu_domain *domain, unsigned long iova,
return __iommu_map_sg(domain, iova, sg, nents, prot, GFP_ATOMIC); return __iommu_map_sg(domain, iova, sg, nents, prot, GFP_ATOMIC);
} }
int iommu_domain_window_enable(struct iommu_domain *domain, u32 wnd_nr,
phys_addr_t paddr, u64 size, int prot)
{
if (unlikely(domain->ops->domain_window_enable == NULL))
return -ENODEV;
return domain->ops->domain_window_enable(domain, wnd_nr, paddr, size,
prot);
}
EXPORT_SYMBOL_GPL(iommu_domain_window_enable);
/** /**
* report_iommu_fault() - report about an IOMMU fault to the IOMMU framework * report_iommu_fault() - report about an IOMMU fault to the IOMMU framework
* @domain: the iommu domain where the fault has happened * @domain: the iommu domain where the fault has happened
...@@ -2675,50 +2665,26 @@ static int __init iommu_init(void) ...@@ -2675,50 +2665,26 @@ static int __init iommu_init(void)
} }
core_initcall(iommu_init); core_initcall(iommu_init);
int iommu_domain_get_attr(struct iommu_domain *domain, int iommu_enable_nesting(struct iommu_domain *domain)
enum iommu_attr attr, void *data)
{ {
struct iommu_domain_geometry *geometry; if (domain->type != IOMMU_DOMAIN_UNMANAGED)
bool *paging; return -EINVAL;
int ret = 0; if (!domain->ops->enable_nesting)
return -EINVAL;
switch (attr) { return domain->ops->enable_nesting(domain);
case DOMAIN_ATTR_GEOMETRY:
geometry = data;
*geometry = domain->geometry;
break;
case DOMAIN_ATTR_PAGING:
paging = data;
*paging = (domain->pgsize_bitmap != 0UL);
break;
default:
if (!domain->ops->domain_get_attr)
return -EINVAL;
ret = domain->ops->domain_get_attr(domain, attr, data);
}
return ret;
} }
EXPORT_SYMBOL_GPL(iommu_domain_get_attr); EXPORT_SYMBOL_GPL(iommu_enable_nesting);
int iommu_domain_set_attr(struct iommu_domain *domain, int iommu_set_pgtable_quirks(struct iommu_domain *domain,
enum iommu_attr attr, void *data) unsigned long quirk)
{ {
int ret = 0; if (domain->type != IOMMU_DOMAIN_UNMANAGED)
return -EINVAL;
switch (attr) { if (!domain->ops->set_pgtable_quirks)
default: return -EINVAL;
if (domain->ops->domain_set_attr == NULL) return domain->ops->set_pgtable_quirks(domain, quirk);
return -EINVAL;
ret = domain->ops->domain_set_attr(domain, attr, data);
}
return ret;
} }
EXPORT_SYMBOL_GPL(iommu_domain_set_attr); EXPORT_SYMBOL_GPL(iommu_set_pgtable_quirks);
void iommu_get_resv_regions(struct device *dev, struct list_head *list) void iommu_get_resv_regions(struct device *dev, struct list_head *list)
{ {
...@@ -2777,16 +2743,14 @@ EXPORT_SYMBOL_GPL(iommu_alloc_resv_region); ...@@ -2777,16 +2743,14 @@ EXPORT_SYMBOL_GPL(iommu_alloc_resv_region);
void iommu_set_default_passthrough(bool cmd_line) void iommu_set_default_passthrough(bool cmd_line)
{ {
if (cmd_line) if (cmd_line)
iommu_set_cmd_line_dma_api(); iommu_cmd_line |= IOMMU_CMD_LINE_DMA_API;
iommu_def_domain_type = IOMMU_DOMAIN_IDENTITY; iommu_def_domain_type = IOMMU_DOMAIN_IDENTITY;
} }
void iommu_set_default_translated(bool cmd_line) void iommu_set_default_translated(bool cmd_line)
{ {
if (cmd_line) if (cmd_line)
iommu_set_cmd_line_dma_api(); iommu_cmd_line |= IOMMU_CMD_LINE_DMA_API;
iommu_def_domain_type = IOMMU_DOMAIN_DMA; iommu_def_domain_type = IOMMU_DOMAIN_DMA;
} }
...@@ -2878,10 +2842,12 @@ EXPORT_SYMBOL_GPL(iommu_fwspec_add_ids); ...@@ -2878,10 +2842,12 @@ EXPORT_SYMBOL_GPL(iommu_fwspec_add_ids);
*/ */
int iommu_dev_enable_feature(struct device *dev, enum iommu_dev_features feat) int iommu_dev_enable_feature(struct device *dev, enum iommu_dev_features feat)
{ {
const struct iommu_ops *ops = dev->bus->iommu_ops; if (dev->iommu && dev->iommu->iommu_dev) {
const struct iommu_ops *ops = dev->iommu->iommu_dev->ops;
if (ops && ops->dev_enable_feat) if (ops->dev_enable_feat)
return ops->dev_enable_feat(dev, feat); return ops->dev_enable_feat(dev, feat);
}
return -ENODEV; return -ENODEV;
} }
...@@ -2894,10 +2860,12 @@ EXPORT_SYMBOL_GPL(iommu_dev_enable_feature); ...@@ -2894,10 +2860,12 @@ EXPORT_SYMBOL_GPL(iommu_dev_enable_feature);
*/ */
int iommu_dev_disable_feature(struct device *dev, enum iommu_dev_features feat) int iommu_dev_disable_feature(struct device *dev, enum iommu_dev_features feat)
{ {
const struct iommu_ops *ops = dev->bus->iommu_ops; if (dev->iommu && dev->iommu->iommu_dev) {
const struct iommu_ops *ops = dev->iommu->iommu_dev->ops;
if (ops && ops->dev_disable_feat) if (ops->dev_disable_feat)
return ops->dev_disable_feat(dev, feat); return ops->dev_disable_feat(dev, feat);
}
return -EBUSY; return -EBUSY;
} }
...@@ -2905,10 +2873,12 @@ EXPORT_SYMBOL_GPL(iommu_dev_disable_feature); ...@@ -2905,10 +2873,12 @@ EXPORT_SYMBOL_GPL(iommu_dev_disable_feature);
bool iommu_dev_feature_enabled(struct device *dev, enum iommu_dev_features feat) bool iommu_dev_feature_enabled(struct device *dev, enum iommu_dev_features feat)
{ {
const struct iommu_ops *ops = dev->bus->iommu_ops; if (dev->iommu && dev->iommu->iommu_dev) {
const struct iommu_ops *ops = dev->iommu->iommu_dev->ops;
if (ops && ops->dev_feat_enabled) if (ops->dev_feat_enabled)
return ops->dev_feat_enabled(dev, feat); return ops->dev_feat_enabled(dev, feat);
}
return false; return false;
} }
......
...@@ -22,11 +22,28 @@ static unsigned long iova_rcache_get(struct iova_domain *iovad, ...@@ -22,11 +22,28 @@ static unsigned long iova_rcache_get(struct iova_domain *iovad,
unsigned long size, unsigned long size,
unsigned long limit_pfn); unsigned long limit_pfn);
static void init_iova_rcaches(struct iova_domain *iovad); static void init_iova_rcaches(struct iova_domain *iovad);
static void free_cpu_cached_iovas(unsigned int cpu, struct iova_domain *iovad);
static void free_iova_rcaches(struct iova_domain *iovad); static void free_iova_rcaches(struct iova_domain *iovad);
static void fq_destroy_all_entries(struct iova_domain *iovad); static void fq_destroy_all_entries(struct iova_domain *iovad);
static void fq_flush_timeout(struct timer_list *t); static void fq_flush_timeout(struct timer_list *t);
static int iova_cpuhp_dead(unsigned int cpu, struct hlist_node *node)
{
struct iova_domain *iovad;
iovad = hlist_entry_safe(node, struct iova_domain, cpuhp_dead);
free_cpu_cached_iovas(cpu, iovad);
return 0;
}
static void free_global_cached_iovas(struct iova_domain *iovad); static void free_global_cached_iovas(struct iova_domain *iovad);
static struct iova *to_iova(struct rb_node *node)
{
return rb_entry(node, struct iova, node);
}
void void
init_iova_domain(struct iova_domain *iovad, unsigned long granule, init_iova_domain(struct iova_domain *iovad, unsigned long granule,
unsigned long start_pfn) unsigned long start_pfn)
...@@ -51,6 +68,7 @@ init_iova_domain(struct iova_domain *iovad, unsigned long granule, ...@@ -51,6 +68,7 @@ init_iova_domain(struct iova_domain *iovad, unsigned long granule,
iovad->anchor.pfn_lo = iovad->anchor.pfn_hi = IOVA_ANCHOR; iovad->anchor.pfn_lo = iovad->anchor.pfn_hi = IOVA_ANCHOR;
rb_link_node(&iovad->anchor.node, NULL, &iovad->rbroot.rb_node); rb_link_node(&iovad->anchor.node, NULL, &iovad->rbroot.rb_node);
rb_insert_color(&iovad->anchor.node, &iovad->rbroot); rb_insert_color(&iovad->anchor.node, &iovad->rbroot);
cpuhp_state_add_instance_nocalls(CPUHP_IOMMU_IOVA_DEAD, &iovad->cpuhp_dead);
init_iova_rcaches(iovad); init_iova_rcaches(iovad);
} }
EXPORT_SYMBOL_GPL(init_iova_domain); EXPORT_SYMBOL_GPL(init_iova_domain);
...@@ -136,7 +154,7 @@ __cached_rbnode_delete_update(struct iova_domain *iovad, struct iova *free) ...@@ -136,7 +154,7 @@ __cached_rbnode_delete_update(struct iova_domain *iovad, struct iova *free)
{ {
struct iova *cached_iova; struct iova *cached_iova;
cached_iova = rb_entry(iovad->cached32_node, struct iova, node); cached_iova = to_iova(iovad->cached32_node);
if (free == cached_iova || if (free == cached_iova ||
(free->pfn_hi < iovad->dma_32bit_pfn && (free->pfn_hi < iovad->dma_32bit_pfn &&
free->pfn_lo >= cached_iova->pfn_lo)) { free->pfn_lo >= cached_iova->pfn_lo)) {
...@@ -144,11 +162,48 @@ __cached_rbnode_delete_update(struct iova_domain *iovad, struct iova *free) ...@@ -144,11 +162,48 @@ __cached_rbnode_delete_update(struct iova_domain *iovad, struct iova *free)
iovad->max32_alloc_size = iovad->dma_32bit_pfn; iovad->max32_alloc_size = iovad->dma_32bit_pfn;
} }
cached_iova = rb_entry(iovad->cached_node, struct iova, node); cached_iova = to_iova(iovad->cached_node);
if (free->pfn_lo >= cached_iova->pfn_lo) if (free->pfn_lo >= cached_iova->pfn_lo)
iovad->cached_node = rb_next(&free->node); iovad->cached_node = rb_next(&free->node);
} }
static struct rb_node *iova_find_limit(struct iova_domain *iovad, unsigned long limit_pfn)
{
struct rb_node *node, *next;
/*
* Ideally what we'd like to judge here is whether limit_pfn is close
* enough to the highest-allocated IOVA that starting the allocation
* walk from the anchor node will be quicker than this initial work to
* find an exact starting point (especially if that ends up being the
* anchor node anyway). This is an incredibly crude approximation which
* only really helps the most likely case, but is at least trivially easy.
*/
if (limit_pfn > iovad->dma_32bit_pfn)
return &iovad->anchor.node;
node = iovad->rbroot.rb_node;
while (to_iova(node)->pfn_hi < limit_pfn)
node = node->rb_right;
search_left:
while (node->rb_left && to_iova(node->rb_left)->pfn_lo >= limit_pfn)
node = node->rb_left;
if (!node->rb_left)
return node;
next = node->rb_left;
while (next->rb_right) {
next = next->rb_right;
if (to_iova(next)->pfn_lo >= limit_pfn) {
node = next;
goto search_left;
}
}
return node;
}
/* Insert the iova into domain rbtree by holding writer lock */ /* Insert the iova into domain rbtree by holding writer lock */
static void static void
iova_insert_rbtree(struct rb_root *root, struct iova *iova, iova_insert_rbtree(struct rb_root *root, struct iova *iova,
...@@ -159,7 +214,7 @@ iova_insert_rbtree(struct rb_root *root, struct iova *iova, ...@@ -159,7 +214,7 @@ iova_insert_rbtree(struct rb_root *root, struct iova *iova,
new = (start) ? &start : &(root->rb_node); new = (start) ? &start : &(root->rb_node);
/* Figure out where to put new node */ /* Figure out where to put new node */
while (*new) { while (*new) {
struct iova *this = rb_entry(*new, struct iova, node); struct iova *this = to_iova(*new);
parent = *new; parent = *new;
...@@ -198,7 +253,7 @@ static int __alloc_and_insert_iova_range(struct iova_domain *iovad, ...@@ -198,7 +253,7 @@ static int __alloc_and_insert_iova_range(struct iova_domain *iovad,
goto iova32_full; goto iova32_full;
curr = __get_cached_rbnode(iovad, limit_pfn); curr = __get_cached_rbnode(iovad, limit_pfn);
curr_iova = rb_entry(curr, struct iova, node); curr_iova = to_iova(curr);
retry_pfn = curr_iova->pfn_hi + 1; retry_pfn = curr_iova->pfn_hi + 1;
retry: retry:
...@@ -207,15 +262,15 @@ static int __alloc_and_insert_iova_range(struct iova_domain *iovad, ...@@ -207,15 +262,15 @@ static int __alloc_and_insert_iova_range(struct iova_domain *iovad,
new_pfn = (high_pfn - size) & align_mask; new_pfn = (high_pfn - size) & align_mask;
prev = curr; prev = curr;
curr = rb_prev(curr); curr = rb_prev(curr);
curr_iova = rb_entry(curr, struct iova, node); curr_iova = to_iova(curr);
} while (curr && new_pfn <= curr_iova->pfn_hi && new_pfn >= low_pfn); } while (curr && new_pfn <= curr_iova->pfn_hi && new_pfn >= low_pfn);
if (high_pfn < size || new_pfn < low_pfn) { if (high_pfn < size || new_pfn < low_pfn) {
if (low_pfn == iovad->start_pfn && retry_pfn < limit_pfn) { if (low_pfn == iovad->start_pfn && retry_pfn < limit_pfn) {
high_pfn = limit_pfn; high_pfn = limit_pfn;
low_pfn = retry_pfn; low_pfn = retry_pfn;
curr = &iovad->anchor.node; curr = iova_find_limit(iovad, limit_pfn);
curr_iova = rb_entry(curr, struct iova, node); curr_iova = to_iova(curr);
goto retry; goto retry;
} }
iovad->max32_alloc_size = size; iovad->max32_alloc_size = size;
...@@ -257,10 +312,21 @@ int iova_cache_get(void) ...@@ -257,10 +312,21 @@ int iova_cache_get(void)
{ {
mutex_lock(&iova_cache_mutex); mutex_lock(&iova_cache_mutex);
if (!iova_cache_users) { if (!iova_cache_users) {
int ret;
ret = cpuhp_setup_state_multi(CPUHP_IOMMU_IOVA_DEAD, "iommu/iova:dead", NULL,
iova_cpuhp_dead);
if (ret) {
mutex_unlock(&iova_cache_mutex);
pr_err("Couldn't register cpuhp handler\n");
return ret;
}
iova_cache = kmem_cache_create( iova_cache = kmem_cache_create(
"iommu_iova", sizeof(struct iova), 0, "iommu_iova", sizeof(struct iova), 0,
SLAB_HWCACHE_ALIGN, NULL); SLAB_HWCACHE_ALIGN, NULL);
if (!iova_cache) { if (!iova_cache) {
cpuhp_remove_multi_state(CPUHP_IOMMU_IOVA_DEAD);
mutex_unlock(&iova_cache_mutex); mutex_unlock(&iova_cache_mutex);
pr_err("Couldn't create iova cache\n"); pr_err("Couldn't create iova cache\n");
return -ENOMEM; return -ENOMEM;
...@@ -282,8 +348,10 @@ void iova_cache_put(void) ...@@ -282,8 +348,10 @@ void iova_cache_put(void)
return; return;
} }
iova_cache_users--; iova_cache_users--;
if (!iova_cache_users) if (!iova_cache_users) {
cpuhp_remove_multi_state(CPUHP_IOMMU_IOVA_DEAD);
kmem_cache_destroy(iova_cache); kmem_cache_destroy(iova_cache);
}
mutex_unlock(&iova_cache_mutex); mutex_unlock(&iova_cache_mutex);
} }
EXPORT_SYMBOL_GPL(iova_cache_put); EXPORT_SYMBOL_GPL(iova_cache_put);
...@@ -331,7 +399,7 @@ private_find_iova(struct iova_domain *iovad, unsigned long pfn) ...@@ -331,7 +399,7 @@ private_find_iova(struct iova_domain *iovad, unsigned long pfn)
assert_spin_locked(&iovad->iova_rbtree_lock); assert_spin_locked(&iovad->iova_rbtree_lock);
while (node) { while (node) {
struct iova *iova = rb_entry(node, struct iova, node); struct iova *iova = to_iova(node);
if (pfn < iova->pfn_lo) if (pfn < iova->pfn_lo)
node = node->rb_left; node = node->rb_left;
...@@ -467,7 +535,6 @@ free_iova_fast(struct iova_domain *iovad, unsigned long pfn, unsigned long size) ...@@ -467,7 +535,6 @@ free_iova_fast(struct iova_domain *iovad, unsigned long pfn, unsigned long size)
free_iova(iovad, pfn); free_iova(iovad, pfn);
} }
EXPORT_SYMBOL_GPL(free_iova_fast);
#define fq_ring_for_each(i, fq) \ #define fq_ring_for_each(i, fq) \
for ((i) = (fq)->head; (i) != (fq)->tail; (i) = ((i) + 1) % IOVA_FQ_SIZE) for ((i) = (fq)->head; (i) != (fq)->tail; (i) = ((i) + 1) % IOVA_FQ_SIZE)
...@@ -606,6 +673,9 @@ void put_iova_domain(struct iova_domain *iovad) ...@@ -606,6 +673,9 @@ void put_iova_domain(struct iova_domain *iovad)
{ {
struct iova *iova, *tmp; struct iova *iova, *tmp;
cpuhp_state_remove_instance_nocalls(CPUHP_IOMMU_IOVA_DEAD,
&iovad->cpuhp_dead);
free_iova_flush_queue(iovad); free_iova_flush_queue(iovad);
free_iova_rcaches(iovad); free_iova_rcaches(iovad);
rbtree_postorder_for_each_entry_safe(iova, tmp, &iovad->rbroot, node) rbtree_postorder_for_each_entry_safe(iova, tmp, &iovad->rbroot, node)
...@@ -617,7 +687,7 @@ static int ...@@ -617,7 +687,7 @@ static int
__is_range_overlap(struct rb_node *node, __is_range_overlap(struct rb_node *node,
unsigned long pfn_lo, unsigned long pfn_hi) unsigned long pfn_lo, unsigned long pfn_hi)
{ {
struct iova *iova = rb_entry(node, struct iova, node); struct iova *iova = to_iova(node);
if ((pfn_lo <= iova->pfn_hi) && (pfn_hi >= iova->pfn_lo)) if ((pfn_lo <= iova->pfn_hi) && (pfn_hi >= iova->pfn_lo))
return 1; return 1;
...@@ -685,7 +755,7 @@ reserve_iova(struct iova_domain *iovad, ...@@ -685,7 +755,7 @@ reserve_iova(struct iova_domain *iovad,
spin_lock_irqsave(&iovad->iova_rbtree_lock, flags); spin_lock_irqsave(&iovad->iova_rbtree_lock, flags);
for (node = rb_first(&iovad->rbroot); node; node = rb_next(node)) { for (node = rb_first(&iovad->rbroot); node; node = rb_next(node)) {
if (__is_range_overlap(node, pfn_lo, pfn_hi)) { if (__is_range_overlap(node, pfn_lo, pfn_hi)) {
iova = rb_entry(node, struct iova, node); iova = to_iova(node);
__adjust_overlap_range(iova, &pfn_lo, &pfn_hi); __adjust_overlap_range(iova, &pfn_lo, &pfn_hi);
if ((pfn_lo >= iova->pfn_lo) && if ((pfn_lo >= iova->pfn_lo) &&
(pfn_hi <= iova->pfn_hi)) (pfn_hi <= iova->pfn_hi))
...@@ -970,7 +1040,7 @@ static void free_iova_rcaches(struct iova_domain *iovad) ...@@ -970,7 +1040,7 @@ static void free_iova_rcaches(struct iova_domain *iovad)
/* /*
* free all the IOVA ranges cached by a cpu (used when cpu is unplugged) * free all the IOVA ranges cached by a cpu (used when cpu is unplugged)
*/ */
void free_cpu_cached_iovas(unsigned int cpu, struct iova_domain *iovad) static void free_cpu_cached_iovas(unsigned int cpu, struct iova_domain *iovad)
{ {
struct iova_cpu_rcache *cpu_rcache; struct iova_cpu_rcache *cpu_rcache;
struct iova_rcache *rcache; struct iova_rcache *rcache;
......
...@@ -17,6 +17,7 @@ ...@@ -17,6 +17,7 @@
#include <linux/iopoll.h> #include <linux/iopoll.h>
#include <linux/list.h> #include <linux/list.h>
#include <linux/mfd/syscon.h> #include <linux/mfd/syscon.h>
#include <linux/module.h>
#include <linux/of_address.h> #include <linux/of_address.h>
#include <linux/of_iommu.h> #include <linux/of_iommu.h>
#include <linux/of_irq.h> #include <linux/of_irq.h>
...@@ -683,18 +684,12 @@ static const struct iommu_ops mtk_iommu_ops = { ...@@ -683,18 +684,12 @@ static const struct iommu_ops mtk_iommu_ops = {
.get_resv_regions = mtk_iommu_get_resv_regions, .get_resv_regions = mtk_iommu_get_resv_regions,
.put_resv_regions = generic_iommu_put_resv_regions, .put_resv_regions = generic_iommu_put_resv_regions,
.pgsize_bitmap = SZ_4K | SZ_64K | SZ_1M | SZ_16M, .pgsize_bitmap = SZ_4K | SZ_64K | SZ_1M | SZ_16M,
.owner = THIS_MODULE,
}; };
static int mtk_iommu_hw_init(const struct mtk_iommu_data *data) static int mtk_iommu_hw_init(const struct mtk_iommu_data *data)
{ {
u32 regval; u32 regval;
int ret;
ret = clk_prepare_enable(data->bclk);
if (ret) {
dev_err(data->dev, "Failed to enable iommu bclk(%d)\n", ret);
return ret;
}
if (data->plat_data->m4u_plat == M4U_MT8173) { if (data->plat_data->m4u_plat == M4U_MT8173) {
regval = F_MMU_PREFETCH_RT_REPLACE_MOD | regval = F_MMU_PREFETCH_RT_REPLACE_MOD |
...@@ -760,7 +755,6 @@ static int mtk_iommu_hw_init(const struct mtk_iommu_data *data) ...@@ -760,7 +755,6 @@ static int mtk_iommu_hw_init(const struct mtk_iommu_data *data)
if (devm_request_irq(data->dev, data->irq, mtk_iommu_isr, 0, if (devm_request_irq(data->dev, data->irq, mtk_iommu_isr, 0,
dev_name(data->dev), (void *)data)) { dev_name(data->dev), (void *)data)) {
writel_relaxed(0, data->base + REG_MMU_PT_BASE_ADDR); writel_relaxed(0, data->base + REG_MMU_PT_BASE_ADDR);
clk_disable_unprepare(data->bclk);
dev_err(data->dev, "Failed @ IRQ-%d Request\n", data->irq); dev_err(data->dev, "Failed @ IRQ-%d Request\n", data->irq);
return -ENODEV; return -ENODEV;
} }
...@@ -977,14 +971,19 @@ static int __maybe_unused mtk_iommu_runtime_resume(struct device *dev) ...@@ -977,14 +971,19 @@ static int __maybe_unused mtk_iommu_runtime_resume(struct device *dev)
void __iomem *base = data->base; void __iomem *base = data->base;
int ret; int ret;
/* Avoid first resume to affect the default value of registers below. */
if (!m4u_dom)
return 0;
ret = clk_prepare_enable(data->bclk); ret = clk_prepare_enable(data->bclk);
if (ret) { if (ret) {
dev_err(data->dev, "Failed to enable clk(%d) in resume\n", ret); dev_err(data->dev, "Failed to enable clk(%d) in resume\n", ret);
return ret; return ret;
} }
/*
* Uppon first resume, only enable the clk and return, since the values of the
* registers are not yet set.
*/
if (!m4u_dom)
return 0;
writel_relaxed(reg->wr_len_ctrl, base + REG_MMU_WR_LEN_CTRL); writel_relaxed(reg->wr_len_ctrl, base + REG_MMU_WR_LEN_CTRL);
writel_relaxed(reg->misc_ctrl, base + REG_MMU_MISC_CTRL); writel_relaxed(reg->misc_ctrl, base + REG_MMU_MISC_CTRL);
writel_relaxed(reg->dcm_dis, base + REG_MMU_DCM_DIS); writel_relaxed(reg->dcm_dis, base + REG_MMU_DCM_DIS);
...@@ -1079,16 +1078,7 @@ static struct platform_driver mtk_iommu_driver = { ...@@ -1079,16 +1078,7 @@ static struct platform_driver mtk_iommu_driver = {
.pm = &mtk_iommu_pm_ops, .pm = &mtk_iommu_pm_ops,
} }
}; };
module_platform_driver(mtk_iommu_driver);
static int __init mtk_iommu_init(void) MODULE_DESCRIPTION("IOMMU API for MediaTek M4U implementations");
{ MODULE_LICENSE("GPL v2");
int ret;
ret = platform_driver_register(&mtk_iommu_driver);
if (ret != 0)
pr_err("Failed to register MTK IOMMU driver\n");
return ret;
}
subsys_initcall(mtk_iommu_init)
...@@ -20,6 +20,7 @@ ...@@ -20,6 +20,7 @@
#include <linux/iommu.h> #include <linux/iommu.h>
#include <linux/iopoll.h> #include <linux/iopoll.h>
#include <linux/list.h> #include <linux/list.h>
#include <linux/module.h>
#include <linux/of_address.h> #include <linux/of_address.h>
#include <linux/of_iommu.h> #include <linux/of_iommu.h>
#include <linux/of_irq.h> #include <linux/of_irq.h>
...@@ -423,23 +424,21 @@ static struct iommu_device *mtk_iommu_probe_device(struct device *dev) ...@@ -423,23 +424,21 @@ static struct iommu_device *mtk_iommu_probe_device(struct device *dev)
{ {
struct iommu_fwspec *fwspec = dev_iommu_fwspec_get(dev); struct iommu_fwspec *fwspec = dev_iommu_fwspec_get(dev);
struct of_phandle_args iommu_spec; struct of_phandle_args iommu_spec;
struct of_phandle_iterator it;
struct mtk_iommu_data *data; struct mtk_iommu_data *data;
int err; int err, idx = 0;
of_for_each_phandle(&it, err, dev->of_node, "iommus", while (!of_parse_phandle_with_args(dev->of_node, "iommus",
"#iommu-cells", -1) { "#iommu-cells",
int count = of_phandle_iterator_args(&it, iommu_spec.args, idx, &iommu_spec)) {
MAX_PHANDLE_ARGS);
iommu_spec.np = of_node_get(it.node);
iommu_spec.args_count = count;
mtk_iommu_create_mapping(dev, &iommu_spec); err = mtk_iommu_create_mapping(dev, &iommu_spec);
of_node_put(iommu_spec.np);
if (err)
return ERR_PTR(err);
/* dev->iommu_fwspec might have changed */ /* dev->iommu_fwspec might have changed */
fwspec = dev_iommu_fwspec_get(dev); fwspec = dev_iommu_fwspec_get(dev);
idx++;
of_node_put(iommu_spec.np);
} }
if (!fwspec || fwspec->ops != &mtk_iommu_ops) if (!fwspec || fwspec->ops != &mtk_iommu_ops)
...@@ -529,6 +528,7 @@ static const struct iommu_ops mtk_iommu_ops = { ...@@ -529,6 +528,7 @@ static const struct iommu_ops mtk_iommu_ops = {
.def_domain_type = mtk_iommu_def_domain_type, .def_domain_type = mtk_iommu_def_domain_type,
.device_group = generic_device_group, .device_group = generic_device_group,
.pgsize_bitmap = ~0UL << MT2701_IOMMU_PAGE_SHIFT, .pgsize_bitmap = ~0UL << MT2701_IOMMU_PAGE_SHIFT,
.owner = THIS_MODULE,
}; };
static const struct of_device_id mtk_iommu_of_ids[] = { static const struct of_device_id mtk_iommu_of_ids[] = {
...@@ -547,10 +547,8 @@ static int mtk_iommu_probe(struct platform_device *pdev) ...@@ -547,10 +547,8 @@ static int mtk_iommu_probe(struct platform_device *pdev)
struct device *dev = &pdev->dev; struct device *dev = &pdev->dev;
struct resource *res; struct resource *res;
struct component_match *match = NULL; struct component_match *match = NULL;
struct of_phandle_args larb_spec;
struct of_phandle_iterator it;
void *protect; void *protect;
int larb_nr, ret, err; int larb_nr, ret, i;
data = devm_kzalloc(dev, sizeof(*data), GFP_KERNEL); data = devm_kzalloc(dev, sizeof(*data), GFP_KERNEL);
if (!data) if (!data)
...@@ -578,35 +576,33 @@ static int mtk_iommu_probe(struct platform_device *pdev) ...@@ -578,35 +576,33 @@ static int mtk_iommu_probe(struct platform_device *pdev)
if (IS_ERR(data->bclk)) if (IS_ERR(data->bclk))
return PTR_ERR(data->bclk); return PTR_ERR(data->bclk);
larb_nr = 0; larb_nr = of_count_phandle_with_args(dev->of_node,
of_for_each_phandle(&it, err, dev->of_node, "mediatek,larbs", NULL);
"mediatek,larbs", NULL, 0) { if (larb_nr < 0)
return larb_nr;
for (i = 0; i < larb_nr; i++) {
struct device_node *larbnode;
struct platform_device *plarbdev; struct platform_device *plarbdev;
int count = of_phandle_iterator_args(&it, larb_spec.args,
MAX_PHANDLE_ARGS);
if (count) larbnode = of_parse_phandle(dev->of_node, "mediatek,larbs", i);
continue; if (!larbnode)
return -EINVAL;
larb_spec.np = of_node_get(it.node); if (!of_device_is_available(larbnode)) {
if (!of_device_is_available(larb_spec.np)) of_node_put(larbnode);
continue; continue;
}
plarbdev = of_find_device_by_node(larb_spec.np); plarbdev = of_find_device_by_node(larbnode);
if (!plarbdev) { if (!plarbdev) {
plarbdev = of_platform_device_create( of_node_put(larbnode);
larb_spec.np, NULL, return -EPROBE_DEFER;
platform_bus_type.dev_root);
if (!plarbdev) {
of_node_put(larb_spec.np);
return -EPROBE_DEFER;
}
} }
data->larb_imu[i].dev = &plarbdev->dev;
data->larb_imu[larb_nr].dev = &plarbdev->dev;
component_match_add_release(dev, &match, release_of, component_match_add_release(dev, &match, release_of,
compare_of, larb_spec.np); compare_of, larbnode);
larb_nr++;
} }
platform_set_drvdata(pdev, data); platform_set_drvdata(pdev, data);
...@@ -624,12 +620,26 @@ static int mtk_iommu_probe(struct platform_device *pdev) ...@@ -624,12 +620,26 @@ static int mtk_iommu_probe(struct platform_device *pdev)
ret = iommu_device_register(&data->iommu); ret = iommu_device_register(&data->iommu);
if (ret) if (ret)
return ret; goto out_sysfs_remove;
if (!iommu_present(&platform_bus_type)) if (!iommu_present(&platform_bus_type)) {
bus_set_iommu(&platform_bus_type, &mtk_iommu_ops); ret = bus_set_iommu(&platform_bus_type, &mtk_iommu_ops);
if (ret)
goto out_dev_unreg;
}
ret = component_master_add_with_match(dev, &mtk_iommu_com_ops, match);
if (ret)
goto out_bus_set_null;
return ret;
return component_master_add_with_match(dev, &mtk_iommu_com_ops, match); out_bus_set_null:
bus_set_iommu(&platform_bus_type, NULL);
out_dev_unreg:
iommu_device_unregister(&data->iommu);
out_sysfs_remove:
iommu_device_sysfs_remove(&data->iommu);
return ret;
} }
static int mtk_iommu_remove(struct platform_device *pdev) static int mtk_iommu_remove(struct platform_device *pdev)
...@@ -691,9 +701,7 @@ static struct platform_driver mtk_iommu_driver = { ...@@ -691,9 +701,7 @@ static struct platform_driver mtk_iommu_driver = {
.pm = &mtk_iommu_pm_ops, .pm = &mtk_iommu_pm_ops,
} }
}; };
module_platform_driver(mtk_iommu_driver);
static int __init m4u_init(void) MODULE_DESCRIPTION("IOMMU API for MediaTek M4U v1 implementations");
{ MODULE_LICENSE("GPL v2");
return platform_driver_register(&mtk_iommu_driver);
}
subsys_initcall(m4u_init);
...@@ -210,11 +210,6 @@ const struct iommu_ops *of_iommu_configure(struct device *dev, ...@@ -210,11 +210,6 @@ const struct iommu_ops *of_iommu_configure(struct device *dev,
of_pci_iommu_init, &info); of_pci_iommu_init, &info);
} else { } else {
err = of_iommu_configure_device(master_np, dev, id); err = of_iommu_configure_device(master_np, dev, id);
fwspec = dev_iommu_fwspec_get(dev);
if (!err && fwspec)
of_property_read_u32(master_np, "pasid-num-bits",
&fwspec->num_pasid_bits);
} }
/* /*
......
此差异已折叠。
...@@ -385,6 +385,33 @@ static void uacce_release(struct device *dev) ...@@ -385,6 +385,33 @@ static void uacce_release(struct device *dev)
kfree(uacce); kfree(uacce);
} }
static unsigned int uacce_enable_sva(struct device *parent, unsigned int flags)
{
if (!(flags & UACCE_DEV_SVA))
return flags;
flags &= ~UACCE_DEV_SVA;
if (iommu_dev_enable_feature(parent, IOMMU_DEV_FEAT_IOPF))
return flags;
if (iommu_dev_enable_feature(parent, IOMMU_DEV_FEAT_SVA)) {
iommu_dev_disable_feature(parent, IOMMU_DEV_FEAT_IOPF);
return flags;
}
return flags | UACCE_DEV_SVA;
}
static void uacce_disable_sva(struct uacce_device *uacce)
{
if (!(uacce->flags & UACCE_DEV_SVA))
return;
iommu_dev_disable_feature(uacce->parent, IOMMU_DEV_FEAT_SVA);
iommu_dev_disable_feature(uacce->parent, IOMMU_DEV_FEAT_IOPF);
}
/** /**
* uacce_alloc() - alloc an accelerator * uacce_alloc() - alloc an accelerator
* @parent: pointer of uacce parent device * @parent: pointer of uacce parent device
...@@ -404,11 +431,7 @@ struct uacce_device *uacce_alloc(struct device *parent, ...@@ -404,11 +431,7 @@ struct uacce_device *uacce_alloc(struct device *parent,
if (!uacce) if (!uacce)
return ERR_PTR(-ENOMEM); return ERR_PTR(-ENOMEM);
if (flags & UACCE_DEV_SVA) { flags = uacce_enable_sva(parent, flags);
ret = iommu_dev_enable_feature(parent, IOMMU_DEV_FEAT_SVA);
if (ret)
flags &= ~UACCE_DEV_SVA;
}
uacce->parent = parent; uacce->parent = parent;
uacce->flags = flags; uacce->flags = flags;
...@@ -432,8 +455,7 @@ struct uacce_device *uacce_alloc(struct device *parent, ...@@ -432,8 +455,7 @@ struct uacce_device *uacce_alloc(struct device *parent,
return uacce; return uacce;
err_with_uacce: err_with_uacce:
if (flags & UACCE_DEV_SVA) uacce_disable_sva(uacce);
iommu_dev_disable_feature(uacce->parent, IOMMU_DEV_FEAT_SVA);
kfree(uacce); kfree(uacce);
return ERR_PTR(ret); return ERR_PTR(ret);
} }
...@@ -487,8 +509,7 @@ void uacce_remove(struct uacce_device *uacce) ...@@ -487,8 +509,7 @@ void uacce_remove(struct uacce_device *uacce)
mutex_unlock(&uacce->queues_lock); mutex_unlock(&uacce->queues_lock);
/* disable sva now since no opened queues */ /* disable sva now since no opened queues */
if (uacce->flags & UACCE_DEV_SVA) uacce_disable_sva(uacce);
iommu_dev_disable_feature(uacce->parent, IOMMU_DEV_FEAT_SVA);
if (uacce->cdev) if (uacce->cdev)
cdev_device_del(uacce->cdev, &uacce->dev); cdev_device_del(uacce->cdev, &uacce->dev);
......
...@@ -46,9 +46,6 @@ static void portal_set_cpu(struct qm_portal_config *pcfg, int cpu) ...@@ -46,9 +46,6 @@ static void portal_set_cpu(struct qm_portal_config *pcfg, int cpu)
{ {
#ifdef CONFIG_FSL_PAMU #ifdef CONFIG_FSL_PAMU
struct device *dev = pcfg->dev; struct device *dev = pcfg->dev;
int window_count = 1;
struct iommu_domain_geometry geom_attr;
struct pamu_stash_attribute stash_attr;
int ret; int ret;
pcfg->iommu_domain = iommu_domain_alloc(&platform_bus_type); pcfg->iommu_domain = iommu_domain_alloc(&platform_bus_type);
...@@ -56,38 +53,9 @@ static void portal_set_cpu(struct qm_portal_config *pcfg, int cpu) ...@@ -56,38 +53,9 @@ static void portal_set_cpu(struct qm_portal_config *pcfg, int cpu)
dev_err(dev, "%s(): iommu_domain_alloc() failed", __func__); dev_err(dev, "%s(): iommu_domain_alloc() failed", __func__);
goto no_iommu; goto no_iommu;
} }
geom_attr.aperture_start = 0; ret = fsl_pamu_configure_l1_stash(pcfg->iommu_domain, cpu);
geom_attr.aperture_end =
((dma_addr_t)1 << min(8 * sizeof(dma_addr_t), (size_t)36)) - 1;
geom_attr.force_aperture = true;
ret = iommu_domain_set_attr(pcfg->iommu_domain, DOMAIN_ATTR_GEOMETRY,
&geom_attr);
if (ret < 0) { if (ret < 0) {
dev_err(dev, "%s(): iommu_domain_set_attr() = %d", __func__, dev_err(dev, "%s(): fsl_pamu_configure_l1_stash() = %d",
ret);
goto out_domain_free;
}
ret = iommu_domain_set_attr(pcfg->iommu_domain, DOMAIN_ATTR_WINDOWS,
&window_count);
if (ret < 0) {
dev_err(dev, "%s(): iommu_domain_set_attr() = %d", __func__,
ret);
goto out_domain_free;
}
stash_attr.cpu = cpu;
stash_attr.cache = PAMU_ATTR_CACHE_L1;
ret = iommu_domain_set_attr(pcfg->iommu_domain,
DOMAIN_ATTR_FSL_PAMU_STASH,
&stash_attr);
if (ret < 0) {
dev_err(dev, "%s(): iommu_domain_set_attr() = %d",
__func__, ret);
goto out_domain_free;
}
ret = iommu_domain_window_enable(pcfg->iommu_domain, 0, 0, 1ULL << 36,
IOMMU_READ | IOMMU_WRITE);
if (ret < 0) {
dev_err(dev, "%s(): iommu_domain_window_enable() = %d",
__func__, ret); __func__, ret);
goto out_domain_free; goto out_domain_free;
} }
...@@ -97,14 +65,6 @@ static void portal_set_cpu(struct qm_portal_config *pcfg, int cpu) ...@@ -97,14 +65,6 @@ static void portal_set_cpu(struct qm_portal_config *pcfg, int cpu)
ret); ret);
goto out_domain_free; goto out_domain_free;
} }
ret = iommu_domain_set_attr(pcfg->iommu_domain,
DOMAIN_ATTR_FSL_PAMU_ENABLE,
&window_count);
if (ret < 0) {
dev_err(dev, "%s(): iommu_domain_set_attr() = %d", __func__,
ret);
goto out_detach_device;
}
no_iommu: no_iommu:
#endif #endif
...@@ -113,8 +73,6 @@ static void portal_set_cpu(struct qm_portal_config *pcfg, int cpu) ...@@ -113,8 +73,6 @@ static void portal_set_cpu(struct qm_portal_config *pcfg, int cpu)
return; return;
#ifdef CONFIG_FSL_PAMU #ifdef CONFIG_FSL_PAMU
out_detach_device:
iommu_detach_device(pcfg->iommu_domain, NULL);
out_domain_free: out_domain_free:
iommu_domain_free(pcfg->iommu_domain); iommu_domain_free(pcfg->iommu_domain);
pcfg->iommu_domain = NULL; pcfg->iommu_domain = NULL;
...@@ -169,15 +127,8 @@ static void qman_portal_update_sdest(const struct qm_portal_config *pcfg, ...@@ -169,15 +127,8 @@ static void qman_portal_update_sdest(const struct qm_portal_config *pcfg,
unsigned int cpu) unsigned int cpu)
{ {
#ifdef CONFIG_FSL_PAMU /* TODO */ #ifdef CONFIG_FSL_PAMU /* TODO */
struct pamu_stash_attribute stash_attr;
int ret;
if (pcfg->iommu_domain) { if (pcfg->iommu_domain) {
stash_attr.cpu = cpu; if (fsl_pamu_configure_l1_stash(pcfg->iommu_domain, cpu) < 0) {
stash_attr.cache = PAMU_ATTR_CACHE_L1;
ret = iommu_domain_set_attr(pcfg->iommu_domain,
DOMAIN_ATTR_FSL_PAMU_STASH, &stash_attr);
if (ret < 0) {
dev_err(pcfg->dev, dev_err(pcfg->dev,
"Failed to update pamu stash setting\n"); "Failed to update pamu stash setting\n");
return; return;
......
...@@ -2262,7 +2262,7 @@ static int vfio_iommu_type1_attach_group(void *iommu_data, ...@@ -2262,7 +2262,7 @@ static int vfio_iommu_type1_attach_group(void *iommu_data,
int ret; int ret;
bool resv_msi, msi_remap; bool resv_msi, msi_remap;
phys_addr_t resv_msi_base = 0; phys_addr_t resv_msi_base = 0;
struct iommu_domain_geometry geo; struct iommu_domain_geometry *geo;
LIST_HEAD(iova_copy); LIST_HEAD(iova_copy);
LIST_HEAD(group_resv_regions); LIST_HEAD(group_resv_regions);
...@@ -2330,10 +2330,7 @@ static int vfio_iommu_type1_attach_group(void *iommu_data, ...@@ -2330,10 +2330,7 @@ static int vfio_iommu_type1_attach_group(void *iommu_data,
} }
if (iommu->nesting) { if (iommu->nesting) {
int attr = 1; ret = iommu_enable_nesting(domain->domain);
ret = iommu_domain_set_attr(domain->domain, DOMAIN_ATTR_NESTING,
&attr);
if (ret) if (ret)
goto out_domain; goto out_domain;
} }
...@@ -2343,10 +2340,9 @@ static int vfio_iommu_type1_attach_group(void *iommu_data, ...@@ -2343,10 +2340,9 @@ static int vfio_iommu_type1_attach_group(void *iommu_data,
goto out_domain; goto out_domain;
/* Get aperture info */ /* Get aperture info */
iommu_domain_get_attr(domain->domain, DOMAIN_ATTR_GEOMETRY, &geo); geo = &domain->domain->geometry;
if (vfio_iommu_aper_conflict(iommu, geo->aperture_start,
if (vfio_iommu_aper_conflict(iommu, geo.aperture_start, geo->aperture_end)) {
geo.aperture_end)) {
ret = -EINVAL; ret = -EINVAL;
goto out_detach; goto out_detach;
} }
...@@ -2369,8 +2365,8 @@ static int vfio_iommu_type1_attach_group(void *iommu_data, ...@@ -2369,8 +2365,8 @@ static int vfio_iommu_type1_attach_group(void *iommu_data,
if (ret) if (ret)
goto out_detach; goto out_detach;
ret = vfio_iommu_aper_resize(&iova_copy, geo.aperture_start, ret = vfio_iommu_aper_resize(&iova_copy, geo->aperture_start,
geo.aperture_end); geo->aperture_end);
if (ret) if (ret)
goto out_detach; goto out_detach;
...@@ -2503,7 +2499,6 @@ static void vfio_iommu_aper_expand(struct vfio_iommu *iommu, ...@@ -2503,7 +2499,6 @@ static void vfio_iommu_aper_expand(struct vfio_iommu *iommu,
struct list_head *iova_copy) struct list_head *iova_copy)
{ {
struct vfio_domain *domain; struct vfio_domain *domain;
struct iommu_domain_geometry geo;
struct vfio_iova *node; struct vfio_iova *node;
dma_addr_t start = 0; dma_addr_t start = 0;
dma_addr_t end = (dma_addr_t)~0; dma_addr_t end = (dma_addr_t)~0;
...@@ -2512,12 +2507,12 @@ static void vfio_iommu_aper_expand(struct vfio_iommu *iommu, ...@@ -2512,12 +2507,12 @@ static void vfio_iommu_aper_expand(struct vfio_iommu *iommu,
return; return;
list_for_each_entry(domain, &iommu->domain_list, next) { list_for_each_entry(domain, &iommu->domain_list, next) {
iommu_domain_get_attr(domain->domain, DOMAIN_ATTR_GEOMETRY, struct iommu_domain_geometry *geo = &domain->domain->geometry;
&geo);
if (geo.aperture_start > start) if (geo->aperture_start > start)
start = geo.aperture_start; start = geo->aperture_start;
if (geo.aperture_end < end) if (geo->aperture_end < end)
end = geo.aperture_end; end = geo->aperture_end;
} }
/* Modify aperture limits. The new aper is either same or bigger */ /* Modify aperture limits. The new aper is either same or bigger */
......
...@@ -832,18 +832,14 @@ static void vhost_vdpa_free_domain(struct vhost_vdpa *v) ...@@ -832,18 +832,14 @@ static void vhost_vdpa_free_domain(struct vhost_vdpa *v)
static void vhost_vdpa_set_iova_range(struct vhost_vdpa *v) static void vhost_vdpa_set_iova_range(struct vhost_vdpa *v)
{ {
struct vdpa_iova_range *range = &v->range; struct vdpa_iova_range *range = &v->range;
struct iommu_domain_geometry geo;
struct vdpa_device *vdpa = v->vdpa; struct vdpa_device *vdpa = v->vdpa;
const struct vdpa_config_ops *ops = vdpa->config; const struct vdpa_config_ops *ops = vdpa->config;
if (ops->get_iova_range) { if (ops->get_iova_range) {
*range = ops->get_iova_range(vdpa); *range = ops->get_iova_range(vdpa);
} else if (v->domain && } else if (v->domain && v->domain->geometry.force_aperture) {
!iommu_domain_get_attr(v->domain, range->first = v->domain->geometry.aperture_start;
DOMAIN_ATTR_GEOMETRY, &geo) && range->last = v->domain->geometry.aperture_end;
geo.force_aperture) {
range->first = geo.aperture_start;
range->last = geo.aperture_end;
} else { } else {
range->first = 0; range->first = 0;
range->last = ULLONG_MAX; range->last = ULLONG_MAX;
......
...@@ -10,6 +10,8 @@ ...@@ -10,6 +10,8 @@
#include <linux/types.h> #include <linux/types.h>
struct amd_iommu;
/* /*
* This is mainly used to communicate information back-and-forth * This is mainly used to communicate information back-and-forth
* between SVM and IOMMU for setting up and tearing down posted * between SVM and IOMMU for setting up and tearing down posted
...@@ -32,24 +34,6 @@ struct pci_dev; ...@@ -32,24 +34,6 @@ struct pci_dev;
extern int amd_iommu_detect(void); extern int amd_iommu_detect(void);
extern int amd_iommu_init_hardware(void); extern int amd_iommu_init_hardware(void);
/**
* amd_iommu_enable_device_erratum() - Enable erratum workaround for device
* in the IOMMUv2 driver
* @pdev: The PCI device the workaround is necessary for
* @erratum: The erratum workaround to enable
*
* The function needs to be called before amd_iommu_init_device().
* Possible values for the erratum number are for now:
* - AMD_PRI_DEV_ERRATUM_ENABLE_RESET - Reset PRI capability when PRI
* is enabled
* - AMD_PRI_DEV_ERRATUM_LIMIT_REQ_ONE - Limit number of outstanding PRI
* requests to one
*/
#define AMD_PRI_DEV_ERRATUM_ENABLE_RESET 0
#define AMD_PRI_DEV_ERRATUM_LIMIT_REQ_ONE 1
extern void amd_iommu_enable_device_erratum(struct pci_dev *pdev, u32 erratum);
/** /**
* amd_iommu_init_device() - Init device for use with IOMMUv2 driver * amd_iommu_init_device() - Init device for use with IOMMUv2 driver
* @pdev: The PCI device to initialize * @pdev: The PCI device to initialize
...@@ -212,4 +196,14 @@ static inline int amd_iommu_deactivate_guest_mode(void *data) ...@@ -212,4 +196,14 @@ static inline int amd_iommu_deactivate_guest_mode(void *data)
} }
#endif /* defined(CONFIG_AMD_IOMMU) && defined(CONFIG_IRQ_REMAP) */ #endif /* defined(CONFIG_AMD_IOMMU) && defined(CONFIG_IRQ_REMAP) */
int amd_iommu_get_num_iommus(void);
bool amd_iommu_pc_supported(void);
u8 amd_iommu_pc_get_max_banks(unsigned int idx);
u8 amd_iommu_pc_get_max_counters(unsigned int idx);
int amd_iommu_pc_set_reg(struct amd_iommu *iommu, u8 bank, u8 cntr, u8 fxn,
u64 *value);
int amd_iommu_pc_get_reg(struct amd_iommu *iommu, u8 bank, u8 cntr, u8 fxn,
u64 *value);
struct amd_iommu *get_amd_iommu(unsigned int idx);
#endif /* _ASM_X86_AMD_IOMMU_H */ #endif /* _ASM_X86_AMD_IOMMU_H */
...@@ -57,7 +57,7 @@ enum cpuhp_state { ...@@ -57,7 +57,7 @@ enum cpuhp_state {
CPUHP_PAGE_ALLOC_DEAD, CPUHP_PAGE_ALLOC_DEAD,
CPUHP_NET_DEV_DEAD, CPUHP_NET_DEV_DEAD,
CPUHP_PCI_XGENE_DEAD, CPUHP_PCI_XGENE_DEAD,
CPUHP_IOMMU_INTEL_DEAD, CPUHP_IOMMU_IOVA_DEAD,
CPUHP_LUSTRE_CFS_DEAD, CPUHP_LUSTRE_CFS_DEAD,
CPUHP_AP_ARM_CACHE_B15_RAC_DEAD, CPUHP_AP_ARM_CACHE_B15_RAC_DEAD,
CPUHP_PADATA_DEAD, CPUHP_PADATA_DEAD,
......
...@@ -40,6 +40,8 @@ void iommu_dma_get_resv_regions(struct device *dev, struct list_head *list); ...@@ -40,6 +40,8 @@ void iommu_dma_get_resv_regions(struct device *dev, struct list_head *list);
void iommu_dma_free_cpu_cached_iovas(unsigned int cpu, void iommu_dma_free_cpu_cached_iovas(unsigned int cpu,
struct iommu_domain *domain); struct iommu_domain *domain);
extern bool iommu_dma_forcedac;
#else /* CONFIG_IOMMU_DMA */ #else /* CONFIG_IOMMU_DMA */
struct iommu_domain; struct iommu_domain;
...@@ -81,10 +83,5 @@ static inline void iommu_dma_get_resv_regions(struct device *dev, struct list_he ...@@ -81,10 +83,5 @@ static inline void iommu_dma_get_resv_regions(struct device *dev, struct list_he
{ {
} }
static inline void iommu_dma_free_cpu_cached_iovas(unsigned int cpu,
struct iommu_domain *domain)
{
}
#endif /* CONFIG_IOMMU_DMA */ #endif /* CONFIG_IOMMU_DMA */
#endif /* __DMA_IOMMU_H */ #endif /* __DMA_IOMMU_H */
...@@ -20,6 +20,7 @@ ...@@ -20,6 +20,7 @@
#include <linux/io-64-nonatomic-lo-hi.h> #include <linux/io-64-nonatomic-lo-hi.h>
#include <linux/dmar.h> #include <linux/dmar.h>
#include <linux/ioasid.h> #include <linux/ioasid.h>
#include <linux/bitfield.h>
#include <asm/cacheflush.h> #include <asm/cacheflush.h>
#include <asm/iommu.h> #include <asm/iommu.h>
...@@ -80,6 +81,7 @@ ...@@ -80,6 +81,7 @@
#define DMAR_IQ_SHIFT 4 /* Invalidation queue head/tail shift */ #define DMAR_IQ_SHIFT 4 /* Invalidation queue head/tail shift */
#define DMAR_IQA_REG 0x90 /* Invalidation queue addr register */ #define DMAR_IQA_REG 0x90 /* Invalidation queue addr register */
#define DMAR_ICS_REG 0x9c /* Invalidation complete status register */ #define DMAR_ICS_REG 0x9c /* Invalidation complete status register */
#define DMAR_IQER_REG 0xb0 /* Invalidation queue error record register */
#define DMAR_IRTA_REG 0xb8 /* Interrupt remapping table addr register */ #define DMAR_IRTA_REG 0xb8 /* Interrupt remapping table addr register */
#define DMAR_PQH_REG 0xc0 /* Page request queue head register */ #define DMAR_PQH_REG 0xc0 /* Page request queue head register */
#define DMAR_PQT_REG 0xc8 /* Page request queue tail register */ #define DMAR_PQT_REG 0xc8 /* Page request queue tail register */
...@@ -126,6 +128,10 @@ ...@@ -126,6 +128,10 @@
#define DMAR_VCMD_REG 0xe10 /* Virtual command register */ #define DMAR_VCMD_REG 0xe10 /* Virtual command register */
#define DMAR_VCRSP_REG 0xe20 /* Virtual command response register */ #define DMAR_VCRSP_REG 0xe20 /* Virtual command response register */
#define DMAR_IQER_REG_IQEI(reg) FIELD_GET(GENMASK_ULL(3, 0), reg)
#define DMAR_IQER_REG_ITESID(reg) FIELD_GET(GENMASK_ULL(47, 32), reg)
#define DMAR_IQER_REG_ICESID(reg) FIELD_GET(GENMASK_ULL(63, 48), reg)
#define OFFSET_STRIDE (9) #define OFFSET_STRIDE (9)
#define dmar_readq(a) readq(a) #define dmar_readq(a) readq(a)
...@@ -372,6 +378,7 @@ enum { ...@@ -372,6 +378,7 @@ enum {
/* PASID cache invalidation granu */ /* PASID cache invalidation granu */
#define QI_PC_ALL_PASIDS 0 #define QI_PC_ALL_PASIDS 0
#define QI_PC_PASID_SEL 1 #define QI_PC_PASID_SEL 1
#define QI_PC_GLOBAL 3
#define QI_EIOTLB_ADDR(addr) ((u64)(addr) & VTD_PAGE_MASK) #define QI_EIOTLB_ADDR(addr) ((u64)(addr) & VTD_PAGE_MASK)
#define QI_EIOTLB_IH(ih) (((u64)ih) << 6) #define QI_EIOTLB_IH(ih) (((u64)ih) << 6)
...@@ -763,14 +770,11 @@ u32 intel_svm_get_pasid(struct iommu_sva *handle); ...@@ -763,14 +770,11 @@ u32 intel_svm_get_pasid(struct iommu_sva *handle);
int intel_svm_page_response(struct device *dev, struct iommu_fault_event *evt, int intel_svm_page_response(struct device *dev, struct iommu_fault_event *evt,
struct iommu_page_response *msg); struct iommu_page_response *msg);
struct svm_dev_ops;
struct intel_svm_dev { struct intel_svm_dev {
struct list_head list; struct list_head list;
struct rcu_head rcu; struct rcu_head rcu;
struct device *dev; struct device *dev;
struct intel_iommu *iommu; struct intel_iommu *iommu;
struct svm_dev_ops *ops;
struct iommu_sva sva; struct iommu_sva sva;
u32 pasid; u32 pasid;
int users; int users;
......
...@@ -8,29 +8,12 @@ ...@@ -8,29 +8,12 @@
#ifndef __INTEL_SVM_H__ #ifndef __INTEL_SVM_H__
#define __INTEL_SVM_H__ #define __INTEL_SVM_H__
struct device;
struct svm_dev_ops {
void (*fault_cb)(struct device *dev, u32 pasid, u64 address,
void *private, int rwxp, int response);
};
/* Values for rxwp in fault_cb callback */ /* Values for rxwp in fault_cb callback */
#define SVM_REQ_READ (1<<3) #define SVM_REQ_READ (1<<3)
#define SVM_REQ_WRITE (1<<2) #define SVM_REQ_WRITE (1<<2)
#define SVM_REQ_EXEC (1<<1) #define SVM_REQ_EXEC (1<<1)
#define SVM_REQ_PRIV (1<<0) #define SVM_REQ_PRIV (1<<0)
/*
* The SVM_FLAG_PRIVATE_PASID flag requests a PASID which is *not* the "main"
* PASID for the current process. Even if a PASID already exists, a new one
* will be allocated. And the PASID allocated with SVM_FLAG_PRIVATE_PASID
* will not be given to subsequent callers. This facility allows a driver to
* disambiguate between multiple device contexts which access the same MM,
* if there is no other way to do so. It should be used sparingly, if at all.
*/
#define SVM_FLAG_PRIVATE_PASID (1<<0)
/* /*
* The SVM_FLAG_SUPERVISOR_MODE flag requests a PASID which can be used only * The SVM_FLAG_SUPERVISOR_MODE flag requests a PASID which can be used only
* for access to kernel addresses. No IOTLB flushes are automatically done * for access to kernel addresses. No IOTLB flushes are automatically done
...@@ -42,18 +25,18 @@ struct svm_dev_ops { ...@@ -42,18 +25,18 @@ struct svm_dev_ops {
* It is unlikely that we will ever hook into flush_tlb_kernel_range() to * It is unlikely that we will ever hook into flush_tlb_kernel_range() to
* do such IOTLB flushes automatically. * do such IOTLB flushes automatically.
*/ */
#define SVM_FLAG_SUPERVISOR_MODE (1<<1) #define SVM_FLAG_SUPERVISOR_MODE BIT(0)
/* /*
* The SVM_FLAG_GUEST_MODE flag is used when a PASID bind is for guest * The SVM_FLAG_GUEST_MODE flag is used when a PASID bind is for guest
* processes. Compared to the host bind, the primary differences are: * processes. Compared to the host bind, the primary differences are:
* 1. mm life cycle management * 1. mm life cycle management
* 2. fault reporting * 2. fault reporting
*/ */
#define SVM_FLAG_GUEST_MODE (1<<2) #define SVM_FLAG_GUEST_MODE BIT(1)
/* /*
* The SVM_FLAG_GUEST_PASID flag is used when a guest has its own PASID space, * The SVM_FLAG_GUEST_PASID flag is used when a guest has its own PASID space,
* which requires guest and host PASID translation at both directions. * which requires guest and host PASID translation at both directions.
*/ */
#define SVM_FLAG_GUEST_PASID (1<<3) #define SVM_FLAG_GUEST_PASID BIT(2)
#endif /* __INTEL_SVM_H__ */ #endif /* __INTEL_SVM_H__ */
...@@ -204,10 +204,6 @@ struct io_pgtable { ...@@ -204,10 +204,6 @@ struct io_pgtable {
#define io_pgtable_ops_to_pgtable(x) container_of((x), struct io_pgtable, ops) #define io_pgtable_ops_to_pgtable(x) container_of((x), struct io_pgtable, ops)
struct io_pgtable_domain_attr {
unsigned long quirks;
};
static inline void io_pgtable_tlb_flush_all(struct io_pgtable *iop) static inline void io_pgtable_tlb_flush_all(struct io_pgtable *iop)
{ {
if (iop->cfg.tlb && iop->cfg.tlb->tlb_flush_all) if (iop->cfg.tlb && iop->cfg.tlb->tlb_flush_all)
......
此差异已折叠。
...@@ -95,6 +95,7 @@ struct iova_domain { ...@@ -95,6 +95,7 @@ struct iova_domain {
flush-queues */ flush-queues */
atomic_t fq_timer_on; /* 1 when timer is active, 0 atomic_t fq_timer_on; /* 1 when timer is active, 0
when not */ when not */
struct hlist_node cpuhp_dead;
}; };
static inline unsigned long iova_size(struct iova *iova) static inline unsigned long iova_size(struct iova *iova)
...@@ -156,7 +157,6 @@ int init_iova_flush_queue(struct iova_domain *iovad, ...@@ -156,7 +157,6 @@ int init_iova_flush_queue(struct iova_domain *iovad,
iova_flush_cb flush_cb, iova_entry_dtor entry_dtor); iova_flush_cb flush_cb, iova_entry_dtor entry_dtor);
struct iova *find_iova(struct iova_domain *iovad, unsigned long pfn); struct iova *find_iova(struct iova_domain *iovad, unsigned long pfn);
void put_iova_domain(struct iova_domain *iovad); void put_iova_domain(struct iova_domain *iovad);
void free_cpu_cached_iovas(unsigned int cpu, struct iova_domain *iovad);
#else #else
static inline int iova_cache_get(void) static inline int iova_cache_get(void)
{ {
...@@ -233,10 +233,6 @@ static inline void put_iova_domain(struct iova_domain *iovad) ...@@ -233,10 +233,6 @@ static inline void put_iova_domain(struct iova_domain *iovad)
{ {
} }
static inline void free_cpu_cached_iovas(unsigned int cpu,
struct iova_domain *iovad)
{
}
#endif #endif
#endif #endif
此差异已折叠。
...@@ -288,7 +288,8 @@ struct iommu_gpasid_bind_data_vtd { ...@@ -288,7 +288,8 @@ struct iommu_gpasid_bind_data_vtd {
#define IOMMU_SVA_VTD_GPASID_PWT (1 << 3) /* page-level write through */ #define IOMMU_SVA_VTD_GPASID_PWT (1 << 3) /* page-level write through */
#define IOMMU_SVA_VTD_GPASID_EMTE (1 << 4) /* extended mem type enable */ #define IOMMU_SVA_VTD_GPASID_EMTE (1 << 4) /* extended mem type enable */
#define IOMMU_SVA_VTD_GPASID_CD (1 << 5) /* PASID-level cache disable */ #define IOMMU_SVA_VTD_GPASID_CD (1 << 5) /* PASID-level cache disable */
#define IOMMU_SVA_VTD_GPASID_LAST (1 << 6) #define IOMMU_SVA_VTD_GPASID_WPE (1 << 6) /* Write protect enable */
#define IOMMU_SVA_VTD_GPASID_LAST (1 << 7)
__u64 flags; __u64 flags;
__u32 pat; __u32 pat;
__u32 emt; __u32 emt;
......
Markdown is supported
0% .
You are about to add 0 people to the discussion. Proceed with caution.
先完成此消息的编辑!
想要评论请 注册