mirror of https://gitee.com/openkylin/linux.git
Merge branches 'iommu/fixes', 'arm/omap' and 'x86/amd' into next
Conflicts: drivers/pci/hotplug/acpiphp_glue.c
This commit is contained in:
commit
00fb5430f5
|
@ -66,6 +66,24 @@ Description:
|
|||
re-discover previously removed devices.
|
||||
Depends on CONFIG_HOTPLUG.
|
||||
|
||||
What: /sys/bus/pci/devices/.../msi_irqs/
|
||||
Date: September, 2011
|
||||
Contact: Neil Horman <nhorman@tuxdriver.com>
|
||||
Description:
|
||||
The /sys/devices/.../msi_irqs directory contains a variable set
|
||||
of sub-directories, with each sub-directory being named after a
|
||||
corresponding msi irq vector allocated to that device. Each
|
||||
numbered sub-directory N contains attributes of that irq.
|
||||
Note that this directory is not created for device drivers which
|
||||
do not support msi irqs
|
||||
|
||||
What: /sys/bus/pci/devices/.../msi_irqs/<N>/mode
|
||||
Date: September 2011
|
||||
Contact: Neil Horman <nhorman@tuxdriver.com>
|
||||
Description:
|
||||
This attribute indicates the mode that the irq vector named by
|
||||
the parent directory is in (msi vs. msix)
|
||||
|
||||
What: /sys/bus/pci/devices/.../remove
|
||||
Date: January 2009
|
||||
Contact: Linux PCI developers <linux-pci@vger.kernel.org>
|
||||
|
|
|
@ -329,6 +329,11 @@ bytes respectively. Such letter suffixes can also be entirely omitted.
|
|||
is a lot of faster
|
||||
off - do not initialize any AMD IOMMU found in
|
||||
the system
|
||||
force_isolation - Force device isolation for all
|
||||
devices. The IOMMU driver is not
|
||||
allowed anymore to lift isolation
|
||||
requirements as needed. This option
|
||||
does not override iommu=pt
|
||||
|
||||
amijoy.map= [HW,JOY] Amiga joystick support
|
||||
Map of devices attached to JOY0DAT and JOY1DAT
|
||||
|
|
|
@ -28,6 +28,7 @@
|
|||
#include <plat/board.h>
|
||||
#include <plat/mcbsp.h>
|
||||
#include <plat/mmc.h>
|
||||
#include <plat/iommu.h>
|
||||
#include <plat/dma.h>
|
||||
#include <plat/omap_hwmod.h>
|
||||
#include <plat/omap_device.h>
|
||||
|
@ -211,9 +212,15 @@ static struct platform_device omap3isp_device = {
|
|||
.resource = omap3isp_resources,
|
||||
};
|
||||
|
||||
static struct omap_iommu_arch_data omap3_isp_iommu = {
|
||||
.name = "isp",
|
||||
};
|
||||
|
||||
int omap3_init_camera(struct isp_platform_data *pdata)
|
||||
{
|
||||
omap3isp_device.dev.platform_data = pdata;
|
||||
omap3isp_device.dev.archdata.iommu = &omap3_isp_iommu;
|
||||
|
||||
return platform_device_register(&omap3isp_device);
|
||||
}
|
||||
|
||||
|
|
|
@ -111,6 +111,32 @@ struct iommu_platform_data {
|
|||
u32 da_end;
|
||||
};
|
||||
|
||||
/**
|
||||
* struct iommu_arch_data - omap iommu private data
|
||||
* @name: name of the iommu device
|
||||
* @iommu_dev: handle of the iommu device
|
||||
*
|
||||
* This is an omap iommu private data object, which binds an iommu user
|
||||
* to its iommu device. This object should be placed at the iommu user's
|
||||
* dev_archdata so generic IOMMU API can be used without having to
|
||||
* utilize omap-specific plumbing anymore.
|
||||
*/
|
||||
struct omap_iommu_arch_data {
|
||||
const char *name;
|
||||
struct omap_iommu *iommu_dev;
|
||||
};
|
||||
|
||||
/**
|
||||
* dev_to_omap_iommu() - retrieves an omap iommu object from a user device
|
||||
* @dev: iommu client device
|
||||
*/
|
||||
static inline struct omap_iommu *dev_to_omap_iommu(struct device *dev)
|
||||
{
|
||||
struct omap_iommu_arch_data *arch_data = dev->archdata.iommu;
|
||||
|
||||
return arch_data->iommu_dev;
|
||||
}
|
||||
|
||||
/* IOMMU errors */
|
||||
#define OMAP_IOMMU_ERR_TLB_MISS (1 << 0)
|
||||
#define OMAP_IOMMU_ERR_TRANS_FAULT (1 << 1)
|
||||
|
@ -163,8 +189,8 @@ extern int omap_iommu_set_isr(const char *name,
|
|||
void *priv),
|
||||
void *isr_priv);
|
||||
|
||||
extern void omap_iommu_save_ctx(struct omap_iommu *obj);
|
||||
extern void omap_iommu_restore_ctx(struct omap_iommu *obj);
|
||||
extern void omap_iommu_save_ctx(struct device *dev);
|
||||
extern void omap_iommu_restore_ctx(struct device *dev);
|
||||
|
||||
extern int omap_install_iommu_arch(const struct iommu_functions *ops);
|
||||
extern void omap_uninstall_iommu_arch(const struct iommu_functions *ops);
|
||||
|
@ -176,6 +202,5 @@ extern ssize_t
|
|||
omap_iommu_dump_ctx(struct omap_iommu *obj, char *buf, ssize_t len);
|
||||
extern size_t
|
||||
omap_dump_tlb_entries(struct omap_iommu *obj, char *buf, ssize_t len);
|
||||
struct device *omap_find_iommu_device(const char *name);
|
||||
|
||||
#endif /* __MACH_IOMMU_H */
|
||||
|
|
|
@ -72,18 +72,18 @@ struct iovm_struct {
|
|||
#define IOVMF_DA_FIXED (1 << (4 + IOVMF_SW_SHIFT))
|
||||
|
||||
|
||||
extern struct iovm_struct *omap_find_iovm_area(struct omap_iommu *obj, u32 da);
|
||||
extern struct iovm_struct *omap_find_iovm_area(struct device *dev, u32 da);
|
||||
extern u32
|
||||
omap_iommu_vmap(struct iommu_domain *domain, struct omap_iommu *obj, u32 da,
|
||||
omap_iommu_vmap(struct iommu_domain *domain, struct device *dev, u32 da,
|
||||
const struct sg_table *sgt, u32 flags);
|
||||
extern struct sg_table *omap_iommu_vunmap(struct iommu_domain *domain,
|
||||
struct omap_iommu *obj, u32 da);
|
||||
struct device *dev, u32 da);
|
||||
extern u32
|
||||
omap_iommu_vmalloc(struct iommu_domain *domain, struct omap_iommu *obj,
|
||||
omap_iommu_vmalloc(struct iommu_domain *domain, struct device *dev,
|
||||
u32 da, size_t bytes, u32 flags);
|
||||
extern void
|
||||
omap_iommu_vfree(struct iommu_domain *domain, struct omap_iommu *obj,
|
||||
omap_iommu_vfree(struct iommu_domain *domain, struct device *dev,
|
||||
const u32 da);
|
||||
extern void *omap_da_to_va(struct omap_iommu *obj, u32 da);
|
||||
extern void *omap_da_to_va(struct device *dev, u32 da);
|
||||
|
||||
#endif /* __IOMMU_MMAP_H */
|
||||
|
|
|
@ -596,6 +596,13 @@ static int __devinit acpi_pci_root_add(struct acpi_device *device)
|
|||
if (ACPI_SUCCESS(status)) {
|
||||
dev_info(root->bus->bridge,
|
||||
"ACPI _OSC control (0x%02x) granted\n", flags);
|
||||
if (acpi_gbl_FADT.boot_flags & ACPI_FADT_NO_ASPM) {
|
||||
/*
|
||||
* We have ASPM control, but the FADT indicates
|
||||
* that it's unsupported. Clear it.
|
||||
*/
|
||||
pcie_clear_aspm(root->bus);
|
||||
}
|
||||
} else {
|
||||
dev_info(root->bus->bridge,
|
||||
"ACPI _OSC request failed (%s), "
|
||||
|
|
|
@ -34,7 +34,9 @@ config AMD_IOMMU
|
|||
bool "AMD IOMMU support"
|
||||
select SWIOTLB
|
||||
select PCI_MSI
|
||||
select PCI_IOV
|
||||
select PCI_ATS
|
||||
select PCI_PRI
|
||||
select PCI_PASID
|
||||
select IOMMU_API
|
||||
depends on X86_64 && PCI && ACPI
|
||||
---help---
|
||||
|
@ -58,6 +60,15 @@ config AMD_IOMMU_STATS
|
|||
information to userspace via debugfs.
|
||||
If unsure, say N.
|
||||
|
||||
config AMD_IOMMU_V2
|
||||
tristate "AMD IOMMU Version 2 driver (EXPERIMENTAL)"
|
||||
depends on AMD_IOMMU && PROFILING && EXPERIMENTAL
|
||||
select MMU_NOTIFIER
|
||||
---help---
|
||||
This option enables support for the AMD IOMMUv2 features of the IOMMU
|
||||
hardware. Select this option if you want to use devices that support
|
||||
the the PCI PRI and PASID interface.
|
||||
|
||||
# Intel IOMMU support
|
||||
config DMAR_TABLE
|
||||
bool
|
||||
|
|
|
@ -1,6 +1,7 @@
|
|||
obj-$(CONFIG_IOMMU_API) += iommu.o
|
||||
obj-$(CONFIG_MSM_IOMMU) += msm_iommu.o msm_iommu_dev.o
|
||||
obj-$(CONFIG_AMD_IOMMU) += amd_iommu.o amd_iommu_init.o
|
||||
obj-$(CONFIG_AMD_IOMMU_V2) += amd_iommu_v2.o
|
||||
obj-$(CONFIG_DMAR_TABLE) += dmar.o
|
||||
obj-$(CONFIG_INTEL_IOMMU) += iova.o intel-iommu.o
|
||||
obj-$(CONFIG_IRQ_REMAP) += intr_remapping.o
|
||||
|
|
File diff suppressed because it is too large
Load Diff
|
@ -25,6 +25,7 @@
|
|||
#include <linux/interrupt.h>
|
||||
#include <linux/msi.h>
|
||||
#include <linux/amd-iommu.h>
|
||||
#include <linux/export.h>
|
||||
#include <asm/pci-direct.h>
|
||||
#include <asm/iommu.h>
|
||||
#include <asm/gart.h>
|
||||
|
@ -141,6 +142,12 @@ int amd_iommus_present;
|
|||
bool amd_iommu_np_cache __read_mostly;
|
||||
bool amd_iommu_iotlb_sup __read_mostly = true;
|
||||
|
||||
u32 amd_iommu_max_pasids __read_mostly = ~0;
|
||||
|
||||
bool amd_iommu_v2_present __read_mostly;
|
||||
|
||||
bool amd_iommu_force_isolation __read_mostly;
|
||||
|
||||
/*
|
||||
* The ACPI table parsing functions set this variable on an error
|
||||
*/
|
||||
|
@ -299,6 +306,16 @@ static void iommu_feature_disable(struct amd_iommu *iommu, u8 bit)
|
|||
writel(ctrl, iommu->mmio_base + MMIO_CONTROL_OFFSET);
|
||||
}
|
||||
|
||||
static void iommu_set_inv_tlb_timeout(struct amd_iommu *iommu, int timeout)
|
||||
{
|
||||
u32 ctrl;
|
||||
|
||||
ctrl = readl(iommu->mmio_base + MMIO_CONTROL_OFFSET);
|
||||
ctrl &= ~CTRL_INV_TO_MASK;
|
||||
ctrl |= (timeout << CONTROL_INV_TIMEOUT) & CTRL_INV_TO_MASK;
|
||||
writel(ctrl, iommu->mmio_base + MMIO_CONTROL_OFFSET);
|
||||
}
|
||||
|
||||
/* Function to enable the hardware */
|
||||
static void iommu_enable(struct amd_iommu *iommu)
|
||||
{
|
||||
|
@ -581,21 +598,69 @@ static void __init free_event_buffer(struct amd_iommu *iommu)
|
|||
free_pages((unsigned long)iommu->evt_buf, get_order(EVT_BUFFER_SIZE));
|
||||
}
|
||||
|
||||
/* allocates the memory where the IOMMU will log its events to */
|
||||
static u8 * __init alloc_ppr_log(struct amd_iommu *iommu)
|
||||
{
|
||||
iommu->ppr_log = (u8 *)__get_free_pages(GFP_KERNEL | __GFP_ZERO,
|
||||
get_order(PPR_LOG_SIZE));
|
||||
|
||||
if (iommu->ppr_log == NULL)
|
||||
return NULL;
|
||||
|
||||
return iommu->ppr_log;
|
||||
}
|
||||
|
||||
static void iommu_enable_ppr_log(struct amd_iommu *iommu)
|
||||
{
|
||||
u64 entry;
|
||||
|
||||
if (iommu->ppr_log == NULL)
|
||||
return;
|
||||
|
||||
entry = (u64)virt_to_phys(iommu->ppr_log) | PPR_LOG_SIZE_512;
|
||||
|
||||
memcpy_toio(iommu->mmio_base + MMIO_PPR_LOG_OFFSET,
|
||||
&entry, sizeof(entry));
|
||||
|
||||
/* set head and tail to zero manually */
|
||||
writel(0x00, iommu->mmio_base + MMIO_PPR_HEAD_OFFSET);
|
||||
writel(0x00, iommu->mmio_base + MMIO_PPR_TAIL_OFFSET);
|
||||
|
||||
iommu_feature_enable(iommu, CONTROL_PPFLOG_EN);
|
||||
iommu_feature_enable(iommu, CONTROL_PPR_EN);
|
||||
}
|
||||
|
||||
static void __init free_ppr_log(struct amd_iommu *iommu)
|
||||
{
|
||||
if (iommu->ppr_log == NULL)
|
||||
return;
|
||||
|
||||
free_pages((unsigned long)iommu->ppr_log, get_order(PPR_LOG_SIZE));
|
||||
}
|
||||
|
||||
static void iommu_enable_gt(struct amd_iommu *iommu)
|
||||
{
|
||||
if (!iommu_feature(iommu, FEATURE_GT))
|
||||
return;
|
||||
|
||||
iommu_feature_enable(iommu, CONTROL_GT_EN);
|
||||
}
|
||||
|
||||
/* sets a specific bit in the device table entry. */
|
||||
static void set_dev_entry_bit(u16 devid, u8 bit)
|
||||
{
|
||||
int i = (bit >> 5) & 0x07;
|
||||
int _bit = bit & 0x1f;
|
||||
int i = (bit >> 6) & 0x03;
|
||||
int _bit = bit & 0x3f;
|
||||
|
||||
amd_iommu_dev_table[devid].data[i] |= (1 << _bit);
|
||||
amd_iommu_dev_table[devid].data[i] |= (1UL << _bit);
|
||||
}
|
||||
|
||||
static int get_dev_entry_bit(u16 devid, u8 bit)
|
||||
{
|
||||
int i = (bit >> 5) & 0x07;
|
||||
int _bit = bit & 0x1f;
|
||||
int i = (bit >> 6) & 0x03;
|
||||
int _bit = bit & 0x3f;
|
||||
|
||||
return (amd_iommu_dev_table[devid].data[i] & (1 << _bit)) >> _bit;
|
||||
return (amd_iommu_dev_table[devid].data[i] & (1UL << _bit)) >> _bit;
|
||||
}
|
||||
|
||||
|
||||
|
@ -699,6 +764,32 @@ static void __init init_iommu_from_pci(struct amd_iommu *iommu)
|
|||
|
||||
iommu->features = ((u64)high << 32) | low;
|
||||
|
||||
if (iommu_feature(iommu, FEATURE_GT)) {
|
||||
int glxval;
|
||||
u32 pasids;
|
||||
u64 shift;
|
||||
|
||||
shift = iommu->features & FEATURE_PASID_MASK;
|
||||
shift >>= FEATURE_PASID_SHIFT;
|
||||
pasids = (1 << shift);
|
||||
|
||||
amd_iommu_max_pasids = min(amd_iommu_max_pasids, pasids);
|
||||
|
||||
glxval = iommu->features & FEATURE_GLXVAL_MASK;
|
||||
glxval >>= FEATURE_GLXVAL_SHIFT;
|
||||
|
||||
if (amd_iommu_max_glx_val == -1)
|
||||
amd_iommu_max_glx_val = glxval;
|
||||
else
|
||||
amd_iommu_max_glx_val = min(amd_iommu_max_glx_val, glxval);
|
||||
}
|
||||
|
||||
if (iommu_feature(iommu, FEATURE_GT) &&
|
||||
iommu_feature(iommu, FEATURE_PPR)) {
|
||||
iommu->is_iommu_v2 = true;
|
||||
amd_iommu_v2_present = true;
|
||||
}
|
||||
|
||||
if (!is_rd890_iommu(iommu->dev))
|
||||
return;
|
||||
|
||||
|
@ -901,6 +992,7 @@ static void __init free_iommu_one(struct amd_iommu *iommu)
|
|||
{
|
||||
free_command_buffer(iommu);
|
||||
free_event_buffer(iommu);
|
||||
free_ppr_log(iommu);
|
||||
iommu_unmap_mmio_space(iommu);
|
||||
}
|
||||
|
||||
|
@ -964,6 +1056,12 @@ static int __init init_iommu_one(struct amd_iommu *iommu, struct ivhd_header *h)
|
|||
init_iommu_from_acpi(iommu, h);
|
||||
init_iommu_devices(iommu);
|
||||
|
||||
if (iommu_feature(iommu, FEATURE_PPR)) {
|
||||
iommu->ppr_log = alloc_ppr_log(iommu);
|
||||
if (!iommu->ppr_log)
|
||||
return -ENOMEM;
|
||||
}
|
||||
|
||||
if (iommu->cap & (1UL << IOMMU_CAP_NPCACHE))
|
||||
amd_iommu_np_cache = true;
|
||||
|
||||
|
@ -1050,6 +1148,9 @@ static int iommu_setup_msi(struct amd_iommu *iommu)
|
|||
iommu->int_enabled = true;
|
||||
iommu_feature_enable(iommu, CONTROL_EVT_INT_EN);
|
||||
|
||||
if (iommu->ppr_log != NULL)
|
||||
iommu_feature_enable(iommu, CONTROL_PPFINT_EN);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
@ -1209,6 +1310,9 @@ static void iommu_init_flags(struct amd_iommu *iommu)
|
|||
* make IOMMU memory accesses cache coherent
|
||||
*/
|
||||
iommu_feature_enable(iommu, CONTROL_COHERENT_EN);
|
||||
|
||||
/* Set IOTLB invalidation timeout to 1s */
|
||||
iommu_set_inv_tlb_timeout(iommu, CTRL_INV_TO_1S);
|
||||
}
|
||||
|
||||
static void iommu_apply_resume_quirks(struct amd_iommu *iommu)
|
||||
|
@ -1274,6 +1378,8 @@ static void enable_iommus(void)
|
|||
iommu_set_device_table(iommu);
|
||||
iommu_enable_command_buffer(iommu);
|
||||
iommu_enable_event_buffer(iommu);
|
||||
iommu_enable_ppr_log(iommu);
|
||||
iommu_enable_gt(iommu);
|
||||
iommu_set_exclusion_range(iommu);
|
||||
iommu_init_msi(iommu);
|
||||
iommu_enable(iommu);
|
||||
|
@ -1303,13 +1409,6 @@ static void amd_iommu_resume(void)
|
|||
|
||||
/* re-load the hardware */
|
||||
enable_iommus();
|
||||
|
||||
/*
|
||||
* we have to flush after the IOMMUs are enabled because a
|
||||
* disabled IOMMU will never execute the commands we send
|
||||
*/
|
||||
for_each_iommu(iommu)
|
||||
iommu_flush_all_caches(iommu);
|
||||
}
|
||||
|
||||
static int amd_iommu_suspend(void)
|
||||
|
@ -1560,6 +1659,8 @@ static int __init parse_amd_iommu_options(char *str)
|
|||
amd_iommu_unmap_flush = true;
|
||||
if (strncmp(str, "off", 3) == 0)
|
||||
amd_iommu_disabled = true;
|
||||
if (strncmp(str, "force_isolation", 15) == 0)
|
||||
amd_iommu_force_isolation = true;
|
||||
}
|
||||
|
||||
return 1;
|
||||
|
@ -1572,3 +1673,9 @@ IOMMU_INIT_FINISH(amd_iommu_detect,
|
|||
gart_iommu_hole_init,
|
||||
0,
|
||||
0);
|
||||
|
||||
bool amd_iommu_v2_supported(void)
|
||||
{
|
||||
return amd_iommu_v2_present;
|
||||
}
|
||||
EXPORT_SYMBOL(amd_iommu_v2_supported);
|
||||
|
|
|
@ -31,6 +31,30 @@ extern int amd_iommu_init_devices(void);
|
|||
extern void amd_iommu_uninit_devices(void);
|
||||
extern void amd_iommu_init_notifier(void);
|
||||
extern void amd_iommu_init_api(void);
|
||||
|
||||
/* IOMMUv2 specific functions */
|
||||
struct iommu_domain;
|
||||
|
||||
extern bool amd_iommu_v2_supported(void);
|
||||
extern int amd_iommu_register_ppr_notifier(struct notifier_block *nb);
|
||||
extern int amd_iommu_unregister_ppr_notifier(struct notifier_block *nb);
|
||||
extern void amd_iommu_domain_direct_map(struct iommu_domain *dom);
|
||||
extern int amd_iommu_domain_enable_v2(struct iommu_domain *dom, int pasids);
|
||||
extern int amd_iommu_flush_page(struct iommu_domain *dom, int pasid,
|
||||
u64 address);
|
||||
extern int amd_iommu_flush_tlb(struct iommu_domain *dom, int pasid);
|
||||
extern int amd_iommu_domain_set_gcr3(struct iommu_domain *dom, int pasid,
|
||||
unsigned long cr3);
|
||||
extern int amd_iommu_domain_clear_gcr3(struct iommu_domain *dom, int pasid);
|
||||
extern struct iommu_domain *amd_iommu_get_v2_domain(struct pci_dev *pdev);
|
||||
|
||||
#define PPR_SUCCESS 0x0
|
||||
#define PPR_INVALID 0x1
|
||||
#define PPR_FAILURE 0xf
|
||||
|
||||
extern int amd_iommu_complete_ppr(struct pci_dev *pdev, int pasid,
|
||||
int status, int tag);
|
||||
|
||||
#ifndef CONFIG_AMD_IOMMU_STATS
|
||||
|
||||
static inline void amd_iommu_stats_init(void) { }
|
||||
|
|
|
@ -69,11 +69,14 @@
|
|||
#define MMIO_EXCL_BASE_OFFSET 0x0020
|
||||
#define MMIO_EXCL_LIMIT_OFFSET 0x0028
|
||||
#define MMIO_EXT_FEATURES 0x0030
|
||||
#define MMIO_PPR_LOG_OFFSET 0x0038
|
||||
#define MMIO_CMD_HEAD_OFFSET 0x2000
|
||||
#define MMIO_CMD_TAIL_OFFSET 0x2008
|
||||
#define MMIO_EVT_HEAD_OFFSET 0x2010
|
||||
#define MMIO_EVT_TAIL_OFFSET 0x2018
|
||||
#define MMIO_STATUS_OFFSET 0x2020
|
||||
#define MMIO_PPR_HEAD_OFFSET 0x2030
|
||||
#define MMIO_PPR_TAIL_OFFSET 0x2038
|
||||
|
||||
|
||||
/* Extended Feature Bits */
|
||||
|
@ -87,8 +90,17 @@
|
|||
#define FEATURE_HE (1ULL<<8)
|
||||
#define FEATURE_PC (1ULL<<9)
|
||||
|
||||
#define FEATURE_PASID_SHIFT 32
|
||||
#define FEATURE_PASID_MASK (0x1fULL << FEATURE_PASID_SHIFT)
|
||||
|
||||
#define FEATURE_GLXVAL_SHIFT 14
|
||||
#define FEATURE_GLXVAL_MASK (0x03ULL << FEATURE_GLXVAL_SHIFT)
|
||||
|
||||
#define PASID_MASK 0x000fffff
|
||||
|
||||
/* MMIO status bits */
|
||||
#define MMIO_STATUS_COM_WAIT_INT_MASK 0x04
|
||||
#define MMIO_STATUS_COM_WAIT_INT_MASK (1 << 2)
|
||||
#define MMIO_STATUS_PPR_INT_MASK (1 << 6)
|
||||
|
||||
/* event logging constants */
|
||||
#define EVENT_ENTRY_SIZE 0x10
|
||||
|
@ -115,6 +127,7 @@
|
|||
#define CONTROL_EVT_LOG_EN 0x02ULL
|
||||
#define CONTROL_EVT_INT_EN 0x03ULL
|
||||
#define CONTROL_COMWAIT_EN 0x04ULL
|
||||
#define CONTROL_INV_TIMEOUT 0x05ULL
|
||||
#define CONTROL_PASSPW_EN 0x08ULL
|
||||
#define CONTROL_RESPASSPW_EN 0x09ULL
|
||||
#define CONTROL_COHERENT_EN 0x0aULL
|
||||
|
@ -122,18 +135,34 @@
|
|||
#define CONTROL_CMDBUF_EN 0x0cULL
|
||||
#define CONTROL_PPFLOG_EN 0x0dULL
|
||||
#define CONTROL_PPFINT_EN 0x0eULL
|
||||
#define CONTROL_PPR_EN 0x0fULL
|
||||
#define CONTROL_GT_EN 0x10ULL
|
||||
|
||||
#define CTRL_INV_TO_MASK (7 << CONTROL_INV_TIMEOUT)
|
||||
#define CTRL_INV_TO_NONE 0
|
||||
#define CTRL_INV_TO_1MS 1
|
||||
#define CTRL_INV_TO_10MS 2
|
||||
#define CTRL_INV_TO_100MS 3
|
||||
#define CTRL_INV_TO_1S 4
|
||||
#define CTRL_INV_TO_10S 5
|
||||
#define CTRL_INV_TO_100S 6
|
||||
|
||||
/* command specific defines */
|
||||
#define CMD_COMPL_WAIT 0x01
|
||||
#define CMD_INV_DEV_ENTRY 0x02
|
||||
#define CMD_INV_IOMMU_PAGES 0x03
|
||||
#define CMD_INV_IOTLB_PAGES 0x04
|
||||
#define CMD_COMPLETE_PPR 0x07
|
||||
#define CMD_INV_ALL 0x08
|
||||
|
||||
#define CMD_COMPL_WAIT_STORE_MASK 0x01
|
||||
#define CMD_COMPL_WAIT_INT_MASK 0x02
|
||||
#define CMD_INV_IOMMU_PAGES_SIZE_MASK 0x01
|
||||
#define CMD_INV_IOMMU_PAGES_PDE_MASK 0x02
|
||||
#define CMD_INV_IOMMU_PAGES_GN_MASK 0x04
|
||||
|
||||
#define PPR_STATUS_MASK 0xf
|
||||
#define PPR_STATUS_SHIFT 12
|
||||
|
||||
#define CMD_INV_IOMMU_ALL_PAGES_ADDRESS 0x7fffffffffffffffULL
|
||||
|
||||
|
@ -165,6 +194,23 @@
|
|||
#define EVT_BUFFER_SIZE 8192 /* 512 entries */
|
||||
#define EVT_LEN_MASK (0x9ULL << 56)
|
||||
|
||||
/* Constants for PPR Log handling */
|
||||
#define PPR_LOG_ENTRIES 512
|
||||
#define PPR_LOG_SIZE_SHIFT 56
|
||||
#define PPR_LOG_SIZE_512 (0x9ULL << PPR_LOG_SIZE_SHIFT)
|
||||
#define PPR_ENTRY_SIZE 16
|
||||
#define PPR_LOG_SIZE (PPR_ENTRY_SIZE * PPR_LOG_ENTRIES)
|
||||
|
||||
#define PPR_REQ_TYPE(x) (((x) >> 60) & 0xfULL)
|
||||
#define PPR_FLAGS(x) (((x) >> 48) & 0xfffULL)
|
||||
#define PPR_DEVID(x) ((x) & 0xffffULL)
|
||||
#define PPR_TAG(x) (((x) >> 32) & 0x3ffULL)
|
||||
#define PPR_PASID1(x) (((x) >> 16) & 0xffffULL)
|
||||
#define PPR_PASID2(x) (((x) >> 42) & 0xfULL)
|
||||
#define PPR_PASID(x) ((PPR_PASID2(x) << 16) | PPR_PASID1(x))
|
||||
|
||||
#define PPR_REQ_FAULT 0x01
|
||||
|
||||
#define PAGE_MODE_NONE 0x00
|
||||
#define PAGE_MODE_1_LEVEL 0x01
|
||||
#define PAGE_MODE_2_LEVEL 0x02
|
||||
|
@ -230,7 +276,24 @@
|
|||
#define IOMMU_PTE_IR (1ULL << 61)
|
||||
#define IOMMU_PTE_IW (1ULL << 62)
|
||||
|
||||
#define DTE_FLAG_IOTLB 0x01
|
||||
#define DTE_FLAG_IOTLB (0x01UL << 32)
|
||||
#define DTE_FLAG_GV (0x01ULL << 55)
|
||||
#define DTE_GLX_SHIFT (56)
|
||||
#define DTE_GLX_MASK (3)
|
||||
|
||||
#define DTE_GCR3_VAL_A(x) (((x) >> 12) & 0x00007ULL)
|
||||
#define DTE_GCR3_VAL_B(x) (((x) >> 15) & 0x0ffffULL)
|
||||
#define DTE_GCR3_VAL_C(x) (((x) >> 31) & 0xfffffULL)
|
||||
|
||||
#define DTE_GCR3_INDEX_A 0
|
||||
#define DTE_GCR3_INDEX_B 1
|
||||
#define DTE_GCR3_INDEX_C 1
|
||||
|
||||
#define DTE_GCR3_SHIFT_A 58
|
||||
#define DTE_GCR3_SHIFT_B 16
|
||||
#define DTE_GCR3_SHIFT_C 43
|
||||
|
||||
#define GCR3_VALID 0x01ULL
|
||||
|
||||
#define IOMMU_PAGE_MASK (((1ULL << 52) - 1) & ~0xfffULL)
|
||||
#define IOMMU_PTE_PRESENT(pte) ((pte) & IOMMU_PTE_P)
|
||||
|
@ -257,6 +320,7 @@
|
|||
domain for an IOMMU */
|
||||
#define PD_PASSTHROUGH_MASK (1UL << 2) /* domain has no page
|
||||
translation */
|
||||
#define PD_IOMMUV2_MASK (1UL << 3) /* domain has gcr3 table */
|
||||
|
||||
extern bool amd_iommu_dump;
|
||||
#define DUMP_printk(format, arg...) \
|
||||
|
@ -285,6 +349,29 @@ extern bool amd_iommu_iotlb_sup;
|
|||
#define APERTURE_RANGE_INDEX(a) ((a) >> APERTURE_RANGE_SHIFT)
|
||||
#define APERTURE_PAGE_INDEX(a) (((a) >> 21) & 0x3fULL)
|
||||
|
||||
|
||||
/*
|
||||
* This struct is used to pass information about
|
||||
* incoming PPR faults around.
|
||||
*/
|
||||
struct amd_iommu_fault {
|
||||
u64 address; /* IO virtual address of the fault*/
|
||||
u32 pasid; /* Address space identifier */
|
||||
u16 device_id; /* Originating PCI device id */
|
||||
u16 tag; /* PPR tag */
|
||||
u16 flags; /* Fault flags */
|
||||
|
||||
};
|
||||
|
||||
#define PPR_FAULT_EXEC (1 << 1)
|
||||
#define PPR_FAULT_READ (1 << 2)
|
||||
#define PPR_FAULT_WRITE (1 << 5)
|
||||
#define PPR_FAULT_USER (1 << 6)
|
||||
#define PPR_FAULT_RSVD (1 << 7)
|
||||
#define PPR_FAULT_GN (1 << 8)
|
||||
|
||||
struct iommu_domain;
|
||||
|
||||
/*
|
||||
* This structure contains generic data for IOMMU protection domains
|
||||
* independent of their use.
|
||||
|
@ -297,11 +384,15 @@ struct protection_domain {
|
|||
u16 id; /* the domain id written to the device table */
|
||||
int mode; /* paging mode (0-6 levels) */
|
||||
u64 *pt_root; /* page table root pointer */
|
||||
int glx; /* Number of levels for GCR3 table */
|
||||
u64 *gcr3_tbl; /* Guest CR3 table */
|
||||
unsigned long flags; /* flags to find out type of domain */
|
||||
bool updated; /* complete domain flush required */
|
||||
unsigned dev_cnt; /* devices assigned to this domain */
|
||||
unsigned dev_iommu[MAX_IOMMUS]; /* per-IOMMU reference count */
|
||||
void *priv; /* private data */
|
||||
struct iommu_domain *iommu_domain; /* Pointer to generic
|
||||
domain structure */
|
||||
|
||||
};
|
||||
|
||||
|
@ -315,10 +406,15 @@ struct iommu_dev_data {
|
|||
struct protection_domain *domain; /* Domain the device is bound to */
|
||||
atomic_t bind; /* Domain attach reverent count */
|
||||
u16 devid; /* PCI Device ID */
|
||||
bool iommu_v2; /* Device can make use of IOMMUv2 */
|
||||
bool passthrough; /* Default for device is pt_domain */
|
||||
struct {
|
||||
bool enabled;
|
||||
int qdep;
|
||||
} ats; /* ATS state */
|
||||
bool pri_tlp; /* PASID TLB required for
|
||||
PPR completions */
|
||||
u32 errata; /* Bitmap for errata to apply */
|
||||
};
|
||||
|
||||
/*
|
||||
|
@ -399,6 +495,9 @@ struct amd_iommu {
|
|||
/* Extended features */
|
||||
u64 features;
|
||||
|
||||
/* IOMMUv2 */
|
||||
bool is_iommu_v2;
|
||||
|
||||
/*
|
||||
* Capability pointer. There could be more than one IOMMU per PCI
|
||||
* device function if there are more than one AMD IOMMU capability
|
||||
|
@ -431,6 +530,9 @@ struct amd_iommu {
|
|||
/* MSI number for event interrupt */
|
||||
u16 evt_msi_num;
|
||||
|
||||
/* Base of the PPR log, if present */
|
||||
u8 *ppr_log;
|
||||
|
||||
/* true if interrupts for this IOMMU are already enabled */
|
||||
bool int_enabled;
|
||||
|
||||
|
@ -484,7 +586,7 @@ extern struct list_head amd_iommu_pd_list;
|
|||
* Structure defining one entry in the device table
|
||||
*/
|
||||
struct dev_table_entry {
|
||||
u32 data[8];
|
||||
u64 data[4];
|
||||
};
|
||||
|
||||
/*
|
||||
|
@ -549,6 +651,16 @@ extern unsigned long *amd_iommu_pd_alloc_bitmap;
|
|||
*/
|
||||
extern bool amd_iommu_unmap_flush;
|
||||
|
||||
/* Smallest number of PASIDs supported by any IOMMU in the system */
|
||||
extern u32 amd_iommu_max_pasids;
|
||||
|
||||
extern bool amd_iommu_v2_present;
|
||||
|
||||
extern bool amd_iommu_force_isolation;
|
||||
|
||||
/* Max levels of glxval supported */
|
||||
extern int amd_iommu_max_glx_val;
|
||||
|
||||
/* takes bus and device/function and returns the device id
|
||||
* FIXME: should that be in generic PCI code? */
|
||||
static inline u16 calc_devid(u8 bus, u8 devfn)
|
||||
|
|
|
@ -0,0 +1,994 @@
|
|||
/*
|
||||
* Copyright (C) 2010-2012 Advanced Micro Devices, Inc.
|
||||
* Author: Joerg Roedel <joerg.roedel@amd.com>
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify it
|
||||
* under the terms of the GNU General Public License version 2 as published
|
||||
* by the Free Software Foundation.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program; if not, write to the Free Software
|
||||
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
|
||||
*/
|
||||
|
||||
#include <linux/mmu_notifier.h>
|
||||
#include <linux/amd-iommu.h>
|
||||
#include <linux/mm_types.h>
|
||||
#include <linux/profile.h>
|
||||
#include <linux/module.h>
|
||||
#include <linux/sched.h>
|
||||
#include <linux/iommu.h>
|
||||
#include <linux/wait.h>
|
||||
#include <linux/pci.h>
|
||||
#include <linux/gfp.h>
|
||||
|
||||
#include "amd_iommu_types.h"
|
||||
#include "amd_iommu_proto.h"
|
||||
|
||||
MODULE_LICENSE("GPL v2");
|
||||
MODULE_AUTHOR("Joerg Roedel <joerg.roedel@amd.com>");
|
||||
|
||||
#define MAX_DEVICES 0x10000
|
||||
#define PRI_QUEUE_SIZE 512
|
||||
|
||||
struct pri_queue {
|
||||
atomic_t inflight;
|
||||
bool finish;
|
||||
int status;
|
||||
};
|
||||
|
||||
struct pasid_state {
|
||||
struct list_head list; /* For global state-list */
|
||||
atomic_t count; /* Reference count */
|
||||
struct task_struct *task; /* Task bound to this PASID */
|
||||
struct mm_struct *mm; /* mm_struct for the faults */
|
||||
struct mmu_notifier mn; /* mmu_otifier handle */
|
||||
struct pri_queue pri[PRI_QUEUE_SIZE]; /* PRI tag states */
|
||||
struct device_state *device_state; /* Link to our device_state */
|
||||
int pasid; /* PASID index */
|
||||
spinlock_t lock; /* Protect pri_queues */
|
||||
wait_queue_head_t wq; /* To wait for count == 0 */
|
||||
};
|
||||
|
||||
struct device_state {
|
||||
atomic_t count;
|
||||
struct pci_dev *pdev;
|
||||
struct pasid_state **states;
|
||||
struct iommu_domain *domain;
|
||||
int pasid_levels;
|
||||
int max_pasids;
|
||||
amd_iommu_invalid_ppr_cb inv_ppr_cb;
|
||||
amd_iommu_invalidate_ctx inv_ctx_cb;
|
||||
spinlock_t lock;
|
||||
wait_queue_head_t wq;
|
||||
};
|
||||
|
||||
struct fault {
|
||||
struct work_struct work;
|
||||
struct device_state *dev_state;
|
||||
struct pasid_state *state;
|
||||
struct mm_struct *mm;
|
||||
u64 address;
|
||||
u16 devid;
|
||||
u16 pasid;
|
||||
u16 tag;
|
||||
u16 finish;
|
||||
u16 flags;
|
||||
};
|
||||
|
||||
struct device_state **state_table;
|
||||
static spinlock_t state_lock;
|
||||
|
||||
/* List and lock for all pasid_states */
|
||||
static LIST_HEAD(pasid_state_list);
|
||||
static DEFINE_SPINLOCK(ps_lock);
|
||||
|
||||
static struct workqueue_struct *iommu_wq;
|
||||
|
||||
/*
|
||||
* Empty page table - Used between
|
||||
* mmu_notifier_invalidate_range_start and
|
||||
* mmu_notifier_invalidate_range_end
|
||||
*/
|
||||
static u64 *empty_page_table;
|
||||
|
||||
static void free_pasid_states(struct device_state *dev_state);
|
||||
static void unbind_pasid(struct device_state *dev_state, int pasid);
|
||||
static int task_exit(struct notifier_block *nb, unsigned long e, void *data);
|
||||
|
||||
static u16 device_id(struct pci_dev *pdev)
|
||||
{
|
||||
u16 devid;
|
||||
|
||||
devid = pdev->bus->number;
|
||||
devid = (devid << 8) | pdev->devfn;
|
||||
|
||||
return devid;
|
||||
}
|
||||
|
||||
static struct device_state *get_device_state(u16 devid)
|
||||
{
|
||||
struct device_state *dev_state;
|
||||
unsigned long flags;
|
||||
|
||||
spin_lock_irqsave(&state_lock, flags);
|
||||
dev_state = state_table[devid];
|
||||
if (dev_state != NULL)
|
||||
atomic_inc(&dev_state->count);
|
||||
spin_unlock_irqrestore(&state_lock, flags);
|
||||
|
||||
return dev_state;
|
||||
}
|
||||
|
||||
static void free_device_state(struct device_state *dev_state)
|
||||
{
|
||||
/*
|
||||
* First detach device from domain - No more PRI requests will arrive
|
||||
* from that device after it is unbound from the IOMMUv2 domain.
|
||||
*/
|
||||
iommu_detach_device(dev_state->domain, &dev_state->pdev->dev);
|
||||
|
||||
/* Everything is down now, free the IOMMUv2 domain */
|
||||
iommu_domain_free(dev_state->domain);
|
||||
|
||||
/* Finally get rid of the device-state */
|
||||
kfree(dev_state);
|
||||
}
|
||||
|
||||
static void put_device_state(struct device_state *dev_state)
|
||||
{
|
||||
if (atomic_dec_and_test(&dev_state->count))
|
||||
wake_up(&dev_state->wq);
|
||||
}
|
||||
|
||||
static void put_device_state_wait(struct device_state *dev_state)
|
||||
{
|
||||
DEFINE_WAIT(wait);
|
||||
|
||||
prepare_to_wait(&dev_state->wq, &wait, TASK_UNINTERRUPTIBLE);
|
||||
if (!atomic_dec_and_test(&dev_state->count))
|
||||
schedule();
|
||||
finish_wait(&dev_state->wq, &wait);
|
||||
|
||||
free_device_state(dev_state);
|
||||
}
|
||||
|
||||
static struct notifier_block profile_nb = {
|
||||
.notifier_call = task_exit,
|
||||
};
|
||||
|
||||
static void link_pasid_state(struct pasid_state *pasid_state)
|
||||
{
|
||||
spin_lock(&ps_lock);
|
||||
list_add_tail(&pasid_state->list, &pasid_state_list);
|
||||
spin_unlock(&ps_lock);
|
||||
}
|
||||
|
||||
static void __unlink_pasid_state(struct pasid_state *pasid_state)
|
||||
{
|
||||
list_del(&pasid_state->list);
|
||||
}
|
||||
|
||||
static void unlink_pasid_state(struct pasid_state *pasid_state)
|
||||
{
|
||||
spin_lock(&ps_lock);
|
||||
__unlink_pasid_state(pasid_state);
|
||||
spin_unlock(&ps_lock);
|
||||
}
|
||||
|
||||
/* Must be called under dev_state->lock */
|
||||
static struct pasid_state **__get_pasid_state_ptr(struct device_state *dev_state,
|
||||
int pasid, bool alloc)
|
||||
{
|
||||
struct pasid_state **root, **ptr;
|
||||
int level, index;
|
||||
|
||||
level = dev_state->pasid_levels;
|
||||
root = dev_state->states;
|
||||
|
||||
while (true) {
|
||||
|
||||
index = (pasid >> (9 * level)) & 0x1ff;
|
||||
ptr = &root[index];
|
||||
|
||||
if (level == 0)
|
||||
break;
|
||||
|
||||
if (*ptr == NULL) {
|
||||
if (!alloc)
|
||||
return NULL;
|
||||
|
||||
*ptr = (void *)get_zeroed_page(GFP_ATOMIC);
|
||||
if (*ptr == NULL)
|
||||
return NULL;
|
||||
}
|
||||
|
||||
root = (struct pasid_state **)*ptr;
|
||||
level -= 1;
|
||||
}
|
||||
|
||||
return ptr;
|
||||
}
|
||||
|
||||
static int set_pasid_state(struct device_state *dev_state,
|
||||
struct pasid_state *pasid_state,
|
||||
int pasid)
|
||||
{
|
||||
struct pasid_state **ptr;
|
||||
unsigned long flags;
|
||||
int ret;
|
||||
|
||||
spin_lock_irqsave(&dev_state->lock, flags);
|
||||
ptr = __get_pasid_state_ptr(dev_state, pasid, true);
|
||||
|
||||
ret = -ENOMEM;
|
||||
if (ptr == NULL)
|
||||
goto out_unlock;
|
||||
|
||||
ret = -ENOMEM;
|
||||
if (*ptr != NULL)
|
||||
goto out_unlock;
|
||||
|
||||
*ptr = pasid_state;
|
||||
|
||||
ret = 0;
|
||||
|
||||
out_unlock:
|
||||
spin_unlock_irqrestore(&dev_state->lock, flags);
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
static void clear_pasid_state(struct device_state *dev_state, int pasid)
|
||||
{
|
||||
struct pasid_state **ptr;
|
||||
unsigned long flags;
|
||||
|
||||
spin_lock_irqsave(&dev_state->lock, flags);
|
||||
ptr = __get_pasid_state_ptr(dev_state, pasid, true);
|
||||
|
||||
if (ptr == NULL)
|
||||
goto out_unlock;
|
||||
|
||||
*ptr = NULL;
|
||||
|
||||
out_unlock:
|
||||
spin_unlock_irqrestore(&dev_state->lock, flags);
|
||||
}
|
||||
|
||||
static struct pasid_state *get_pasid_state(struct device_state *dev_state,
|
||||
int pasid)
|
||||
{
|
||||
struct pasid_state **ptr, *ret = NULL;
|
||||
unsigned long flags;
|
||||
|
||||
spin_lock_irqsave(&dev_state->lock, flags);
|
||||
ptr = __get_pasid_state_ptr(dev_state, pasid, false);
|
||||
|
||||
if (ptr == NULL)
|
||||
goto out_unlock;
|
||||
|
||||
ret = *ptr;
|
||||
if (ret)
|
||||
atomic_inc(&ret->count);
|
||||
|
||||
out_unlock:
|
||||
spin_unlock_irqrestore(&dev_state->lock, flags);
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
static void free_pasid_state(struct pasid_state *pasid_state)
|
||||
{
|
||||
kfree(pasid_state);
|
||||
}
|
||||
|
||||
static void put_pasid_state(struct pasid_state *pasid_state)
|
||||
{
|
||||
if (atomic_dec_and_test(&pasid_state->count)) {
|
||||
put_device_state(pasid_state->device_state);
|
||||
wake_up(&pasid_state->wq);
|
||||
}
|
||||
}
|
||||
|
||||
static void put_pasid_state_wait(struct pasid_state *pasid_state)
|
||||
{
|
||||
DEFINE_WAIT(wait);
|
||||
|
||||
prepare_to_wait(&pasid_state->wq, &wait, TASK_UNINTERRUPTIBLE);
|
||||
|
||||
if (atomic_dec_and_test(&pasid_state->count))
|
||||
put_device_state(pasid_state->device_state);
|
||||
else
|
||||
schedule();
|
||||
|
||||
finish_wait(&pasid_state->wq, &wait);
|
||||
mmput(pasid_state->mm);
|
||||
free_pasid_state(pasid_state);
|
||||
}
|
||||
|
||||
static void __unbind_pasid(struct pasid_state *pasid_state)
|
||||
{
|
||||
struct iommu_domain *domain;
|
||||
|
||||
domain = pasid_state->device_state->domain;
|
||||
|
||||
amd_iommu_domain_clear_gcr3(domain, pasid_state->pasid);
|
||||
clear_pasid_state(pasid_state->device_state, pasid_state->pasid);
|
||||
|
||||
/* Make sure no more pending faults are in the queue */
|
||||
flush_workqueue(iommu_wq);
|
||||
|
||||
mmu_notifier_unregister(&pasid_state->mn, pasid_state->mm);
|
||||
|
||||
put_pasid_state(pasid_state); /* Reference taken in bind() function */
|
||||
}
|
||||
|
||||
static void unbind_pasid(struct device_state *dev_state, int pasid)
|
||||
{
|
||||
struct pasid_state *pasid_state;
|
||||
|
||||
pasid_state = get_pasid_state(dev_state, pasid);
|
||||
if (pasid_state == NULL)
|
||||
return;
|
||||
|
||||
unlink_pasid_state(pasid_state);
|
||||
__unbind_pasid(pasid_state);
|
||||
put_pasid_state_wait(pasid_state); /* Reference taken in this function */
|
||||
}
|
||||
|
||||
static void free_pasid_states_level1(struct pasid_state **tbl)
|
||||
{
|
||||
int i;
|
||||
|
||||
for (i = 0; i < 512; ++i) {
|
||||
if (tbl[i] == NULL)
|
||||
continue;
|
||||
|
||||
free_page((unsigned long)tbl[i]);
|
||||
}
|
||||
}
|
||||
|
||||
static void free_pasid_states_level2(struct pasid_state **tbl)
|
||||
{
|
||||
struct pasid_state **ptr;
|
||||
int i;
|
||||
|
||||
for (i = 0; i < 512; ++i) {
|
||||
if (tbl[i] == NULL)
|
||||
continue;
|
||||
|
||||
ptr = (struct pasid_state **)tbl[i];
|
||||
free_pasid_states_level1(ptr);
|
||||
}
|
||||
}
|
||||
|
||||
static void free_pasid_states(struct device_state *dev_state)
|
||||
{
|
||||
struct pasid_state *pasid_state;
|
||||
int i;
|
||||
|
||||
for (i = 0; i < dev_state->max_pasids; ++i) {
|
||||
pasid_state = get_pasid_state(dev_state, i);
|
||||
if (pasid_state == NULL)
|
||||
continue;
|
||||
|
||||
put_pasid_state(pasid_state);
|
||||
unbind_pasid(dev_state, i);
|
||||
}
|
||||
|
||||
if (dev_state->pasid_levels == 2)
|
||||
free_pasid_states_level2(dev_state->states);
|
||||
else if (dev_state->pasid_levels == 1)
|
||||
free_pasid_states_level1(dev_state->states);
|
||||
else if (dev_state->pasid_levels != 0)
|
||||
BUG();
|
||||
|
||||
free_page((unsigned long)dev_state->states);
|
||||
}
|
||||
|
||||
static struct pasid_state *mn_to_state(struct mmu_notifier *mn)
|
||||
{
|
||||
return container_of(mn, struct pasid_state, mn);
|
||||
}
|
||||
|
||||
static void __mn_flush_page(struct mmu_notifier *mn,
|
||||
unsigned long address)
|
||||
{
|
||||
struct pasid_state *pasid_state;
|
||||
struct device_state *dev_state;
|
||||
|
||||
pasid_state = mn_to_state(mn);
|
||||
dev_state = pasid_state->device_state;
|
||||
|
||||
amd_iommu_flush_page(dev_state->domain, pasid_state->pasid, address);
|
||||
}
|
||||
|
||||
static int mn_clear_flush_young(struct mmu_notifier *mn,
|
||||
struct mm_struct *mm,
|
||||
unsigned long address)
|
||||
{
|
||||
__mn_flush_page(mn, address);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void mn_change_pte(struct mmu_notifier *mn,
|
||||
struct mm_struct *mm,
|
||||
unsigned long address,
|
||||
pte_t pte)
|
||||
{
|
||||
__mn_flush_page(mn, address);
|
||||
}
|
||||
|
||||
static void mn_invalidate_page(struct mmu_notifier *mn,
|
||||
struct mm_struct *mm,
|
||||
unsigned long address)
|
||||
{
|
||||
__mn_flush_page(mn, address);
|
||||
}
|
||||
|
||||
static void mn_invalidate_range_start(struct mmu_notifier *mn,
|
||||
struct mm_struct *mm,
|
||||
unsigned long start, unsigned long end)
|
||||
{
|
||||
struct pasid_state *pasid_state;
|
||||
struct device_state *dev_state;
|
||||
|
||||
pasid_state = mn_to_state(mn);
|
||||
dev_state = pasid_state->device_state;
|
||||
|
||||
amd_iommu_domain_set_gcr3(dev_state->domain, pasid_state->pasid,
|
||||
__pa(empty_page_table));
|
||||
}
|
||||
|
||||
static void mn_invalidate_range_end(struct mmu_notifier *mn,
|
||||
struct mm_struct *mm,
|
||||
unsigned long start, unsigned long end)
|
||||
{
|
||||
struct pasid_state *pasid_state;
|
||||
struct device_state *dev_state;
|
||||
|
||||
pasid_state = mn_to_state(mn);
|
||||
dev_state = pasid_state->device_state;
|
||||
|
||||
amd_iommu_domain_set_gcr3(dev_state->domain, pasid_state->pasid,
|
||||
__pa(pasid_state->mm->pgd));
|
||||
}
|
||||
|
||||
static struct mmu_notifier_ops iommu_mn = {
|
||||
.clear_flush_young = mn_clear_flush_young,
|
||||
.change_pte = mn_change_pte,
|
||||
.invalidate_page = mn_invalidate_page,
|
||||
.invalidate_range_start = mn_invalidate_range_start,
|
||||
.invalidate_range_end = mn_invalidate_range_end,
|
||||
};
|
||||
|
||||
static void set_pri_tag_status(struct pasid_state *pasid_state,
|
||||
u16 tag, int status)
|
||||
{
|
||||
unsigned long flags;
|
||||
|
||||
spin_lock_irqsave(&pasid_state->lock, flags);
|
||||
pasid_state->pri[tag].status = status;
|
||||
spin_unlock_irqrestore(&pasid_state->lock, flags);
|
||||
}
|
||||
|
||||
static void finish_pri_tag(struct device_state *dev_state,
|
||||
struct pasid_state *pasid_state,
|
||||
u16 tag)
|
||||
{
|
||||
unsigned long flags;
|
||||
|
||||
spin_lock_irqsave(&pasid_state->lock, flags);
|
||||
if (atomic_dec_and_test(&pasid_state->pri[tag].inflight) &&
|
||||
pasid_state->pri[tag].finish) {
|
||||
amd_iommu_complete_ppr(dev_state->pdev, pasid_state->pasid,
|
||||
pasid_state->pri[tag].status, tag);
|
||||
pasid_state->pri[tag].finish = false;
|
||||
pasid_state->pri[tag].status = PPR_SUCCESS;
|
||||
}
|
||||
spin_unlock_irqrestore(&pasid_state->lock, flags);
|
||||
}
|
||||
|
||||
static void do_fault(struct work_struct *work)
|
||||
{
|
||||
struct fault *fault = container_of(work, struct fault, work);
|
||||
int npages, write;
|
||||
struct page *page;
|
||||
|
||||
write = !!(fault->flags & PPR_FAULT_WRITE);
|
||||
|
||||
npages = get_user_pages(fault->state->task, fault->state->mm,
|
||||
fault->address, 1, write, 0, &page, NULL);
|
||||
|
||||
if (npages == 1) {
|
||||
put_page(page);
|
||||
} else if (fault->dev_state->inv_ppr_cb) {
|
||||
int status;
|
||||
|
||||
status = fault->dev_state->inv_ppr_cb(fault->dev_state->pdev,
|
||||
fault->pasid,
|
||||
fault->address,
|
||||
fault->flags);
|
||||
switch (status) {
|
||||
case AMD_IOMMU_INV_PRI_RSP_SUCCESS:
|
||||
set_pri_tag_status(fault->state, fault->tag, PPR_SUCCESS);
|
||||
break;
|
||||
case AMD_IOMMU_INV_PRI_RSP_INVALID:
|
||||
set_pri_tag_status(fault->state, fault->tag, PPR_INVALID);
|
||||
break;
|
||||
case AMD_IOMMU_INV_PRI_RSP_FAIL:
|
||||
set_pri_tag_status(fault->state, fault->tag, PPR_FAILURE);
|
||||
break;
|
||||
default:
|
||||
BUG();
|
||||
}
|
||||
} else {
|
||||
set_pri_tag_status(fault->state, fault->tag, PPR_INVALID);
|
||||
}
|
||||
|
||||
finish_pri_tag(fault->dev_state, fault->state, fault->tag);
|
||||
|
||||
put_pasid_state(fault->state);
|
||||
|
||||
kfree(fault);
|
||||
}
|
||||
|
||||
static int ppr_notifier(struct notifier_block *nb, unsigned long e, void *data)
|
||||
{
|
||||
struct amd_iommu_fault *iommu_fault;
|
||||
struct pasid_state *pasid_state;
|
||||
struct device_state *dev_state;
|
||||
unsigned long flags;
|
||||
struct fault *fault;
|
||||
bool finish;
|
||||
u16 tag;
|
||||
int ret;
|
||||
|
||||
iommu_fault = data;
|
||||
tag = iommu_fault->tag & 0x1ff;
|
||||
finish = (iommu_fault->tag >> 9) & 1;
|
||||
|
||||
ret = NOTIFY_DONE;
|
||||
dev_state = get_device_state(iommu_fault->device_id);
|
||||
if (dev_state == NULL)
|
||||
goto out;
|
||||
|
||||
pasid_state = get_pasid_state(dev_state, iommu_fault->pasid);
|
||||
if (pasid_state == NULL) {
|
||||
/* We know the device but not the PASID -> send INVALID */
|
||||
amd_iommu_complete_ppr(dev_state->pdev, iommu_fault->pasid,
|
||||
PPR_INVALID, tag);
|
||||
goto out_drop_state;
|
||||
}
|
||||
|
||||
spin_lock_irqsave(&pasid_state->lock, flags);
|
||||
atomic_inc(&pasid_state->pri[tag].inflight);
|
||||
if (finish)
|
||||
pasid_state->pri[tag].finish = true;
|
||||
spin_unlock_irqrestore(&pasid_state->lock, flags);
|
||||
|
||||
fault = kzalloc(sizeof(*fault), GFP_ATOMIC);
|
||||
if (fault == NULL) {
|
||||
/* We are OOM - send success and let the device re-fault */
|
||||
finish_pri_tag(dev_state, pasid_state, tag);
|
||||
goto out_drop_state;
|
||||
}
|
||||
|
||||
fault->dev_state = dev_state;
|
||||
fault->address = iommu_fault->address;
|
||||
fault->state = pasid_state;
|
||||
fault->tag = tag;
|
||||
fault->finish = finish;
|
||||
fault->flags = iommu_fault->flags;
|
||||
INIT_WORK(&fault->work, do_fault);
|
||||
|
||||
queue_work(iommu_wq, &fault->work);
|
||||
|
||||
ret = NOTIFY_OK;
|
||||
|
||||
out_drop_state:
|
||||
put_device_state(dev_state);
|
||||
|
||||
out:
|
||||
return ret;
|
||||
}
|
||||
|
||||
static struct notifier_block ppr_nb = {
|
||||
.notifier_call = ppr_notifier,
|
||||
};
|
||||
|
||||
static int task_exit(struct notifier_block *nb, unsigned long e, void *data)
|
||||
{
|
||||
struct pasid_state *pasid_state;
|
||||
struct task_struct *task;
|
||||
|
||||
task = data;
|
||||
|
||||
/*
|
||||
* Using this notifier is a hack - but there is no other choice
|
||||
* at the moment. What I really want is a sleeping notifier that
|
||||
* is called when an MM goes down. But such a notifier doesn't
|
||||
* exist yet. The notifier needs to sleep because it has to make
|
||||
* sure that the device does not use the PASID and the address
|
||||
* space anymore before it is destroyed. This includes waiting
|
||||
* for pending PRI requests to pass the workqueue. The
|
||||
* MMU-Notifiers would be a good fit, but they use RCU and so
|
||||
* they are not allowed to sleep. Lets see how we can solve this
|
||||
* in a more intelligent way in the future.
|
||||
*/
|
||||
again:
|
||||
spin_lock(&ps_lock);
|
||||
list_for_each_entry(pasid_state, &pasid_state_list, list) {
|
||||
struct device_state *dev_state;
|
||||
int pasid;
|
||||
|
||||
if (pasid_state->task != task)
|
||||
continue;
|
||||
|
||||
/* Drop Lock and unbind */
|
||||
spin_unlock(&ps_lock);
|
||||
|
||||
dev_state = pasid_state->device_state;
|
||||
pasid = pasid_state->pasid;
|
||||
|
||||
if (pasid_state->device_state->inv_ctx_cb)
|
||||
dev_state->inv_ctx_cb(dev_state->pdev, pasid);
|
||||
|
||||
unbind_pasid(dev_state, pasid);
|
||||
|
||||
/* Task may be in the list multiple times */
|
||||
goto again;
|
||||
}
|
||||
spin_unlock(&ps_lock);
|
||||
|
||||
return NOTIFY_OK;
|
||||
}
|
||||
|
||||
int amd_iommu_bind_pasid(struct pci_dev *pdev, int pasid,
|
||||
struct task_struct *task)
|
||||
{
|
||||
struct pasid_state *pasid_state;
|
||||
struct device_state *dev_state;
|
||||
u16 devid;
|
||||
int ret;
|
||||
|
||||
might_sleep();
|
||||
|
||||
if (!amd_iommu_v2_supported())
|
||||
return -ENODEV;
|
||||
|
||||
devid = device_id(pdev);
|
||||
dev_state = get_device_state(devid);
|
||||
|
||||
if (dev_state == NULL)
|
||||
return -EINVAL;
|
||||
|
||||
ret = -EINVAL;
|
||||
if (pasid < 0 || pasid >= dev_state->max_pasids)
|
||||
goto out;
|
||||
|
||||
ret = -ENOMEM;
|
||||
pasid_state = kzalloc(sizeof(*pasid_state), GFP_KERNEL);
|
||||
if (pasid_state == NULL)
|
||||
goto out;
|
||||
|
||||
atomic_set(&pasid_state->count, 1);
|
||||
init_waitqueue_head(&pasid_state->wq);
|
||||
pasid_state->task = task;
|
||||
pasid_state->mm = get_task_mm(task);
|
||||
pasid_state->device_state = dev_state;
|
||||
pasid_state->pasid = pasid;
|
||||
pasid_state->mn.ops = &iommu_mn;
|
||||
|
||||
if (pasid_state->mm == NULL)
|
||||
goto out_free;
|
||||
|
||||
mmu_notifier_register(&pasid_state->mn, pasid_state->mm);
|
||||
|
||||
ret = set_pasid_state(dev_state, pasid_state, pasid);
|
||||
if (ret)
|
||||
goto out_unregister;
|
||||
|
||||
ret = amd_iommu_domain_set_gcr3(dev_state->domain, pasid,
|
||||
__pa(pasid_state->mm->pgd));
|
||||
if (ret)
|
||||
goto out_clear_state;
|
||||
|
||||
link_pasid_state(pasid_state);
|
||||
|
||||
return 0;
|
||||
|
||||
out_clear_state:
|
||||
clear_pasid_state(dev_state, pasid);
|
||||
|
||||
out_unregister:
|
||||
mmu_notifier_unregister(&pasid_state->mn, pasid_state->mm);
|
||||
|
||||
out_free:
|
||||
free_pasid_state(pasid_state);
|
||||
|
||||
out:
|
||||
put_device_state(dev_state);
|
||||
|
||||
return ret;
|
||||
}
|
||||
EXPORT_SYMBOL(amd_iommu_bind_pasid);
|
||||
|
||||
void amd_iommu_unbind_pasid(struct pci_dev *pdev, int pasid)
|
||||
{
|
||||
struct device_state *dev_state;
|
||||
u16 devid;
|
||||
|
||||
might_sleep();
|
||||
|
||||
if (!amd_iommu_v2_supported())
|
||||
return;
|
||||
|
||||
devid = device_id(pdev);
|
||||
dev_state = get_device_state(devid);
|
||||
if (dev_state == NULL)
|
||||
return;
|
||||
|
||||
if (pasid < 0 || pasid >= dev_state->max_pasids)
|
||||
goto out;
|
||||
|
||||
unbind_pasid(dev_state, pasid);
|
||||
|
||||
out:
|
||||
put_device_state(dev_state);
|
||||
}
|
||||
EXPORT_SYMBOL(amd_iommu_unbind_pasid);
|
||||
|
||||
int amd_iommu_init_device(struct pci_dev *pdev, int pasids)
|
||||
{
|
||||
struct device_state *dev_state;
|
||||
unsigned long flags;
|
||||
int ret, tmp;
|
||||
u16 devid;
|
||||
|
||||
might_sleep();
|
||||
|
||||
if (!amd_iommu_v2_supported())
|
||||
return -ENODEV;
|
||||
|
||||
if (pasids <= 0 || pasids > (PASID_MASK + 1))
|
||||
return -EINVAL;
|
||||
|
||||
devid = device_id(pdev);
|
||||
|
||||
dev_state = kzalloc(sizeof(*dev_state), GFP_KERNEL);
|
||||
if (dev_state == NULL)
|
||||
return -ENOMEM;
|
||||
|
||||
spin_lock_init(&dev_state->lock);
|
||||
init_waitqueue_head(&dev_state->wq);
|
||||
dev_state->pdev = pdev;
|
||||
|
||||
tmp = pasids;
|
||||
for (dev_state->pasid_levels = 0; (tmp - 1) & ~0x1ff; tmp >>= 9)
|
||||
dev_state->pasid_levels += 1;
|
||||
|
||||
atomic_set(&dev_state->count, 1);
|
||||
dev_state->max_pasids = pasids;
|
||||
|
||||
ret = -ENOMEM;
|
||||
dev_state->states = (void *)get_zeroed_page(GFP_KERNEL);
|
||||
if (dev_state->states == NULL)
|
||||
goto out_free_dev_state;
|
||||
|
||||
dev_state->domain = iommu_domain_alloc(&pci_bus_type);
|
||||
if (dev_state->domain == NULL)
|
||||
goto out_free_states;
|
||||
|
||||
amd_iommu_domain_direct_map(dev_state->domain);
|
||||
|
||||
ret = amd_iommu_domain_enable_v2(dev_state->domain, pasids);
|
||||
if (ret)
|
||||
goto out_free_domain;
|
||||
|
||||
ret = iommu_attach_device(dev_state->domain, &pdev->dev);
|
||||
if (ret != 0)
|
||||
goto out_free_domain;
|
||||
|
||||
spin_lock_irqsave(&state_lock, flags);
|
||||
|
||||
if (state_table[devid] != NULL) {
|
||||
spin_unlock_irqrestore(&state_lock, flags);
|
||||
ret = -EBUSY;
|
||||
goto out_free_domain;
|
||||
}
|
||||
|
||||
state_table[devid] = dev_state;
|
||||
|
||||
spin_unlock_irqrestore(&state_lock, flags);
|
||||
|
||||
return 0;
|
||||
|
||||
out_free_domain:
|
||||
iommu_domain_free(dev_state->domain);
|
||||
|
||||
out_free_states:
|
||||
free_page((unsigned long)dev_state->states);
|
||||
|
||||
out_free_dev_state:
|
||||
kfree(dev_state);
|
||||
|
||||
return ret;
|
||||
}
|
||||
EXPORT_SYMBOL(amd_iommu_init_device);
|
||||
|
||||
void amd_iommu_free_device(struct pci_dev *pdev)
|
||||
{
|
||||
struct device_state *dev_state;
|
||||
unsigned long flags;
|
||||
u16 devid;
|
||||
|
||||
if (!amd_iommu_v2_supported())
|
||||
return;
|
||||
|
||||
devid = device_id(pdev);
|
||||
|
||||
spin_lock_irqsave(&state_lock, flags);
|
||||
|
||||
dev_state = state_table[devid];
|
||||
if (dev_state == NULL) {
|
||||
spin_unlock_irqrestore(&state_lock, flags);
|
||||
return;
|
||||
}
|
||||
|
||||
state_table[devid] = NULL;
|
||||
|
||||
spin_unlock_irqrestore(&state_lock, flags);
|
||||
|
||||
/* Get rid of any remaining pasid states */
|
||||
free_pasid_states(dev_state);
|
||||
|
||||
put_device_state_wait(dev_state);
|
||||
}
|
||||
EXPORT_SYMBOL(amd_iommu_free_device);
|
||||
|
||||
int amd_iommu_set_invalid_ppr_cb(struct pci_dev *pdev,
|
||||
amd_iommu_invalid_ppr_cb cb)
|
||||
{
|
||||
struct device_state *dev_state;
|
||||
unsigned long flags;
|
||||
u16 devid;
|
||||
int ret;
|
||||
|
||||
if (!amd_iommu_v2_supported())
|
||||
return -ENODEV;
|
||||
|
||||
devid = device_id(pdev);
|
||||
|
||||
spin_lock_irqsave(&state_lock, flags);
|
||||
|
||||
ret = -EINVAL;
|
||||
dev_state = state_table[devid];
|
||||
if (dev_state == NULL)
|
||||
goto out_unlock;
|
||||
|
||||
dev_state->inv_ppr_cb = cb;
|
||||
|
||||
ret = 0;
|
||||
|
||||
out_unlock:
|
||||
spin_unlock_irqrestore(&state_lock, flags);
|
||||
|
||||
return ret;
|
||||
}
|
||||
EXPORT_SYMBOL(amd_iommu_set_invalid_ppr_cb);
|
||||
|
||||
int amd_iommu_set_invalidate_ctx_cb(struct pci_dev *pdev,
|
||||
amd_iommu_invalidate_ctx cb)
|
||||
{
|
||||
struct device_state *dev_state;
|
||||
unsigned long flags;
|
||||
u16 devid;
|
||||
int ret;
|
||||
|
||||
if (!amd_iommu_v2_supported())
|
||||
return -ENODEV;
|
||||
|
||||
devid = device_id(pdev);
|
||||
|
||||
spin_lock_irqsave(&state_lock, flags);
|
||||
|
||||
ret = -EINVAL;
|
||||
dev_state = state_table[devid];
|
||||
if (dev_state == NULL)
|
||||
goto out_unlock;
|
||||
|
||||
dev_state->inv_ctx_cb = cb;
|
||||
|
||||
ret = 0;
|
||||
|
||||
out_unlock:
|
||||
spin_unlock_irqrestore(&state_lock, flags);
|
||||
|
||||
return ret;
|
||||
}
|
||||
EXPORT_SYMBOL(amd_iommu_set_invalidate_ctx_cb);
|
||||
|
||||
static int __init amd_iommu_v2_init(void)
|
||||
{
|
||||
size_t state_table_size;
|
||||
int ret;
|
||||
|
||||
pr_info("AMD IOMMUv2 driver by Joerg Roedel <joerg.roedel@amd.com>");
|
||||
|
||||
spin_lock_init(&state_lock);
|
||||
|
||||
state_table_size = MAX_DEVICES * sizeof(struct device_state *);
|
||||
state_table = (void *)__get_free_pages(GFP_KERNEL | __GFP_ZERO,
|
||||
get_order(state_table_size));
|
||||
if (state_table == NULL)
|
||||
return -ENOMEM;
|
||||
|
||||
ret = -ENOMEM;
|
||||
iommu_wq = create_workqueue("amd_iommu_v2");
|
||||
if (iommu_wq == NULL)
|
||||
goto out_free;
|
||||
|
||||
ret = -ENOMEM;
|
||||
empty_page_table = (u64 *)get_zeroed_page(GFP_KERNEL);
|
||||
if (empty_page_table == NULL)
|
||||
goto out_destroy_wq;
|
||||
|
||||
amd_iommu_register_ppr_notifier(&ppr_nb);
|
||||
profile_event_register(PROFILE_TASK_EXIT, &profile_nb);
|
||||
|
||||
return 0;
|
||||
|
||||
out_destroy_wq:
|
||||
destroy_workqueue(iommu_wq);
|
||||
|
||||
out_free:
|
||||
free_pages((unsigned long)state_table, get_order(state_table_size));
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
static void __exit amd_iommu_v2_exit(void)
|
||||
{
|
||||
struct device_state *dev_state;
|
||||
size_t state_table_size;
|
||||
int i;
|
||||
|
||||
profile_event_unregister(PROFILE_TASK_EXIT, &profile_nb);
|
||||
amd_iommu_unregister_ppr_notifier(&ppr_nb);
|
||||
|
||||
flush_workqueue(iommu_wq);
|
||||
|
||||
/*
|
||||
* The loop below might call flush_workqueue(), so call
|
||||
* destroy_workqueue() after it
|
||||
*/
|
||||
for (i = 0; i < MAX_DEVICES; ++i) {
|
||||
dev_state = get_device_state(i);
|
||||
|
||||
if (dev_state == NULL)
|
||||
continue;
|
||||
|
||||
WARN_ON_ONCE(1);
|
||||
|
||||
put_device_state(dev_state);
|
||||
amd_iommu_free_device(dev_state->pdev);
|
||||
}
|
||||
|
||||
destroy_workqueue(iommu_wq);
|
||||
|
||||
state_table_size = MAX_DEVICES * sizeof(struct device_state *);
|
||||
free_pages((unsigned long)state_table, get_order(state_table_size));
|
||||
|
||||
free_page((unsigned long)empty_page_table);
|
||||
}
|
||||
|
||||
module_init(amd_iommu_v2_init);
|
||||
module_exit(amd_iommu_v2_exit);
|
|
@ -78,6 +78,24 @@
|
|||
#define LEVEL_STRIDE (9)
|
||||
#define LEVEL_MASK (((u64)1 << LEVEL_STRIDE) - 1)
|
||||
|
||||
/*
|
||||
* This bitmap is used to advertise the page sizes our hardware support
|
||||
* to the IOMMU core, which will then use this information to split
|
||||
* physically contiguous memory regions it is mapping into page sizes
|
||||
* that we support.
|
||||
*
|
||||
* Traditionally the IOMMU core just handed us the mappings directly,
|
||||
* after making sure the size is an order of a 4KiB page and that the
|
||||
* mapping has natural alignment.
|
||||
*
|
||||
* To retain this behavior, we currently advertise that we support
|
||||
* all page sizes that are an order of 4KiB.
|
||||
*
|
||||
* If at some point we'd like to utilize the IOMMU core's new behavior,
|
||||
* we could change this to advertise the real page sizes we support.
|
||||
*/
|
||||
#define INTEL_IOMMU_PGSIZES (~0xFFFUL)
|
||||
|
||||
static inline int agaw_to_level(int agaw)
|
||||
{
|
||||
return agaw + 2;
|
||||
|
@ -3984,12 +4002,11 @@ static void intel_iommu_detach_device(struct iommu_domain *domain,
|
|||
|
||||
static int intel_iommu_map(struct iommu_domain *domain,
|
||||
unsigned long iova, phys_addr_t hpa,
|
||||
int gfp_order, int iommu_prot)
|
||||
size_t size, int iommu_prot)
|
||||
{
|
||||
struct dmar_domain *dmar_domain = domain->priv;
|
||||
u64 max_addr;
|
||||
int prot = 0;
|
||||
size_t size;
|
||||
int ret;
|
||||
|
||||
if (iommu_prot & IOMMU_READ)
|
||||
|
@ -3999,7 +4016,6 @@ static int intel_iommu_map(struct iommu_domain *domain,
|
|||
if ((iommu_prot & IOMMU_CACHE) && dmar_domain->iommu_snooping)
|
||||
prot |= DMA_PTE_SNP;
|
||||
|
||||
size = PAGE_SIZE << gfp_order;
|
||||
max_addr = iova + size;
|
||||
if (dmar_domain->max_addr < max_addr) {
|
||||
u64 end;
|
||||
|
@ -4022,11 +4038,10 @@ static int intel_iommu_map(struct iommu_domain *domain,
|
|||
return ret;
|
||||
}
|
||||
|
||||
static int intel_iommu_unmap(struct iommu_domain *domain,
|
||||
unsigned long iova, int gfp_order)
|
||||
static size_t intel_iommu_unmap(struct iommu_domain *domain,
|
||||
unsigned long iova, size_t size)
|
||||
{
|
||||
struct dmar_domain *dmar_domain = domain->priv;
|
||||
size_t size = PAGE_SIZE << gfp_order;
|
||||
int order;
|
||||
|
||||
order = dma_pte_clear_range(dmar_domain, iova >> VTD_PAGE_SHIFT,
|
||||
|
@ -4035,7 +4050,7 @@ static int intel_iommu_unmap(struct iommu_domain *domain,
|
|||
if (dmar_domain->max_addr == iova + size)
|
||||
dmar_domain->max_addr = iova;
|
||||
|
||||
return order;
|
||||
return PAGE_SIZE << order;
|
||||
}
|
||||
|
||||
static phys_addr_t intel_iommu_iova_to_phys(struct iommu_domain *domain,
|
||||
|
@ -4074,6 +4089,7 @@ static struct iommu_ops intel_iommu_ops = {
|
|||
.unmap = intel_iommu_unmap,
|
||||
.iova_to_phys = intel_iommu_iova_to_phys,
|
||||
.domain_has_cap = intel_iommu_domain_has_cap,
|
||||
.pgsize_bitmap = INTEL_IOMMU_PGSIZES,
|
||||
};
|
||||
|
||||
static void __devinit quirk_iommu_rwbf(struct pci_dev *dev)
|
||||
|
|
|
@ -16,6 +16,8 @@
|
|||
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
|
||||
*/
|
||||
|
||||
#define pr_fmt(fmt) "%s: " fmt, __func__
|
||||
|
||||
#include <linux/device.h>
|
||||
#include <linux/kernel.h>
|
||||
#include <linux/bug.h>
|
||||
|
@ -157,32 +159,125 @@ int iommu_domain_has_cap(struct iommu_domain *domain,
|
|||
EXPORT_SYMBOL_GPL(iommu_domain_has_cap);
|
||||
|
||||
int iommu_map(struct iommu_domain *domain, unsigned long iova,
|
||||
phys_addr_t paddr, int gfp_order, int prot)
|
||||
phys_addr_t paddr, size_t size, int prot)
|
||||
{
|
||||
size_t size;
|
||||
unsigned long orig_iova = iova;
|
||||
unsigned int min_pagesz;
|
||||
size_t orig_size = size;
|
||||
int ret = 0;
|
||||
|
||||
if (unlikely(domain->ops->map == NULL))
|
||||
return -ENODEV;
|
||||
|
||||
size = PAGE_SIZE << gfp_order;
|
||||
/* find out the minimum page size supported */
|
||||
min_pagesz = 1 << __ffs(domain->ops->pgsize_bitmap);
|
||||
|
||||
BUG_ON(!IS_ALIGNED(iova | paddr, size));
|
||||
/*
|
||||
* both the virtual address and the physical one, as well as
|
||||
* the size of the mapping, must be aligned (at least) to the
|
||||
* size of the smallest page supported by the hardware
|
||||
*/
|
||||
if (!IS_ALIGNED(iova | paddr | size, min_pagesz)) {
|
||||
pr_err("unaligned: iova 0x%lx pa 0x%lx size 0x%lx min_pagesz "
|
||||
"0x%x\n", iova, (unsigned long)paddr,
|
||||
(unsigned long)size, min_pagesz);
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
return domain->ops->map(domain, iova, paddr, gfp_order, prot);
|
||||
pr_debug("map: iova 0x%lx pa 0x%lx size 0x%lx\n", iova,
|
||||
(unsigned long)paddr, (unsigned long)size);
|
||||
|
||||
while (size) {
|
||||
unsigned long pgsize, addr_merge = iova | paddr;
|
||||
unsigned int pgsize_idx;
|
||||
|
||||
/* Max page size that still fits into 'size' */
|
||||
pgsize_idx = __fls(size);
|
||||
|
||||
/* need to consider alignment requirements ? */
|
||||
if (likely(addr_merge)) {
|
||||
/* Max page size allowed by both iova and paddr */
|
||||
unsigned int align_pgsize_idx = __ffs(addr_merge);
|
||||
|
||||
pgsize_idx = min(pgsize_idx, align_pgsize_idx);
|
||||
}
|
||||
|
||||
/* build a mask of acceptable page sizes */
|
||||
pgsize = (1UL << (pgsize_idx + 1)) - 1;
|
||||
|
||||
/* throw away page sizes not supported by the hardware */
|
||||
pgsize &= domain->ops->pgsize_bitmap;
|
||||
|
||||
/* make sure we're still sane */
|
||||
BUG_ON(!pgsize);
|
||||
|
||||
/* pick the biggest page */
|
||||
pgsize_idx = __fls(pgsize);
|
||||
pgsize = 1UL << pgsize_idx;
|
||||
|
||||
pr_debug("mapping: iova 0x%lx pa 0x%lx pgsize %lu\n", iova,
|
||||
(unsigned long)paddr, pgsize);
|
||||
|
||||
ret = domain->ops->map(domain, iova, paddr, pgsize, prot);
|
||||
if (ret)
|
||||
break;
|
||||
|
||||
iova += pgsize;
|
||||
paddr += pgsize;
|
||||
size -= pgsize;
|
||||
}
|
||||
|
||||
/* unroll mapping in case something went wrong */
|
||||
if (ret)
|
||||
iommu_unmap(domain, orig_iova, orig_size - size);
|
||||
|
||||
return ret;
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(iommu_map);
|
||||
|
||||
int iommu_unmap(struct iommu_domain *domain, unsigned long iova, int gfp_order)
|
||||
size_t iommu_unmap(struct iommu_domain *domain, unsigned long iova, size_t size)
|
||||
{
|
||||
size_t size;
|
||||
size_t unmapped_page, unmapped = 0;
|
||||
unsigned int min_pagesz;
|
||||
|
||||
if (unlikely(domain->ops->unmap == NULL))
|
||||
return -ENODEV;
|
||||
|
||||
size = PAGE_SIZE << gfp_order;
|
||||
/* find out the minimum page size supported */
|
||||
min_pagesz = 1 << __ffs(domain->ops->pgsize_bitmap);
|
||||
|
||||
BUG_ON(!IS_ALIGNED(iova, size));
|
||||
/*
|
||||
* The virtual address, as well as the size of the mapping, must be
|
||||
* aligned (at least) to the size of the smallest page supported
|
||||
* by the hardware
|
||||
*/
|
||||
if (!IS_ALIGNED(iova | size, min_pagesz)) {
|
||||
pr_err("unaligned: iova 0x%lx size 0x%lx min_pagesz 0x%x\n",
|
||||
iova, (unsigned long)size, min_pagesz);
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
return domain->ops->unmap(domain, iova, gfp_order);
|
||||
pr_debug("unmap this: iova 0x%lx size 0x%lx\n", iova,
|
||||
(unsigned long)size);
|
||||
|
||||
/*
|
||||
* Keep iterating until we either unmap 'size' bytes (or more)
|
||||
* or we hit an area that isn't mapped.
|
||||
*/
|
||||
while (unmapped < size) {
|
||||
size_t left = size - unmapped;
|
||||
|
||||
unmapped_page = domain->ops->unmap(domain, iova, left);
|
||||
if (!unmapped_page)
|
||||
break;
|
||||
|
||||
pr_debug("unmapped: iova 0x%lx size %lx\n", iova,
|
||||
(unsigned long)unmapped_page);
|
||||
|
||||
iova += unmapped_page;
|
||||
unmapped += unmapped_page;
|
||||
}
|
||||
|
||||
return unmapped;
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(iommu_unmap);
|
||||
|
|
|
@ -42,6 +42,9 @@ __asm__ __volatile__ ( \
|
|||
#define RCP15_PRRR(reg) MRC(reg, p15, 0, c10, c2, 0)
|
||||
#define RCP15_NMRR(reg) MRC(reg, p15, 0, c10, c2, 1)
|
||||
|
||||
/* bitmap of the page sizes currently supported */
|
||||
#define MSM_IOMMU_PGSIZES (SZ_4K | SZ_64K | SZ_1M | SZ_16M)
|
||||
|
||||
static int msm_iommu_tex_class[4];
|
||||
|
||||
DEFINE_SPINLOCK(msm_iommu_lock);
|
||||
|
@ -352,7 +355,7 @@ static void msm_iommu_detach_dev(struct iommu_domain *domain,
|
|||
}
|
||||
|
||||
static int msm_iommu_map(struct iommu_domain *domain, unsigned long va,
|
||||
phys_addr_t pa, int order, int prot)
|
||||
phys_addr_t pa, size_t len, int prot)
|
||||
{
|
||||
struct msm_priv *priv;
|
||||
unsigned long flags;
|
||||
|
@ -363,7 +366,6 @@ static int msm_iommu_map(struct iommu_domain *domain, unsigned long va,
|
|||
unsigned long *sl_pte;
|
||||
unsigned long sl_offset;
|
||||
unsigned int pgprot;
|
||||
size_t len = 0x1000UL << order;
|
||||
int ret = 0, tex, sh;
|
||||
|
||||
spin_lock_irqsave(&msm_iommu_lock, flags);
|
||||
|
@ -463,8 +465,8 @@ static int msm_iommu_map(struct iommu_domain *domain, unsigned long va,
|
|||
return ret;
|
||||
}
|
||||
|
||||
static int msm_iommu_unmap(struct iommu_domain *domain, unsigned long va,
|
||||
int order)
|
||||
static size_t msm_iommu_unmap(struct iommu_domain *domain, unsigned long va,
|
||||
size_t len)
|
||||
{
|
||||
struct msm_priv *priv;
|
||||
unsigned long flags;
|
||||
|
@ -474,7 +476,6 @@ static int msm_iommu_unmap(struct iommu_domain *domain, unsigned long va,
|
|||
unsigned long *sl_table;
|
||||
unsigned long *sl_pte;
|
||||
unsigned long sl_offset;
|
||||
size_t len = 0x1000UL << order;
|
||||
int i, ret = 0;
|
||||
|
||||
spin_lock_irqsave(&msm_iommu_lock, flags);
|
||||
|
@ -544,15 +545,12 @@ static int msm_iommu_unmap(struct iommu_domain *domain, unsigned long va,
|
|||
|
||||
ret = __flush_iotlb(domain);
|
||||
|
||||
/*
|
||||
* the IOMMU API requires us to return the order of the unmapped
|
||||
* page (on success).
|
||||
*/
|
||||
if (!ret)
|
||||
ret = order;
|
||||
fail:
|
||||
spin_unlock_irqrestore(&msm_iommu_lock, flags);
|
||||
return ret;
|
||||
|
||||
/* the IOMMU API requires us to return how many bytes were unmapped */
|
||||
len = ret ? 0 : len;
|
||||
return len;
|
||||
}
|
||||
|
||||
static phys_addr_t msm_iommu_iova_to_phys(struct iommu_domain *domain,
|
||||
|
@ -684,7 +682,8 @@ static struct iommu_ops msm_iommu_ops = {
|
|||
.map = msm_iommu_map,
|
||||
.unmap = msm_iommu_unmap,
|
||||
.iova_to_phys = msm_iommu_iova_to_phys,
|
||||
.domain_has_cap = msm_iommu_domain_has_cap
|
||||
.domain_has_cap = msm_iommu_domain_has_cap,
|
||||
.pgsize_bitmap = MSM_IOMMU_PGSIZES,
|
||||
};
|
||||
|
||||
static int __init get_tex_class(int icp, int ocp, int mt, int nos)
|
||||
|
|
|
@ -33,6 +33,9 @@
|
|||
(__i < (n)) && (cr = __iotlb_read_cr((obj), __i), true); \
|
||||
__i++)
|
||||
|
||||
/* bitmap of the page sizes currently supported */
|
||||
#define OMAP_IOMMU_PGSIZES (SZ_4K | SZ_64K | SZ_1M | SZ_16M)
|
||||
|
||||
/**
|
||||
* struct omap_iommu_domain - omap iommu domain
|
||||
* @pgtable: the page table
|
||||
|
@ -86,20 +89,24 @@ EXPORT_SYMBOL_GPL(omap_uninstall_iommu_arch);
|
|||
|
||||
/**
|
||||
* omap_iommu_save_ctx - Save registers for pm off-mode support
|
||||
* @obj: target iommu
|
||||
* @dev: client device
|
||||
**/
|
||||
void omap_iommu_save_ctx(struct omap_iommu *obj)
|
||||
void omap_iommu_save_ctx(struct device *dev)
|
||||
{
|
||||
struct omap_iommu *obj = dev_to_omap_iommu(dev);
|
||||
|
||||
arch_iommu->save_ctx(obj);
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(omap_iommu_save_ctx);
|
||||
|
||||
/**
|
||||
* omap_iommu_restore_ctx - Restore registers for pm off-mode support
|
||||
* @obj: target iommu
|
||||
* @dev: client device
|
||||
**/
|
||||
void omap_iommu_restore_ctx(struct omap_iommu *obj)
|
||||
void omap_iommu_restore_ctx(struct device *dev)
|
||||
{
|
||||
struct omap_iommu *obj = dev_to_omap_iommu(dev);
|
||||
|
||||
arch_iommu->restore_ctx(obj);
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(omap_iommu_restore_ctx);
|
||||
|
@ -819,36 +826,24 @@ static int device_match_by_alias(struct device *dev, void *data)
|
|||
return strcmp(obj->name, name) == 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* omap_find_iommu_device() - find an omap iommu device by name
|
||||
* @name: name of the iommu device
|
||||
*
|
||||
* The generic iommu API requires the caller to provide the device
|
||||
* he wishes to attach to a certain iommu domain.
|
||||
*
|
||||
* Drivers generally should not bother with this as it should just
|
||||
* be taken care of by the DMA-API using dev_archdata.
|
||||
*
|
||||
* This function is provided as an interim solution until the latter
|
||||
* materializes, and omap3isp is fully migrated to the DMA-API.
|
||||
*/
|
||||
struct device *omap_find_iommu_device(const char *name)
|
||||
{
|
||||
return driver_find_device(&omap_iommu_driver.driver, NULL,
|
||||
(void *)name,
|
||||
device_match_by_alias);
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(omap_find_iommu_device);
|
||||
|
||||
/**
|
||||
* omap_iommu_attach() - attach iommu device to an iommu domain
|
||||
* @dev: target omap iommu device
|
||||
* @name: name of target omap iommu device
|
||||
* @iopgd: page table
|
||||
**/
|
||||
static struct omap_iommu *omap_iommu_attach(struct device *dev, u32 *iopgd)
|
||||
static struct omap_iommu *omap_iommu_attach(const char *name, u32 *iopgd)
|
||||
{
|
||||
int err = -ENOMEM;
|
||||
struct omap_iommu *obj = to_iommu(dev);
|
||||
struct device *dev;
|
||||
struct omap_iommu *obj;
|
||||
|
||||
dev = driver_find_device(&omap_iommu_driver.driver, NULL,
|
||||
(void *)name,
|
||||
device_match_by_alias);
|
||||
if (!dev)
|
||||
return NULL;
|
||||
|
||||
obj = to_iommu(dev);
|
||||
|
||||
spin_lock(&obj->iommu_lock);
|
||||
|
||||
|
@ -1019,12 +1014,11 @@ static void iopte_cachep_ctor(void *iopte)
|
|||
}
|
||||
|
||||
static int omap_iommu_map(struct iommu_domain *domain, unsigned long da,
|
||||
phys_addr_t pa, int order, int prot)
|
||||
phys_addr_t pa, size_t bytes, int prot)
|
||||
{
|
||||
struct omap_iommu_domain *omap_domain = domain->priv;
|
||||
struct omap_iommu *oiommu = omap_domain->iommu_dev;
|
||||
struct device *dev = oiommu->dev;
|
||||
size_t bytes = PAGE_SIZE << order;
|
||||
struct iotlb_entry e;
|
||||
int omap_pgsz;
|
||||
u32 ret, flags;
|
||||
|
@ -1049,19 +1043,16 @@ static int omap_iommu_map(struct iommu_domain *domain, unsigned long da,
|
|||
return ret;
|
||||
}
|
||||
|
||||
static int omap_iommu_unmap(struct iommu_domain *domain, unsigned long da,
|
||||
int order)
|
||||
static size_t omap_iommu_unmap(struct iommu_domain *domain, unsigned long da,
|
||||
size_t size)
|
||||
{
|
||||
struct omap_iommu_domain *omap_domain = domain->priv;
|
||||
struct omap_iommu *oiommu = omap_domain->iommu_dev;
|
||||
struct device *dev = oiommu->dev;
|
||||
size_t unmap_size;
|
||||
|
||||
dev_dbg(dev, "unmapping da 0x%lx order %d\n", da, order);
|
||||
dev_dbg(dev, "unmapping da 0x%lx size %u\n", da, size);
|
||||
|
||||
unmap_size = iopgtable_clear_entry(oiommu, da);
|
||||
|
||||
return unmap_size ? get_order(unmap_size) : -EINVAL;
|
||||
return iopgtable_clear_entry(oiommu, da);
|
||||
}
|
||||
|
||||
static int
|
||||
|
@ -1069,6 +1060,7 @@ omap_iommu_attach_dev(struct iommu_domain *domain, struct device *dev)
|
|||
{
|
||||
struct omap_iommu_domain *omap_domain = domain->priv;
|
||||
struct omap_iommu *oiommu;
|
||||
struct omap_iommu_arch_data *arch_data = dev->archdata.iommu;
|
||||
int ret = 0;
|
||||
|
||||
spin_lock(&omap_domain->lock);
|
||||
|
@ -1081,14 +1073,14 @@ omap_iommu_attach_dev(struct iommu_domain *domain, struct device *dev)
|
|||
}
|
||||
|
||||
/* get a handle to and enable the omap iommu */
|
||||
oiommu = omap_iommu_attach(dev, omap_domain->pgtable);
|
||||
oiommu = omap_iommu_attach(arch_data->name, omap_domain->pgtable);
|
||||
if (IS_ERR(oiommu)) {
|
||||
ret = PTR_ERR(oiommu);
|
||||
dev_err(dev, "can't get omap iommu: %d\n", ret);
|
||||
goto out;
|
||||
}
|
||||
|
||||
omap_domain->iommu_dev = oiommu;
|
||||
omap_domain->iommu_dev = arch_data->iommu_dev = oiommu;
|
||||
oiommu->domain = domain;
|
||||
|
||||
out:
|
||||
|
@ -1100,7 +1092,8 @@ static void omap_iommu_detach_dev(struct iommu_domain *domain,
|
|||
struct device *dev)
|
||||
{
|
||||
struct omap_iommu_domain *omap_domain = domain->priv;
|
||||
struct omap_iommu *oiommu = to_iommu(dev);
|
||||
struct omap_iommu_arch_data *arch_data = dev->archdata.iommu;
|
||||
struct omap_iommu *oiommu = dev_to_omap_iommu(dev);
|
||||
|
||||
spin_lock(&omap_domain->lock);
|
||||
|
||||
|
@ -1114,7 +1107,7 @@ static void omap_iommu_detach_dev(struct iommu_domain *domain,
|
|||
|
||||
omap_iommu_detach(oiommu);
|
||||
|
||||
omap_domain->iommu_dev = NULL;
|
||||
omap_domain->iommu_dev = arch_data->iommu_dev = NULL;
|
||||
|
||||
out:
|
||||
spin_unlock(&omap_domain->lock);
|
||||
|
@ -1183,14 +1176,14 @@ static phys_addr_t omap_iommu_iova_to_phys(struct iommu_domain *domain,
|
|||
else if (iopte_is_large(*pte))
|
||||
ret = omap_iommu_translate(*pte, da, IOLARGE_MASK);
|
||||
else
|
||||
dev_err(dev, "bogus pte 0x%x", *pte);
|
||||
dev_err(dev, "bogus pte 0x%x, da 0x%lx", *pte, da);
|
||||
} else {
|
||||
if (iopgd_is_section(*pgd))
|
||||
ret = omap_iommu_translate(*pgd, da, IOSECTION_MASK);
|
||||
else if (iopgd_is_super(*pgd))
|
||||
ret = omap_iommu_translate(*pgd, da, IOSUPER_MASK);
|
||||
else
|
||||
dev_err(dev, "bogus pgd 0x%x", *pgd);
|
||||
dev_err(dev, "bogus pgd 0x%x, da 0x%lx", *pgd, da);
|
||||
}
|
||||
|
||||
return ret;
|
||||
|
@ -1211,6 +1204,7 @@ static struct iommu_ops omap_iommu_ops = {
|
|||
.unmap = omap_iommu_unmap,
|
||||
.iova_to_phys = omap_iommu_iova_to_phys,
|
||||
.domain_has_cap = omap_iommu_domain_has_cap,
|
||||
.pgsize_bitmap = OMAP_IOMMU_PGSIZES,
|
||||
};
|
||||
|
||||
static int __init omap_iommu_init(void)
|
||||
|
|
|
@ -231,12 +231,14 @@ static struct iovm_struct *__find_iovm_area(struct omap_iommu *obj,
|
|||
|
||||
/**
|
||||
* omap_find_iovm_area - find iovma which includes @da
|
||||
* @dev: client device
|
||||
* @da: iommu device virtual address
|
||||
*
|
||||
* Find the existing iovma starting at @da
|
||||
*/
|
||||
struct iovm_struct *omap_find_iovm_area(struct omap_iommu *obj, u32 da)
|
||||
struct iovm_struct *omap_find_iovm_area(struct device *dev, u32 da)
|
||||
{
|
||||
struct omap_iommu *obj = dev_to_omap_iommu(dev);
|
||||
struct iovm_struct *area;
|
||||
|
||||
mutex_lock(&obj->mmap_lock);
|
||||
|
@ -343,14 +345,15 @@ static void free_iovm_area(struct omap_iommu *obj, struct iovm_struct *area)
|
|||
|
||||
/**
|
||||
* omap_da_to_va - convert (d) to (v)
|
||||
* @obj: objective iommu
|
||||
* @dev: client device
|
||||
* @da: iommu device virtual address
|
||||
* @va: mpu virtual address
|
||||
*
|
||||
* Returns mpu virtual addr which corresponds to a given device virtual addr
|
||||
*/
|
||||
void *omap_da_to_va(struct omap_iommu *obj, u32 da)
|
||||
void *omap_da_to_va(struct device *dev, u32 da)
|
||||
{
|
||||
struct omap_iommu *obj = dev_to_omap_iommu(dev);
|
||||
void *va = NULL;
|
||||
struct iovm_struct *area;
|
||||
|
||||
|
@ -410,7 +413,6 @@ static int map_iovm_area(struct iommu_domain *domain, struct iovm_struct *new,
|
|||
unsigned int i, j;
|
||||
struct scatterlist *sg;
|
||||
u32 da = new->da_start;
|
||||
int order;
|
||||
|
||||
if (!domain || !sgt)
|
||||
return -EINVAL;
|
||||
|
@ -429,12 +431,10 @@ static int map_iovm_area(struct iommu_domain *domain, struct iovm_struct *new,
|
|||
if (bytes_to_iopgsz(bytes) < 0)
|
||||
goto err_out;
|
||||
|
||||
order = get_order(bytes);
|
||||
|
||||
pr_debug("%s: [%d] %08x %08x(%x)\n", __func__,
|
||||
i, da, pa, bytes);
|
||||
|
||||
err = iommu_map(domain, da, pa, order, flags);
|
||||
err = iommu_map(domain, da, pa, bytes, flags);
|
||||
if (err)
|
||||
goto err_out;
|
||||
|
||||
|
@ -449,10 +449,9 @@ static int map_iovm_area(struct iommu_domain *domain, struct iovm_struct *new,
|
|||
size_t bytes;
|
||||
|
||||
bytes = sg->length + sg->offset;
|
||||
order = get_order(bytes);
|
||||
|
||||
/* ignore failures.. we're already handling one */
|
||||
iommu_unmap(domain, da, order);
|
||||
iommu_unmap(domain, da, bytes);
|
||||
|
||||
da += bytes;
|
||||
}
|
||||
|
@ -467,7 +466,8 @@ static void unmap_iovm_area(struct iommu_domain *domain, struct omap_iommu *obj,
|
|||
size_t total = area->da_end - area->da_start;
|
||||
const struct sg_table *sgt = area->sgt;
|
||||
struct scatterlist *sg;
|
||||
int i, err;
|
||||
int i;
|
||||
size_t unmapped;
|
||||
|
||||
BUG_ON(!sgtable_ok(sgt));
|
||||
BUG_ON((!total) || !IS_ALIGNED(total, PAGE_SIZE));
|
||||
|
@ -475,13 +475,11 @@ static void unmap_iovm_area(struct iommu_domain *domain, struct omap_iommu *obj,
|
|||
start = area->da_start;
|
||||
for_each_sg(sgt->sgl, sg, sgt->nents, i) {
|
||||
size_t bytes;
|
||||
int order;
|
||||
|
||||
bytes = sg->length + sg->offset;
|
||||
order = get_order(bytes);
|
||||
|
||||
err = iommu_unmap(domain, start, order);
|
||||
if (err < 0)
|
||||
unmapped = iommu_unmap(domain, start, bytes);
|
||||
if (unmapped < bytes)
|
||||
break;
|
||||
|
||||
dev_dbg(obj->dev, "%s: unmap %08x(%x) %08x\n",
|
||||
|
@ -582,16 +580,18 @@ __iommu_vmap(struct iommu_domain *domain, struct omap_iommu *obj,
|
|||
|
||||
/**
|
||||
* omap_iommu_vmap - (d)-(p)-(v) address mapper
|
||||
* @obj: objective iommu
|
||||
* @domain: iommu domain
|
||||
* @dev: client device
|
||||
* @sgt: address of scatter gather table
|
||||
* @flags: iovma and page property
|
||||
*
|
||||
* Creates 1-n-1 mapping with given @sgt and returns @da.
|
||||
* All @sgt element must be io page size aligned.
|
||||
*/
|
||||
u32 omap_iommu_vmap(struct iommu_domain *domain, struct omap_iommu *obj, u32 da,
|
||||
u32 omap_iommu_vmap(struct iommu_domain *domain, struct device *dev, u32 da,
|
||||
const struct sg_table *sgt, u32 flags)
|
||||
{
|
||||
struct omap_iommu *obj = dev_to_omap_iommu(dev);
|
||||
size_t bytes;
|
||||
void *va = NULL;
|
||||
|
||||
|
@ -622,15 +622,17 @@ EXPORT_SYMBOL_GPL(omap_iommu_vmap);
|
|||
|
||||
/**
|
||||
* omap_iommu_vunmap - release virtual mapping obtained by 'omap_iommu_vmap()'
|
||||
* @obj: objective iommu
|
||||
* @domain: iommu domain
|
||||
* @dev: client device
|
||||
* @da: iommu device virtual address
|
||||
*
|
||||
* Free the iommu virtually contiguous memory area starting at
|
||||
* @da, which was returned by 'omap_iommu_vmap()'.
|
||||
*/
|
||||
struct sg_table *
|
||||
omap_iommu_vunmap(struct iommu_domain *domain, struct omap_iommu *obj, u32 da)
|
||||
omap_iommu_vunmap(struct iommu_domain *domain, struct device *dev, u32 da)
|
||||
{
|
||||
struct omap_iommu *obj = dev_to_omap_iommu(dev);
|
||||
struct sg_table *sgt;
|
||||
/*
|
||||
* 'sgt' is allocated before 'omap_iommu_vmalloc()' is called.
|
||||
|
@ -647,7 +649,7 @@ EXPORT_SYMBOL_GPL(omap_iommu_vunmap);
|
|||
|
||||
/**
|
||||
* omap_iommu_vmalloc - (d)-(p)-(v) address allocator and mapper
|
||||
* @obj: objective iommu
|
||||
* @dev: client device
|
||||
* @da: contiguous iommu virtual memory
|
||||
* @bytes: allocation size
|
||||
* @flags: iovma and page property
|
||||
|
@ -656,9 +658,10 @@ EXPORT_SYMBOL_GPL(omap_iommu_vunmap);
|
|||
* @da again, which might be adjusted if 'IOVMF_DA_FIXED' is not set.
|
||||
*/
|
||||
u32
|
||||
omap_iommu_vmalloc(struct iommu_domain *domain, struct omap_iommu *obj, u32 da,
|
||||
omap_iommu_vmalloc(struct iommu_domain *domain, struct device *dev, u32 da,
|
||||
size_t bytes, u32 flags)
|
||||
{
|
||||
struct omap_iommu *obj = dev_to_omap_iommu(dev);
|
||||
void *va;
|
||||
struct sg_table *sgt;
|
||||
|
||||
|
@ -698,15 +701,16 @@ EXPORT_SYMBOL_GPL(omap_iommu_vmalloc);
|
|||
|
||||
/**
|
||||
* omap_iommu_vfree - release memory allocated by 'omap_iommu_vmalloc()'
|
||||
* @obj: objective iommu
|
||||
* @dev: client device
|
||||
* @da: iommu device virtual address
|
||||
*
|
||||
* Frees the iommu virtually continuous memory area starting at
|
||||
* @da, as obtained from 'omap_iommu_vmalloc()'.
|
||||
*/
|
||||
void omap_iommu_vfree(struct iommu_domain *domain, struct omap_iommu *obj,
|
||||
void omap_iommu_vfree(struct iommu_domain *domain, struct device *dev,
|
||||
const u32 da)
|
||||
{
|
||||
struct omap_iommu *obj = dev_to_omap_iommu(dev);
|
||||
struct sg_table *sgt;
|
||||
|
||||
sgt = unmap_vm_area(domain, obj, da, vfree,
|
||||
|
|
|
@ -80,13 +80,6 @@
|
|||
#include "isph3a.h"
|
||||
#include "isphist.h"
|
||||
|
||||
/*
|
||||
* this is provided as an interim solution until omap3isp doesn't need
|
||||
* any omap-specific iommu API
|
||||
*/
|
||||
#define to_iommu(dev) \
|
||||
(struct omap_iommu *)platform_get_drvdata(to_platform_device(dev))
|
||||
|
||||
static unsigned int autoidle;
|
||||
module_param(autoidle, int, 0444);
|
||||
MODULE_PARM_DESC(autoidle, "Enable OMAP3ISP AUTOIDLE support");
|
||||
|
@ -1114,8 +1107,7 @@ isp_restore_context(struct isp_device *isp, struct isp_reg *reg_list)
|
|||
static void isp_save_ctx(struct isp_device *isp)
|
||||
{
|
||||
isp_save_context(isp, isp_reg_list);
|
||||
if (isp->iommu)
|
||||
omap_iommu_save_ctx(isp->iommu);
|
||||
omap_iommu_save_ctx(isp->dev);
|
||||
}
|
||||
|
||||
/*
|
||||
|
@ -1128,8 +1120,7 @@ static void isp_save_ctx(struct isp_device *isp)
|
|||
static void isp_restore_ctx(struct isp_device *isp)
|
||||
{
|
||||
isp_restore_context(isp, isp_reg_list);
|
||||
if (isp->iommu)
|
||||
omap_iommu_restore_ctx(isp->iommu);
|
||||
omap_iommu_restore_ctx(isp->dev);
|
||||
omap3isp_ccdc_restore_context(isp);
|
||||
omap3isp_preview_restore_context(isp);
|
||||
}
|
||||
|
@ -1983,7 +1974,7 @@ static int isp_remove(struct platform_device *pdev)
|
|||
isp_cleanup_modules(isp);
|
||||
|
||||
omap3isp_get(isp);
|
||||
iommu_detach_device(isp->domain, isp->iommu_dev);
|
||||
iommu_detach_device(isp->domain, &pdev->dev);
|
||||
iommu_domain_free(isp->domain);
|
||||
omap3isp_put(isp);
|
||||
|
||||
|
@ -2131,17 +2122,6 @@ static int isp_probe(struct platform_device *pdev)
|
|||
}
|
||||
}
|
||||
|
||||
/* IOMMU */
|
||||
isp->iommu_dev = omap_find_iommu_device("isp");
|
||||
if (!isp->iommu_dev) {
|
||||
dev_err(isp->dev, "omap_find_iommu_device failed\n");
|
||||
ret = -ENODEV;
|
||||
goto error_isp;
|
||||
}
|
||||
|
||||
/* to be removed once iommu migration is complete */
|
||||
isp->iommu = to_iommu(isp->iommu_dev);
|
||||
|
||||
isp->domain = iommu_domain_alloc(pdev->dev.bus);
|
||||
if (!isp->domain) {
|
||||
dev_err(isp->dev, "can't alloc iommu domain\n");
|
||||
|
@ -2149,7 +2129,7 @@ static int isp_probe(struct platform_device *pdev)
|
|||
goto error_isp;
|
||||
}
|
||||
|
||||
ret = iommu_attach_device(isp->domain, isp->iommu_dev);
|
||||
ret = iommu_attach_device(isp->domain, &pdev->dev);
|
||||
if (ret) {
|
||||
dev_err(&pdev->dev, "can't attach iommu device: %d\n", ret);
|
||||
goto free_domain;
|
||||
|
@ -2188,7 +2168,7 @@ static int isp_probe(struct platform_device *pdev)
|
|||
error_irq:
|
||||
free_irq(isp->irq_num, isp);
|
||||
detach_dev:
|
||||
iommu_detach_device(isp->domain, isp->iommu_dev);
|
||||
iommu_detach_device(isp->domain, &pdev->dev);
|
||||
free_domain:
|
||||
iommu_domain_free(isp->domain);
|
||||
error_isp:
|
||||
|
|
|
@ -212,9 +212,7 @@ struct isp_device {
|
|||
unsigned int sbl_resources;
|
||||
unsigned int subclk_resources;
|
||||
|
||||
struct omap_iommu *iommu;
|
||||
struct iommu_domain *domain;
|
||||
struct device *iommu_dev;
|
||||
|
||||
struct isp_platform_callback platform_cb;
|
||||
};
|
||||
|
|
|
@ -366,7 +366,7 @@ static void ccdc_lsc_free_request(struct isp_ccdc_device *ccdc,
|
|||
dma_unmap_sg(isp->dev, req->iovm->sgt->sgl,
|
||||
req->iovm->sgt->nents, DMA_TO_DEVICE);
|
||||
if (req->table)
|
||||
omap_iommu_vfree(isp->domain, isp->iommu, req->table);
|
||||
omap_iommu_vfree(isp->domain, isp->dev, req->table);
|
||||
kfree(req);
|
||||
}
|
||||
|
||||
|
@ -438,7 +438,7 @@ static int ccdc_lsc_config(struct isp_ccdc_device *ccdc,
|
|||
|
||||
req->enable = 1;
|
||||
|
||||
req->table = omap_iommu_vmalloc(isp->domain, isp->iommu, 0,
|
||||
req->table = omap_iommu_vmalloc(isp->domain, isp->dev, 0,
|
||||
req->config.size, IOMMU_FLAG);
|
||||
if (IS_ERR_VALUE(req->table)) {
|
||||
req->table = 0;
|
||||
|
@ -446,7 +446,7 @@ static int ccdc_lsc_config(struct isp_ccdc_device *ccdc,
|
|||
goto done;
|
||||
}
|
||||
|
||||
req->iovm = omap_find_iovm_area(isp->iommu, req->table);
|
||||
req->iovm = omap_find_iovm_area(isp->dev, req->table);
|
||||
if (req->iovm == NULL) {
|
||||
ret = -ENOMEM;
|
||||
goto done;
|
||||
|
@ -462,7 +462,7 @@ static int ccdc_lsc_config(struct isp_ccdc_device *ccdc,
|
|||
dma_sync_sg_for_cpu(isp->dev, req->iovm->sgt->sgl,
|
||||
req->iovm->sgt->nents, DMA_TO_DEVICE);
|
||||
|
||||
table = omap_da_to_va(isp->iommu, req->table);
|
||||
table = omap_da_to_va(isp->dev, req->table);
|
||||
if (copy_from_user(table, config->lsc, req->config.size)) {
|
||||
ret = -EFAULT;
|
||||
goto done;
|
||||
|
@ -734,15 +734,15 @@ static int ccdc_config(struct isp_ccdc_device *ccdc,
|
|||
* already done by omap_iommu_vmalloc().
|
||||
*/
|
||||
size = ccdc->fpc.fpnum * 4;
|
||||
table_new = omap_iommu_vmalloc(isp->domain, isp->iommu,
|
||||
table_new = omap_iommu_vmalloc(isp->domain, isp->dev,
|
||||
0, size, IOMMU_FLAG);
|
||||
if (IS_ERR_VALUE(table_new))
|
||||
return -ENOMEM;
|
||||
|
||||
if (copy_from_user(omap_da_to_va(isp->iommu, table_new),
|
||||
if (copy_from_user(omap_da_to_va(isp->dev, table_new),
|
||||
(__force void __user *)
|
||||
ccdc->fpc.fpcaddr, size)) {
|
||||
omap_iommu_vfree(isp->domain, isp->iommu,
|
||||
omap_iommu_vfree(isp->domain, isp->dev,
|
||||
table_new);
|
||||
return -EFAULT;
|
||||
}
|
||||
|
@ -753,7 +753,7 @@ static int ccdc_config(struct isp_ccdc_device *ccdc,
|
|||
|
||||
ccdc_configure_fpc(ccdc);
|
||||
if (table_old != 0)
|
||||
omap_iommu_vfree(isp->domain, isp->iommu, table_old);
|
||||
omap_iommu_vfree(isp->domain, isp->dev, table_old);
|
||||
}
|
||||
|
||||
return ccdc_lsc_config(ccdc, ccdc_struct);
|
||||
|
@ -2309,7 +2309,7 @@ void omap3isp_ccdc_cleanup(struct isp_device *isp)
|
|||
ccdc_lsc_free_queue(ccdc, &ccdc->lsc.free_queue);
|
||||
|
||||
if (ccdc->fpc.fpcaddr != 0)
|
||||
omap_iommu_vfree(isp->domain, isp->iommu, ccdc->fpc.fpcaddr);
|
||||
omap_iommu_vfree(isp->domain, isp->dev, ccdc->fpc.fpcaddr);
|
||||
|
||||
mutex_destroy(&ccdc->ioctl_lock);
|
||||
}
|
||||
|
|
|
@ -366,7 +366,7 @@ static void isp_stat_bufs_free(struct ispstat *stat)
|
|||
dma_unmap_sg(isp->dev, buf->iovm->sgt->sgl,
|
||||
buf->iovm->sgt->nents,
|
||||
DMA_FROM_DEVICE);
|
||||
omap_iommu_vfree(isp->domain, isp->iommu,
|
||||
omap_iommu_vfree(isp->domain, isp->dev,
|
||||
buf->iommu_addr);
|
||||
} else {
|
||||
if (!buf->virt_addr)
|
||||
|
@ -400,7 +400,7 @@ static int isp_stat_bufs_alloc_iommu(struct ispstat *stat, unsigned int size)
|
|||
struct iovm_struct *iovm;
|
||||
|
||||
WARN_ON(buf->dma_addr);
|
||||
buf->iommu_addr = omap_iommu_vmalloc(isp->domain, isp->iommu, 0,
|
||||
buf->iommu_addr = omap_iommu_vmalloc(isp->domain, isp->dev, 0,
|
||||
size, IOMMU_FLAG);
|
||||
if (IS_ERR((void *)buf->iommu_addr)) {
|
||||
dev_err(stat->isp->dev,
|
||||
|
@ -410,7 +410,7 @@ static int isp_stat_bufs_alloc_iommu(struct ispstat *stat, unsigned int size)
|
|||
return -ENOMEM;
|
||||
}
|
||||
|
||||
iovm = omap_find_iovm_area(isp->iommu, buf->iommu_addr);
|
||||
iovm = omap_find_iovm_area(isp->dev, buf->iommu_addr);
|
||||
if (!iovm ||
|
||||
!dma_map_sg(isp->dev, iovm->sgt->sgl, iovm->sgt->nents,
|
||||
DMA_FROM_DEVICE)) {
|
||||
|
@ -419,7 +419,7 @@ static int isp_stat_bufs_alloc_iommu(struct ispstat *stat, unsigned int size)
|
|||
}
|
||||
buf->iovm = iovm;
|
||||
|
||||
buf->virt_addr = omap_da_to_va(stat->isp->iommu,
|
||||
buf->virt_addr = omap_da_to_va(stat->isp->dev,
|
||||
(u32)buf->iommu_addr);
|
||||
buf->empty = 1;
|
||||
dev_dbg(stat->isp->dev, "%s: buffer[%d] allocated."
|
||||
|
|
|
@ -453,7 +453,7 @@ ispmmu_vmap(struct isp_device *isp, const struct scatterlist *sglist, int sglen)
|
|||
sgt->nents = sglen;
|
||||
sgt->orig_nents = sglen;
|
||||
|
||||
da = omap_iommu_vmap(isp->domain, isp->iommu, 0, sgt, IOMMU_FLAG);
|
||||
da = omap_iommu_vmap(isp->domain, isp->dev, 0, sgt, IOMMU_FLAG);
|
||||
if (IS_ERR_VALUE(da))
|
||||
kfree(sgt);
|
||||
|
||||
|
@ -469,7 +469,7 @@ static void ispmmu_vunmap(struct isp_device *isp, dma_addr_t da)
|
|||
{
|
||||
struct sg_table *sgt;
|
||||
|
||||
sgt = omap_iommu_vunmap(isp->domain, isp->iommu, (u32)da);
|
||||
sgt = omap_iommu_vunmap(isp->domain, isp->dev, (u32)da);
|
||||
kfree(sgt);
|
||||
}
|
||||
|
||||
|
|
|
@ -175,21 +175,22 @@ int pci_enable_pri(struct pci_dev *pdev, u32 reqs)
|
|||
u32 max_requests;
|
||||
int pos;
|
||||
|
||||
pos = pci_find_ext_capability(pdev, PCI_PRI_CAP);
|
||||
pos = pci_find_ext_capability(pdev, PCI_EXT_CAP_ID_PRI);
|
||||
if (!pos)
|
||||
return -EINVAL;
|
||||
|
||||
pci_read_config_word(pdev, pos + PCI_PRI_CONTROL_OFF, &control);
|
||||
pci_read_config_word(pdev, pos + PCI_PRI_STATUS_OFF, &status);
|
||||
if ((control & PCI_PRI_ENABLE) || !(status & PCI_PRI_STATUS_STOPPED))
|
||||
pci_read_config_word(pdev, pos + PCI_PRI_CTRL, &control);
|
||||
pci_read_config_word(pdev, pos + PCI_PRI_STATUS, &status);
|
||||
if ((control & PCI_PRI_CTRL_ENABLE) ||
|
||||
!(status & PCI_PRI_STATUS_STOPPED))
|
||||
return -EBUSY;
|
||||
|
||||
pci_read_config_dword(pdev, pos + PCI_PRI_MAX_REQ_OFF, &max_requests);
|
||||
pci_read_config_dword(pdev, pos + PCI_PRI_MAX_REQ, &max_requests);
|
||||
reqs = min(max_requests, reqs);
|
||||
pci_write_config_dword(pdev, pos + PCI_PRI_ALLOC_REQ_OFF, reqs);
|
||||
pci_write_config_dword(pdev, pos + PCI_PRI_ALLOC_REQ, reqs);
|
||||
|
||||
control |= PCI_PRI_ENABLE;
|
||||
pci_write_config_word(pdev, pos + PCI_PRI_CONTROL_OFF, control);
|
||||
control |= PCI_PRI_CTRL_ENABLE;
|
||||
pci_write_config_word(pdev, pos + PCI_PRI_CTRL, control);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
@ -206,13 +207,13 @@ void pci_disable_pri(struct pci_dev *pdev)
|
|||
u16 control;
|
||||
int pos;
|
||||
|
||||
pos = pci_find_ext_capability(pdev, PCI_PRI_CAP);
|
||||
pos = pci_find_ext_capability(pdev, PCI_EXT_CAP_ID_PRI);
|
||||
if (!pos)
|
||||
return;
|
||||
|
||||
pci_read_config_word(pdev, pos + PCI_PRI_CONTROL_OFF, &control);
|
||||
control &= ~PCI_PRI_ENABLE;
|
||||
pci_write_config_word(pdev, pos + PCI_PRI_CONTROL_OFF, control);
|
||||
pci_read_config_word(pdev, pos + PCI_PRI_CTRL, &control);
|
||||
control &= ~PCI_PRI_CTRL_ENABLE;
|
||||
pci_write_config_word(pdev, pos + PCI_PRI_CTRL, control);
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(pci_disable_pri);
|
||||
|
||||
|
@ -227,13 +228,13 @@ bool pci_pri_enabled(struct pci_dev *pdev)
|
|||
u16 control;
|
||||
int pos;
|
||||
|
||||
pos = pci_find_ext_capability(pdev, PCI_PRI_CAP);
|
||||
pos = pci_find_ext_capability(pdev, PCI_EXT_CAP_ID_PRI);
|
||||
if (!pos)
|
||||
return false;
|
||||
|
||||
pci_read_config_word(pdev, pos + PCI_PRI_CONTROL_OFF, &control);
|
||||
pci_read_config_word(pdev, pos + PCI_PRI_CTRL, &control);
|
||||
|
||||
return (control & PCI_PRI_ENABLE) ? true : false;
|
||||
return (control & PCI_PRI_CTRL_ENABLE) ? true : false;
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(pci_pri_enabled);
|
||||
|
||||
|
@ -249,17 +250,17 @@ int pci_reset_pri(struct pci_dev *pdev)
|
|||
u16 control;
|
||||
int pos;
|
||||
|
||||
pos = pci_find_ext_capability(pdev, PCI_PRI_CAP);
|
||||
pos = pci_find_ext_capability(pdev, PCI_EXT_CAP_ID_PRI);
|
||||
if (!pos)
|
||||
return -EINVAL;
|
||||
|
||||
pci_read_config_word(pdev, pos + PCI_PRI_CONTROL_OFF, &control);
|
||||
if (control & PCI_PRI_ENABLE)
|
||||
pci_read_config_word(pdev, pos + PCI_PRI_CTRL, &control);
|
||||
if (control & PCI_PRI_CTRL_ENABLE)
|
||||
return -EBUSY;
|
||||
|
||||
control |= PCI_PRI_RESET;
|
||||
control |= PCI_PRI_CTRL_RESET;
|
||||
|
||||
pci_write_config_word(pdev, pos + PCI_PRI_CONTROL_OFF, control);
|
||||
pci_write_config_word(pdev, pos + PCI_PRI_CTRL, control);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
@ -282,14 +283,14 @@ bool pci_pri_stopped(struct pci_dev *pdev)
|
|||
u16 control, status;
|
||||
int pos;
|
||||
|
||||
pos = pci_find_ext_capability(pdev, PCI_PRI_CAP);
|
||||
pos = pci_find_ext_capability(pdev, PCI_EXT_CAP_ID_PRI);
|
||||
if (!pos)
|
||||
return true;
|
||||
|
||||
pci_read_config_word(pdev, pos + PCI_PRI_CONTROL_OFF, &control);
|
||||
pci_read_config_word(pdev, pos + PCI_PRI_STATUS_OFF, &status);
|
||||
pci_read_config_word(pdev, pos + PCI_PRI_CTRL, &control);
|
||||
pci_read_config_word(pdev, pos + PCI_PRI_STATUS, &status);
|
||||
|
||||
if (control & PCI_PRI_ENABLE)
|
||||
if (control & PCI_PRI_CTRL_ENABLE)
|
||||
return false;
|
||||
|
||||
return (status & PCI_PRI_STATUS_STOPPED) ? true : false;
|
||||
|
@ -311,15 +312,15 @@ int pci_pri_status(struct pci_dev *pdev)
|
|||
u16 status, control;
|
||||
int pos;
|
||||
|
||||
pos = pci_find_ext_capability(pdev, PCI_PRI_CAP);
|
||||
pos = pci_find_ext_capability(pdev, PCI_EXT_CAP_ID_PRI);
|
||||
if (!pos)
|
||||
return -EINVAL;
|
||||
|
||||
pci_read_config_word(pdev, pos + PCI_PRI_CONTROL_OFF, &control);
|
||||
pci_read_config_word(pdev, pos + PCI_PRI_STATUS_OFF, &status);
|
||||
pci_read_config_word(pdev, pos + PCI_PRI_CTRL, &control);
|
||||
pci_read_config_word(pdev, pos + PCI_PRI_STATUS, &status);
|
||||
|
||||
/* Stopped bit is undefined when enable == 1, so clear it */
|
||||
if (control & PCI_PRI_ENABLE)
|
||||
if (control & PCI_PRI_CTRL_ENABLE)
|
||||
status &= ~PCI_PRI_STATUS_STOPPED;
|
||||
|
||||
return status;
|
||||
|
@ -342,25 +343,25 @@ int pci_enable_pasid(struct pci_dev *pdev, int features)
|
|||
u16 control, supported;
|
||||
int pos;
|
||||
|
||||
pos = pci_find_ext_capability(pdev, PCI_PASID_CAP);
|
||||
pos = pci_find_ext_capability(pdev, PCI_EXT_CAP_ID_PASID);
|
||||
if (!pos)
|
||||
return -EINVAL;
|
||||
|
||||
pci_read_config_word(pdev, pos + PCI_PASID_CONTROL_OFF, &control);
|
||||
pci_read_config_word(pdev, pos + PCI_PASID_CAP_OFF, &supported);
|
||||
pci_read_config_word(pdev, pos + PCI_PASID_CTRL, &control);
|
||||
pci_read_config_word(pdev, pos + PCI_PASID_CAP, &supported);
|
||||
|
||||
if (!(supported & PCI_PASID_ENABLE))
|
||||
if (control & PCI_PASID_CTRL_ENABLE)
|
||||
return -EINVAL;
|
||||
|
||||
supported &= PCI_PASID_EXEC | PCI_PASID_PRIV;
|
||||
supported &= PCI_PASID_CAP_EXEC | PCI_PASID_CAP_PRIV;
|
||||
|
||||
/* User wants to enable anything unsupported? */
|
||||
if ((supported & features) != features)
|
||||
return -EINVAL;
|
||||
|
||||
control = PCI_PASID_ENABLE | features;
|
||||
control = PCI_PASID_CTRL_ENABLE | features;
|
||||
|
||||
pci_write_config_word(pdev, pos + PCI_PASID_CONTROL_OFF, control);
|
||||
pci_write_config_word(pdev, pos + PCI_PASID_CTRL, control);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
@ -376,11 +377,11 @@ void pci_disable_pasid(struct pci_dev *pdev)
|
|||
u16 control = 0;
|
||||
int pos;
|
||||
|
||||
pos = pci_find_ext_capability(pdev, PCI_PASID_CAP);
|
||||
pos = pci_find_ext_capability(pdev, PCI_EXT_CAP_ID_PASID);
|
||||
if (!pos)
|
||||
return;
|
||||
|
||||
pci_write_config_word(pdev, pos + PCI_PASID_CONTROL_OFF, control);
|
||||
pci_write_config_word(pdev, pos + PCI_PASID_CTRL, control);
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(pci_disable_pasid);
|
||||
|
||||
|
@ -391,22 +392,21 @@ EXPORT_SYMBOL_GPL(pci_disable_pasid);
|
|||
* Returns a negative value when no PASI capability is present.
|
||||
* Otherwise is returns a bitmask with supported features. Current
|
||||
* features reported are:
|
||||
* PCI_PASID_ENABLE - PASID capability can be enabled
|
||||
* PCI_PASID_EXEC - Execute permission supported
|
||||
* PCI_PASID_PRIV - Priviledged mode supported
|
||||
* PCI_PASID_CAP_EXEC - Execute permission supported
|
||||
* PCI_PASID_CAP_PRIV - Priviledged mode supported
|
||||
*/
|
||||
int pci_pasid_features(struct pci_dev *pdev)
|
||||
{
|
||||
u16 supported;
|
||||
int pos;
|
||||
|
||||
pos = pci_find_ext_capability(pdev, PCI_PASID_CAP);
|
||||
pos = pci_find_ext_capability(pdev, PCI_EXT_CAP_ID_PASID);
|
||||
if (!pos)
|
||||
return -EINVAL;
|
||||
|
||||
pci_read_config_word(pdev, pos + PCI_PASID_CAP_OFF, &supported);
|
||||
pci_read_config_word(pdev, pos + PCI_PASID_CAP, &supported);
|
||||
|
||||
supported &= PCI_PASID_ENABLE | PCI_PASID_EXEC | PCI_PASID_PRIV;
|
||||
supported &= PCI_PASID_CAP_EXEC | PCI_PASID_CAP_PRIV;
|
||||
|
||||
return supported;
|
||||
}
|
||||
|
@ -426,11 +426,11 @@ int pci_max_pasids(struct pci_dev *pdev)
|
|||
u16 supported;
|
||||
int pos;
|
||||
|
||||
pos = pci_find_ext_capability(pdev, PCI_PASID_CAP);
|
||||
pos = pci_find_ext_capability(pdev, PCI_EXT_CAP_ID_PASID);
|
||||
if (!pos)
|
||||
return -EINVAL;
|
||||
|
||||
pci_read_config_word(pdev, pos + PCI_PASID_CAP_OFF, &supported);
|
||||
pci_read_config_word(pdev, pos + PCI_PASID_CAP, &supported);
|
||||
|
||||
supported = (supported & PASID_NUMBER_MASK) >> PASID_NUMBER_SHIFT;
|
||||
|
||||
|
|
|
@ -45,7 +45,6 @@ extern int pciehp_poll_time;
|
|||
extern int pciehp_debug;
|
||||
extern int pciehp_force;
|
||||
extern struct workqueue_struct *pciehp_wq;
|
||||
extern struct workqueue_struct *pciehp_ordered_wq;
|
||||
|
||||
#define dbg(format, arg...) \
|
||||
do { \
|
||||
|
|
|
@ -43,7 +43,6 @@ int pciehp_poll_mode;
|
|||
int pciehp_poll_time;
|
||||
int pciehp_force;
|
||||
struct workqueue_struct *pciehp_wq;
|
||||
struct workqueue_struct *pciehp_ordered_wq;
|
||||
|
||||
#define DRIVER_VERSION "0.4"
|
||||
#define DRIVER_AUTHOR "Dan Zink <dan.zink@compaq.com>, Greg Kroah-Hartman <greg@kroah.com>, Dely Sy <dely.l.sy@intel.com>"
|
||||
|
@ -345,18 +344,11 @@ static int __init pcied_init(void)
|
|||
if (!pciehp_wq)
|
||||
return -ENOMEM;
|
||||
|
||||
pciehp_ordered_wq = alloc_ordered_workqueue("pciehp_ordered", 0);
|
||||
if (!pciehp_ordered_wq) {
|
||||
destroy_workqueue(pciehp_wq);
|
||||
return -ENOMEM;
|
||||
}
|
||||
|
||||
pciehp_firmware_init();
|
||||
retval = pcie_port_service_register(&hpdriver_portdrv);
|
||||
dbg("pcie_port_service_register = %d\n", retval);
|
||||
info(DRIVER_DESC " version: " DRIVER_VERSION "\n");
|
||||
if (retval) {
|
||||
destroy_workqueue(pciehp_ordered_wq);
|
||||
destroy_workqueue(pciehp_wq);
|
||||
dbg("Failure to register service\n");
|
||||
}
|
||||
|
@ -366,9 +358,8 @@ static int __init pcied_init(void)
|
|||
static void __exit pcied_cleanup(void)
|
||||
{
|
||||
dbg("unload_pciehpd()\n");
|
||||
destroy_workqueue(pciehp_ordered_wq);
|
||||
destroy_workqueue(pciehp_wq);
|
||||
pcie_port_service_unregister(&hpdriver_portdrv);
|
||||
destroy_workqueue(pciehp_wq);
|
||||
info(DRIVER_DESC " version: " DRIVER_VERSION " unloaded\n");
|
||||
}
|
||||
|
||||
|
|
|
@ -344,7 +344,7 @@ void pciehp_queue_pushbutton_work(struct work_struct *work)
|
|||
kfree(info);
|
||||
goto out;
|
||||
}
|
||||
queue_work(pciehp_ordered_wq, &info->work);
|
||||
queue_work(pciehp_wq, &info->work);
|
||||
out:
|
||||
mutex_unlock(&p_slot->lock);
|
||||
}
|
||||
|
@ -439,7 +439,7 @@ static void handle_surprise_event(struct slot *p_slot)
|
|||
else
|
||||
p_slot->state = POWERON_STATE;
|
||||
|
||||
queue_work(pciehp_ordered_wq, &info->work);
|
||||
queue_work(pciehp_wq, &info->work);
|
||||
}
|
||||
|
||||
static void interrupt_event_handler(struct work_struct *work)
|
||||
|
|
|
@ -806,7 +806,6 @@ static void pcie_cleanup_slot(struct controller *ctrl)
|
|||
struct slot *slot = ctrl->slot;
|
||||
cancel_delayed_work(&slot->work);
|
||||
flush_workqueue(pciehp_wq);
|
||||
flush_workqueue(pciehp_ordered_wq);
|
||||
kfree(slot);
|
||||
}
|
||||
|
||||
|
|
|
@ -323,6 +323,8 @@ static void free_msi_irqs(struct pci_dev *dev)
|
|||
if (list_is_last(&entry->list, &dev->msi_list))
|
||||
iounmap(entry->mask_base);
|
||||
}
|
||||
kobject_del(&entry->kobj);
|
||||
kobject_put(&entry->kobj);
|
||||
list_del(&entry->list);
|
||||
kfree(entry);
|
||||
}
|
||||
|
@ -403,6 +405,98 @@ void pci_restore_msi_state(struct pci_dev *dev)
|
|||
}
|
||||
EXPORT_SYMBOL_GPL(pci_restore_msi_state);
|
||||
|
||||
|
||||
#define to_msi_attr(obj) container_of(obj, struct msi_attribute, attr)
|
||||
#define to_msi_desc(obj) container_of(obj, struct msi_desc, kobj)
|
||||
|
||||
struct msi_attribute {
|
||||
struct attribute attr;
|
||||
ssize_t (*show)(struct msi_desc *entry, struct msi_attribute *attr,
|
||||
char *buf);
|
||||
ssize_t (*store)(struct msi_desc *entry, struct msi_attribute *attr,
|
||||
const char *buf, size_t count);
|
||||
};
|
||||
|
||||
static ssize_t show_msi_mode(struct msi_desc *entry, struct msi_attribute *atr,
|
||||
char *buf)
|
||||
{
|
||||
return sprintf(buf, "%s\n", entry->msi_attrib.is_msix ? "msix" : "msi");
|
||||
}
|
||||
|
||||
static ssize_t msi_irq_attr_show(struct kobject *kobj,
|
||||
struct attribute *attr, char *buf)
|
||||
{
|
||||
struct msi_attribute *attribute = to_msi_attr(attr);
|
||||
struct msi_desc *entry = to_msi_desc(kobj);
|
||||
|
||||
if (!attribute->show)
|
||||
return -EIO;
|
||||
|
||||
return attribute->show(entry, attribute, buf);
|
||||
}
|
||||
|
||||
static const struct sysfs_ops msi_irq_sysfs_ops = {
|
||||
.show = msi_irq_attr_show,
|
||||
};
|
||||
|
||||
static struct msi_attribute mode_attribute =
|
||||
__ATTR(mode, S_IRUGO, show_msi_mode, NULL);
|
||||
|
||||
|
||||
struct attribute *msi_irq_default_attrs[] = {
|
||||
&mode_attribute.attr,
|
||||
NULL
|
||||
};
|
||||
|
||||
void msi_kobj_release(struct kobject *kobj)
|
||||
{
|
||||
struct msi_desc *entry = to_msi_desc(kobj);
|
||||
|
||||
pci_dev_put(entry->dev);
|
||||
}
|
||||
|
||||
static struct kobj_type msi_irq_ktype = {
|
||||
.release = msi_kobj_release,
|
||||
.sysfs_ops = &msi_irq_sysfs_ops,
|
||||
.default_attrs = msi_irq_default_attrs,
|
||||
};
|
||||
|
||||
static int populate_msi_sysfs(struct pci_dev *pdev)
|
||||
{
|
||||
struct msi_desc *entry;
|
||||
struct kobject *kobj;
|
||||
int ret;
|
||||
int count = 0;
|
||||
|
||||
pdev->msi_kset = kset_create_and_add("msi_irqs", NULL, &pdev->dev.kobj);
|
||||
if (!pdev->msi_kset)
|
||||
return -ENOMEM;
|
||||
|
||||
list_for_each_entry(entry, &pdev->msi_list, list) {
|
||||
kobj = &entry->kobj;
|
||||
kobj->kset = pdev->msi_kset;
|
||||
pci_dev_get(pdev);
|
||||
ret = kobject_init_and_add(kobj, &msi_irq_ktype, NULL,
|
||||
"%u", entry->irq);
|
||||
if (ret)
|
||||
goto out_unroll;
|
||||
|
||||
count++;
|
||||
}
|
||||
|
||||
return 0;
|
||||
|
||||
out_unroll:
|
||||
list_for_each_entry(entry, &pdev->msi_list, list) {
|
||||
if (!count)
|
||||
break;
|
||||
kobject_del(&entry->kobj);
|
||||
kobject_put(&entry->kobj);
|
||||
count--;
|
||||
}
|
||||
return ret;
|
||||
}
|
||||
|
||||
/**
|
||||
* msi_capability_init - configure device's MSI capability structure
|
||||
* @dev: pointer to the pci_dev data structure of MSI device function
|
||||
|
@ -454,6 +548,13 @@ static int msi_capability_init(struct pci_dev *dev, int nvec)
|
|||
return ret;
|
||||
}
|
||||
|
||||
ret = populate_msi_sysfs(dev);
|
||||
if (ret) {
|
||||
msi_mask_irq(entry, mask, ~mask);
|
||||
free_msi_irqs(dev);
|
||||
return ret;
|
||||
}
|
||||
|
||||
/* Set MSI enabled bits */
|
||||
pci_intx_for_msi(dev, 0);
|
||||
msi_set_enable(dev, pos, 1);
|
||||
|
@ -574,6 +675,12 @@ static int msix_capability_init(struct pci_dev *dev,
|
|||
|
||||
msix_program_entries(dev, entries);
|
||||
|
||||
ret = populate_msi_sysfs(dev);
|
||||
if (ret) {
|
||||
ret = 0;
|
||||
goto error;
|
||||
}
|
||||
|
||||
/* Set MSI-X enabled bits and unmask the function */
|
||||
pci_intx_for_msi(dev, 0);
|
||||
dev->msix_enabled = 1;
|
||||
|
@ -732,6 +839,8 @@ void pci_disable_msi(struct pci_dev *dev)
|
|||
|
||||
pci_msi_shutdown(dev);
|
||||
free_msi_irqs(dev);
|
||||
kset_unregister(dev->msi_kset);
|
||||
dev->msi_kset = NULL;
|
||||
}
|
||||
EXPORT_SYMBOL(pci_disable_msi);
|
||||
|
||||
|
@ -830,6 +939,8 @@ void pci_disable_msix(struct pci_dev *dev)
|
|||
|
||||
pci_msix_shutdown(dev);
|
||||
free_msi_irqs(dev);
|
||||
kset_unregister(dev->msi_kset);
|
||||
dev->msi_kset = NULL;
|
||||
}
|
||||
EXPORT_SYMBOL(pci_disable_msix);
|
||||
|
||||
|
@ -870,5 +981,15 @@ EXPORT_SYMBOL(pci_msi_enabled);
|
|||
|
||||
void pci_msi_init_pci_dev(struct pci_dev *dev)
|
||||
{
|
||||
int pos;
|
||||
INIT_LIST_HEAD(&dev->msi_list);
|
||||
|
||||
/* Disable the msi hardware to avoid screaming interrupts
|
||||
* during boot. This is the power on reset default so
|
||||
* usually this should be a noop.
|
||||
*/
|
||||
pos = pci_find_capability(dev, PCI_CAP_ID_MSI);
|
||||
if (pos)
|
||||
msi_set_enable(dev, pos, 0);
|
||||
msix_set_enable(dev, 0);
|
||||
}
|
||||
|
|
|
@ -45,16 +45,20 @@ static void pci_acpi_wake_dev(acpi_handle handle, u32 event, void *context)
|
|||
{
|
||||
struct pci_dev *pci_dev = context;
|
||||
|
||||
if (event == ACPI_NOTIFY_DEVICE_WAKE && pci_dev) {
|
||||
if (event != ACPI_NOTIFY_DEVICE_WAKE || !pci_dev)
|
||||
return;
|
||||
|
||||
if (!pci_dev->pm_cap || !pci_dev->pme_support
|
||||
|| pci_check_pme_status(pci_dev)) {
|
||||
if (pci_dev->pme_poll)
|
||||
pci_dev->pme_poll = false;
|
||||
|
||||
pci_wakeup_event(pci_dev);
|
||||
pci_check_pme_status(pci_dev);
|
||||
pm_runtime_resume(&pci_dev->dev);
|
||||
if (pci_dev->subordinate)
|
||||
pci_pme_wakeup_bus(pci_dev->subordinate);
|
||||
}
|
||||
|
||||
if (pci_dev->subordinate)
|
||||
pci_pme_wakeup_bus(pci_dev->subordinate);
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -395,7 +399,6 @@ static int __init acpi_pci_init(void)
|
|||
|
||||
if (acpi_gbl_FADT.boot_flags & ACPI_FADT_NO_ASPM) {
|
||||
printk(KERN_INFO"ACPI FADT declares the system doesn't support PCIe ASPM, so disable it\n");
|
||||
pcie_clear_aspm();
|
||||
pcie_no_aspm();
|
||||
}
|
||||
|
||||
|
|
|
@ -68,7 +68,7 @@ struct pcie_link_state {
|
|||
struct aspm_latency acceptable[8];
|
||||
};
|
||||
|
||||
static int aspm_disabled, aspm_force, aspm_clear_state;
|
||||
static int aspm_disabled, aspm_force;
|
||||
static bool aspm_support_enabled = true;
|
||||
static DEFINE_MUTEX(aspm_lock);
|
||||
static LIST_HEAD(link_list);
|
||||
|
@ -500,9 +500,6 @@ static int pcie_aspm_sanity_check(struct pci_dev *pdev)
|
|||
int pos;
|
||||
u32 reg32;
|
||||
|
||||
if (aspm_clear_state)
|
||||
return -EINVAL;
|
||||
|
||||
/*
|
||||
* Some functions in a slot might not all be PCIe functions,
|
||||
* very strange. Disable ASPM for the whole slot
|
||||
|
@ -574,9 +571,6 @@ void pcie_aspm_init_link_state(struct pci_dev *pdev)
|
|||
pdev->pcie_type != PCI_EXP_TYPE_DOWNSTREAM)
|
||||
return;
|
||||
|
||||
if (aspm_disabled && !aspm_clear_state)
|
||||
return;
|
||||
|
||||
/* VIA has a strange chipset, root port is under a bridge */
|
||||
if (pdev->pcie_type == PCI_EXP_TYPE_ROOT_PORT &&
|
||||
pdev->bus->self)
|
||||
|
@ -608,7 +602,7 @@ void pcie_aspm_init_link_state(struct pci_dev *pdev)
|
|||
* the BIOS's expectation, we'll do so once pci_enable_device() is
|
||||
* called.
|
||||
*/
|
||||
if (aspm_policy != POLICY_POWERSAVE || aspm_clear_state) {
|
||||
if (aspm_policy != POLICY_POWERSAVE) {
|
||||
pcie_config_aspm_path(link);
|
||||
pcie_set_clkpm(link, policy_to_clkpm_state(link));
|
||||
}
|
||||
|
@ -649,8 +643,7 @@ void pcie_aspm_exit_link_state(struct pci_dev *pdev)
|
|||
struct pci_dev *parent = pdev->bus->self;
|
||||
struct pcie_link_state *link, *root, *parent_link;
|
||||
|
||||
if ((aspm_disabled && !aspm_clear_state) || !pci_is_pcie(pdev) ||
|
||||
!parent || !parent->link_state)
|
||||
if (!pci_is_pcie(pdev) || !parent || !parent->link_state)
|
||||
return;
|
||||
if ((parent->pcie_type != PCI_EXP_TYPE_ROOT_PORT) &&
|
||||
(parent->pcie_type != PCI_EXP_TYPE_DOWNSTREAM))
|
||||
|
@ -734,13 +727,18 @@ void pcie_aspm_powersave_config_link(struct pci_dev *pdev)
|
|||
* pci_disable_link_state - disable pci device's link state, so the link will
|
||||
* never enter specific states
|
||||
*/
|
||||
static void __pci_disable_link_state(struct pci_dev *pdev, int state, bool sem)
|
||||
static void __pci_disable_link_state(struct pci_dev *pdev, int state, bool sem,
|
||||
bool force)
|
||||
{
|
||||
struct pci_dev *parent = pdev->bus->self;
|
||||
struct pcie_link_state *link;
|
||||
|
||||
if (aspm_disabled || !pci_is_pcie(pdev))
|
||||
if (aspm_disabled && !force)
|
||||
return;
|
||||
|
||||
if (!pci_is_pcie(pdev))
|
||||
return;
|
||||
|
||||
if (pdev->pcie_type == PCI_EXP_TYPE_ROOT_PORT ||
|
||||
pdev->pcie_type == PCI_EXP_TYPE_DOWNSTREAM)
|
||||
parent = pdev;
|
||||
|
@ -768,16 +766,31 @@ static void __pci_disable_link_state(struct pci_dev *pdev, int state, bool sem)
|
|||
|
||||
void pci_disable_link_state_locked(struct pci_dev *pdev, int state)
|
||||
{
|
||||
__pci_disable_link_state(pdev, state, false);
|
||||
__pci_disable_link_state(pdev, state, false, false);
|
||||
}
|
||||
EXPORT_SYMBOL(pci_disable_link_state_locked);
|
||||
|
||||
void pci_disable_link_state(struct pci_dev *pdev, int state)
|
||||
{
|
||||
__pci_disable_link_state(pdev, state, true);
|
||||
__pci_disable_link_state(pdev, state, true, false);
|
||||
}
|
||||
EXPORT_SYMBOL(pci_disable_link_state);
|
||||
|
||||
void pcie_clear_aspm(struct pci_bus *bus)
|
||||
{
|
||||
struct pci_dev *child;
|
||||
|
||||
/*
|
||||
* Clear any ASPM setup that the firmware has carried out on this bus
|
||||
*/
|
||||
list_for_each_entry(child, &bus->devices, bus_list) {
|
||||
__pci_disable_link_state(child, PCIE_LINK_STATE_L0S |
|
||||
PCIE_LINK_STATE_L1 |
|
||||
PCIE_LINK_STATE_CLKPM,
|
||||
false, true);
|
||||
}
|
||||
}
|
||||
|
||||
static int pcie_aspm_set_policy(const char *val, struct kernel_param *kp)
|
||||
{
|
||||
int i;
|
||||
|
@ -935,6 +948,7 @@ void pcie_aspm_remove_sysfs_dev_files(struct pci_dev *pdev)
|
|||
static int __init pcie_aspm_disable(char *str)
|
||||
{
|
||||
if (!strcmp(str, "off")) {
|
||||
aspm_policy = POLICY_DEFAULT;
|
||||
aspm_disabled = 1;
|
||||
aspm_support_enabled = false;
|
||||
printk(KERN_INFO "PCIe ASPM is disabled\n");
|
||||
|
@ -947,16 +961,18 @@ static int __init pcie_aspm_disable(char *str)
|
|||
|
||||
__setup("pcie_aspm=", pcie_aspm_disable);
|
||||
|
||||
void pcie_clear_aspm(void)
|
||||
{
|
||||
if (!aspm_force)
|
||||
aspm_clear_state = 1;
|
||||
}
|
||||
|
||||
void pcie_no_aspm(void)
|
||||
{
|
||||
if (!aspm_force)
|
||||
/*
|
||||
* Disabling ASPM is intended to prevent the kernel from modifying
|
||||
* existing hardware state, not to clear existing state. To that end:
|
||||
* (a) set policy to POLICY_DEFAULT in order to avoid changing state
|
||||
* (b) prevent userspace from changing policy
|
||||
*/
|
||||
if (!aspm_force) {
|
||||
aspm_policy = POLICY_DEFAULT;
|
||||
aspm_disabled = 1;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
|
@ -302,6 +302,10 @@ extern bool osc_sb_apei_support_acked;
|
|||
OSC_PCI_EXPRESS_PME_CONTROL | \
|
||||
OSC_PCI_EXPRESS_AER_CONTROL | \
|
||||
OSC_PCI_EXPRESS_CAP_STRUCTURE_CONTROL)
|
||||
|
||||
#define OSC_PCI_NATIVE_HOTPLUG (OSC_PCI_EXPRESS_NATIVE_HP_CONTROL | \
|
||||
OSC_SHPC_NATIVE_HP_CONTROL)
|
||||
|
||||
extern acpi_status acpi_pci_osc_control_set(acpi_handle handle,
|
||||
u32 *mask, u32 req);
|
||||
extern void acpi_early_init(void);
|
||||
|
|
|
@ -20,12 +20,148 @@
|
|||
#ifndef _ASM_X86_AMD_IOMMU_H
|
||||
#define _ASM_X86_AMD_IOMMU_H
|
||||
|
||||
#include <linux/irqreturn.h>
|
||||
#include <linux/types.h>
|
||||
|
||||
#ifdef CONFIG_AMD_IOMMU
|
||||
|
||||
struct task_struct;
|
||||
struct pci_dev;
|
||||
|
||||
extern int amd_iommu_detect(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
|
||||
* @pdev: The PCI device to initialize
|
||||
* @pasids: Number of PASIDs to support for this device
|
||||
*
|
||||
* This function does all setup for the device pdev so that it can be
|
||||
* used with IOMMUv2.
|
||||
* Returns 0 on success or negative value on error.
|
||||
*/
|
||||
extern int amd_iommu_init_device(struct pci_dev *pdev, int pasids);
|
||||
|
||||
/**
|
||||
* amd_iommu_free_device() - Free all IOMMUv2 related device resources
|
||||
* and disable IOMMUv2 usage for this device
|
||||
* @pdev: The PCI device to disable IOMMUv2 usage for'
|
||||
*/
|
||||
extern void amd_iommu_free_device(struct pci_dev *pdev);
|
||||
|
||||
/**
|
||||
* amd_iommu_bind_pasid() - Bind a given task to a PASID on a device
|
||||
* @pdev: The PCI device to bind the task to
|
||||
* @pasid: The PASID on the device the task should be bound to
|
||||
* @task: the task to bind
|
||||
*
|
||||
* The function returns 0 on success or a negative value on error.
|
||||
*/
|
||||
extern int amd_iommu_bind_pasid(struct pci_dev *pdev, int pasid,
|
||||
struct task_struct *task);
|
||||
|
||||
/**
|
||||
* amd_iommu_unbind_pasid() - Unbind a PASID from its task on
|
||||
* a device
|
||||
* @pdev: The device of the PASID
|
||||
* @pasid: The PASID to unbind
|
||||
*
|
||||
* When this function returns the device is no longer using the PASID
|
||||
* and the PASID is no longer bound to its task.
|
||||
*/
|
||||
extern void amd_iommu_unbind_pasid(struct pci_dev *pdev, int pasid);
|
||||
|
||||
/**
|
||||
* amd_iommu_set_invalid_ppr_cb() - Register a call-back for failed
|
||||
* PRI requests
|
||||
* @pdev: The PCI device the call-back should be registered for
|
||||
* @cb: The call-back function
|
||||
*
|
||||
* The IOMMUv2 driver invokes this call-back when it is unable to
|
||||
* successfully handle a PRI request. The device driver can then decide
|
||||
* which PRI response the device should see. Possible return values for
|
||||
* the call-back are:
|
||||
*
|
||||
* - AMD_IOMMU_INV_PRI_RSP_SUCCESS - Send SUCCESS back to the device
|
||||
* - AMD_IOMMU_INV_PRI_RSP_INVALID - Send INVALID back to the device
|
||||
* - AMD_IOMMU_INV_PRI_RSP_FAIL - Send Failure back to the device,
|
||||
* the device is required to disable
|
||||
* PRI when it receives this response
|
||||
*
|
||||
* The function returns 0 on success or negative value on error.
|
||||
*/
|
||||
#define AMD_IOMMU_INV_PRI_RSP_SUCCESS 0
|
||||
#define AMD_IOMMU_INV_PRI_RSP_INVALID 1
|
||||
#define AMD_IOMMU_INV_PRI_RSP_FAIL 2
|
||||
|
||||
typedef int (*amd_iommu_invalid_ppr_cb)(struct pci_dev *pdev,
|
||||
int pasid,
|
||||
unsigned long address,
|
||||
u16);
|
||||
|
||||
extern int amd_iommu_set_invalid_ppr_cb(struct pci_dev *pdev,
|
||||
amd_iommu_invalid_ppr_cb cb);
|
||||
|
||||
/**
|
||||
* amd_iommu_device_info() - Get information about IOMMUv2 support of a
|
||||
* PCI device
|
||||
* @pdev: PCI device to query information from
|
||||
* @info: A pointer to an amd_iommu_device_info structure which will contain
|
||||
* the information about the PCI device
|
||||
*
|
||||
* Returns 0 on success, negative value on error
|
||||
*/
|
||||
|
||||
#define AMD_IOMMU_DEVICE_FLAG_ATS_SUP 0x1 /* ATS feature supported */
|
||||
#define AMD_IOMMU_DEVICE_FLAG_PRI_SUP 0x2 /* PRI feature supported */
|
||||
#define AMD_IOMMU_DEVICE_FLAG_PASID_SUP 0x4 /* PASID context supported */
|
||||
#define AMD_IOMMU_DEVICE_FLAG_EXEC_SUP 0x8 /* Device may request execution
|
||||
on memory pages */
|
||||
#define AMD_IOMMU_DEVICE_FLAG_PRIV_SUP 0x10 /* Device may request
|
||||
super-user privileges */
|
||||
|
||||
struct amd_iommu_device_info {
|
||||
int max_pasids;
|
||||
u32 flags;
|
||||
};
|
||||
|
||||
extern int amd_iommu_device_info(struct pci_dev *pdev,
|
||||
struct amd_iommu_device_info *info);
|
||||
|
||||
/**
|
||||
* amd_iommu_set_invalidate_ctx_cb() - Register a call-back for invalidating
|
||||
* a pasid context. This call-back is
|
||||
* invoked when the IOMMUv2 driver needs to
|
||||
* invalidate a PASID context, for example
|
||||
* because the task that is bound to that
|
||||
* context is about to exit.
|
||||
*
|
||||
* @pdev: The PCI device the call-back should be registered for
|
||||
* @cb: The call-back function
|
||||
*/
|
||||
|
||||
typedef void (*amd_iommu_invalidate_ctx)(struct pci_dev *pdev, int pasid);
|
||||
|
||||
extern int amd_iommu_set_invalidate_ctx_cb(struct pci_dev *pdev,
|
||||
amd_iommu_invalidate_ctx cb);
|
||||
|
||||
#else
|
||||
|
||||
static inline int amd_iommu_detect(void) { return -ENODEV; }
|
||||
|
|
|
@ -48,19 +48,33 @@ struct iommu_domain {
|
|||
|
||||
#ifdef CONFIG_IOMMU_API
|
||||
|
||||
/**
|
||||
* struct iommu_ops - iommu ops and capabilities
|
||||
* @domain_init: init iommu domain
|
||||
* @domain_destroy: destroy iommu domain
|
||||
* @attach_dev: attach device to an iommu domain
|
||||
* @detach_dev: detach device from an iommu domain
|
||||
* @map: map a physically contiguous memory region to an iommu domain
|
||||
* @unmap: unmap a physically contiguous memory region from an iommu domain
|
||||
* @iova_to_phys: translate iova to physical address
|
||||
* @domain_has_cap: domain capabilities query
|
||||
* @commit: commit iommu domain
|
||||
* @pgsize_bitmap: bitmap of supported page sizes
|
||||
*/
|
||||
struct iommu_ops {
|
||||
int (*domain_init)(struct iommu_domain *domain);
|
||||
void (*domain_destroy)(struct iommu_domain *domain);
|
||||
int (*attach_dev)(struct iommu_domain *domain, struct device *dev);
|
||||
void (*detach_dev)(struct iommu_domain *domain, struct device *dev);
|
||||
int (*map)(struct iommu_domain *domain, unsigned long iova,
|
||||
phys_addr_t paddr, int gfp_order, int prot);
|
||||
int (*unmap)(struct iommu_domain *domain, unsigned long iova,
|
||||
int gfp_order);
|
||||
phys_addr_t paddr, size_t size, int prot);
|
||||
size_t (*unmap)(struct iommu_domain *domain, unsigned long iova,
|
||||
size_t size);
|
||||
phys_addr_t (*iova_to_phys)(struct iommu_domain *domain,
|
||||
unsigned long iova);
|
||||
int (*domain_has_cap)(struct iommu_domain *domain,
|
||||
unsigned long cap);
|
||||
unsigned long pgsize_bitmap;
|
||||
};
|
||||
|
||||
extern int bus_set_iommu(struct bus_type *bus, struct iommu_ops *ops);
|
||||
|
@ -72,9 +86,9 @@ extern int iommu_attach_device(struct iommu_domain *domain,
|
|||
extern void iommu_detach_device(struct iommu_domain *domain,
|
||||
struct device *dev);
|
||||
extern int iommu_map(struct iommu_domain *domain, unsigned long iova,
|
||||
phys_addr_t paddr, int gfp_order, int prot);
|
||||
extern int iommu_unmap(struct iommu_domain *domain, unsigned long iova,
|
||||
int gfp_order);
|
||||
phys_addr_t paddr, size_t size, int prot);
|
||||
extern size_t iommu_unmap(struct iommu_domain *domain, unsigned long iova,
|
||||
size_t size);
|
||||
extern phys_addr_t iommu_iova_to_phys(struct iommu_domain *domain,
|
||||
unsigned long iova);
|
||||
extern int iommu_domain_has_cap(struct iommu_domain *domain,
|
||||
|
|
|
@ -1,6 +1,7 @@
|
|||
#ifndef LINUX_MSI_H
|
||||
#define LINUX_MSI_H
|
||||
|
||||
#include <linux/kobject.h>
|
||||
#include <linux/list.h>
|
||||
|
||||
struct msi_msg {
|
||||
|
@ -44,6 +45,8 @@ struct msi_desc {
|
|||
|
||||
/* Last set MSI message */
|
||||
struct msi_msg msg;
|
||||
|
||||
struct kobject kobj;
|
||||
};
|
||||
|
||||
/*
|
||||
|
|
|
@ -29,7 +29,7 @@ extern void pcie_aspm_pm_state_change(struct pci_dev *pdev);
|
|||
extern void pcie_aspm_powersave_config_link(struct pci_dev *pdev);
|
||||
extern void pci_disable_link_state(struct pci_dev *pdev, int state);
|
||||
extern void pci_disable_link_state_locked(struct pci_dev *pdev, int state);
|
||||
extern void pcie_clear_aspm(void);
|
||||
extern void pcie_clear_aspm(struct pci_bus *bus);
|
||||
extern void pcie_no_aspm(void);
|
||||
#else
|
||||
static inline void pcie_aspm_init_link_state(struct pci_dev *pdev)
|
||||
|
@ -47,7 +47,7 @@ static inline void pcie_aspm_powersave_config_link(struct pci_dev *pdev)
|
|||
static inline void pci_disable_link_state(struct pci_dev *pdev, int state)
|
||||
{
|
||||
}
|
||||
static inline void pcie_clear_aspm(void)
|
||||
static inline void pcie_clear_aspm(struct pci_bus *bus)
|
||||
{
|
||||
}
|
||||
static inline void pcie_no_aspm(void)
|
||||
|
|
|
@ -336,6 +336,7 @@ struct pci_dev {
|
|||
struct bin_attribute *res_attr_wc[DEVICE_COUNT_RESOURCE]; /* sysfs file for WC mapping of resources */
|
||||
#ifdef CONFIG_PCI_MSI
|
||||
struct list_head msi_list;
|
||||
struct kset *msi_kset;
|
||||
#endif
|
||||
struct pci_vpd *vpd;
|
||||
#ifdef CONFIG_PCI_ATS
|
||||
|
|
|
@ -537,7 +537,9 @@
|
|||
#define PCI_EXT_CAP_ID_ARI 14
|
||||
#define PCI_EXT_CAP_ID_ATS 15
|
||||
#define PCI_EXT_CAP_ID_SRIOV 16
|
||||
#define PCI_EXT_CAP_ID_PRI 19
|
||||
#define PCI_EXT_CAP_ID_LTR 24
|
||||
#define PCI_EXT_CAP_ID_PASID 27
|
||||
|
||||
/* Advanced Error Reporting */
|
||||
#define PCI_ERR_UNCOR_STATUS 4 /* Uncorrectable Error Status */
|
||||
|
@ -664,24 +666,24 @@
|
|||
#define PCI_ATS_MIN_STU 12 /* shift of minimum STU block */
|
||||
|
||||
/* Page Request Interface */
|
||||
#define PCI_PRI_CAP 0x13 /* PRI capability ID */
|
||||
#define PCI_PRI_CONTROL_OFF 0x04 /* Offset of control register */
|
||||
#define PCI_PRI_STATUS_OFF 0x06 /* Offset of status register */
|
||||
#define PCI_PRI_ENABLE 0x0001 /* Enable mask */
|
||||
#define PCI_PRI_RESET 0x0002 /* Reset bit mask */
|
||||
#define PCI_PRI_STATUS_RF 0x0001 /* Request Failure */
|
||||
#define PCI_PRI_STATUS_UPRGI 0x0002 /* Unexpected PRG index */
|
||||
#define PCI_PRI_STATUS_STOPPED 0x0100 /* PRI Stopped */
|
||||
#define PCI_PRI_MAX_REQ_OFF 0x08 /* Cap offset for max reqs supported */
|
||||
#define PCI_PRI_ALLOC_REQ_OFF 0x0c /* Cap offset for max reqs allowed */
|
||||
#define PCI_PRI_CTRL 0x04 /* PRI control register */
|
||||
#define PCI_PRI_CTRL_ENABLE 0x01 /* Enable */
|
||||
#define PCI_PRI_CTRL_RESET 0x02 /* Reset */
|
||||
#define PCI_PRI_STATUS 0x06 /* PRI status register */
|
||||
#define PCI_PRI_STATUS_RF 0x001 /* Response Failure */
|
||||
#define PCI_PRI_STATUS_UPRGI 0x002 /* Unexpected PRG index */
|
||||
#define PCI_PRI_STATUS_STOPPED 0x100 /* PRI Stopped */
|
||||
#define PCI_PRI_MAX_REQ 0x08 /* PRI max reqs supported */
|
||||
#define PCI_PRI_ALLOC_REQ 0x0c /* PRI max reqs allowed */
|
||||
|
||||
/* PASID capability */
|
||||
#define PCI_PASID_CAP 0x1b /* PASID capability ID */
|
||||
#define PCI_PASID_CAP_OFF 0x04 /* PASID feature register */
|
||||
#define PCI_PASID_CONTROL_OFF 0x06 /* PASID control register */
|
||||
#define PCI_PASID_ENABLE 0x01 /* Enable/Supported bit */
|
||||
#define PCI_PASID_EXEC 0x02 /* Exec permissions Enable/Supported */
|
||||
#define PCI_PASID_PRIV 0x04 /* Priviledge Mode Enable/Support */
|
||||
#define PCI_PASID_CAP 0x04 /* PASID feature register */
|
||||
#define PCI_PASID_CAP_EXEC 0x02 /* Exec permissions Supported */
|
||||
#define PCI_PASID_CAP_PRIV 0x04 /* Priviledge Mode Supported */
|
||||
#define PCI_PASID_CTRL 0x06 /* PASID control register */
|
||||
#define PCI_PASID_CTRL_ENABLE 0x01 /* Enable bit */
|
||||
#define PCI_PASID_CTRL_EXEC 0x02 /* Exec permissions Enable */
|
||||
#define PCI_PASID_CTRL_PRIV 0x04 /* Priviledge Mode Enable */
|
||||
|
||||
/* Single Root I/O Virtualization */
|
||||
#define PCI_SRIOV_CAP 0x04 /* SR-IOV Capabilities */
|
||||
|
|
|
@ -113,7 +113,7 @@ int kvm_iommu_map_pages(struct kvm *kvm, struct kvm_memory_slot *slot)
|
|||
|
||||
/* Map into IO address space */
|
||||
r = iommu_map(domain, gfn_to_gpa(gfn), pfn_to_hpa(pfn),
|
||||
get_order(page_size), flags);
|
||||
page_size, flags);
|
||||
if (r) {
|
||||
printk(KERN_ERR "kvm_iommu_map_address:"
|
||||
"iommu failed to map pfn=%llx\n", pfn);
|
||||
|
@ -292,15 +292,15 @@ static void kvm_iommu_put_pages(struct kvm *kvm,
|
|||
|
||||
while (gfn < end_gfn) {
|
||||
unsigned long unmap_pages;
|
||||
int order;
|
||||
size_t size;
|
||||
|
||||
/* Get physical address */
|
||||
phys = iommu_iova_to_phys(domain, gfn_to_gpa(gfn));
|
||||
pfn = phys >> PAGE_SHIFT;
|
||||
|
||||
/* Unmap address from IO address space */
|
||||
order = iommu_unmap(domain, gfn_to_gpa(gfn), 0);
|
||||
unmap_pages = 1ULL << order;
|
||||
size = iommu_unmap(domain, gfn_to_gpa(gfn), PAGE_SIZE);
|
||||
unmap_pages = 1ULL << get_order(size);
|
||||
|
||||
/* Unpin all pages we just unmapped to not leak any memory */
|
||||
kvm_unpin_pages(kvm, pfn, unmap_pages);
|
||||
|
|
Loading…
Reference in New Issue