@@ -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);
@@ -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_ */
@@ -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__ */
@@ -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;
@@ -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)
@@ -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?
*/
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(-)