iommu: arm-smmu: Add atos self test

Add atos self test to perform atos for different sids. To enable,
set CONFIG_ARM_SMMU_SELFTEST and arm_smmu.selftest=1 and set the
sids and masks in arm_smmu.selftestsids in the below format
smmu_name,no_of_sids,sid:mask.

for eg: arm_smmu.selftestsids=kgsl,0x1,0x7:0x400,apps,0x2,
	0x1:0x0,0x51f:0x0

Change-Id: I74134ed712e06a17b1570256e2ee3820df2f4457
Signed-off-by: Charan Teja Reddy <charante@codeaurora.org>
Signed-off-by: Prakash Gupta <guptap@codeaurora.org>
Signed-off-by: Vijayanand Jitta <vjitta@codeaurora.org>
This commit is contained in:
Prakash Gupta 2018-12-03 18:49:03 +05:30 committed by Vijayanand Jitta
parent 70f32294d6
commit b86d7318eb
4 changed files with 351 additions and 22 deletions

View file

@ -417,7 +417,6 @@ struct qsmmuv500_archdata {
u32 actlr_tbl_size;
u32 testbus_version;
};
#define get_qsmmuv500_archdata(smmu) \
((struct qsmmuv500_archdata *)(smmu->archdata))
@ -491,6 +490,7 @@ static int arm_smmu_setup_default_domain(struct device *dev,
struct iommu_domain *domain);
static int __arm_smmu_domain_set_attr(struct iommu_domain *domain,
enum iommu_attr attr, void *data);
struct iommu_device *get_iommu_by_fwnode(struct fwnode_handle *fwnode);
static struct arm_smmu_domain *to_smmu_domain(struct iommu_domain *dom)
{
@ -585,12 +585,37 @@ static void arm_smmu_secure_domain_unlock(struct arm_smmu_domain *smmu_domain)
mutex_unlock(&smmu_domain->assign_lock);
}
static struct qsmmuv500_tbu_device *qsmmuv500_find_tbu(
struct arm_smmu_device *smmu, u32 sid)
{
struct qsmmuv500_tbu_device *tbu = NULL;
struct qsmmuv500_archdata *data = get_qsmmuv500_archdata(smmu);
list_for_each_entry(tbu, &data->tbus, list) {
if (tbu->sid_start <= sid &&
sid < tbu->sid_start + tbu->num_sids)
return tbu;
}
return NULL;
}
static bool selftest_running;
#ifdef CONFIG_ARM_SMMU_SELFTEST
struct sme_pair {
u32 num_smrs;
struct arm_smmu_smr *smrs;
};
static int selftest;
module_param_named(selftest, selftest, int, 0644);
static int irq_count;
#define MAXLEN 1000
static char selftestsids[MAXLEN];
module_param_string(selftestsids, selftestsids, sizeof(selftestsids), 0644);
static DECLARE_WAIT_QUEUE_HEAD(wait_int);
static irqreturn_t arm_smmu_cf_selftest(int irq, void *cb_base)
{
@ -681,10 +706,273 @@ static void arm_smmu_interrupt_selftest(struct arm_smmu_device *smmu)
WARN_ON(cb_count != irq_count);
irq_count = 0;
}
static int arm_smmu_find_sme(struct arm_smmu_smr *, u32, u16, u16);
static int arm_smmu_run_atos(struct device *dev)
{
dma_addr_t iova;
phys_addr_t phys, output, phys_soft;
struct page *page = NULL;
struct iommu_domain *domain;
int ret = 0;
page = alloc_page(GFP_KERNEL);
if (!page) {
dev_err(dev, "Unable to allocate memory\n");
return -ENOMEM;
}
phys = page_to_phys(page);
domain = iommu_get_domain_for_dev(dev);
domain->is_debug_domain = true;
iova = 0x1000;
if (iommu_map(domain, iova, phys, SZ_4K,
IOMMU_READ | IOMMU_WRITE)) {
dev_err(dev, "Mapping failed\n");
goto out_detach;
}
output = iommu_iova_to_phys_hard(domain, iova, IOMMU_TRANS_DEFAULT);
if (!output || output != phys) {
phys_soft = arm_smmu_iova_to_phys(domain, iova);
dev_err(dev, "atos is failed, output : %pa\n", &output);
dev_err(dev, "soft iova-to-phys : %pa\n", &phys_soft);
} else
dev_err(dev, "atos succeeded, output : %pa\n", &output);
iommu_unmap(domain, iova, SZ_4K);
out_detach:
__free_pages(page, 0);
return ret;
}
static int of_iommu_do_atos(struct device *dev, struct sme_pair *sme,
struct of_phandle_args *iommu_spec)
{
u16 i;
int err = 0;
bool set_iommu_ops = false;
const struct iommu_ops *ops = NULL;
for (i = 0; i < sme->num_smrs; ++i) {
struct arm_smmu_smr *smr;
smr = &sme->smrs[i];
if (!smr->valid) {
dev_info(dev, "Can't run atos smr idx %d\n", i);
continue;
}
iommu_spec->args[0] = smr->id;
iommu_spec->args[1] = smr->mask;
dev_dbg(dev, "ATOS for : SID 0x%x, MASK 0x%x\n",
iommu_spec->args[0], iommu_spec->args[1]);
err = of_iommu_fill_fwspec(dev, iommu_spec);
if (err) {
dev_err(dev, "Failed to do the of_iommu_xlate\n");
break;
}
ops = dev->iommu_fwspec->ops;
if (!platform_bus_type.iommu_ops) {
platform_bus_type.iommu_ops = ops;
set_iommu_ops = true;
}
if (ops && ops->add_device && dev->bus && !dev->iommu_group)
err = ops->add_device(dev);
if (err) {
dev_err(dev, "Adding to IOMMU failed: %d\n", err);
return err;
}
/* Now we have every thing. Run ATOS. */
arm_smmu_run_atos(dev);
if (ops->remove_device && dev->iommu_group)
ops->remove_device(dev);
if (set_iommu_ops)
platform_bus_type.iommu_ops = NULL;
}
return err;
}
static bool arm_smmu_valid_smr(struct arm_smmu_device *smmu, u32 idx,
u32 sid, u32 mask)
{
u32 smr1, smr2;
void __iomem *gr0_smr = ARM_SMMU_GR0(smmu) + ARM_SMMU_GR0_SMR(idx);
smr1 = SMR_VALID | sid << SMR_ID_SHIFT | mask << SMR_MASK_SHIFT;
writel_relaxed(smr1, gr0_smr);
smr2 = readl_relaxed(gr0_smr);
writel_relaxed(0, gr0_smr);
return smr1 == smr2;
}
static int get_atos_selftest_sids(struct arm_smmu_device *smmu,
struct sme_pair *sme)
{
struct device *dev = smmu->dev;
struct arm_smmu_smr *smrs = smmu->smrs;
struct arm_smmu_smr *selftest_smrs;
enum arm_smmu_implementation model;
struct qsmmuv500_tbu_device *tbu;
int i, idx, sid_count, ret = 0;
char *name, *buf, *split, *sid, *buf_start;
buf = kstrdup(selftestsids, GFP_KERNEL);
buf_start = buf;
while (buf) {
name = strsep(&buf, ",");
if (strnstr(dev_name(dev), name, strlen(dev_name(dev)))) {
kstrtoint(strsep(&buf, ","), 0, &sid_count);
if (sid_count <= 0) {
dev_err(smmu->dev, "Invalid sid_count : %d\n",
sid_count);
goto out;
}
sme->smrs = kcalloc(sid_count, sizeof(*smmu->smrs),
GFP_KERNEL);
if (!sme->smrs) {
ret = -ENOMEM;
goto out;
}
selftest_smrs = sme->smrs;
for (i = 0; i < sid_count; i++) {
split = strsep(&buf, ",");
sid = strsep(&split, ":");
if (!split) {
ret = -EINVAL;
goto invalid_format;
}
kstrtou16(sid, 0,
&selftest_smrs[i].id);
kstrtou16(split, 0, &selftest_smrs[i].mask);
}
sme->num_smrs = sid_count;
for (i = 0; i < sid_count; i++) {
mutex_lock(&smmu->stream_map_mutex);
idx = arm_smmu_find_sme(smrs,
smmu->num_mapping_groups,
selftest_smrs[i].id,
selftest_smrs[i].mask);
mutex_unlock(&smmu->stream_map_mutex);
if (idx < 0) {
selftest_smrs[i].valid = false;
} else if ((idx >= 0) && smrs &&
(smrs[idx].valid)) {
dev_err(dev,
"sid : 0x%x is already present at idx = %d choose a different sid\n",
selftest_smrs[i].id, idx);
selftest_smrs[i].valid = false;
} else {
if (!arm_smmu_valid_smr(smmu, idx,
selftest_smrs[i].id,
selftest_smrs[i].mask))
selftest_smrs[i].valid = false;
else
selftest_smrs[i].valid = true;
}
model = smmu->model;
switch (model) {
case QCOM_SMMUV500:
tbu = qsmmuv500_find_tbu(smmu,
selftest_smrs[i].id);
dev_info(tbu->dev, "idx = %d valid: %d, sid : 0x%x, mask: 0x%x\n",
idx,
selftest_smrs[i].valid,
selftest_smrs[i].id,
selftest_smrs[i].mask);
break;
case QCOM_SMMUV2:
dev_info(smmu->dev, "idx = %d valid: %d, sid : 0x%x, mask: 0x%x\n",
idx,
selftest_smrs[i].valid,
selftest_smrs[i].id,
selftest_smrs[i].mask);
break;
default:
ret = -EINVAL;
goto out;
}
}
}
}
ret = sid_count;
goto out;
invalid_format:
dev_err(smmu->dev, "Invalid Format : <%s> Expected Format : <smmu_name,sid_count,sid:mask>\n",
selftestsids);
kfree(sme->smrs);
out:
kfree(buf_start);
return ret;
}
static void arm_smmu_atos_selftest(struct arm_smmu_device *smmu)
{
struct platform_device *pdev;
struct device *smmu_dev = smmu->dev;
struct device *atos_dev;
struct of_phandle_args iommu_spec = {0};
struct sme_pair sme = {0};
int ret;
if (!selftest)
return;
dev_notice(smmu_dev, "ATOS Self test started\n");
ret = get_atos_selftest_sids(smmu, &sme);
if (ret <= 0) {
dev_err(smmu_dev, "ATOS Self test failed ret %d!!\n", ret);
return;
}
pdev = platform_device_register_simple("atos_test_device",
-1, NULL, 0);
if (!pdev) {
dev_err(smmu_dev, "Unable to create a atos test device\n");
return;
}
atos_dev = &pdev->dev;
/* try to fill the iommu_fwspec to use. */
iommu_spec.np = of_node_get(smmu_dev->of_node);
iommu_spec.args_count = (smmu->model == QCOM_SMMUV2) ? 1 : 2;
selftest_running = true;
of_iommu_do_atos(atos_dev, &sme, &iommu_spec);
selftest_running = false;
dev_notice(smmu_dev, "ATOS Self test complete\n");
kfree(sme.smrs);
of_node_put(iommu_spec.np);
platform_device_unregister(pdev);
}
#else
static void arm_smmu_interrupt_selftest(struct arm_smmu_device *smmu)
{
}
static void arm_smmu_atos_selftest(struct arm_smmu_device *smmu)
{
}
#endif
/*
@ -1156,20 +1444,6 @@ static void arm_smmu_domain_power_off(struct iommu_domain *domain,
arm_smmu_power_off(smmu->pwr);
}
static struct qsmmuv500_tbu_device *qsmmuv500_find_tbu(
struct arm_smmu_device *smmu, u32 sid)
{
struct qsmmuv500_tbu_device *tbu = NULL;
struct qsmmuv500_archdata *data = get_qsmmuv500_archdata(smmu);
list_for_each_entry(tbu, &data->tbus, list) {
if (tbu->sid_start <= sid &&
sid < tbu->sid_start + tbu->num_sids)
return tbu;
}
return NULL;
}
static void arm_smmu_testbus_dump(struct arm_smmu_device *smmu, u16 sid)
{
if (smmu->model == QCOM_SMMUV500 &&
@ -2616,9 +2890,9 @@ static void arm_smmu_test_smr_masks(struct arm_smmu_device *smmu)
smmu->smr_mask_mask = smr >> SMR_MASK_SHIFT;
}
static int arm_smmu_find_sme(struct arm_smmu_device *smmu, u16 id, u16 mask)
static int arm_smmu_find_sme(struct arm_smmu_smr *smrs, u32 count, u16 id,
u16 mask)
{
struct arm_smmu_smr *smrs = smmu->smrs;
int i, free_idx = -ENOSPC;
/* Stream indexing is blissfully easy */
@ -2626,7 +2900,7 @@ static int arm_smmu_find_sme(struct arm_smmu_device *smmu, u16 id, u16 mask)
return id;
/* Validating SMRs is... less so */
for (i = 0; i < smmu->num_mapping_groups; ++i) {
for (i = 0; i < count; ++i) {
if (!smrs[i].valid) {
/*
* Note the first free entry we come across, which
@ -2691,7 +2965,8 @@ static int arm_smmu_master_alloc_smes(struct device *dev)
goto sme_err;
}
ret = arm_smmu_find_sme(smmu, sid, mask);
ret = arm_smmu_find_sme(smrs, smmu->num_mapping_groups, sid,
mask);
if (ret < 0)
goto sme_err;
@ -3486,7 +3761,8 @@ static phys_addr_t arm_smmu_iova_to_phys_hard(struct iommu_domain *domain,
struct arm_smmu_device *smmu = smmu_domain->smmu;
if (smmu->options & ARM_SMMU_OPT_DISABLE_ATOS)
return 0;
if (!selftest_running)
return 0;
if (arm_smmu_power_on(smmu_domain->smmu->pwr))
return 0;
@ -3613,13 +3889,28 @@ static int arm_smmu_add_device(struct device *dev)
if (ret)
goto out_free;
} else if (fwspec && fwspec->ops == &arm_smmu_ops) {
struct fwnode_handle *iommu_fwnode = fwspec->iommu_fwnode;
smmu = arm_smmu_get_by_fwnode(fwspec->iommu_fwnode);
if (!smmu)
if (!smmu) {
if (IS_ENABLED(CONFIG_ARM_SMMU_SELFTEST)) {
struct iommu_device *iommu = NULL;
iommu = get_iommu_by_fwnode(iommu_fwnode);
smmu = iommu ? container_of(iommu, struct
arm_smmu_device,
iommu) : NULL;
if (smmu)
goto cont;
}
return -ENODEV;
}
} else {
return -ENODEV;
}
cont:
ret = arm_smmu_power_on(smmu->pwr);
if (ret)
goto out_free;
@ -5377,6 +5668,7 @@ static int arm_smmu_device_dt_probe(struct platform_device *pdev)
arm_smmu_device_reset(smmu);
arm_smmu_test_smr_masks(smmu);
arm_smmu_interrupt_selftest(smmu);
arm_smmu_atos_selftest(smmu);
arm_smmu_power_off(smmu->pwr);
/*
@ -5657,7 +5949,6 @@ static void qsmmuv500_tbu_resume(struct qsmmuv500_tbu_device *tbu)
spin_unlock_irqrestore(&tbu->halt_lock, flags);
}
static int qsmmuv500_ecats_lock(struct arm_smmu_domain *smmu_domain,
struct qsmmuv500_tbu_device *tbu,
unsigned long *flags)

View file

@ -102,6 +102,29 @@ int iommu_device_register(struct iommu_device *iommu)
return 0;
}
#ifdef CONFIG_ARM_SMMU_SELFTEST
struct iommu_device *get_iommu_by_fwnode(struct fwnode_handle *fwnode)
{
struct iommu_device *iommu;
spin_lock(&iommu_device_lock);
list_for_each_entry(iommu, &iommu_device_list, list) {
if (iommu->fwnode == fwnode) {
spin_unlock(&iommu_device_lock);
return iommu;
}
}
spin_unlock(&iommu_device_lock);
return NULL;
}
#else
struct iommu_device *get_iommu_by_fwnode(struct fwnode_handle *fwnode)
{
return NULL;
}
#endif
void iommu_device_unregister(struct iommu_device *iommu)
{
spin_lock(&iommu_device_lock);

View file

@ -222,3 +222,15 @@ const struct iommu_ops *of_iommu_configure(struct device *dev,
return ops;
}
#ifdef CONFIG_ARM_SMMU_SELFTEST
int of_iommu_fill_fwspec(struct device *dev, struct of_phandle_args *iommu_spec)
{
return of_iommu_xlate(dev, iommu_spec);
}
#else
int of_iommu_fill_fwspec(struct device *dev, struct of_phandle_args *iommu_spec)
{
return 0;
}
#endif

View file

@ -15,6 +15,9 @@ extern int of_get_dma_window(struct device_node *dn, const char *prefix,
extern const struct iommu_ops *of_iommu_configure(struct device *dev,
struct device_node *master_np);
extern int of_iommu_fill_fwspec(struct device *dev, struct of_phandle_args
*iommu_spec);
#else
static inline int of_get_dma_window(struct device_node *dn, const char *prefix,