[RFC PATCH] powerpc/64s: introduce call_realmode to run C code in real-mode
From: Nicholas Piggin <npiggin@gmail.com>
Date: 2021-02-12 01:22:42
Subsystem:
linux for powerpc (32-bit and 64-bit), the rest · Maintainers:
Madhavan Srinivasan, Linus Torvalds
The regular kernel stack can not be accessed in real mode in hash guest kernels, which prevents the MMU from being disabled in general C code. Provide a helper that can call a function pointer in real mode using the emergency stack (accessable in real mode). Signed-off-by: Nicholas Piggin <npiggin@gmail.com> --- arch/powerpc/include/asm/asm-prototypes.h | 1 + arch/powerpc/include/asm/book3s/64/mmu.h | 2 + arch/powerpc/include/asm/thread_info.h | 16 ++++++++ arch/powerpc/kernel/irq.c | 16 -------- arch/powerpc/kernel/misc_64.S | 22 +++++++++++ arch/powerpc/mm/book3s64/pgtable.c | 48 +++++++++++++++++++++++ 6 files changed, 89 insertions(+), 16 deletions(-)
diff --git a/arch/powerpc/include/asm/asm-prototypes.h b/arch/powerpc/include/asm/asm-prototypes.h
index d0b832cbbec8..a973023c390a 100644
--- a/arch/powerpc/include/asm/asm-prototypes.h
+++ b/arch/powerpc/include/asm/asm-prototypes.h@@ -126,6 +126,7 @@ extern s64 __ashldi3(s64, int); extern s64 __ashrdi3(s64, int); extern int __cmpdi2(s64, s64); extern int __ucmpdi2(u64, u64); +int __call_realmode(int (*fn)(void *arg), void *arg, void *sp); /* tracing */ void _mcount(void);
diff --git a/arch/powerpc/include/asm/book3s/64/mmu.h b/arch/powerpc/include/asm/book3s/64/mmu.h
index 995bbcdd0ef8..80b0d24415ac 100644
--- a/arch/powerpc/include/asm/book3s/64/mmu.h
+++ b/arch/powerpc/include/asm/book3s/64/mmu.h@@ -274,5 +274,7 @@ static inline unsigned long get_user_vsid(mm_context_t *ctx, return get_vsid(context, ea, ssize); } +int call_realmode(int (*fn)(void *arg), void *arg); + #endif /* __ASSEMBLY__ */ #endif /* _ASM_POWERPC_BOOK3S_64_MMU_H_ */
diff --git a/arch/powerpc/include/asm/thread_info.h b/arch/powerpc/include/asm/thread_info.h
index 3d8a47af7a25..9279e472d51e 100644
--- a/arch/powerpc/include/asm/thread_info.h
+++ b/arch/powerpc/include/asm/thread_info.h@@ -172,6 +172,22 @@ static inline bool test_thread_local_flags(unsigned int flags) #define is_elf2_task() (0) #endif +static inline void check_stack_overflow(void) +{ + long sp; + + if (!IS_ENABLED(CONFIG_DEBUG_STACKOVERFLOW)) + return; + + sp = current_stack_pointer & (THREAD_SIZE - 1); + + /* check for stack overflow: is there less than 2KB free? */ + if (unlikely(sp < 2048)) { + pr_err("do_IRQ: stack overflow: %ld\n", sp); + dump_stack(); + } +} + #endif /* !__ASSEMBLY__ */ #endif /* __KERNEL__ */
diff --git a/arch/powerpc/kernel/irq.c b/arch/powerpc/kernel/irq.c
index 6b1eca53e36c..193b47b5b6a5 100644
--- a/arch/powerpc/kernel/irq.c
+++ b/arch/powerpc/kernel/irq.c@@ -620,22 +620,6 @@ u64 arch_irq_stat_cpu(unsigned int cpu) return sum; } -static inline void check_stack_overflow(void) -{ - long sp; - - if (!IS_ENABLED(CONFIG_DEBUG_STACKOVERFLOW)) - return; - - sp = current_stack_pointer & (THREAD_SIZE - 1); - - /* check for stack overflow: is there less than 2KB free? */ - if (unlikely(sp < 2048)) { - pr_err("do_IRQ: stack overflow: %ld\n", sp); - dump_stack(); - } -} - void __do_irq(struct pt_regs *regs) { unsigned int irq;
diff --git a/arch/powerpc/kernel/misc_64.S b/arch/powerpc/kernel/misc_64.S
index 070465825c21..5e911d0b0b16 100644
--- a/arch/powerpc/kernel/misc_64.S
+++ b/arch/powerpc/kernel/misc_64.S@@ -27,6 +27,28 @@ .text +#ifdef CONFIG_PPC_BOOK3S_64 +_GLOBAL(__call_realmode) + mflr r0 + std r0,16(r1) + stdu r1,THREAD_SIZE-STACK_FRAME_OVERHEAD(r5) + mr r1,r5 + mtctr r3 + mr r3,r4 + mfmsr r4 + xori r4,r4,(MSR_IR|MSR_DR) + mtmsrd r4 + bctrl + mfmsr r4 + xori r4,r4,(MSR_IR|MSR_DR) + mtmsrd r4 + ld r1,0(r1) + ld r0,16(r1) + mtlr r0 + blr + +#endif + _GLOBAL(call_do_softirq) mflr r0 std r0,16(r1)
diff --git a/arch/powerpc/mm/book3s64/pgtable.c b/arch/powerpc/mm/book3s64/pgtable.c
index 5b3a3bae21aa..aad0e2059305 100644
--- a/arch/powerpc/mm/book3s64/pgtable.c
+++ b/arch/powerpc/mm/book3s64/pgtable.c@@ -474,6 +474,54 @@ int pmd_move_must_withdraw(struct spinlock *new_pmd_ptl, return true; } +/* + * Executing C code in real-mode in general Book3S-64 code can only be done + * via this function that switches the stack to one inside the real-mode-area, + * which may cover only a small first part of real memory on hash guest LPARs. + * fn must be NOKPROBES, must not access vmalloc or anything outside the RMA, + * probably shouldn't enable the MMU or interrupts, etc, and be very careful + * about calling other generic kernel or powerpc functions. + */ +int call_realmode(int (*fn)(void *arg), void *arg) +{ + unsigned long flags; + void *cursp, *emsp; + int ret; + + if (WARN_ON_ONCE(!(mfmsr() & MSR_DR))) + return -EINVAL; + if (WARN_ON_ONCE(!(mfmsr() & MSR_IR))) + return -EINVAL; + + /* + * The switch to emergency stack is only really required for HPT LPAR, + * but do it for all to help test coverage of tricky code. + */ + cursp = (void *)(current_stack_pointer & ~(THREAD_SIZE - 1)); + emsp = (void *)(local_paca->emergency_sp - THREAD_SIZE); + + /* + * It's probably okay to go to real-mode and call directly in case we + * are already on the emergency stack, so allow it. But we may want to + * prevent callers from doing this in future though, so warn. + */ + WARN_ON_ONCE(cursp == emsp); + + check_stack_overflow(); + + local_irq_save(flags); + hard_irq_disable(); + + if (cursp == emsp) + ret = fn(arg); + else + ret = __call_realmode(fn, arg, emsp); + + local_irq_restore(flags); + + return ret; +} + /* * Does the CPU support tlbie? */
--
2.23.0