From 3fdd2629516dd8be2b52a3a13e7ef5713411c7bf Mon Sep 17 00:00:00 2001 From: Nicholas Piggin Date: Sun, 8 Apr 2018 16:49:38 +1000 Subject: core/opal: Emergency stack for re-entry This detects OPAL being re-entered by the OS, and switches to an emergency stack if it was. This protects the firmware's main stack from re-entrancy and allows the OS to use NMI facilities for crash / debug functionality. Further nested re-entry will destroy the previous emergency stack and prevent returning, but those should be rare cases. This stack is sized at 16kB, which doubles the size of CPU stacks, so as not to introduce a regression in primary stack size. The 16kB stack originally had a 4kB machine check stack at the top, which was removed by 80eee1946 ("opal: Remove machine check interrupt patching in OPAL."). So it is possible the size could be tightened again, but that would require further analysis. Signed-off-by: Nicholas Piggin Signed-off-by: Stewart Smith --- core/cpu.c | 13 ++++++++----- core/stack.c | 13 +++++++++++-- 2 files changed, 19 insertions(+), 7 deletions(-) (limited to 'core') diff --git a/core/cpu.c b/core/cpu.c index b8d31e2..b2eecda 100644 --- a/core/cpu.c +++ b/core/cpu.c @@ -79,15 +79,18 @@ unsigned long __attrconst cpu_stack_bottom(unsigned int pir) unsigned long __attrconst cpu_stack_top(unsigned int pir) { - /* This is the top of the MC stack which is above the normal - * stack, which means a SP between cpu_stack_bottom() and - * cpu_stack_top() can either be a normal stack pointer or - * a Machine Check stack pointer - */ + /* This is the top of the normal stack. */ return ((unsigned long)&cpu_stacks[pir]) + NORMAL_STACK_SIZE - STACK_TOP_GAP; } +unsigned long __attrconst cpu_emergency_stack_top(unsigned int pir) +{ + /* This is the top of the emergency stack, above the normal stack. */ + return ((unsigned long)&cpu_stacks[pir]) + + NORMAL_STACK_SIZE + EMERGENCY_STACK_SIZE - STACK_TOP_GAP; +} + static void cpu_wake(struct cpu_thread *cpu) { /* Is it idle ? If not, no need to wake */ diff --git a/core/stack.c b/core/stack.c index 10118e4..73700ce 100644 --- a/core/stack.c +++ b/core/stack.c @@ -71,7 +71,7 @@ void ___print_backtrace(unsigned int pir, struct bt_entry *entries, static char bt_text_buf[4096]; int i, l = 0, max; char *buf = out_buf; - unsigned long bottom, top, tbot, ttop; + unsigned long bottom, top, normal_top, tbot, ttop; char mark; if (!out_buf) { @@ -81,7 +81,8 @@ void ___print_backtrace(unsigned int pir, struct bt_entry *entries, max = *len - 1; bottom = cpu_stack_bottom(pir); - top = cpu_stack_top(pir); + normal_top = cpu_stack_top(pir); + top = cpu_emergency_stack_top(pir); tbot = SKIBOOT_BASE; ttop = (unsigned long)&_etext; @@ -89,6 +90,8 @@ void ___print_backtrace(unsigned int pir, struct bt_entry *entries, for (i = 0; i < count && l < max; i++) { if (entries->sp < bottom || entries->sp > top) mark = '!'; + else if (entries->sp > normal_top) + mark = 'E'; else if (entries->pc < tbot || entries->pc > ttop) mark = '*'; else @@ -162,6 +165,12 @@ void __nomcount __mcount_stack_check(uint64_t sp, uint64_t lr) uint64_t top = base + NORMAL_STACK_SIZE; /* + * Don't check the emergency stack just yet. + */ + if (c->in_opal_call > 1) + return; + + /* * Don't re-enter on this CPU or don't enter at all if somebody * has spotted an overflow */ -- cgit v1.1