Skip to content

Commit c64074b

Browse files
Sam Protsenkojoergroedel
authored andcommitted
iommu/exynos: Abstract getting the fault info
Fault info obtaining is implemented for SysMMU v1..v5 in a very hardware specific way, as it relies on: - interrupt bits being tied to read or write access - having separate registers for the fault address w.r.t. AR/AW ops Newer SysMMU versions (like SysMMU v7) have different way of providing the fault info via registers: - the transaction type (read or write) should be read from the register (instead of hard-coding it w.r.t. corresponding interrupt status bit) - there is only one single register for storing the fault address Because of that, it is not possible to add newer SysMMU support into existing paradigm. Also it's not very effective performance-wise: - checking SysMMU version in ISR each time is not necessary - performing linear search to find the fault info by interrupt bit can be replaced with a single lookup operation Pave the way for adding support for new SysMMU versions by abstracting the getting of fault info in ISR. While at it, do some related style cleanups as well. This is mostly a refactoring patch, but there are some minor functional changes: - fault message format is a bit different; now instead of AR/AW prefixes for the fault's name, the request direction is printed as [READ]/[WRITE]. It has to be done to prepare an abstraction for SysMMU v7 support - don't panic on unknown interrupts; print corresponding message and continue - if fault wasn't recovered, panic with some sane message instead of just doing BUG_ON() The whole fault message looks like this now: [READ] PAGE FAULT occurred at 0x12341000 Signed-off-by: Sam Protsenko <[email protected]> Acked-by: Marek Szyprowski <[email protected]> Link: https://lore.kernel.org/r/[email protected] Signed-off-by: Joerg Roedel <[email protected]>
1 parent 5371987 commit c64074b

File tree

1 file changed

+100
-62
lines changed

1 file changed

+100
-62
lines changed

drivers/iommu/exynos-iommu.c

Lines changed: 100 additions & 62 deletions
Original file line numberDiff line numberDiff line change
@@ -185,38 +185,36 @@ static sysmmu_pte_t *page_entry(sysmmu_pte_t *sent, sysmmu_iova_t iova)
185185
lv2table_base(sent)) + lv2ent_offset(iova);
186186
}
187187

188-
/*
189-
* IOMMU fault information register
190-
*/
191-
struct sysmmu_fault_info {
192-
unsigned int bit; /* bit number in STATUS register */
193-
unsigned short addr_reg; /* register to read VA fault address */
188+
struct sysmmu_fault {
189+
sysmmu_iova_t addr; /* IOVA address that caused fault */
190+
const char *name; /* human readable fault name */
191+
unsigned int type; /* fault type for report_iommu_fault() */
192+
};
193+
194+
struct sysmmu_v1_fault_info {
195+
unsigned short addr_reg; /* register to read IOVA fault address */
194196
const char *name; /* human readable fault name */
195197
unsigned int type; /* fault type for report_iommu_fault */
196198
};
197199

198-
static const struct sysmmu_fault_info sysmmu_faults[] = {
199-
{ 0, REG_PAGE_FAULT_ADDR, "PAGE", IOMMU_FAULT_READ },
200-
{ 1, REG_AR_FAULT_ADDR, "AR MULTI-HIT", IOMMU_FAULT_READ },
201-
{ 2, REG_AW_FAULT_ADDR, "AW MULTI-HIT", IOMMU_FAULT_WRITE },
202-
{ 3, REG_DEFAULT_SLAVE_ADDR, "BUS ERROR", IOMMU_FAULT_READ },
203-
{ 4, REG_AR_FAULT_ADDR, "AR SECURITY PROTECTION", IOMMU_FAULT_READ },
204-
{ 5, REG_AR_FAULT_ADDR, "AR ACCESS PROTECTION", IOMMU_FAULT_READ },
205-
{ 6, REG_AW_FAULT_ADDR, "AW SECURITY PROTECTION", IOMMU_FAULT_WRITE },
206-
{ 7, REG_AW_FAULT_ADDR, "AW ACCESS PROTECTION", IOMMU_FAULT_WRITE },
200+
static const struct sysmmu_v1_fault_info sysmmu_v1_faults[] = {
201+
{ REG_PAGE_FAULT_ADDR, "PAGE", IOMMU_FAULT_READ },
202+
{ REG_AR_FAULT_ADDR, "MULTI-HIT", IOMMU_FAULT_READ },
203+
{ REG_AW_FAULT_ADDR, "MULTI-HIT", IOMMU_FAULT_WRITE },
204+
{ REG_DEFAULT_SLAVE_ADDR, "BUS ERROR", IOMMU_FAULT_READ },
205+
{ REG_AR_FAULT_ADDR, "SECURITY PROTECTION", IOMMU_FAULT_READ },
206+
{ REG_AR_FAULT_ADDR, "ACCESS PROTECTION", IOMMU_FAULT_READ },
207+
{ REG_AW_FAULT_ADDR, "SECURITY PROTECTION", IOMMU_FAULT_WRITE },
208+
{ REG_AW_FAULT_ADDR, "ACCESS PROTECTION", IOMMU_FAULT_WRITE },
207209
};
208210

209-
static const struct sysmmu_fault_info sysmmu_v5_faults[] = {
210-
{ 0, REG_V5_FAULT_AR_VA, "AR PTW", IOMMU_FAULT_READ },
211-
{ 1, REG_V5_FAULT_AR_VA, "AR PAGE", IOMMU_FAULT_READ },
212-
{ 2, REG_V5_FAULT_AR_VA, "AR MULTI-HIT", IOMMU_FAULT_READ },
213-
{ 3, REG_V5_FAULT_AR_VA, "AR ACCESS PROTECTION", IOMMU_FAULT_READ },
214-
{ 4, REG_V5_FAULT_AR_VA, "AR SECURITY PROTECTION", IOMMU_FAULT_READ },
215-
{ 16, REG_V5_FAULT_AW_VA, "AW PTW", IOMMU_FAULT_WRITE },
216-
{ 17, REG_V5_FAULT_AW_VA, "AW PAGE", IOMMU_FAULT_WRITE },
217-
{ 18, REG_V5_FAULT_AW_VA, "AW MULTI-HIT", IOMMU_FAULT_WRITE },
218-
{ 19, REG_V5_FAULT_AW_VA, "AW ACCESS PROTECTION", IOMMU_FAULT_WRITE },
219-
{ 20, REG_V5_FAULT_AW_VA, "AW SECURITY PROTECTION", IOMMU_FAULT_WRITE },
211+
/* SysMMU v5 has the same faults for AR (0..4 bits) and AW (16..20 bits) */
212+
static const char * const sysmmu_v5_fault_names[] = {
213+
"PTW",
214+
"PAGE",
215+
"MULTI-HIT",
216+
"ACCESS PROTECTION",
217+
"SECURITY PROTECTION"
220218
};
221219

222220
/*
@@ -246,9 +244,12 @@ struct exynos_iommu_domain {
246244
struct iommu_domain domain; /* generic domain data structure */
247245
};
248246

247+
struct sysmmu_drvdata;
248+
249249
/*
250250
* SysMMU version specific data. Contains offsets for the registers which can
251251
* be found in different SysMMU variants, but have different offset values.
252+
* Also contains version specific callbacks to abstract the hardware.
252253
*/
253254
struct sysmmu_variant {
254255
u32 pt_base; /* page table base address (physical) */
@@ -259,6 +260,9 @@ struct sysmmu_variant {
259260
u32 flush_end; /* end address of range invalidation */
260261
u32 int_status; /* interrupt status information */
261262
u32 int_clear; /* clear the interrupt */
263+
264+
int (*get_fault_info)(struct sysmmu_drvdata *data, unsigned int itype,
265+
struct sysmmu_fault *fault);
262266
};
263267

264268
/*
@@ -293,13 +297,55 @@ struct sysmmu_drvdata {
293297

294298
#define SYSMMU_REG(data, reg) ((data)->sfrbase + (data)->variant->reg)
295299

300+
static int exynos_sysmmu_v1_get_fault_info(struct sysmmu_drvdata *data,
301+
unsigned int itype,
302+
struct sysmmu_fault *fault)
303+
{
304+
const struct sysmmu_v1_fault_info *finfo;
305+
306+
if (itype >= ARRAY_SIZE(sysmmu_v1_faults))
307+
return -ENXIO;
308+
309+
finfo = &sysmmu_v1_faults[itype];
310+
fault->addr = readl(data->sfrbase + finfo->addr_reg);
311+
fault->name = finfo->name;
312+
fault->type = finfo->type;
313+
314+
return 0;
315+
}
316+
317+
static int exynos_sysmmu_v5_get_fault_info(struct sysmmu_drvdata *data,
318+
unsigned int itype,
319+
struct sysmmu_fault *fault)
320+
{
321+
unsigned int addr_reg;
322+
323+
if (itype < ARRAY_SIZE(sysmmu_v5_fault_names)) {
324+
fault->type = IOMMU_FAULT_READ;
325+
addr_reg = REG_V5_FAULT_AR_VA;
326+
} else if (itype >= 16 && itype <= 20) {
327+
fault->type = IOMMU_FAULT_WRITE;
328+
addr_reg = REG_V5_FAULT_AW_VA;
329+
itype -= 16;
330+
} else {
331+
return -ENXIO;
332+
}
333+
334+
fault->name = sysmmu_v5_fault_names[itype];
335+
fault->addr = readl(data->sfrbase + addr_reg);
336+
337+
return 0;
338+
}
339+
296340
/* SysMMU v1..v3 */
297341
static const struct sysmmu_variant sysmmu_v1_variant = {
298342
.flush_all = 0x0c,
299343
.flush_entry = 0x10,
300344
.pt_base = 0x14,
301345
.int_status = 0x18,
302346
.int_clear = 0x1c,
347+
348+
.get_fault_info = exynos_sysmmu_v1_get_fault_info,
303349
};
304350

305351
/* SysMMU v5 and v7 (non-VM capable) */
@@ -312,6 +358,8 @@ static const struct sysmmu_variant sysmmu_v5_variant = {
312358
.flush_end = 0x24,
313359
.int_status = 0x60,
314360
.int_clear = 0x64,
361+
362+
.get_fault_info = exynos_sysmmu_v5_get_fault_info,
315363
};
316364

317365
/* SysMMU v7: VM capable register set */
@@ -324,6 +372,8 @@ static const struct sysmmu_variant sysmmu_v7_vm_variant = {
324372
.flush_end = 0x8024,
325373
.int_status = 0x60,
326374
.int_clear = 0x64,
375+
376+
.get_fault_info = exynos_sysmmu_v5_get_fault_info,
327377
};
328378

329379
static struct exynos_iommu_domain *to_exynos_domain(struct iommu_domain *dom)
@@ -453,68 +503,56 @@ static void __sysmmu_get_version(struct sysmmu_drvdata *data)
453503
}
454504

455505
static void show_fault_information(struct sysmmu_drvdata *data,
456-
const struct sysmmu_fault_info *finfo,
457-
sysmmu_iova_t fault_addr)
506+
const struct sysmmu_fault *fault)
458507
{
459508
sysmmu_pte_t *ent;
460509

461-
dev_err(data->sysmmu, "%s: %s FAULT occurred at %#x\n",
462-
dev_name(data->master), finfo->name, fault_addr);
510+
dev_err(data->sysmmu, "%s: [%s] %s FAULT occurred at %#x\n",
511+
dev_name(data->master),
512+
fault->type == IOMMU_FAULT_READ ? "READ" : "WRITE",
513+
fault->name, fault->addr);
463514
dev_dbg(data->sysmmu, "Page table base: %pa\n", &data->pgtable);
464-
ent = section_entry(phys_to_virt(data->pgtable), fault_addr);
515+
ent = section_entry(phys_to_virt(data->pgtable), fault->addr);
465516
dev_dbg(data->sysmmu, "\tLv1 entry: %#x\n", *ent);
466517
if (lv1ent_page(ent)) {
467-
ent = page_entry(ent, fault_addr);
518+
ent = page_entry(ent, fault->addr);
468519
dev_dbg(data->sysmmu, "\t Lv2 entry: %#x\n", *ent);
469520
}
470521
}
471522

472523
static irqreturn_t exynos_sysmmu_irq(int irq, void *dev_id)
473524
{
474-
/* SYSMMU is in blocked state when interrupt occurred. */
475525
struct sysmmu_drvdata *data = dev_id;
476-
const struct sysmmu_fault_info *finfo;
477-
unsigned int i, n, itype;
478-
sysmmu_iova_t fault_addr;
526+
unsigned int itype;
527+
struct sysmmu_fault fault;
479528
int ret = -ENOSYS;
480529

481530
WARN_ON(!data->active);
482531

483-
if (MMU_MAJ_VER(data->version) < 5) {
484-
finfo = sysmmu_faults;
485-
n = ARRAY_SIZE(sysmmu_faults);
486-
} else {
487-
finfo = sysmmu_v5_faults;
488-
n = ARRAY_SIZE(sysmmu_v5_faults);
489-
}
490-
491532
spin_lock(&data->lock);
492-
493533
clk_enable(data->clk_master);
494534

495535
itype = __ffs(readl(SYSMMU_REG(data, int_status)));
496-
for (i = 0; i < n; i++, finfo++)
497-
if (finfo->bit == itype)
498-
break;
499-
/* unknown/unsupported fault */
500-
BUG_ON(i == n);
501-
502-
/* print debug message */
503-
fault_addr = readl(data->sfrbase + finfo->addr_reg);
504-
show_fault_information(data, finfo, fault_addr);
505-
506-
if (data->domain)
507-
ret = report_iommu_fault(&data->domain->domain,
508-
data->master, fault_addr, finfo->type);
509-
/* fault is not recovered by fault handler */
510-
BUG_ON(ret != 0);
536+
ret = data->variant->get_fault_info(data, itype, &fault);
537+
if (ret) {
538+
dev_err(data->sysmmu, "Unhandled interrupt bit %u\n", itype);
539+
goto out;
540+
}
541+
show_fault_information(data, &fault);
511542

543+
if (data->domain) {
544+
ret = report_iommu_fault(&data->domain->domain, data->master,
545+
fault.addr, fault.type);
546+
}
547+
if (ret)
548+
panic("Unrecoverable System MMU Fault!");
549+
550+
out:
512551
writel(1 << itype, SYSMMU_REG(data, int_clear));
513552

553+
/* SysMMU is in blocked state when interrupt occurred */
514554
sysmmu_unblock(data);
515-
516555
clk_disable(data->clk_master);
517-
518556
spin_unlock(&data->lock);
519557

520558
return IRQ_HANDLED;

0 commit comments

Comments
 (0)