Merge branch 'core' into amd/amd-vi
This commit is contained in:
@@ -120,8 +120,8 @@ nvkm_device_tegra_probe_iommu(struct nvkm_device_tegra *tdev)
|
||||
mutex_init(&tdev->iommu.mutex);
|
||||
|
||||
if (device_iommu_mapped(dev)) {
|
||||
tdev->iommu.domain = iommu_domain_alloc(&platform_bus_type);
|
||||
if (!tdev->iommu.domain)
|
||||
tdev->iommu.domain = iommu_paging_domain_alloc(dev);
|
||||
if (IS_ERR(tdev->iommu.domain))
|
||||
goto error;
|
||||
|
||||
/*
|
||||
|
||||
@@ -46,6 +46,7 @@ extern int amd_iommu_gpt_level;
|
||||
extern unsigned long amd_iommu_pgsize_bitmap;
|
||||
|
||||
/* Protection domain ops */
|
||||
void amd_iommu_init_identity_domain(void);
|
||||
struct protection_domain *protection_domain_alloc(unsigned int type, int nid);
|
||||
void protection_domain_free(struct protection_domain *domain);
|
||||
struct iommu_domain *amd_iommu_domain_alloc_sva(struct device *dev,
|
||||
@@ -118,9 +119,14 @@ static inline bool check_feature2(u64 mask)
|
||||
return (amd_iommu_efr2 & mask);
|
||||
}
|
||||
|
||||
static inline bool amd_iommu_v2_pgtbl_supported(void)
|
||||
{
|
||||
return (check_feature(FEATURE_GIOSUP) && check_feature(FEATURE_GT));
|
||||
}
|
||||
|
||||
static inline bool amd_iommu_gt_ppr_supported(void)
|
||||
{
|
||||
return (check_feature(FEATURE_GT) &&
|
||||
return (amd_iommu_v2_pgtbl_supported() &&
|
||||
check_feature(FEATURE_PPR) &&
|
||||
check_feature(FEATURE_EPHSUP));
|
||||
}
|
||||
|
||||
@@ -2070,14 +2070,6 @@ static int __init iommu_init_pci(struct amd_iommu *iommu)
|
||||
|
||||
init_iommu_perf_ctr(iommu);
|
||||
|
||||
if (amd_iommu_pgtable == AMD_IOMMU_V2) {
|
||||
if (!check_feature(FEATURE_GIOSUP) ||
|
||||
!check_feature(FEATURE_GT)) {
|
||||
pr_warn("Cannot enable v2 page table for DMA-API. Fallback to v1.\n");
|
||||
amd_iommu_pgtable = AMD_IOMMU_V1;
|
||||
}
|
||||
}
|
||||
|
||||
if (is_rd890_iommu(iommu->dev)) {
|
||||
int i, j;
|
||||
|
||||
@@ -2172,6 +2164,9 @@ static int __init amd_iommu_init_pci(void)
|
||||
struct amd_iommu_pci_seg *pci_seg;
|
||||
int ret;
|
||||
|
||||
/* Init global identity domain before registering IOMMU */
|
||||
amd_iommu_init_identity_domain();
|
||||
|
||||
for_each_iommu(iommu) {
|
||||
ret = iommu_init_pci(iommu);
|
||||
if (ret) {
|
||||
@@ -3087,6 +3082,13 @@ static int __init early_amd_iommu_init(void)
|
||||
FIELD_GET(FEATURE_GATS, amd_iommu_efr) == GUEST_PGTABLE_5_LEVEL)
|
||||
amd_iommu_gpt_level = PAGE_MODE_5_LEVEL;
|
||||
|
||||
if (amd_iommu_pgtable == AMD_IOMMU_V2) {
|
||||
if (!amd_iommu_v2_pgtbl_supported()) {
|
||||
pr_warn("Cannot enable v2 page table for DMA-API. Fallback to v1.\n");
|
||||
amd_iommu_pgtable = AMD_IOMMU_V1;
|
||||
}
|
||||
}
|
||||
|
||||
/* Disable any previously enabled IOMMUs */
|
||||
if (!is_kdump_kernel() || amd_iommu_disabled)
|
||||
disable_iommus();
|
||||
|
||||
+100
-45
@@ -74,6 +74,9 @@ struct kmem_cache *amd_iommu_irq_cache;
|
||||
|
||||
static void detach_device(struct device *dev);
|
||||
|
||||
static int amd_iommu_attach_device(struct iommu_domain *dom,
|
||||
struct device *dev);
|
||||
|
||||
static void set_dte_entry(struct amd_iommu *iommu,
|
||||
struct iommu_dev_data *dev_data);
|
||||
|
||||
@@ -2263,44 +2266,42 @@ void protection_domain_free(struct protection_domain *domain)
|
||||
kfree(domain);
|
||||
}
|
||||
|
||||
static void protection_domain_init(struct protection_domain *domain, int nid)
|
||||
{
|
||||
spin_lock_init(&domain->lock);
|
||||
INIT_LIST_HEAD(&domain->dev_list);
|
||||
INIT_LIST_HEAD(&domain->dev_data_list);
|
||||
domain->iop.pgtbl.cfg.amd.nid = nid;
|
||||
}
|
||||
|
||||
struct protection_domain *protection_domain_alloc(unsigned int type, int nid)
|
||||
{
|
||||
struct io_pgtable_ops *pgtbl_ops;
|
||||
struct protection_domain *domain;
|
||||
int pgtable;
|
||||
|
||||
domain = kzalloc(sizeof(*domain), GFP_KERNEL);
|
||||
if (!domain)
|
||||
return NULL;
|
||||
|
||||
domain->id = domain_id_alloc();
|
||||
if (!domain->id)
|
||||
goto err_free;
|
||||
|
||||
spin_lock_init(&domain->lock);
|
||||
INIT_LIST_HEAD(&domain->dev_list);
|
||||
INIT_LIST_HEAD(&domain->dev_data_list);
|
||||
domain->iop.pgtbl.cfg.amd.nid = nid;
|
||||
|
||||
switch (type) {
|
||||
/* No need to allocate io pgtable ops in passthrough mode */
|
||||
case IOMMU_DOMAIN_IDENTITY:
|
||||
case IOMMU_DOMAIN_SVA:
|
||||
return domain;
|
||||
case IOMMU_DOMAIN_DMA:
|
||||
pgtable = amd_iommu_pgtable;
|
||||
break;
|
||||
/*
|
||||
* Force IOMMU v1 page table when allocating
|
||||
* domain for pass-through devices.
|
||||
*/
|
||||
case IOMMU_DOMAIN_UNMANAGED:
|
||||
pgtable = AMD_IOMMU_V1;
|
||||
break;
|
||||
default:
|
||||
goto err_id;
|
||||
if (!domain->id) {
|
||||
kfree(domain);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
protection_domain_init(domain, nid);
|
||||
|
||||
return domain;
|
||||
}
|
||||
|
||||
static int pdom_setup_pgtable(struct protection_domain *domain,
|
||||
unsigned int type, int pgtable)
|
||||
{
|
||||
struct io_pgtable_ops *pgtbl_ops;
|
||||
|
||||
/* No need to allocate io pgtable ops in passthrough mode */
|
||||
if (!(type & __IOMMU_DOMAIN_PAGING))
|
||||
return 0;
|
||||
|
||||
switch (pgtable) {
|
||||
case AMD_IOMMU_V1:
|
||||
domain->pd_mode = PD_MODE_V1;
|
||||
@@ -2309,25 +2310,20 @@ struct protection_domain *protection_domain_alloc(unsigned int type, int nid)
|
||||
domain->pd_mode = PD_MODE_V2;
|
||||
break;
|
||||
default:
|
||||
goto err_id;
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
pgtbl_ops =
|
||||
alloc_io_pgtable_ops(pgtable, &domain->iop.pgtbl.cfg, domain);
|
||||
if (!pgtbl_ops)
|
||||
goto err_id;
|
||||
return -ENOMEM;
|
||||
|
||||
return domain;
|
||||
err_id:
|
||||
domain_id_free(domain->id);
|
||||
err_free:
|
||||
kfree(domain);
|
||||
return NULL;
|
||||
return 0;
|
||||
}
|
||||
|
||||
static inline u64 dma_max_address(void)
|
||||
static inline u64 dma_max_address(int pgtable)
|
||||
{
|
||||
if (amd_iommu_pgtable == AMD_IOMMU_V1)
|
||||
if (pgtable == AMD_IOMMU_V1)
|
||||
return ~0ULL;
|
||||
|
||||
/* V2 with 4/5 level page table */
|
||||
@@ -2340,11 +2336,13 @@ static bool amd_iommu_hd_support(struct amd_iommu *iommu)
|
||||
}
|
||||
|
||||
static struct iommu_domain *do_iommu_domain_alloc(unsigned int type,
|
||||
struct device *dev, u32 flags)
|
||||
struct device *dev,
|
||||
u32 flags, int pgtable)
|
||||
{
|
||||
bool dirty_tracking = flags & IOMMU_HWPT_ALLOC_DIRTY_TRACKING;
|
||||
struct protection_domain *domain;
|
||||
struct amd_iommu *iommu = NULL;
|
||||
int ret;
|
||||
|
||||
if (dev)
|
||||
iommu = get_amd_iommu_from_dev(dev);
|
||||
@@ -2356,16 +2354,20 @@ static struct iommu_domain *do_iommu_domain_alloc(unsigned int type,
|
||||
if (amd_iommu_snp_en && (type == IOMMU_DOMAIN_IDENTITY))
|
||||
return ERR_PTR(-EINVAL);
|
||||
|
||||
if (dirty_tracking && !amd_iommu_hd_support(iommu))
|
||||
return ERR_PTR(-EOPNOTSUPP);
|
||||
|
||||
domain = protection_domain_alloc(type,
|
||||
dev ? dev_to_node(dev) : NUMA_NO_NODE);
|
||||
if (!domain)
|
||||
return ERR_PTR(-ENOMEM);
|
||||
|
||||
ret = pdom_setup_pgtable(domain, type, pgtable);
|
||||
if (ret) {
|
||||
domain_id_free(domain->id);
|
||||
kfree(domain);
|
||||
return ERR_PTR(ret);
|
||||
}
|
||||
|
||||
domain->domain.geometry.aperture_start = 0;
|
||||
domain->domain.geometry.aperture_end = dma_max_address();
|
||||
domain->domain.geometry.aperture_end = dma_max_address(pgtable);
|
||||
domain->domain.geometry.force_aperture = true;
|
||||
domain->domain.pgsize_bitmap = domain->iop.pgtbl.cfg.pgsize_bitmap;
|
||||
|
||||
@@ -2383,8 +2385,16 @@ static struct iommu_domain *do_iommu_domain_alloc(unsigned int type,
|
||||
static struct iommu_domain *amd_iommu_domain_alloc(unsigned int type)
|
||||
{
|
||||
struct iommu_domain *domain;
|
||||
int pgtable = amd_iommu_pgtable;
|
||||
|
||||
domain = do_iommu_domain_alloc(type, NULL, 0);
|
||||
/*
|
||||
* Force IOMMU v1 page table when allocating
|
||||
* domain for pass-through devices.
|
||||
*/
|
||||
if (type == IOMMU_DOMAIN_UNMANAGED)
|
||||
pgtable = AMD_IOMMU_V1;
|
||||
|
||||
domain = do_iommu_domain_alloc(type, NULL, 0, pgtable);
|
||||
if (IS_ERR(domain))
|
||||
return NULL;
|
||||
|
||||
@@ -2398,11 +2408,36 @@ amd_iommu_domain_alloc_user(struct device *dev, u32 flags,
|
||||
|
||||
{
|
||||
unsigned int type = IOMMU_DOMAIN_UNMANAGED;
|
||||
struct amd_iommu *iommu = NULL;
|
||||
const u32 supported_flags = IOMMU_HWPT_ALLOC_DIRTY_TRACKING |
|
||||
IOMMU_HWPT_ALLOC_PASID;
|
||||
|
||||
if ((flags & ~IOMMU_HWPT_ALLOC_DIRTY_TRACKING) || parent || user_data)
|
||||
if (dev)
|
||||
iommu = get_amd_iommu_from_dev(dev);
|
||||
|
||||
if ((flags & ~supported_flags) || parent || user_data)
|
||||
return ERR_PTR(-EOPNOTSUPP);
|
||||
|
||||
return do_iommu_domain_alloc(type, dev, flags);
|
||||
/* Allocate domain with v2 page table if IOMMU supports PASID. */
|
||||
if (flags & IOMMU_HWPT_ALLOC_PASID) {
|
||||
if (!amd_iommu_pasid_supported())
|
||||
return ERR_PTR(-EOPNOTSUPP);
|
||||
|
||||
return do_iommu_domain_alloc(type, dev, flags, AMD_IOMMU_V2);
|
||||
}
|
||||
|
||||
/* Allocate domain with v1 page table for dirty tracking */
|
||||
if (flags & IOMMU_HWPT_ALLOC_DIRTY_TRACKING) {
|
||||
if (iommu && amd_iommu_hd_support(iommu)) {
|
||||
return do_iommu_domain_alloc(type, dev,
|
||||
flags, AMD_IOMMU_V1);
|
||||
}
|
||||
|
||||
return ERR_PTR(-EOPNOTSUPP);
|
||||
}
|
||||
|
||||
/* If nothing specific is required use the kernel commandline default */
|
||||
return do_iommu_domain_alloc(type, dev, 0, amd_iommu_pgtable);
|
||||
}
|
||||
|
||||
void amd_iommu_domain_free(struct iommu_domain *dom)
|
||||
@@ -2444,6 +2479,25 @@ static struct iommu_domain blocked_domain = {
|
||||
}
|
||||
};
|
||||
|
||||
static struct protection_domain identity_domain;
|
||||
|
||||
static const struct iommu_domain_ops identity_domain_ops = {
|
||||
.attach_dev = amd_iommu_attach_device,
|
||||
};
|
||||
|
||||
void amd_iommu_init_identity_domain(void)
|
||||
{
|
||||
struct iommu_domain *domain = &identity_domain.domain;
|
||||
|
||||
domain->type = IOMMU_DOMAIN_IDENTITY;
|
||||
domain->ops = &identity_domain_ops;
|
||||
domain->owner = &amd_iommu_ops;
|
||||
|
||||
identity_domain.id = domain_id_alloc();
|
||||
|
||||
protection_domain_init(&identity_domain, NUMA_NO_NODE);
|
||||
}
|
||||
|
||||
static int amd_iommu_attach_device(struct iommu_domain *dom,
|
||||
struct device *dev)
|
||||
{
|
||||
@@ -2842,6 +2896,7 @@ static int amd_iommu_dev_disable_feature(struct device *dev,
|
||||
const struct iommu_ops amd_iommu_ops = {
|
||||
.capable = amd_iommu_capable,
|
||||
.blocked_domain = &blocked_domain,
|
||||
.identity_domain = &identity_domain.domain,
|
||||
.domain_alloc = amd_iommu_domain_alloc,
|
||||
.domain_alloc_user = amd_iommu_domain_alloc_user,
|
||||
.domain_alloc_sva = amd_iommu_domain_alloc_sva,
|
||||
|
||||
@@ -3084,7 +3084,8 @@ arm_smmu_domain_alloc_user(struct device *dev, u32 flags,
|
||||
const struct iommu_user_data *user_data)
|
||||
{
|
||||
struct arm_smmu_master *master = dev_iommu_priv_get(dev);
|
||||
const u32 PAGING_FLAGS = IOMMU_HWPT_ALLOC_DIRTY_TRACKING;
|
||||
const u32 PAGING_FLAGS = IOMMU_HWPT_ALLOC_DIRTY_TRACKING |
|
||||
IOMMU_HWPT_ALLOC_PASID;
|
||||
struct arm_smmu_domain *smmu_domain;
|
||||
int ret;
|
||||
|
||||
@@ -3093,6 +3094,9 @@ arm_smmu_domain_alloc_user(struct device *dev, u32 flags,
|
||||
if (parent || user_data)
|
||||
return ERR_PTR(-EOPNOTSUPP);
|
||||
|
||||
if (flags & IOMMU_HWPT_ALLOC_PASID)
|
||||
return arm_smmu_domain_alloc_paging(dev);
|
||||
|
||||
smmu_domain = arm_smmu_domain_alloc();
|
||||
if (IS_ERR(smmu_domain))
|
||||
return ERR_CAST(smmu_domain);
|
||||
|
||||
@@ -34,7 +34,7 @@ static void release_device(struct device *dev)
|
||||
kfree(dev);
|
||||
}
|
||||
|
||||
static struct class iommu_class = {
|
||||
static const struct class iommu_class = {
|
||||
.name = "iommu",
|
||||
.dev_release = release_device,
|
||||
.dev_groups = dev_groups,
|
||||
|
||||
+118
-135
@@ -32,6 +32,7 @@
|
||||
#include <trace/events/iommu.h>
|
||||
#include <linux/sched/mm.h>
|
||||
#include <linux/msi.h>
|
||||
#include <uapi/linux/iommufd.h>
|
||||
|
||||
#include "dma-iommu.h"
|
||||
#include "iommu-priv.h"
|
||||
@@ -93,12 +94,13 @@ static const char * const iommu_group_resv_type_string[] = {
|
||||
static int iommu_bus_notifier(struct notifier_block *nb,
|
||||
unsigned long action, void *data);
|
||||
static void iommu_release_device(struct device *dev);
|
||||
static struct iommu_domain *
|
||||
__iommu_group_domain_alloc(struct iommu_group *group, unsigned int type);
|
||||
static int __iommu_attach_device(struct iommu_domain *domain,
|
||||
struct device *dev);
|
||||
static int __iommu_attach_group(struct iommu_domain *domain,
|
||||
struct iommu_group *group);
|
||||
static struct iommu_domain *__iommu_paging_domain_alloc_flags(struct device *dev,
|
||||
unsigned int type,
|
||||
unsigned int flags);
|
||||
|
||||
enum {
|
||||
IOMMU_SET_DOMAIN_MUST_SUCCEED = 1 << 0,
|
||||
@@ -133,6 +135,8 @@ static struct group_device *iommu_group_alloc_device(struct iommu_group *group,
|
||||
struct device *dev);
|
||||
static void __iommu_group_free_device(struct iommu_group *group,
|
||||
struct group_device *grp_dev);
|
||||
static void iommu_domain_init(struct iommu_domain *domain, unsigned int type,
|
||||
const struct iommu_ops *ops);
|
||||
|
||||
#define IOMMU_GROUP_ATTR(_name, _mode, _show, _store) \
|
||||
struct iommu_group_attribute iommu_group_attr_##_name = \
|
||||
@@ -1141,10 +1145,6 @@ map_end:
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
if (!list_empty(&mappings) && iommu_is_dma_domain(domain))
|
||||
iommu_flush_iotlb_all(domain);
|
||||
|
||||
out:
|
||||
iommu_put_resv_regions(dev, &mappings);
|
||||
|
||||
@@ -1586,12 +1586,59 @@ struct iommu_group *fsl_mc_device_group(struct device *dev)
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(fsl_mc_device_group);
|
||||
|
||||
static struct iommu_domain *__iommu_alloc_identity_domain(struct device *dev)
|
||||
{
|
||||
const struct iommu_ops *ops = dev_iommu_ops(dev);
|
||||
struct iommu_domain *domain;
|
||||
|
||||
if (ops->identity_domain)
|
||||
return ops->identity_domain;
|
||||
|
||||
/* Older drivers create the identity domain via ops->domain_alloc() */
|
||||
if (!ops->domain_alloc)
|
||||
return ERR_PTR(-EOPNOTSUPP);
|
||||
|
||||
domain = ops->domain_alloc(IOMMU_DOMAIN_IDENTITY);
|
||||
if (IS_ERR(domain))
|
||||
return domain;
|
||||
if (!domain)
|
||||
return ERR_PTR(-ENOMEM);
|
||||
|
||||
iommu_domain_init(domain, IOMMU_DOMAIN_IDENTITY, ops);
|
||||
return domain;
|
||||
}
|
||||
|
||||
static struct iommu_domain *
|
||||
__iommu_group_alloc_default_domain(struct iommu_group *group, int req_type)
|
||||
{
|
||||
struct device *dev = iommu_group_first_dev(group);
|
||||
struct iommu_domain *dom;
|
||||
|
||||
if (group->default_domain && group->default_domain->type == req_type)
|
||||
return group->default_domain;
|
||||
return __iommu_group_domain_alloc(group, req_type);
|
||||
|
||||
/*
|
||||
* When allocating the DMA API domain assume that the driver is going to
|
||||
* use PASID and make sure the RID's domain is PASID compatible.
|
||||
*/
|
||||
if (req_type & __IOMMU_DOMAIN_PAGING) {
|
||||
dom = __iommu_paging_domain_alloc_flags(dev, req_type,
|
||||
dev->iommu->max_pasids ? IOMMU_HWPT_ALLOC_PASID : 0);
|
||||
|
||||
/*
|
||||
* If driver does not support PASID feature then
|
||||
* try to allocate non-PASID domain
|
||||
*/
|
||||
if (PTR_ERR(dom) == -EOPNOTSUPP)
|
||||
dom = __iommu_paging_domain_alloc_flags(dev, req_type, 0);
|
||||
|
||||
return dom;
|
||||
}
|
||||
|
||||
if (req_type == IOMMU_DOMAIN_IDENTITY)
|
||||
return __iommu_alloc_identity_domain(dev);
|
||||
|
||||
return ERR_PTR(-EINVAL);
|
||||
}
|
||||
|
||||
/*
|
||||
@@ -1840,31 +1887,6 @@ int bus_iommu_probe(const struct bus_type *bus)
|
||||
return 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* iommu_present() - make platform-specific assumptions about an IOMMU
|
||||
* @bus: bus to check
|
||||
*
|
||||
* Do not use this function. You want device_iommu_mapped() instead.
|
||||
*
|
||||
* Return: true if some IOMMU is present and aware of devices on the given bus;
|
||||
* in general it may not be the only IOMMU, and it may not have anything to do
|
||||
* with whatever device you are ultimately interested in.
|
||||
*/
|
||||
bool iommu_present(const struct bus_type *bus)
|
||||
{
|
||||
bool ret = false;
|
||||
|
||||
for (int i = 0; i < ARRAY_SIZE(iommu_buses); i++) {
|
||||
if (iommu_buses[i] == bus) {
|
||||
spin_lock(&iommu_device_lock);
|
||||
ret = !list_empty(&iommu_device_list);
|
||||
spin_unlock(&iommu_device_lock);
|
||||
}
|
||||
}
|
||||
return ret;
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(iommu_present);
|
||||
|
||||
/**
|
||||
* device_iommu_capable() - check for a general IOMMU capability
|
||||
* @dev: device to which the capability would be relevant, if available
|
||||
@@ -1934,117 +1956,67 @@ void iommu_set_fault_handler(struct iommu_domain *domain,
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(iommu_set_fault_handler);
|
||||
|
||||
static struct iommu_domain *__iommu_domain_alloc(const struct iommu_ops *ops,
|
||||
struct device *dev,
|
||||
unsigned int type)
|
||||
static void iommu_domain_init(struct iommu_domain *domain, unsigned int type,
|
||||
const struct iommu_ops *ops)
|
||||
{
|
||||
struct iommu_domain *domain;
|
||||
unsigned int alloc_type = type & IOMMU_DOMAIN_ALLOC_FLAGS;
|
||||
|
||||
if (alloc_type == IOMMU_DOMAIN_IDENTITY && ops->identity_domain)
|
||||
return ops->identity_domain;
|
||||
else if (alloc_type == IOMMU_DOMAIN_BLOCKED && ops->blocked_domain)
|
||||
return ops->blocked_domain;
|
||||
else if (type & __IOMMU_DOMAIN_PAGING && ops->domain_alloc_paging)
|
||||
domain = ops->domain_alloc_paging(dev);
|
||||
else if (ops->domain_alloc)
|
||||
domain = ops->domain_alloc(alloc_type);
|
||||
else
|
||||
return ERR_PTR(-EOPNOTSUPP);
|
||||
|
||||
/*
|
||||
* Many domain_alloc ops now return ERR_PTR, make things easier for the
|
||||
* driver by accepting ERR_PTR from all domain_alloc ops instead of
|
||||
* having two rules.
|
||||
*/
|
||||
if (IS_ERR(domain))
|
||||
return domain;
|
||||
if (!domain)
|
||||
return ERR_PTR(-ENOMEM);
|
||||
|
||||
domain->type = type;
|
||||
domain->owner = ops;
|
||||
if (!domain->ops)
|
||||
domain->ops = ops->default_domain_ops;
|
||||
|
||||
/*
|
||||
* If not already set, assume all sizes by default; the driver
|
||||
* may override this later
|
||||
*/
|
||||
if (!domain->pgsize_bitmap)
|
||||
domain->pgsize_bitmap = ops->pgsize_bitmap;
|
||||
|
||||
if (!domain->ops)
|
||||
domain->ops = ops->default_domain_ops;
|
||||
|
||||
if (iommu_is_dma_domain(domain)) {
|
||||
int rc;
|
||||
|
||||
rc = iommu_get_dma_cookie(domain);
|
||||
if (rc) {
|
||||
iommu_domain_free(domain);
|
||||
return ERR_PTR(rc);
|
||||
}
|
||||
}
|
||||
return domain;
|
||||
}
|
||||
|
||||
static struct iommu_domain *
|
||||
__iommu_group_domain_alloc(struct iommu_group *group, unsigned int type)
|
||||
__iommu_paging_domain_alloc_flags(struct device *dev, unsigned int type,
|
||||
unsigned int flags)
|
||||
{
|
||||
struct device *dev = iommu_group_first_dev(group);
|
||||
|
||||
return __iommu_domain_alloc(dev_iommu_ops(dev), dev, type);
|
||||
}
|
||||
|
||||
static int __iommu_domain_alloc_dev(struct device *dev, void *data)
|
||||
{
|
||||
const struct iommu_ops **ops = data;
|
||||
|
||||
if (!dev_has_iommu(dev))
|
||||
return 0;
|
||||
|
||||
if (WARN_ONCE(*ops && *ops != dev_iommu_ops(dev),
|
||||
"Multiple IOMMU drivers present for bus %s, which the public IOMMU API can't fully support yet. You will still need to disable one or more for this to work, sorry!\n",
|
||||
dev_bus_name(dev)))
|
||||
return -EBUSY;
|
||||
|
||||
*ops = dev_iommu_ops(dev);
|
||||
return 0;
|
||||
}
|
||||
|
||||
/*
|
||||
* The iommu ops in bus has been retired. Do not use this interface in
|
||||
* new drivers.
|
||||
*/
|
||||
struct iommu_domain *iommu_domain_alloc(const struct bus_type *bus)
|
||||
{
|
||||
const struct iommu_ops *ops = NULL;
|
||||
int err = bus_for_each_dev(bus, NULL, &ops, __iommu_domain_alloc_dev);
|
||||
const struct iommu_ops *ops;
|
||||
struct iommu_domain *domain;
|
||||
|
||||
if (err || !ops)
|
||||
return NULL;
|
||||
|
||||
domain = __iommu_domain_alloc(ops, NULL, IOMMU_DOMAIN_UNMANAGED);
|
||||
if (IS_ERR(domain))
|
||||
return NULL;
|
||||
return domain;
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(iommu_domain_alloc);
|
||||
|
||||
/**
|
||||
* iommu_paging_domain_alloc() - Allocate a paging domain
|
||||
* @dev: device for which the domain is allocated
|
||||
*
|
||||
* Allocate a paging domain which will be managed by a kernel driver. Return
|
||||
* allocated domain if successful, or a ERR pointer for failure.
|
||||
*/
|
||||
struct iommu_domain *iommu_paging_domain_alloc(struct device *dev)
|
||||
{
|
||||
if (!dev_has_iommu(dev))
|
||||
return ERR_PTR(-ENODEV);
|
||||
|
||||
return __iommu_domain_alloc(dev_iommu_ops(dev), dev, IOMMU_DOMAIN_UNMANAGED);
|
||||
ops = dev_iommu_ops(dev);
|
||||
|
||||
if (ops->domain_alloc_paging && !flags)
|
||||
domain = ops->domain_alloc_paging(dev);
|
||||
else if (ops->domain_alloc_user)
|
||||
domain = ops->domain_alloc_user(dev, flags, NULL, NULL);
|
||||
else if (ops->domain_alloc && !flags)
|
||||
domain = ops->domain_alloc(IOMMU_DOMAIN_UNMANAGED);
|
||||
else
|
||||
return ERR_PTR(-EOPNOTSUPP);
|
||||
|
||||
if (IS_ERR(domain))
|
||||
return domain;
|
||||
if (!domain)
|
||||
return ERR_PTR(-ENOMEM);
|
||||
|
||||
iommu_domain_init(domain, type, ops);
|
||||
return domain;
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(iommu_paging_domain_alloc);
|
||||
|
||||
/**
|
||||
* iommu_paging_domain_alloc_flags() - Allocate a paging domain
|
||||
* @dev: device for which the domain is allocated
|
||||
* @flags: Bitmap of iommufd_hwpt_alloc_flags
|
||||
*
|
||||
* Allocate a paging domain which will be managed by a kernel driver. Return
|
||||
* allocated domain if successful, or an ERR pointer for failure.
|
||||
*/
|
||||
struct iommu_domain *iommu_paging_domain_alloc_flags(struct device *dev,
|
||||
unsigned int flags)
|
||||
{
|
||||
return __iommu_paging_domain_alloc_flags(dev,
|
||||
IOMMU_DOMAIN_UNMANAGED, flags);
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(iommu_paging_domain_alloc_flags);
|
||||
|
||||
void iommu_domain_free(struct iommu_domain *domain)
|
||||
{
|
||||
@@ -2216,8 +2188,8 @@ EXPORT_SYMBOL_GPL(iommu_attach_group);
|
||||
|
||||
/**
|
||||
* iommu_group_replace_domain - replace the domain that a group is attached to
|
||||
* @new_domain: new IOMMU domain to replace with
|
||||
* @group: IOMMU group that will be attached to the new domain
|
||||
* @new_domain: new IOMMU domain to replace with
|
||||
*
|
||||
* This API allows the group to switch domains without being forced to go to
|
||||
* the blocking domain in-between.
|
||||
@@ -2965,6 +2937,14 @@ static int iommu_setup_default_domain(struct iommu_group *group,
|
||||
if (group->default_domain == dom)
|
||||
return 0;
|
||||
|
||||
if (iommu_is_dma_domain(dom)) {
|
||||
ret = iommu_get_dma_cookie(dom);
|
||||
if (ret) {
|
||||
iommu_domain_free(dom);
|
||||
return ret;
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* IOMMU_RESV_DIRECT and IOMMU_RESV_DIRECT_RELAXABLE regions must be
|
||||
* mapped before their device is attached, in order to guarantee
|
||||
@@ -3152,22 +3132,25 @@ void iommu_device_unuse_default_domain(struct device *dev)
|
||||
|
||||
static int __iommu_group_alloc_blocking_domain(struct iommu_group *group)
|
||||
{
|
||||
struct device *dev = iommu_group_first_dev(group);
|
||||
const struct iommu_ops *ops = dev_iommu_ops(dev);
|
||||
struct iommu_domain *domain;
|
||||
|
||||
if (group->blocking_domain)
|
||||
return 0;
|
||||
|
||||
domain = __iommu_group_domain_alloc(group, IOMMU_DOMAIN_BLOCKED);
|
||||
if (IS_ERR(domain)) {
|
||||
/*
|
||||
* For drivers that do not yet understand IOMMU_DOMAIN_BLOCKED
|
||||
* create an empty domain instead.
|
||||
*/
|
||||
domain = __iommu_group_domain_alloc(group,
|
||||
IOMMU_DOMAIN_UNMANAGED);
|
||||
if (IS_ERR(domain))
|
||||
return PTR_ERR(domain);
|
||||
if (ops->blocked_domain) {
|
||||
group->blocking_domain = ops->blocked_domain;
|
||||
return 0;
|
||||
}
|
||||
|
||||
/*
|
||||
* For drivers that do not yet understand IOMMU_DOMAIN_BLOCKED create an
|
||||
* empty PAGING domain instead.
|
||||
*/
|
||||
domain = iommu_paging_domain_alloc(dev);
|
||||
if (IS_ERR(domain))
|
||||
return PTR_ERR(domain);
|
||||
group->blocking_domain = domain;
|
||||
return 0;
|
||||
}
|
||||
|
||||
@@ -78,9 +78,10 @@ int tegra_vde_iommu_init(struct tegra_vde *vde)
|
||||
arm_iommu_release_mapping(mapping);
|
||||
}
|
||||
#endif
|
||||
vde->domain = iommu_domain_alloc(&platform_bus_type);
|
||||
if (!vde->domain) {
|
||||
err = -ENOMEM;
|
||||
vde->domain = iommu_paging_domain_alloc(dev);
|
||||
if (IS_ERR(vde->domain)) {
|
||||
err = PTR_ERR(vde->domain);
|
||||
vde->domain = NULL;
|
||||
goto put_group;
|
||||
}
|
||||
|
||||
|
||||
@@ -109,10 +109,10 @@ static int rproc_enable_iommu(struct rproc *rproc)
|
||||
return 0;
|
||||
}
|
||||
|
||||
domain = iommu_domain_alloc(dev->bus);
|
||||
if (!domain) {
|
||||
domain = iommu_paging_domain_alloc(dev);
|
||||
if (IS_ERR(domain)) {
|
||||
dev_err(dev, "can't alloc iommu domain\n");
|
||||
return -ENOMEM;
|
||||
return PTR_ERR(domain);
|
||||
}
|
||||
|
||||
iommu_set_fault_handler(domain, rproc_iommu_fault, rproc);
|
||||
|
||||
+8
-12
@@ -511,8 +511,6 @@ static inline int __iommu_copy_struct_from_user_array(
|
||||
* the caller iommu_domain_alloc() returns.
|
||||
* @domain_alloc_user: Allocate an iommu domain corresponding to the input
|
||||
* parameters as defined in include/uapi/linux/iommufd.h.
|
||||
* Unlike @domain_alloc, it is called only by IOMMUFD and
|
||||
* must fully initialize the new domain before return.
|
||||
* Upon success, if the @user_data is valid and the @parent
|
||||
* points to a kernel-managed domain, the new domain must be
|
||||
* IOMMU_DOMAIN_NESTED type; otherwise, the @parent must be
|
||||
@@ -785,11 +783,13 @@ static inline void iommu_iotlb_gather_init(struct iommu_iotlb_gather *gather)
|
||||
}
|
||||
|
||||
extern int bus_iommu_probe(const struct bus_type *bus);
|
||||
extern bool iommu_present(const struct bus_type *bus);
|
||||
extern bool device_iommu_capable(struct device *dev, enum iommu_cap cap);
|
||||
extern bool iommu_group_has_isolated_msi(struct iommu_group *group);
|
||||
extern struct iommu_domain *iommu_domain_alloc(const struct bus_type *bus);
|
||||
struct iommu_domain *iommu_paging_domain_alloc(struct device *dev);
|
||||
struct iommu_domain *iommu_paging_domain_alloc_flags(struct device *dev, unsigned int flags);
|
||||
static inline struct iommu_domain *iommu_paging_domain_alloc(struct device *dev)
|
||||
{
|
||||
return iommu_paging_domain_alloc_flags(dev, 0);
|
||||
}
|
||||
extern void iommu_domain_free(struct iommu_domain *domain);
|
||||
extern int iommu_attach_device(struct iommu_domain *domain,
|
||||
struct device *dev);
|
||||
@@ -1081,19 +1081,15 @@ struct iommu_iotlb_gather {};
|
||||
struct iommu_dirty_bitmap {};
|
||||
struct iommu_dirty_ops {};
|
||||
|
||||
static inline bool iommu_present(const struct bus_type *bus)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
static inline bool device_iommu_capable(struct device *dev, enum iommu_cap cap)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
static inline struct iommu_domain *iommu_domain_alloc(const struct bus_type *bus)
|
||||
static inline struct iommu_domain *iommu_paging_domain_alloc_flags(struct device *dev,
|
||||
unsigned int flags)
|
||||
{
|
||||
return NULL;
|
||||
return ERR_PTR(-ENODEV);
|
||||
}
|
||||
|
||||
static inline struct iommu_domain *iommu_paging_domain_alloc(struct device *dev)
|
||||
|
||||
@@ -359,11 +359,19 @@ struct iommu_vfio_ioas {
|
||||
* enforced on device attachment
|
||||
* @IOMMU_HWPT_FAULT_ID_VALID: The fault_id field of hwpt allocation data is
|
||||
* valid.
|
||||
* @IOMMU_HWPT_ALLOC_PASID: Requests a domain that can be used with PASID. The
|
||||
* domain can be attached to any PASID on the device.
|
||||
* Any domain attached to the non-PASID part of the
|
||||
* device must also be flaged, otherwise attaching a
|
||||
* PASID will blocked.
|
||||
* If IOMMU does not support PASID it will return
|
||||
* error (-EOPNOTSUPP).
|
||||
*/
|
||||
enum iommufd_hwpt_alloc_flags {
|
||||
IOMMU_HWPT_ALLOC_NEST_PARENT = 1 << 0,
|
||||
IOMMU_HWPT_ALLOC_DIRTY_TRACKING = 1 << 1,
|
||||
IOMMU_HWPT_FAULT_ID_VALID = 1 << 2,
|
||||
IOMMU_HWPT_ALLOC_PASID = 1 << 3,
|
||||
};
|
||||
|
||||
/**
|
||||
|
||||
Reference in New Issue
Block a user