mirror of
https://github.com/BobTheBlinker/android_kernel_motorola_sm6375.git
synced 2026-10-09 13:49:24 -04:00
Merge "iommu/arm-smmu: replicate faulty transaction"
This commit is contained in:
commit
4d3d7cf067
5 changed files with 107 additions and 37 deletions
|
|
@ -152,7 +152,7 @@ static struct arm_smmu_option_prop arm_smmu_options[] = {
|
|||
static phys_addr_t arm_smmu_iova_to_phys(struct iommu_domain *domain,
|
||||
dma_addr_t iova);
|
||||
static phys_addr_t arm_smmu_iova_to_phys_hard(struct iommu_domain *domain,
|
||||
dma_addr_t iova);
|
||||
dma_addr_t iova, unsigned long trans_flags);
|
||||
static void arm_smmu_destroy_domain_context(struct iommu_domain *domain);
|
||||
|
||||
static int arm_smmu_prepare_pgtable(void *addr, void *cookie);
|
||||
|
|
@ -393,7 +393,8 @@ struct arm_smmu_arch_ops {
|
|||
int (*init)(struct arm_smmu_device *smmu);
|
||||
void (*device_reset)(struct arm_smmu_device *smmu);
|
||||
phys_addr_t (*iova_to_phys_hard)(struct arm_smmu_domain *smmu_domain,
|
||||
dma_addr_t iova);
|
||||
dma_addr_t iova,
|
||||
unsigned long trans_flags);
|
||||
void (*init_context_bank)(struct arm_smmu_domain *smmu_domain,
|
||||
struct device *dev);
|
||||
int (*device_group)(struct device *dev, struct iommu_group *group);
|
||||
|
|
@ -1319,25 +1320,57 @@ static void print_ctx_regs(struct arm_smmu_device *smmu, struct arm_smmu_cfg
|
|||
}
|
||||
|
||||
static phys_addr_t arm_smmu_verify_fault(struct iommu_domain *domain,
|
||||
dma_addr_t iova, u32 fsr)
|
||||
dma_addr_t iova, u32 fsr, u32 fsynr0)
|
||||
{
|
||||
struct arm_smmu_domain *smmu_domain = to_smmu_domain(domain);
|
||||
struct arm_smmu_device *smmu = smmu_domain->smmu;
|
||||
struct msm_io_pgtable_info *pgtbl_info = &smmu_domain->pgtbl_info;
|
||||
phys_addr_t phys;
|
||||
phys_addr_t phys_post_tlbiall;
|
||||
phys_addr_t phys_hard_priv = 0;
|
||||
phys_addr_t phys_stimu, phys_stimu_post_tlbiall;
|
||||
unsigned long flags = 0;
|
||||
|
||||
phys = arm_smmu_iova_to_phys_hard(domain, iova);
|
||||
/* Get the transaction type */
|
||||
if (fsynr0 & FSYNR0_WNR)
|
||||
flags |= IOMMU_TRANS_WRITE;
|
||||
if (fsynr0 & FSYNR0_PNU)
|
||||
flags |= IOMMU_TRANS_PRIV;
|
||||
if (fsynr0 & FSYNR0_IND)
|
||||
flags |= IOMMU_TRANS_INST;
|
||||
|
||||
/* Now replicate the faulty transaction */
|
||||
phys_stimu = arm_smmu_iova_to_phys_hard(domain, iova, flags);
|
||||
|
||||
/*
|
||||
* If the replicated transaction fails, it could be due to legitimate
|
||||
* unmapped access (translation fault) or stale TLB with insufficient
|
||||
* privileges (permission fault). Try ATOS operation with full access
|
||||
* privileges to rule out stale entry with insufficient privileges case.
|
||||
*/
|
||||
if (!phys_stimu)
|
||||
phys_hard_priv = arm_smmu_iova_to_phys_hard(domain, iova,
|
||||
IOMMU_TRANS_DEFAULT |
|
||||
IOMMU_TRANS_PRIV);
|
||||
|
||||
/* Now replicate the faulty transaction post tlbiall */
|
||||
pgtbl_info->pgtbl_cfg.tlb->tlb_flush_all(smmu_domain);
|
||||
phys_post_tlbiall = arm_smmu_iova_to_phys_hard(domain, iova);
|
||||
phys_stimu_post_tlbiall = arm_smmu_iova_to_phys_hard(domain, iova,
|
||||
flags);
|
||||
|
||||
if (phys != phys_post_tlbiall) {
|
||||
if (!phys_stimu && phys_hard_priv) {
|
||||
dev_err(smmu->dev,
|
||||
"ATOS results differed across TLBIALL...\n"
|
||||
"Before: %pa After: %pa\n", &phys, &phys_post_tlbiall);
|
||||
"ATOS results differed across access privileges...\n"
|
||||
"Before: %pa After: %pa\n",
|
||||
&phys_stimu, &phys_hard_priv);
|
||||
}
|
||||
|
||||
return (phys == 0 ? phys_post_tlbiall : phys);
|
||||
if (phys_stimu != phys_stimu_post_tlbiall) {
|
||||
dev_err(smmu->dev,
|
||||
"ATOS results differed across TLBIALL...\n"
|
||||
"Before: %pa After: %pa\n", &phys_stimu,
|
||||
&phys_stimu_post_tlbiall);
|
||||
}
|
||||
|
||||
return (phys_stimu == 0 ? phys_stimu_post_tlbiall : phys_stimu);
|
||||
}
|
||||
|
||||
static irqreturn_t arm_smmu_context_fault(int irq, void *dev)
|
||||
|
|
@ -1412,7 +1445,8 @@ static irqreturn_t arm_smmu_context_fault(int irq, void *dev)
|
|||
phys_addr_t phys_atos;
|
||||
|
||||
print_ctx_regs(smmu, cfg, fsr);
|
||||
phys_atos = arm_smmu_verify_fault(domain, iova, fsr);
|
||||
phys_atos = arm_smmu_verify_fault(domain, iova, fsr,
|
||||
fsynr0);
|
||||
dev_err(smmu->dev,
|
||||
"Unhandled context fault: iova=0x%08lx, cb=%d, fsr=0x%x, fsynr0=0x%x, fsynr1=0x%x\n",
|
||||
iova, cfg->cbndx, fsr, fsynr0, fsynr1);
|
||||
|
|
@ -3177,7 +3211,7 @@ static phys_addr_t arm_smmu_iova_to_phys(struct iommu_domain *domain,
|
|||
* original iova_to_phys() op.
|
||||
*/
|
||||
static phys_addr_t arm_smmu_iova_to_phys_hard(struct iommu_domain *domain,
|
||||
dma_addr_t iova)
|
||||
dma_addr_t iova, unsigned long trans_flags)
|
||||
{
|
||||
phys_addr_t ret = 0;
|
||||
unsigned long flags;
|
||||
|
|
@ -3193,7 +3227,7 @@ static phys_addr_t arm_smmu_iova_to_phys_hard(struct iommu_domain *domain,
|
|||
if (smmu_domain->smmu->arch_ops &&
|
||||
smmu_domain->smmu->arch_ops->iova_to_phys_hard) {
|
||||
ret = smmu_domain->smmu->arch_ops->iova_to_phys_hard(
|
||||
smmu_domain, iova);
|
||||
smmu_domain, iova, trans_flags);
|
||||
goto out;
|
||||
}
|
||||
|
||||
|
|
@ -4989,7 +5023,13 @@ module_exit(arm_smmu_exit);
|
|||
#define DEBUG_TXN_TRIGG_REG 0x18
|
||||
#define DEBUG_TXN_AXPROT GENMASK(8, 6)
|
||||
#define DEBUG_TXN_AXCACHE GENMASK(5, 2)
|
||||
#define DEBUG_TRX_WRITE (0x1 << 1)
|
||||
#define DEBUG_TXN_WRITE BIT(1)
|
||||
#define DEBUG_TXN_AXPROT_PRIV 0x1
|
||||
#define DEBUG_TXN_AXPROT_UNPRIV 0x0
|
||||
#define DEBUG_TXN_AXPROT_NSEC 0x2
|
||||
#define DEBUG_TXN_AXPROT_SEC 0x0
|
||||
#define DEBUG_TXN_AXPROT_INST 0x4
|
||||
#define DEBUG_TXN_AXPROT_DATA 0x0
|
||||
#define DEBUG_TXN_READ (0x0 << 1)
|
||||
#define DEBUG_TXN_TRIGGER BIT(0)
|
||||
|
||||
|
|
@ -5417,7 +5457,8 @@ static void qsmmuv500_ecats_unlock(struct arm_smmu_domain *smmu_domain,
|
|||
* Zero means failure.
|
||||
*/
|
||||
static phys_addr_t qsmmuv500_iova_to_phys(
|
||||
struct arm_smmu_domain *smmu_domain, dma_addr_t iova, u32 sid)
|
||||
struct arm_smmu_domain *smmu_domain, dma_addr_t iova, u32 sid,
|
||||
unsigned long trans_flags)
|
||||
{
|
||||
struct arm_smmu_device *smmu = smmu_domain->smmu;
|
||||
struct arm_smmu_cfg *cfg = &smmu_domain->cfg;
|
||||
|
|
@ -5492,14 +5533,25 @@ redo:
|
|||
val = FIELD_PREP(DEBUG_AXUSER_CDMID, DEBUG_AXUSER_CDMID_VAL);
|
||||
writeq_relaxed(val, tbu->base + DEBUG_AXUSER_REG);
|
||||
|
||||
/*
|
||||
* Write-back Read and Write-Allocate
|
||||
* Priviledged, nonsecure, data transaction
|
||||
* Read operation.
|
||||
*/
|
||||
val = FIELD_PREP(DEBUG_TXN_AXCACHE, 0xF) |
|
||||
FIELD_PREP(DEBUG_TXN_AXPROT, 0x3) |
|
||||
DEBUG_TXN_TRIGGER;
|
||||
/* Write-back Read and Write-Allocate */
|
||||
val = FIELD_PREP(DEBUG_TXN_AXCACHE, 0xF);
|
||||
|
||||
/* Non-secure Access */
|
||||
val |= FIELD_PREP(DEBUG_TXN_AXPROT, DEBUG_TXN_AXPROT_NSEC);
|
||||
|
||||
/* Write or Read Access */
|
||||
if (flags & IOMMU_TRANS_WRITE)
|
||||
val |= DEBUG_TXN_WRITE;
|
||||
|
||||
/* Priviledged or Unpriviledged Access */
|
||||
if (flags & IOMMU_TRANS_PRIV)
|
||||
val |= FIELD_PREP(DEBUG_TXN_AXPROT, DEBUG_TXN_AXPROT_PRIV);
|
||||
|
||||
/* Data or Instruction Access */
|
||||
if (flags & IOMMU_TRANS_INST)
|
||||
val |= FIELD_PREP(DEBUG_TXN_AXPROT, DEBUG_TXN_AXPROT_INST);
|
||||
|
||||
val |= DEBUG_TXN_TRIGGER;
|
||||
writeq_relaxed(val, tbu->base + DEBUG_TXN_TRIGG_REG);
|
||||
|
||||
ret = 0;
|
||||
|
|
@ -5579,7 +5631,8 @@ out_power_off:
|
|||
|
||||
static phys_addr_t qsmmuv500_iova_to_phys_hard(
|
||||
struct arm_smmu_domain *smmu_domain,
|
||||
dma_addr_t iova)
|
||||
dma_addr_t iova,
|
||||
unsigned long trans_flags)
|
||||
{
|
||||
u16 sid;
|
||||
struct msm_iommu_domain *msm_domain = &smmu_domain->domain;
|
||||
|
|
@ -5605,7 +5658,7 @@ static phys_addr_t qsmmuv500_iova_to_phys_hard(
|
|||
frsynra &= CBFRSYNRA_SID_MASK;
|
||||
sid = frsynra;
|
||||
}
|
||||
return qsmmuv500_iova_to_phys(smmu_domain, iova, sid);
|
||||
return qsmmuv500_iova_to_phys(smmu_domain, iova, sid, trans_flags);
|
||||
}
|
||||
|
||||
static void qsmmuv500_release_group_iommudata(void *data)
|
||||
|
|
|
|||
|
|
@ -209,6 +209,9 @@ enum arm_smmu_cbar_type {
|
|||
|
||||
#define ARM_SMMU_CB_FSYNR0 0x68
|
||||
#define FSYNR0_WNR BIT(4)
|
||||
#define FSYNR0_PNU BIT(5)
|
||||
#define FSYNR0_IND BIT(6)
|
||||
#define FSYNR0_NSATTR BIT(8)
|
||||
|
||||
#define ARM_SMMU_CB_FSYNR1 0x6c
|
||||
#define FSYNR1_BID GENMASK(15, 13)
|
||||
|
|
|
|||
|
|
@ -902,7 +902,8 @@ out:
|
|||
static int __check_mapping(struct device *dev, struct iommu_domain *domain,
|
||||
dma_addr_t iova, phys_addr_t expected)
|
||||
{
|
||||
phys_addr_t res = iommu_iova_to_phys_hard(domain, iova);
|
||||
phys_addr_t res = iommu_iova_to_phys_hard(domain, iova,
|
||||
IOMMU_TRANS_DEFAULT);
|
||||
phys_addr_t res2 = iommu_iova_to_phys(domain, iova);
|
||||
|
||||
WARN(res != res2, "hard/soft iova_to_phys fns don't agree...");
|
||||
|
|
@ -1127,7 +1128,8 @@ static int __functional_dma_api_basic_test(struct device *dev,
|
|||
memset(data, 0xa5, size);
|
||||
iova = dma_map_single(dev, data, size, DMA_TO_DEVICE);
|
||||
pa = iommu_iova_to_phys(domain, iova);
|
||||
pa2 = iommu_iova_to_phys_hard(domain, iova);
|
||||
pa2 = iommu_iova_to_phys_hard(domain, iova,
|
||||
IOMMU_TRANS_DEFAULT);
|
||||
if (pa != pa2) {
|
||||
dev_err_ratelimited(dev,
|
||||
"iova_to_phys doesn't match iova_to_phys_hard: %pa != %pa\n",
|
||||
|
|
@ -1200,7 +1202,8 @@ static int __functional_dma_api_map_sg_test(struct device *dev,
|
|||
for_each_sg(table.sgl, sg, count, i) {
|
||||
iova = sg_dma_address(sg);
|
||||
pa = iommu_iova_to_phys(domain, iova);
|
||||
pa2 = iommu_iova_to_phys_hard(domain, iova);
|
||||
pa2 = iommu_iova_to_phys_hard(domain, iova,
|
||||
IOMMU_TRANS_DEFAULT);
|
||||
if (pa != pa2) {
|
||||
dev_err_ratelimited(dev,
|
||||
"iova_to_phys doesn't match iova_to_phys_hard: %pa != %pa\n",
|
||||
|
|
@ -1211,7 +1214,8 @@ static int __functional_dma_api_map_sg_test(struct device *dev,
|
|||
/* check mappings at end of buffer */
|
||||
iova += sg_dma_len(sg) - 1;
|
||||
pa = iommu_iova_to_phys(domain, iova);
|
||||
pa2 = iommu_iova_to_phys_hard(domain, iova);
|
||||
pa2 = iommu_iova_to_phys_hard(domain, iova,
|
||||
IOMMU_TRANS_DEFAULT);
|
||||
if (pa != pa2) {
|
||||
dev_err_ratelimited(dev,
|
||||
"iova_to_phys doesn't match iova_to_phys_hard: %pa != %pa\n",
|
||||
|
|
@ -1553,7 +1557,8 @@ static ssize_t iommu_debug_atos_read(struct file *file, char __user *ubuf,
|
|||
|
||||
memset(buf, 0, 100);
|
||||
|
||||
phys = iommu_iova_to_phys_hard(ddev->domain, ddev->iova);
|
||||
phys = iommu_iova_to_phys_hard(ddev->domain, ddev->iova,
|
||||
IOMMU_TRANS_DEFAULT);
|
||||
if (!phys) {
|
||||
strlcpy(buf, "FAIL\n", 100);
|
||||
phys = iommu_iova_to_phys(ddev->domain, ddev->iova);
|
||||
|
|
@ -1596,7 +1601,7 @@ static ssize_t iommu_debug_dma_atos_read(struct file *file, char __user *ubuf,
|
|||
memset(buf, 0, sizeof(buf));
|
||||
|
||||
phys = iommu_iova_to_phys_hard(ddev->domain,
|
||||
ddev->iova);
|
||||
ddev->iova, IOMMU_TRANS_DEFAULT);
|
||||
if (!phys)
|
||||
strlcpy(buf, "FAIL\n", sizeof(buf));
|
||||
else
|
||||
|
|
|
|||
|
|
@ -1838,14 +1838,14 @@ phys_addr_t iommu_iova_to_phys(struct iommu_domain *domain, dma_addr_t iova)
|
|||
EXPORT_SYMBOL_GPL(iommu_iova_to_phys);
|
||||
|
||||
phys_addr_t iommu_iova_to_phys_hard(struct iommu_domain *domain,
|
||||
dma_addr_t iova)
|
||||
dma_addr_t iova, unsigned long trans_flags)
|
||||
{
|
||||
struct msm_iommu_ops *ops = to_msm_iommu_ops(domain->ops);
|
||||
|
||||
if (unlikely(ops->iova_to_phys_hard == NULL))
|
||||
return 0;
|
||||
|
||||
return ops->iova_to_phys_hard(domain, iova);
|
||||
return ops->iova_to_phys_hard(domain, iova, trans_flags);
|
||||
}
|
||||
|
||||
uint64_t iommu_iova_to_pte(struct iommu_domain *domain,
|
||||
|
|
|
|||
|
|
@ -71,6 +71,15 @@ struct iommu_domain_geometry {
|
|||
bool force_aperture; /* DMA only allowed in mappable range? */
|
||||
};
|
||||
|
||||
/* iommu transaction flags */
|
||||
#define IOMMU_TRANS_WRITE BIT(0) /* 1 Write, 0 Read */
|
||||
#define IOMMU_TRANS_PRIV BIT(1) /* 1 Privileged, 0 Unprivileged */
|
||||
#define IOMMU_TRANS_INST BIT(2) /* 1 Instruction fetch, 0 Data access */
|
||||
#define IOMMU_TRANS_SEC BIT(3) /* 1 Secure, 0 Non-secure access*/
|
||||
|
||||
/* Non secure unprivileged Data read operation */
|
||||
#define IOMMU_TRANS_DEFAULT (0U)
|
||||
|
||||
struct iommu_pgtbl_info {
|
||||
void *ops;
|
||||
};
|
||||
|
|
@ -384,7 +393,8 @@ struct msm_iommu_ops {
|
|||
size_t (*map_sg)(struct iommu_domain *domain, unsigned long iova,
|
||||
struct scatterlist *sg, unsigned int nents, int prot);
|
||||
phys_addr_t (*iova_to_phys_hard)(struct iommu_domain *domain,
|
||||
dma_addr_t iova);
|
||||
dma_addr_t iova,
|
||||
unsigned long trans_flags);
|
||||
bool (*is_iova_coherent)(struct iommu_domain *domain, dma_addr_t iova);
|
||||
void (*trigger_fault)(struct iommu_domain *domain, unsigned long flags);
|
||||
void (*tlbi_domain)(struct iommu_domain *domain);
|
||||
|
|
@ -519,7 +529,7 @@ extern size_t default_iommu_map_sg(struct iommu_domain *domain,
|
|||
unsigned int nents, int prot);
|
||||
extern phys_addr_t iommu_iova_to_phys(struct iommu_domain *domain, dma_addr_t iova);
|
||||
extern phys_addr_t iommu_iova_to_phys_hard(struct iommu_domain *domain,
|
||||
dma_addr_t iova);
|
||||
dma_addr_t iova, unsigned long trans_flags);
|
||||
extern bool iommu_is_iova_coherent(struct iommu_domain *domain,
|
||||
dma_addr_t iova);
|
||||
extern void iommu_set_fault_handler(struct iommu_domain *domain,
|
||||
|
|
@ -833,7 +843,7 @@ static inline phys_addr_t iommu_iova_to_phys(struct iommu_domain *domain, dma_ad
|
|||
}
|
||||
|
||||
static inline phys_addr_t iommu_iova_to_phys_hard(struct iommu_domain *domain,
|
||||
dma_addr_t iova)
|
||||
dma_addr_t iova, unsigned long trans_flags)
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
|
|
@ -853,7 +863,6 @@ static inline void iommu_get_resv_regions(struct device *dev,
|
|||
struct list_head *list)
|
||||
{
|
||||
}
|
||||
|
||||
static inline void iommu_put_resv_regions(struct device *dev,
|
||||
struct list_head *list)
|
||||
{
|
||||
|
|
|
|||
Loading…
Reference in a new issue