diff mbox

[v4,10/21] arm64: entry.S: move SError handling into a C function for future expansion

Message ID 20171019145807.23251-11-james.morse@arm.com (mailing list archive)
State New, archived
Headers show

Commit Message

James Morse Oct. 19, 2017, 2:57 p.m. UTC
From: Xie XiuQi <xiexiuqi@huawei.com>

Today SError is taken using the inv_entry macro that ends up in
bad_mode.

SError can be used by the RAS Extensions to notify either the OS or
firmware of CPU problems, some of which may have been corrected.

To allow this handling to be added, add a do_serror() C function
that just panic()s. Add the entry.S boiler plate to save/restore the
CPU registers and unmask debug exceptions. Future patches may change
do_serror() to return if the SError Interrupt was notification of a
corrected error.

Signed-off-by: Xie XiuQi <xiexiuqi@huawei.com>
Signed-off-by: Wang Xiongfeng <wangxiongfengi2@huawei.com>
[Split out of a bigger patch, added compat path, renamed, enabled debug
 exceptions]
Signed-off-by: James Morse <james.morse@arm.com>
Reviewed-by: Catalin Marinas <catalin.marinas@arm.com>
---
 arch/arm64/Kconfig        |  2 +-
 arch/arm64/kernel/entry.S | 36 +++++++++++++++++++++++++++++-------
 arch/arm64/kernel/traps.c | 13 +++++++++++++
 3 files changed, 43 insertions(+), 8 deletions(-)

Comments

Adam Wallis Jan. 2, 2018, 9:07 p.m. UTC | #1
James

On 10/19/2017 10:57 AM, James Morse wrote:
[..]
>  	kernel_ventry	el1_fiq_invalid			// FIQ EL1h
> -	kernel_ventry	el1_error_invalid		// Error EL1h
> +	kernel_ventry	el1_error			// Error EL1h
>  
>  	kernel_ventry	el0_sync			// Synchronous 64-bit EL0
>  	kernel_ventry	el0_irq				// IRQ 64-bit EL0
>  	kernel_ventry	el0_fiq_invalid			// FIQ 64-bit EL0
> -	kernel_ventry	el0_error_invalid		// Error 64-bit EL0
> +	kernel_ventry	el0_error			// Error 64-bit EL0
>  
>  #ifdef CONFIG_COMPAT
>  	kernel_ventry	el0_sync_compat			// Synchronous 32-bit EL0
>  	kernel_ventry	el0_irq_compat			// IRQ 32-bit EL0
>  	kernel_ventry	el0_fiq_invalid_compat		// FIQ 32-bit EL0
> -	kernel_ventry	el0_error_invalid_compat	// Error 32-bit EL0
> +	kernel_ventry	el0_error_compat		// Error 32-bit EL0
>  #else
>  	kernel_ventry	el0_sync_invalid		// Synchronous 32-bit EL0
>  	kernel_ventry	el0_irq_invalid			// IRQ 32-bit EL0
> @@ -455,10 +455,6 @@ ENDPROC(el0_error_invalid)
>  el0_fiq_invalid_compat:
>  	inv_entry 0, BAD_FIQ, 32
>  ENDPROC(el0_fiq_invalid_compat)
> -
> -el0_error_invalid_compat:
> -	inv_entry 0, BAD_ERROR, 32
> -ENDPROC(el0_error_invalid_compat)
>  #endif

Perhaps I missed something quite obvious, but is there any reason to not also
remove el1_error_invalid, since SError handling now jumps to el1_error?

>  el1_sync_invalid:
> @@ -663,6 +659,10 @@ el0_svc_compat:
>  el0_irq_compat:
>  	kernel_entry 0, 32
>  	b	el0_irq_naked
> +
> +el0_error_compat:
> +	kernel_entry 0, 32
> +	b	el0_error_naked
>  #endif
>  
>  el0_da:
> @@ -780,6 +780,28 @@ el0_irq_naked:
>  	b	ret_to_user
>  ENDPROC(el0_irq)
>  
> +el1_error:
> +	kernel_entry 1
> +	mrs	x1, esr_el1
> +	enable_dbg
> +	mov	x0, sp
> +	bl	do_serror
> +	kernel_exit 1
> +ENDPROC(el1_error)
> +
> +el0_error:
> +	kernel_entry 0
> +el0_error_naked:
> +	mrs	x1, esr_el1
> +	enable_dbg
> +	mov	x0, sp
> +	bl	do_serror
> +	enable_daif
> +	ct_user_exit
> +	b	ret_to_user
> +ENDPROC(el0_error)
[..]
Thanks

Adam
James Morse Jan. 3, 2018, 4 p.m. UTC | #2
Hi Adam,

On 02/01/18 21:07, Adam Wallis wrote:
> On 10/19/2017 10:57 AM, James Morse wrote:
> [..]
>>  	kernel_ventry	el1_fiq_invalid			// FIQ EL1h
>> -	kernel_ventry	el1_error_invalid		// Error EL1h
>> +	kernel_ventry	el1_error			// Error EL1h

>>  	kernel_ventry	el0_sync			// Synchronous 64-bit EL0
>>  	kernel_ventry	el0_irq				// IRQ 64-bit EL0
>>  	kernel_ventry	el0_fiq_invalid			// FIQ 64-bit EL0
>> -	kernel_ventry	el0_error_invalid		// Error 64-bit EL0
>> +	kernel_ventry	el0_error			// Error 64-bit EL0
>>  
>>  #ifdef CONFIG_COMPAT
>>  	kernel_ventry	el0_sync_compat			// Synchronous 32-bit EL0
>>  	kernel_ventry	el0_irq_compat			// IRQ 32-bit EL0
>>  	kernel_ventry	el0_fiq_invalid_compat		// FIQ 32-bit EL0
>> -	kernel_ventry	el0_error_invalid_compat	// Error 32-bit EL0
>> +	kernel_ventry	el0_error_compat		// Error 32-bit EL0
>>  #else
>>  	kernel_ventry	el0_sync_invalid		// Synchronous 32-bit EL0
>>  	kernel_ventry	el0_irq_invalid			// IRQ 32-bit EL0
>> @@ -455,10 +455,6 @@ ENDPROC(el0_error_invalid)
>>  el0_fiq_invalid_compat:
>>  	inv_entry 0, BAD_FIQ, 32
>>  ENDPROC(el0_fiq_invalid_compat)
>> -
>> -el0_error_invalid_compat:
>> -	inv_entry 0, BAD_ERROR, 32
>> -ENDPROC(el0_error_invalid_compat)
>>  #endif

> Perhaps I missed something quite obvious, but is there any reason to not also
> remove el1_error_invalid, since SError handling now jumps to el1_error?

There is still a caller for el1_error_invalid: depending on SPSel we are in
thread or handler mode, which causes exceptions to use a different entry in the
vectors. The kernel always uses handler mode, all the thread mode entries point
at their '_invalid' versions.

If we take an SError from EL1t, SPsel==0 then it uses vectors+0x180. (just cut
off the top of this diff). The el1_error change above is for EL1h, SPsel==1,
which uses vectors+0x380.


Thanks for taking a look!

James
diff mbox

Patch

diff --git a/arch/arm64/Kconfig b/arch/arm64/Kconfig
index 0df64a6a56d4..70dfe4e9ccc5 100644
--- a/arch/arm64/Kconfig
+++ b/arch/arm64/Kconfig
@@ -98,7 +98,7 @@  config ARM64
 	select HAVE_IRQ_TIME_ACCOUNTING
 	select HAVE_MEMBLOCK
 	select HAVE_MEMBLOCK_NODE_MAP if NUMA
-	select HAVE_NMI if ACPI_APEI_SEA
+	select HAVE_NMI
 	select HAVE_PATA_PLATFORM
 	select HAVE_PERF_EVENTS
 	select HAVE_PERF_REGS
diff --git a/arch/arm64/kernel/entry.S b/arch/arm64/kernel/entry.S
index df085ec003b0..e147c1d00b41 100644
--- a/arch/arm64/kernel/entry.S
+++ b/arch/arm64/kernel/entry.S
@@ -375,18 +375,18 @@  ENTRY(vectors)
 	kernel_ventry	el1_sync			// Synchronous EL1h
 	kernel_ventry	el1_irq				// IRQ EL1h
 	kernel_ventry	el1_fiq_invalid			// FIQ EL1h
-	kernel_ventry	el1_error_invalid		// Error EL1h
+	kernel_ventry	el1_error			// Error EL1h
 
 	kernel_ventry	el0_sync			// Synchronous 64-bit EL0
 	kernel_ventry	el0_irq				// IRQ 64-bit EL0
 	kernel_ventry	el0_fiq_invalid			// FIQ 64-bit EL0
-	kernel_ventry	el0_error_invalid		// Error 64-bit EL0
+	kernel_ventry	el0_error			// Error 64-bit EL0
 
 #ifdef CONFIG_COMPAT
 	kernel_ventry	el0_sync_compat			// Synchronous 32-bit EL0
 	kernel_ventry	el0_irq_compat			// IRQ 32-bit EL0
 	kernel_ventry	el0_fiq_invalid_compat		// FIQ 32-bit EL0
-	kernel_ventry	el0_error_invalid_compat	// Error 32-bit EL0
+	kernel_ventry	el0_error_compat		// Error 32-bit EL0
 #else
 	kernel_ventry	el0_sync_invalid		// Synchronous 32-bit EL0
 	kernel_ventry	el0_irq_invalid			// IRQ 32-bit EL0
@@ -455,10 +455,6 @@  ENDPROC(el0_error_invalid)
 el0_fiq_invalid_compat:
 	inv_entry 0, BAD_FIQ, 32
 ENDPROC(el0_fiq_invalid_compat)
-
-el0_error_invalid_compat:
-	inv_entry 0, BAD_ERROR, 32
-ENDPROC(el0_error_invalid_compat)
 #endif
 
 el1_sync_invalid:
@@ -663,6 +659,10 @@  el0_svc_compat:
 el0_irq_compat:
 	kernel_entry 0, 32
 	b	el0_irq_naked
+
+el0_error_compat:
+	kernel_entry 0, 32
+	b	el0_error_naked
 #endif
 
 el0_da:
@@ -780,6 +780,28 @@  el0_irq_naked:
 	b	ret_to_user
 ENDPROC(el0_irq)
 
+el1_error:
+	kernel_entry 1
+	mrs	x1, esr_el1
+	enable_dbg
+	mov	x0, sp
+	bl	do_serror
+	kernel_exit 1
+ENDPROC(el1_error)
+
+el0_error:
+	kernel_entry 0
+el0_error_naked:
+	mrs	x1, esr_el1
+	enable_dbg
+	mov	x0, sp
+	bl	do_serror
+	enable_daif
+	ct_user_exit
+	b	ret_to_user
+ENDPROC(el0_error)
+
+
 /*
  * This is the fast syscall return path.  We do as little as possible here,
  * and this includes saving x0 back into the kernel stack.
diff --git a/arch/arm64/kernel/traps.c b/arch/arm64/kernel/traps.c
index 1808be65d22f..773aae69c376 100644
--- a/arch/arm64/kernel/traps.c
+++ b/arch/arm64/kernel/traps.c
@@ -709,6 +709,19 @@  asmlinkage void handle_bad_stack(struct pt_regs *regs)
 }
 #endif
 
+asmlinkage void do_serror(struct pt_regs *regs, unsigned int esr)
+{
+	nmi_enter();
+
+	console_verbose();
+
+	pr_crit("SError Interrupt on CPU%d, code 0x%08x -- %s\n",
+		smp_processor_id(), esr, esr_get_class_string(esr));
+	__show_regs(regs);
+
+	panic("Asynchronous SError Interrupt");
+}
+
 void __pte_error(const char *file, int line, unsigned long val)
 {
 	pr_err("%s:%d: bad pte %016lx.\n", file, line, val);