Skip to contentSkip to conversation

ARM: fix exceptions handling

PATCH New Kirill A. Shutsemov 3 participants 11 messages Jul 19, 2010 – Jul 27, 2010

Subsystems (3)
lore
Share & tools
lore
#0 lore raw

cover ARM: fix exceptions handling

From: Kirill A. Shutemov <[email protected]>

Kirill A. Shutemov (5):
Use SIGBUS for unalinged access instead of SIGILL
Add 'code' parameter for hook_fault_code()
Check arch version and modify fsr_info[] depends on it at runtime ARMv6K and ARMv7 use fault statuses 3 and 6 as Access Flag fault Workaround infinity loop in handling of translation faults

 arch/arm/include/asm/system.h     |    2 +-
 arch/arm/mach-integrator/pci_v3.c |    8 ++--
 arch/arm/mach-iop13xx/pci.c       |    2 +-
 arch/arm/mach-ixp2000/pci.c       |    2 +-
 arch/arm/mach-ixp23xx/pci.c       |    2 +-
 arch/arm/mach-ixp4xx/common-pci.c |    3 +-
 arch/arm/mach-ks8695/pci.c        |    4 +-
 arch/arm/mm/alignment.c           |   16 +++++++++-
 arch/arm/mm/fault.c               |   56 +++++++++++++++++++++++++++---------
 arch/arm/plat-iop/pci.c           |    2 +-
 10 files changed, 69 insertions(+), 28 deletions(-)
#1 lore raw

PATCH 1/5 Use SIGBUS for unalinged access instead of SIGILL

From: Kirill A. Shutemov <[email protected]>

POSIX specify to use signal SIGBUS with code BUS_ADRALN for invalid address alignment.

---
 arch/arm/mm/alignment.c |    4 ++--
 arch/arm/mm/fault.c     |    4 ++--
 2 files changed, 4 insertions(+), 4 deletions(-)
arch/arm/mm/alignment.c +2 -2
index 6f98c35..53a6096 100644
@@ -924,8 +924,8 @@ static int __init alignment_init(void)
 		ai_usermode = UM_FIXUP;
 	}
 
-	hook_fault_code(1, do_alignment, SIGILL, "alignment exception");
-	hook_fault_code(3, do_alignment, SIGILL, "alignment exception");
+	hook_fault_code(1, do_alignment, SIGBUS, "alignment exception");
+	hook_fault_code(3, do_alignment, SIGBUS, "alignment exception");
 
 	return 0;
 }
arch/arm/mm/fault.c +2 -2
index cbfb2ed..ce6f3a4 100644
@@ -463,9 +463,9 @@ static struct fsr_info {
 	 * defines these to be "precise" aborts.
 	 */
 	{ do_bad,		SIGSEGV, 0,		"vector exception"		   },
-	{ do_bad,		SIGILL,	 BUS_ADRALN,	"alignment exception"		   },
+	{ do_bad,		SIGBUS,	 BUS_ADRALN,	"alignment exception"		   },
 	{ do_bad,		SIGKILL, 0,		"terminal exception"		   },
-	{ do_bad,		SIGILL,	 BUS_ADRALN,	"alignment exception"		   },
+	{ do_bad,		SIGBUS,	 BUS_ADRALN,	"alignment exception"		   },
 /* Do we need runtime check ? */
 #if __LINUX_ARM_ARCH__ < 6
 	{ do_bad,		SIGBUS,	 0,		"external abort on linefetch"	   },
Signature · 1 line
1.7.1.1
#2 lore raw

PATCH 2/5 Add 'code' parameter for hook_fault_code()

In reply to Kirill A. Shutsemov

From: Kirill A. Shutemov <[email protected]>

Add one more parameter to hook_fault_code() to be able to set 'code' field of struct fsr_info.

---
 arch/arm/include/asm/system.h     |    2 +-
 arch/arm/mach-integrator/pci_v3.c |    8 ++++----
 arch/arm/mach-iop13xx/pci.c       |    2 +-
 arch/arm/mach-ixp2000/pci.c       |    2 +-
 arch/arm/mach-ixp23xx/pci.c       |    2 +-
 arch/arm/mach-ixp4xx/common-pci.c |    3 ++-
 arch/arm/mach-ks8695/pci.c        |    4 ++--
 arch/arm/mm/alignment.c           |    6 ++++--
 arch/arm/mm/fault.c               |   14 ++++++++------
 arch/arm/plat-iop/pci.c           |    2 +-
 10 files changed, 25 insertions(+), 20 deletions(-)
arch/arm/include/asm/system.h +1 -1
index 5f4f480..8ba1ccf 100644
@@ -83,7 +83,7 @@ void arm_notify_die(const char *str, struct pt_regs *regs, struct siginfo *info,
 
 void hook_fault_code(int nr, int (*fn)(unsigned long, unsigned int,
 				       struct pt_regs *),
-		     int sig, const char *name);
+		     int sig, int code, const char *name);
 
 #define xchg(ptr,x) \
 	((__typeof__(*(ptr)))__xchg((unsigned long)(x),(ptr),sizeof(*(ptr))))
arch/arm/mach-integrator/pci_v3.c +4 -4
index 9cef059..6467d99 100644
@@ -505,10 +505,10 @@ void __init pci_v3_preinit(void)
 	/*
 	 * Hook in our fault handler for PCI errors
 	 */
-	hook_fault_code(4, v3_pci_fault, SIGBUS, "external abort on linefetch");
-	hook_fault_code(6, v3_pci_fault, SIGBUS, "external abort on linefetch");
-	hook_fault_code(8, v3_pci_fault, SIGBUS, "external abort on non-linefetch");
-	hook_fault_code(10, v3_pci_fault, SIGBUS, "external abort on non-linefetch");
+	hook_fault_code(4, v3_pci_fault, SIGBUS, 0, "external abort on linefetch");
+	hook_fault_code(6, v3_pci_fault, SIGBUS, 0, "external abort on linefetch");
+	hook_fault_code(8, v3_pci_fault, SIGBUS, 0, "external abort on non-linefetch");
+	hook_fault_code(10, v3_pci_fault, SIGBUS, 0, "external abort on non-linefetch");
 
 	spin_lock_irqsave(&v3_lock, flags);
 
arch/arm/mach-iop13xx/pci.c +1 -1
index 6d5a908..773ea0c 100644
@@ -987,7 +987,7 @@ void __init iop13xx_pci_init(void)
 		iop13xx_atux_setup();
 	}
 
-	hook_fault_code(16+6, iop13xx_pci_abort, SIGBUS,
+	hook_fault_code(16+6, iop13xx_pci_abort, SIGBUS, 0,
 			"imprecise external abort");
 }
 
arch/arm/mach-ixp2000/pci.c +1 -1
index 90771ca..853fd90 100644
@@ -209,7 +209,7 @@ ixp2000_pci_preinit(void)
 			"the needed workaround has not been configured in");
 #endif
 
-	hook_fault_code(16+6, ixp2000_pci_abort_handler, SIGBUS,
+	hook_fault_code(16+6, ixp2000_pci_abort_handler, SIGBUS, 0
 				"PCI config cycle to non-existent device");
 }
 
arch/arm/mach-ixp23xx/pci.c +1 -1
index 4b0e598..563819a 100644
@@ -229,7 +229,7 @@ void __init ixp23xx_pci_preinit(void)
 {
 	ixp23xx_pci_common_init();
 
-	hook_fault_code(16+6, ixp23xx_pci_abort_handler, SIGBUS,
+	hook_fault_code(16+6, ixp23xx_pci_abort_handler, SIGBUS, 0,
 			"PCI config cycle to non-existent device");
 
 	*IXP23XX_PCI_ADDR_EXT = 0x0000e000;
arch/arm/mach-ixp4xx/common-pci.c +2 -1
index e318153..f4fbb5e 100644
@@ -382,7 +382,8 @@ void __init ixp4xx_pci_preinit(void)
 
 
 	/* hook in our fault handler for PCI errors */
-	hook_fault_code(16+6, abort_handler, SIGBUS, "imprecise external abort");
+	hook_fault_code(16+6, abort_handler, SIGBUS, 0,
+			"imprecise external abort");
 
 	pr_debug("setup PCI-AHB(inbound) and AHB-PCI(outbound) address mappings\n");
 
arch/arm/mach-ks8695/pci.c +2 -2
index 7849966..5fcd082 100644
@@ -268,8 +268,8 @@ static void __init ks8695_pci_preinit(void)
 	__raw_writel(0, KS8695_PCI_VA + KS8695_PIOBAC);
 
 	/* hook in fault handlers */
-	hook_fault_code(8, ks8695_pci_fault, SIGBUS, "external abort on non-linefetch");
-	hook_fault_code(10, ks8695_pci_fault, SIGBUS, "external abort on non-linefetch");
+	hook_fault_code(8, ks8695_pci_fault, SIGBUS, 0, "external abort on non-linefetch");
+	hook_fault_code(10, ks8695_pci_fault, SIGBUS, 0, "external abort on non-linefetch");
 }
 
 static void ks8695_show_pciregs(void)
arch/arm/mm/alignment.c +4 -2
index 53a6096..77cfdbe 100644
@@ -924,8 +924,10 @@ static int __init alignment_init(void)
 		ai_usermode = UM_FIXUP;
 	}
 
-	hook_fault_code(1, do_alignment, SIGBUS, "alignment exception");
-	hook_fault_code(3, do_alignment, SIGBUS, "alignment exception");
+	hook_fault_code(1, do_alignment, SIGBUS, BUS_ADRALN,
+			"alignment exception");
+	hook_fault_code(3, do_alignment, SIGBUS, BUS_ADRALN,
+			"alignment exception");
 
 	return 0;
 }
arch/arm/mm/fault.c +8 -6
index ce6f3a4..84131c8 100644
@@ -508,13 +508,15 @@ static struct fsr_info {
 
 void __init
 hook_fault_code(int nr, int (*fn)(unsigned long, unsigned int, struct pt_regs *),
-		int sig, const char *name)
+		int sig, int code, const char *name)
 {
-	if (nr >= 0 && nr < ARRAY_SIZE(fsr_info)) {
-		fsr_info[nr].fn   = fn;
-		fsr_info[nr].sig  = sig;
-		fsr_info[nr].name = name;
-	}
+	if (nr < 0 || nr >= ARRAY_SIZE(fsr_info))
+		BUG();
+
+	fsr_info[nr].fn   = fn;
+	fsr_info[nr].sig  = sig;
+	fsr_info[nr].code = code;
+	fsr_info[nr].name = name;
 }
 
 /*
arch/arm/plat-iop/pci.c +1 -1
index ce31f31..43f2b15 100644
@@ -359,7 +359,7 @@ static void __init iop3xx_atu_debug(void)
 	DBG("ATU: IOP3XX_ATUCMD=0x%04x\n", *IOP3XX_ATUCMD);
 	DBG("ATU: IOP3XX_ATUCR=0x%08x\n", *IOP3XX_ATUCR);
 
-	hook_fault_code(16+6, iop3xx_pci_abort, SIGBUS, "imprecise external abort");
+	hook_fault_code(16+6, iop3xx_pci_abort, SIGBUS, 0, "imprecise external abort");
 }
 
 /* for platforms that might be host-bus-adapters */
Signature · 1 line
1.7.1.1
#3 lore raw

PATCH 2/5 Add 'code' parameter for hook_fault_code()

arch/arm/mach-ixp2000/pci.c ixp2000_pci_preinit(void) · line 213

Quoted 11 lines · Kirill A. Shutsemov
arch/arm/mach-ixp2000/pci.c +1 -1
index 90771ca..853fd90 100644
@@ -209,7 +209,7 @@ ixp2000_pci_preinit(void)
 			"the needed workaround has not been configured in");
 #endif
 
-	hook_fault_code(16+6, ixp2000_pci_abort_handler, SIGBUS,
+	hook_fault_code(16+6, ixp2000_pci_abort_handler, SIGBUS, 0
 				"PCI config cycle to non-existent device");

This is broken - can you submit a fixed version please?

#4 lore raw

PATCH 2/5 Add 'code' parameter for hook_fault_code()

Quoted 9 lines · Russell King - ARM Linux
Quoted 8 lines · Kirill A. Shutsemov
arch/arm/mach-ixp2000/pci.c +0 -0
index 90771ca..853fd90 100644
@@ -209,7 +209,7 @@ ixp2000_pci_preinit(void)
 			"the needed workaround has not been configured in");
 #endif
 
Quoted 5 lines

arch/arm/mach-ixp2000/pci.c ixp2000_pci_preinit(void) · line 213

Quoted 3 lines
arch/arm/mach-ixp2000/pci.c +1 -1
-	hook_fault_code(16+6, ixp2000_pci_abort_handler, SIGBUS,
+	hook_fault_code(16+6, ixp2000_pci_abort_handler, SIGBUS, 0
 				"PCI config cycle to non-existent device");

This is broken - can you submit a fixed version please?

Done.

Signature · 1 line
 Kirill A. Shutemov
#5 lore raw

PATCH 3/5 Check arch version and modify fsr_info[] depends on it at runtime

In reply to Kirill A. Shutsemov

From: Kirill A. Shutemov <[email protected]>

---
 arch/arm/mm/fault.c |   16 +++++++++++-----
 1 files changed, 11 insertions(+), 5 deletions(-)
arch/arm/mm/fault.c +11 -5
index 84131c8..345a3c9 100644
@@ -466,12 +466,7 @@ static struct fsr_info {
 	{ do_bad,		SIGBUS,	 BUS_ADRALN,	"alignment exception"		   },
 	{ do_bad,		SIGKILL, 0,		"terminal exception"		   },
 	{ do_bad,		SIGBUS,	 BUS_ADRALN,	"alignment exception"		   },
-/* Do we need runtime check ? */
-#if __LINUX_ARM_ARCH__ < 6
 	{ do_bad,		SIGBUS,	 0,		"external abort on linefetch"	   },
-#else
-	{ do_translation_fault,	SIGSEGV, SEGV_MAPERR,	"I-cache maintenance fault"	   },
-#endif
 	{ do_translation_fault,	SIGSEGV, SEGV_MAPERR,	"section translation fault"	   },
 	{ do_bad,		SIGBUS,	 0,		"external abort on linefetch"	   },
 	{ do_page_fault,	SIGSEGV, SEGV_MAPERR,	"page translation fault"	   },
@@ -596,3 +591,14 @@ do_PrefetchAbort(unsigned long addr, unsigned int ifsr, struct pt_regs *regs)
 	arm_notify_die("", regs, &info, ifsr, 0);
 }
 
+static int __init exceptions_init(void)
+{
+	if (cpu_architecture() >= CPU_ARCH_ARMv6) {
+		hook_fault_code(4, do_translation_fault, SIGSEGV, SEGV_MAPERR,
+				"I-cache maintenance fault");
+	}
+
+	return 0;
+}
+
+arch_initcall(exceptions_init);
Signature · 1 line
1.7.1.1
#6 lore raw

PATCH 4/5 ARMv6K and ARMv7 use fault statuses 3 and 6 as Access Flag fault

In reply to Kirill A. Shutsemov

From: Kirill A. Shutemov <[email protected]>

Statuses 3 (0b00011) and 6 (0x00110) of DFSR are Access Flags faults on ARMv6K and ARMv7. Let's patch fsr_info[] at runtime if we are on ARMv7 or later.

Unfortunately, we don't have runtime check for 'K' extension, so we can't check for it.

---
 arch/arm/mm/alignment.c |   14 ++++++++++++--
 arch/arm/mm/fault.c     |   11 +++++++++++
 2 files changed, 23 insertions(+), 2 deletions(-)
arch/arm/mm/alignment.c +12 -2
index 77cfdbe..d073b64 100644
@@ -926,8 +926,18 @@ static int __init alignment_init(void)
 
 	hook_fault_code(1, do_alignment, SIGBUS, BUS_ADRALN,
 			"alignment exception");
-	hook_fault_code(3, do_alignment, SIGBUS, BUS_ADRALN,
-			"alignment exception");
+
+	/*
+	 * ARMv6K and ARMv7 use fault status 3 (0b00011) as Access Flag section
+	 * fault, not as alignment error.
+	 *
+	 * TODO: handle ARMv6K properly. Runtime check for 'K' extension is
+	 * needed.
+	 */
+	if (cpu_architecture() <= CPU_ARCH_ARMv6) {
+		hook_fault_code(3, do_alignment, SIGBUS, BUS_ADRALN,
+				"alignment exception");
+	}
 
 	return 0;
 }
arch/arm/mm/fault.c +11 -0
index 345a3c9..7e866e9 100644
@@ -598,6 +598,17 @@ static int __init exceptions_init(void)
 				"I-cache maintenance fault");
 	}
 
+	if (cpu_architecture() >= CPU_ARCH_ARMv7) {
+		/*
+		 * TODO: Access flag faults introduced in ARMv6K.
+		 * Runtime check for 'K' extension is needed
+		 */
+		hook_fault_code(3, do_bad, SIGSEGV, SEGV_MAPPERR,
+				"section access flag fault");
+		hook_fault_code(6, do_bad, SIGSEGV, SEGV_MAPPERR,
+				"section access flag fault");
+	}
+
 	return 0;
 }
 
Signature · 1 line
1.7.1.1
#7 lore raw

PATCH 4/5 ARMv6K and ARMv7 use fault statuses 3 and 6 as Access Flag fault

Hello.

Quoted 8 lines · Kirill A. Shutsemov

Statuses 3 (0b00011) and 6 (0x00110) of DFSR are Access Flags faults on ARMv6K and ARMv7. Let's patch fsr_info[] at runtime if we are on ARMv7 or later.

Unfortunately, we don't have runtime check for 'K' extension, so we can't check for it.

[...]

Quoted 12 lines
arch/arm/mm/alignment.c +2 -2
index 77cfdbe..d073b64 100644
@@ -926,8 +926,18 @@ static int __init alignment_init(void)
 
 	hook_fault_code(1, do_alignment, SIGBUS, BUS_ADRALN,
 			"alignment exception");
-	hook_fault_code(3, do_alignment, SIGBUS, BUS_ADRALN,
-			"alignment exception");
+
+	/*

arch/arm/mm/alignment.c static int __init alignment_init(void) · line 940

Quoted 10 lines
arch/arm/mm/alignment.c +10 -0
+	 * ARMv6K and ARMv7 use fault status 3 (0b00011) as Access Flag section
+	 * fault, not as alignment error.
+	 *
+	 * TODO: handle ARMv6K properly. Runtime check for 'K' extension is
+	 * needed.
+	 */
+	if (cpu_architecture() <= CPU_ARCH_ARMv6) {
+		hook_fault_code(3, do_alignment, SIGBUS, BUS_ADRALN,
+				"alignment exception");
+	}

Curly braces not neeed here. I assume you haven't run your patch thru
scripts/checkpatch.pl?

WBR, Sergei

#8 lore raw

PATCH 4/5 ARMv6K and ARMv7 use fault statuses 3 and 6 as Access Flag fault

Quoted 30 lines · Sergei Shtylyov

Hello.

Quoted 8 lines · Kirill A. Shutsemov

Statuses 3 (0b00011) and 6 (0x00110) of DFSR are Access Flags faults on ARMv6K and ARMv7. Let's patch fsr_info[] at runtime if we are on ARMv7 or later.

Unfortunately, we don't have runtime check for 'K' extension, so we can't check for it.

[...]

Quoted 17 lines
arch/arm/mm/alignment.c +7 -2
index 77cfdbe..d073b64 100644
@@ -926,8 +926,18 @@ static int __init alignment_init(void)
 
 	hook_fault_code(1, do_alignment, SIGBUS, BUS_ADRALN,
 			"alignment exception");
-	hook_fault_code(3, do_alignment, SIGBUS, BUS_ADRALN,
-			"alignment exception");
+
+	/*
+	 * ARMv6K and ARMv7 use fault status 3 (0b00011) as Access Flag section
+	 * fault, not as alignment error.
+	 *
+	 * TODO: handle ARMv6K properly. Runtime check for 'K' extension is
+	 * needed.
Quoted 8 lines

arch/arm/mm/alignment.c static int __init alignment_init(void) · line 940

Quoted 5 lines
arch/arm/mm/alignment.c +5 -0
+	 */
+	if (cpu_architecture() <= CPU_ARCH_ARMv6) {
+		hook_fault_code(3, do_alignment, SIGBUS, BUS_ADRALN,
+				"alignment exception");
+	}

Curly braces not neeed here. I assume you haven't run your patch thru
scripts/checkpatch.pl?

total: 0 errors, 0 warnings, 37 lines checked

I'll remove it if you want. But I think it reasonable to leave braces in cases like:

if (condition) {
	do_this(a, very_long, list, of, parameters,
		second, part, of, the, list);
}

What do you think?

Signature · 1 line
 Kirill A. Shutemov
#9 lore raw

PATCH 4/5 ARMv6K and ARMv7 use fault statuses 3 and 6 as Access Flag fault

Quoted 37 lines · Kirill A. Shutemov
Quoted 28 lines

Hello.

Quoted 6 lines · Kirill A. Shutsemov

Statuses 3 (0b00011) and 6 (0x00110) of DFSR are Access Flags faults on ARMv6K and ARMv7. Let's patch fsr_info[] at runtime if we are on ARMv7 or later.
Unfortunately, we don't have runtime check for 'K' extension, so we can't check for it.

[...]

Quoted 17 lines
arch/arm/mm/alignment.c +7 -2
index 77cfdbe..d073b64 100644
@@ -926,8 +926,18 @@ static int __init alignment_init(void)
 
 	hook_fault_code(1, do_alignment, SIGBUS, BUS_ADRALN,
 			"alignment exception");
-	hook_fault_code(3, do_alignment, SIGBUS, BUS_ADRALN,
-			"alignment exception");
+
+	/*
+	 * ARMv6K and ARMv7 use fault status 3 (0b00011) as Access Flag section
+	 * fault, not as alignment error.
+	 *
+	 * TODO: handle ARMv6K properly. Runtime check for 'K' extension is
+	 * needed.
Quoted 8 lines

arch/arm/mm/alignment.c static int __init alignment_init(void) · line 940

Quoted 5 lines
arch/arm/mm/alignment.c +5 -0
+	 */
+	if (cpu_architecture() <= CPU_ARCH_ARMv6) {
+		hook_fault_code(3, do_alignment, SIGBUS, BUS_ADRALN,
+				"alignment exception");
+	}

Curly braces not neeed here. I assume you haven't run your patch thru
scripts/checkpatch.pl?

Quoted 1 line

total: 0 errors, 0 warnings, 37 lines checked

Strange, it used to warn about superfluous braces...

Quoted 9 lines

I'll remove it if you want. But I think it reasonable to leave braces in cases like:

if (condition) {
	do_this(a, very_long, list, of, parameters,
		second, part, of, the, list);
}

What do you think?

I wouldn't use braces in this case. But it's up to you after all, if
checkpatch.pl is indifferent about them...

WBR, Sergei

#10 lore raw

PATCH 5/5 Workaround infinity loop in handling of translation faults

In reply to Kirill A. Shutsemov

From: Kirill A. Shutemov <[email protected]>

On ARM one Linux PGD entry contains two hardware entries (see page tables layout in pgtable.h). We normally guarantee that we always fill both L1 entries. But create_mapping() doesn't follow the rule. It can create inidividual L1 entries, so here we have to call pmd_none() check in do_translation_fault() for the entry really corresponded to address, not for the first of pair.

---
 arch/arm/mm/fault.c |   11 ++++++++++-
 1 files changed, 10 insertions(+), 1 deletions(-)
arch/arm/mm/fault.c +10 -1
index 7e866e9..f5f1190 100644
@@ -413,7 +413,16 @@ do_translation_fault(unsigned long addr, unsigned int fsr,
 	pmd_k = pmd_offset(pgd_k, addr);
 	pmd   = pmd_offset(pgd, addr);
 
-	if (pmd_none(*pmd_k))
+	/*
+	 * On ARM one Linux PGD entry contains two hardware entries (see page
+	 * tables layout in pgtable.h). We normally guarantee that we always
+	 * fill both L1 entries. But create_mapping() doesn't follow the rule.
+	 * It can create inidividual L1 entries, so here we have to call
+	 * pmd_none() check for the entry really corresponded to address, not
+	 * for the first of pair.
+	 */
+	index = (addr >> SECTION_SHIFT) & 1;
+	if (pmd_none(pmd_k[index]))
 		goto bad_area;
 
 	copy_pmd(pmd, pmd_k);
Signature · 1 line
1.7.1.1