diff mbox series

[RFC,2/6] powerpc/64s: Helpers to switch between linear and vmapped stack pointers

Message ID 20221104172737.391978-3-ajd@linux.ibm.com (mailing list archive)
State New
Headers show
Series VMAP_STACK support for book3s64 | expand

Commit Message

Andrew Donnellan Nov. 4, 2022, 5:27 p.m. UTC
powerpc unfortunately has too many places where we run stuff in real mode.

With CONFIG_VMAP_STACK enabled, this means we need to be able to swap the
stack pointer to use the linear mapping when we enter a real mode section,
and back afterwards.

Store the top bits of the stack pointer in both the linear map and the
vmalloc space in the PACA, and add some helper macros/functions to swap
between them.

Signed-off-by: Andrew Donnellan <ajd@linux.ibm.com>

---

Some of the helpers that are currently unused will be used in the next
version of the series for the KVM real mode handling
---
 arch/powerpc/include/asm/book3s/64/stack.h | 71 ++++++++++++++++++++++
 arch/powerpc/include/asm/opal.h            |  1 +
 arch/powerpc/include/asm/paca.h            |  4 ++
 arch/powerpc/include/asm/processor.h       |  6 ++
 arch/powerpc/kernel/asm-offsets.c          |  8 +++
 arch/powerpc/kernel/entry_64.S             |  7 +++
 arch/powerpc/kernel/process.c              |  4 ++
 arch/powerpc/kernel/smp.c                  |  7 +++
 arch/powerpc/xmon/xmon.c                   |  4 ++
 9 files changed, 112 insertions(+)
 create mode 100644 arch/powerpc/include/asm/book3s/64/stack.h

Comments

Christophe Leroy Nov. 5, 2022, 8 a.m. UTC | #1
Le 04/11/2022 à 18:27, Andrew Donnellan a écrit :
> powerpc unfortunately has too many places where we run stuff in real mode.
> 
> With CONFIG_VMAP_STACK enabled, this means we need to be able to swap the
> stack pointer to use the linear mapping when we enter a real mode section,
> and back afterwards.
> 
> Store the top bits of the stack pointer in both the linear map and the
> vmalloc space in the PACA, and add some helper macros/functions to swap
> between them.

That may work when pagesize is 64k because stack is on a single page, 
but I doubt is works with 4k pages, because vmalloc may allocate non 
contiguous pages.

> 
> Signed-off-by: Andrew Donnellan <ajd@linux.ibm.com>
> 
> ---
> 
> Some of the helpers that are currently unused will be used in the next
> version of the series for the KVM real mode handling
> ---
>   arch/powerpc/include/asm/book3s/64/stack.h | 71 ++++++++++++++++++++++
>   arch/powerpc/include/asm/opal.h            |  1 +
>   arch/powerpc/include/asm/paca.h            |  4 ++
>   arch/powerpc/include/asm/processor.h       |  6 ++
>   arch/powerpc/kernel/asm-offsets.c          |  8 +++
>   arch/powerpc/kernel/entry_64.S             |  7 +++
>   arch/powerpc/kernel/process.c              |  4 ++
>   arch/powerpc/kernel/smp.c                  |  7 +++
>   arch/powerpc/xmon/xmon.c                   |  4 ++
>   9 files changed, 112 insertions(+)
>   create mode 100644 arch/powerpc/include/asm/book3s/64/stack.h
> 
> diff --git a/arch/powerpc/include/asm/book3s/64/stack.h b/arch/powerpc/include/asm/book3s/64/stack.h
> new file mode 100644
> index 000000000000..6b31adb1a026
> --- /dev/null
> +++ b/arch/powerpc/include/asm/book3s/64/stack.h
> @@ -0,0 +1,71 @@
> +// SPDX-License-Identifier: GPL-2.0-or-later
> +
> +// Helpers for VMAP_STACK on book3s64
> +// Copyright (C) 2022 IBM Corporation (Andrew Donnellan)
> +
> +#ifndef _ASM_POWERPC_BOOK3S_64_STACK_H
> +#define _ASM_POWERPC_BOOK3S_64_STACK_H
> +
> +#include <asm/thread_info.h>
> +
> +#if defined(CONFIG_VMAP_STACK) && defined(CONFIG_PPC_BOOK3S_64)
> +
> +#ifdef __ASSEMBLY__
> +// Switch the current stack pointer in r1 between a linear map address and a
> +// vmalloc address. Used when we need to go in and out of real mode with
> +// CONFIG_VMAP_STACK enabled.
> +//
> +// tmp: scratch register that can be clobbered
> +
> +#define SWAP_STACK_LINEAR(tmp)			\
> +	ld	tmp, PACAKSTACK_LINEAR_BASE(r13);	\
> +	andi.	r1, r1, THREAD_SIZE - 1;		\

Do you assume THREAD_SIZE to never be more than 64k ?

> +	or	r1, r1, tmp;

You can probably do better with rldimi instruction.

> +#define SWAP_STACK_VMALLOC(tmp)			\
> +	ld	tmp, PACAKSTACK_VMALLOC_BASE(r13);	\
> +	andi.	r1, r1, THREAD_SIZE - 1;		\
> +	or	r1, r1, tmp;

Same

> +
> +#else // __ASSEMBLY__
> +
> +#include <asm/paca.h>
> +#include <asm/reg.h>
> +#include <linux/mm.h>
> +
> +#define stack_pa(ptr) (is_vmalloc_addr((ptr)) ? (void *)vmalloc_to_phys((void *)(ptr)) : (void *)ptr)
> +
> +static __always_inline void swap_stack_linear(void)
> +{
> +	current_stack_pointer = get_paca()->kstack_linear_base |	\
> +		(current_stack_pointer & (THREAD_SIZE - 1));

That looks hacky. I think you can't just change current_stack_pointer on 
the fly. You have to provide something similar to call_do_softirq() or 
call_do_irq()

> +}
> +
> +static __always_inline void swap_stack_vmalloc(void)
> +{
> +	current_stack_pointer = get_paca()->kstack_vmalloc_base |	\
> +		(current_stack_pointer & (THREAD_SIZE - 1));

Same

> +}
> +
> +#endif // __ASSEMBLY__
> +
> +#else // CONFIG_VMAP_STACK && CONFIG_PPC_BOOK3S_64
> +
> +#define SWAP_STACK_LINEAR(tmp)
> +#define SWAP_STACK_VMALLOC(tmp)
> +
> +static __always_inline void *stack_pa(void *ptr)
> +{
> +	return ptr;
> +}
> +
> +static __always_inline void swap_stack_linear(void)
> +{
> +}
> +
> +static __always_inline void swap_stack_vmalloc(void)
> +{
> +}
> +
> +#endif // CONFIG_VMAP_STACK && CONFIG_PPC_BOOK3S_64
> +
> +#endif // _ASM_POWERPC_BOOK3S_64_STACK_H
> diff --git a/arch/powerpc/include/asm/opal.h b/arch/powerpc/include/asm/opal.h
> index 726125a534de..0360360ad2cf 100644
> --- a/arch/powerpc/include/asm/opal.h
> +++ b/arch/powerpc/include/asm/opal.h
> @@ -13,6 +13,7 @@
>   #ifndef __ASSEMBLY__
>   
>   #include <linux/notifier.h>
> +#include <asm/book3s/64/stack.h>
>   
>   /* We calculate number of sg entries based on PAGE_SIZE */
>   #define SG_ENTRIES_PER_NODE ((PAGE_SIZE - 16) / sizeof(struct opal_sg_entry))
> diff --git a/arch/powerpc/include/asm/paca.h b/arch/powerpc/include/asm/paca.h
> index 09f1790d0ae1..51d060036fa1 100644
> --- a/arch/powerpc/include/asm/paca.h
> +++ b/arch/powerpc/include/asm/paca.h
> @@ -163,6 +163,10 @@ struct paca_struct {
>   	 */
>   	struct task_struct *__current;	/* Pointer to current */
>   	u64 kstack;			/* Saved Kernel stack addr */
> +#if defined(CONFIG_VMAP_STACK) && defined(CONFIG_PPC_BOOK3S_64)
> +	u64 kstack_vmalloc_base;	/* Base address of stack in the vmalloc mapping */
> +	u64 kstack_linear_base;		/* Base address of stack in the linear mapping */
> +#endif /* CONFIG_VMAP_STACK && CONFIG_PPC_BOOK3S_64 */
>   	u64 saved_r1;			/* r1 save for RTAS calls or PM or EE=0 */
>   	u64 saved_msr;			/* MSR saved here by enter_rtas */
>   #ifdef CONFIG_PPC64
> diff --git a/arch/powerpc/include/asm/processor.h b/arch/powerpc/include/asm/processor.h
> index 631802999d59..999078452aa4 100644
> --- a/arch/powerpc/include/asm/processor.h
> +++ b/arch/powerpc/include/asm/processor.h
> @@ -132,6 +132,12 @@ struct debug_reg {
>   
>   struct thread_struct {
>   	unsigned long	ksp;		/* Kernel stack pointer */
> +#if defined(CONFIG_VMAP_STACK) && defined(CONFIG_PPC_BOOK3S_64)
> +	// Kernel stack base addresses in vmalloc and linear mappings
> +	// Used for swapping to linear map in real mode code
> +	unsigned long	ksp_vmalloc_base;
> +	unsigned long	ksp_linear_base;
> +#endif /* CONFIG_VMAP_STACK && CONFIG_PPC_BOOK3S_64 */
>   
>   #ifdef CONFIG_PPC64
>   	unsigned long	ksp_vsid;
> diff --git a/arch/powerpc/kernel/asm-offsets.c b/arch/powerpc/kernel/asm-offsets.c
> index 4ce2a4aa3985..46ace958d3ce 100644
> --- a/arch/powerpc/kernel/asm-offsets.c
> +++ b/arch/powerpc/kernel/asm-offsets.c
> @@ -99,6 +99,10 @@ int main(void)
>   #endif
>   
>   	OFFSET(KSP, thread_struct, ksp);
> +#ifdef CONFIG_VMAP_STACK
> +	OFFSET(KSP_VMALLOC_BASE, thread_struct, ksp_vmalloc_base);
> +	OFFSET(KSP_LINEAR_BASE, thread_struct, ksp_linear_base);
> +#endif /* CONFIG_VMAP_STACK */
>   	OFFSET(PT_REGS, thread_struct, regs);
>   #ifdef CONFIG_BOOKE
>   	OFFSET(THREAD_NORMSAVES, thread_struct, normsave[0]);
> @@ -181,6 +185,10 @@ int main(void)
>   	OFFSET(PACAPACAINDEX, paca_struct, paca_index);
>   	OFFSET(PACAPROCSTART, paca_struct, cpu_start);
>   	OFFSET(PACAKSAVE, paca_struct, kstack);
> +#if defined(CONFIG_VMAP_STACK) && defined(CONFIG_PPC_BOOK3S_64)
> +	OFFSET(PACAKSTACK_VMALLOC_BASE, paca_struct, kstack_vmalloc_base);
> +	OFFSET(PACAKSTACK_LINEAR_BASE, paca_struct, kstack_linear_base);
> +#endif /* CONFIG_VMAP_STACK && CONFIG_PPC_BOOK3S_64 */
>   	OFFSET(PACACURRENT, paca_struct, __current);
>   	DEFINE(PACA_THREAD_INFO, offsetof(struct paca_struct, __current) +
>   				 offsetof(struct task_struct, thread_info));
> diff --git a/arch/powerpc/kernel/entry_64.S b/arch/powerpc/kernel/entry_64.S
> index af25db6e0205..cd9e56b25934 100644
> --- a/arch/powerpc/kernel/entry_64.S
> +++ b/arch/powerpc/kernel/entry_64.S
> @@ -253,6 +253,13 @@ END_FTR_SECTION_IFCLR(CPU_FTR_ARCH_207S)
>   	mr	r1,r8		/* start using new stack pointer */
>   	std	r7,PACAKSAVE(r13)
>   
> +#if defined(CONFIG_VMAP_STACK) && defined(CONFIG_PPC_BOOK3S_64)
> +	ld	r8,KSP_LINEAR_BASE(r4)
> +	std	r8,PACAKSTACK_LINEAR_BASE(r13)
> +	ld	r8,KSP_VMALLOC_BASE(r4)
> +	std	r8,PACAKSTACK_VMALLOC_BASE(r13)

Do you only have r8 to play with ? Otherwise I'd suggest to perform the 
two ld then the two std. Or maybe that doesn't matter on ppc64.

> +#endif /* CONFIG_VMAP_STACK && CONFIG_PPC_BOOK3S_64 */
> +
>   	ld	r6,_CCR(r1)
>   	mtcrf	0xFF,r6
>   
> diff --git a/arch/powerpc/kernel/process.c b/arch/powerpc/kernel/process.c
> index 67da147fe34d..07917726c629 100644
> --- a/arch/powerpc/kernel/process.c
> +++ b/arch/powerpc/kernel/process.c
> @@ -1782,6 +1782,10 @@ int copy_thread(struct task_struct *p, const struct kernel_clone_args *args)
>   	kregs = (struct pt_regs *) sp;
>   	sp -= STACK_FRAME_OVERHEAD;
>   	p->thread.ksp = sp;
> +#if defined(CONFIG_VMAP_STACK) && defined(CONFIG_PPC_BOOK3S_64)
> +	p->thread.ksp_vmalloc_base = sp & ~(THREAD_SIZE - 1);
> +	p->thread.ksp_linear_base = (u64)__va(vmalloc_to_pfn((void *)sp) << PAGE_SHIFT);

What about:

	page_to_virt(vmalloc_to_page((void *)sp))

But is that really the linear base you want, isn't it the phys address ? 
In that case you can do:

	page_to_phys(vmalloc_to_page((void *)sp))



> +#endif /* CONFIG_VMAP_STACK && CONFIG_PPC_BOOK3S_64 */
>   #ifdef CONFIG_HAVE_HW_BREAKPOINT
>   	for (i = 0; i < nr_wp_slots(); i++)
>   		p->thread.ptrace_bps[i] = NULL;
> diff --git a/arch/powerpc/kernel/smp.c b/arch/powerpc/kernel/smp.c
> index 0da6e59161cd..466ccab5adb8 100644
> --- a/arch/powerpc/kernel/smp.c
> +++ b/arch/powerpc/kernel/smp.c
> @@ -60,6 +60,7 @@
>   #include <asm/ftrace.h>
>   #include <asm/kup.h>
>   #include <asm/fadump.h>
> +#include <asm/book3s/64/stack.h>

Could we avoid including a book3s/64 header directly ? Could it come via 
a more generic one, maybe pgtable.h ?

As you can see with

	git grep "asm/book3s/64" arch/powerpc/

There are no direct inclusion of book3s64 headers in generic C files.


>   
>   #ifdef DEBUG
>   #include <asm/udbg.h>
> @@ -1250,6 +1251,12 @@ static void cpu_idle_thread_init(unsigned int cpu, struct task_struct *idle)
>   	paca_ptrs[cpu]->__current = idle;
>   	paca_ptrs[cpu]->kstack = (unsigned long)task_stack_page(idle) +
>   				 THREAD_SIZE - STACK_FRAME_OVERHEAD;
> +#if defined(CONFIG_VMAP_STACK) && defined(CONFIG_PPC_BOOK3S_64)
> +	paca_ptrs[cpu]->kstack_linear_base = is_vmalloc_addr((void *)paca_ptrs[cpu]->kstack) ?
> +		vmalloc_to_phys((void *)(paca_ptrs[cpu]->kstack)) :
> +		paca_ptrs[cpu]->kstack;
> +	paca_ptrs[cpu]->kstack_vmalloc_base = paca_ptrs[cpu]->kstack & (THREAD_SIZE - 1);
> +#endif // CONFIG_VMAP_STACK && CONFIG_PPC_BOOK3S_64
>   #endif
>   	task_thread_info(idle)->cpu = cpu;
>   	secondary_current = current_set[cpu] = idle;
> diff --git a/arch/powerpc/xmon/xmon.c b/arch/powerpc/xmon/xmon.c
> index f51c882bf902..236287c4a231 100644
> --- a/arch/powerpc/xmon/xmon.c
> +++ b/arch/powerpc/xmon/xmon.c
> @@ -2697,6 +2697,10 @@ static void dump_one_paca(int cpu)
>   	DUMP(p, __current, "%-*px");
>   	DUMP(p, kstack, "%#-*llx");
>   	printf(" %-*s = 0x%016llx\n", 25, "kstack_base", p->kstack & ~(THREAD_SIZE - 1));
> +#if defined(CONFIG_VMAP_STACK) && defined(CONFIG_PPC_BOOK3S_64)
> +	DUMP(p, kstack_linear_base, "%#-*llx");
> +	DUMP(p, kstack_vmalloc_base, "%#-*llx");
> +#endif
>   #ifdef CONFIG_STACKPROTECTOR
>   	DUMP(p, canary, "%#-*lx");
>   #endif
Christophe Leroy Nov. 5, 2022, 7:28 p.m. UTC | #2
Le 05/11/2022 à 09:00, Christophe Leroy a écrit :
> 
> 
> Le 04/11/2022 à 18:27, Andrew Donnellan a écrit :
>> powerpc unfortunately has too many places where we run stuff in real mode.
>>
>> With CONFIG_VMAP_STACK enabled, this means we need to be able to swap the
>> stack pointer to use the linear mapping when we enter a real mode section,
>> and back afterwards.
>>
>> Store the top bits of the stack pointer in both the linear map and the
>> vmalloc space in the PACA, and add some helper macros/functions to swap
>> between them.
> 
> That may work when pagesize is 64k because stack is on a single page,
> but I doubt is works with 4k pages, because vmalloc may allocate non
> contiguous pages.
> 

[snip]

> 
>> +
>> +#else // __ASSEMBLY__
>> +
>> +#include <asm/paca.h>
>> +#include <asm/reg.h>
>> +#include <linux/mm.h>
>> +
>> +#define stack_pa(ptr) (is_vmalloc_addr((ptr)) ? (void *)vmalloc_to_phys((void *)(ptr)) : (void *)ptr)
>> +
>> +static __always_inline void swap_stack_linear(void)
>> +{
>> +	current_stack_pointer = get_paca()->kstack_linear_base |	\
>> +		(current_stack_pointer & (THREAD_SIZE - 1));
> 
> That looks hacky. I think you can't just change current_stack_pointer on
> the fly. You have to provide something similar to call_do_softirq() or
> call_do_irq()
> 

Maybe you can have a look at Nic's RFC for calling functions in real 
mode : 
https://patchwork.ozlabs.org/project/linuxppc-dev/patch/20210212012041.392566-1-npiggin@gmail.com/

Christophe
Nicholas Piggin Nov. 7, 2022, 12:38 p.m. UTC | #3
On Sat Nov 5, 2022 at 6:00 PM AEST, Christophe Leroy wrote:
>
>
> Le 04/11/2022 à 18:27, Andrew Donnellan a écrit :
> > powerpc unfortunately has too many places where we run stuff in real mode.
> > 
> > With CONFIG_VMAP_STACK enabled, this means we need to be able to swap the
> > stack pointer to use the linear mapping when we enter a real mode section,
> > and back afterwards.
> > 
> > Store the top bits of the stack pointer in both the linear map and the
> > vmalloc space in the PACA, and add some helper macros/functions to swap
> > between them.
>
> That may work when pagesize is 64k because stack is on a single page, 
> but I doubt is works with 4k pages, because vmalloc may allocate non 
> contiguous pages.

Yeah. This could be a first-stage though, and depend on 64k page size
and stack size, or !KVM or whatever. When the real-mode code is solved,
that could be relaxed.

Thanks,
Nick
diff mbox series

Patch

diff --git a/arch/powerpc/include/asm/book3s/64/stack.h b/arch/powerpc/include/asm/book3s/64/stack.h
new file mode 100644
index 000000000000..6b31adb1a026
--- /dev/null
+++ b/arch/powerpc/include/asm/book3s/64/stack.h
@@ -0,0 +1,71 @@ 
+// SPDX-License-Identifier: GPL-2.0-or-later
+
+// Helpers for VMAP_STACK on book3s64
+// Copyright (C) 2022 IBM Corporation (Andrew Donnellan)
+
+#ifndef _ASM_POWERPC_BOOK3S_64_STACK_H
+#define _ASM_POWERPC_BOOK3S_64_STACK_H
+
+#include <asm/thread_info.h>
+
+#if defined(CONFIG_VMAP_STACK) && defined(CONFIG_PPC_BOOK3S_64)
+
+#ifdef __ASSEMBLY__
+// Switch the current stack pointer in r1 between a linear map address and a
+// vmalloc address. Used when we need to go in and out of real mode with
+// CONFIG_VMAP_STACK enabled.
+//
+// tmp: scratch register that can be clobbered
+
+#define SWAP_STACK_LINEAR(tmp)			\
+	ld	tmp, PACAKSTACK_LINEAR_BASE(r13);	\
+	andi.	r1, r1, THREAD_SIZE - 1;		\
+	or	r1, r1, tmp;
+#define SWAP_STACK_VMALLOC(tmp)			\
+	ld	tmp, PACAKSTACK_VMALLOC_BASE(r13);	\
+	andi.	r1, r1, THREAD_SIZE - 1;		\
+	or	r1, r1, tmp;
+
+#else // __ASSEMBLY__
+
+#include <asm/paca.h>
+#include <asm/reg.h>
+#include <linux/mm.h>
+
+#define stack_pa(ptr) (is_vmalloc_addr((ptr)) ? (void *)vmalloc_to_phys((void *)(ptr)) : (void *)ptr)
+
+static __always_inline void swap_stack_linear(void)
+{
+	current_stack_pointer = get_paca()->kstack_linear_base |	\
+		(current_stack_pointer & (THREAD_SIZE - 1));
+}
+
+static __always_inline void swap_stack_vmalloc(void)
+{
+	current_stack_pointer = get_paca()->kstack_vmalloc_base |	\
+		(current_stack_pointer & (THREAD_SIZE - 1));
+}
+
+#endif // __ASSEMBLY__
+
+#else // CONFIG_VMAP_STACK && CONFIG_PPC_BOOK3S_64
+
+#define SWAP_STACK_LINEAR(tmp)
+#define SWAP_STACK_VMALLOC(tmp)
+
+static __always_inline void *stack_pa(void *ptr)
+{
+	return ptr;
+}
+
+static __always_inline void swap_stack_linear(void)
+{
+}
+
+static __always_inline void swap_stack_vmalloc(void)
+{
+}
+
+#endif // CONFIG_VMAP_STACK && CONFIG_PPC_BOOK3S_64
+
+#endif // _ASM_POWERPC_BOOK3S_64_STACK_H
diff --git a/arch/powerpc/include/asm/opal.h b/arch/powerpc/include/asm/opal.h
index 726125a534de..0360360ad2cf 100644
--- a/arch/powerpc/include/asm/opal.h
+++ b/arch/powerpc/include/asm/opal.h
@@ -13,6 +13,7 @@ 
 #ifndef __ASSEMBLY__
 
 #include <linux/notifier.h>
+#include <asm/book3s/64/stack.h>
 
 /* We calculate number of sg entries based on PAGE_SIZE */
 #define SG_ENTRIES_PER_NODE ((PAGE_SIZE - 16) / sizeof(struct opal_sg_entry))
diff --git a/arch/powerpc/include/asm/paca.h b/arch/powerpc/include/asm/paca.h
index 09f1790d0ae1..51d060036fa1 100644
--- a/arch/powerpc/include/asm/paca.h
+++ b/arch/powerpc/include/asm/paca.h
@@ -163,6 +163,10 @@  struct paca_struct {
 	 */
 	struct task_struct *__current;	/* Pointer to current */
 	u64 kstack;			/* Saved Kernel stack addr */
+#if defined(CONFIG_VMAP_STACK) && defined(CONFIG_PPC_BOOK3S_64)
+	u64 kstack_vmalloc_base;	/* Base address of stack in the vmalloc mapping */
+	u64 kstack_linear_base;		/* Base address of stack in the linear mapping */
+#endif /* CONFIG_VMAP_STACK && CONFIG_PPC_BOOK3S_64 */
 	u64 saved_r1;			/* r1 save for RTAS calls or PM or EE=0 */
 	u64 saved_msr;			/* MSR saved here by enter_rtas */
 #ifdef CONFIG_PPC64
diff --git a/arch/powerpc/include/asm/processor.h b/arch/powerpc/include/asm/processor.h
index 631802999d59..999078452aa4 100644
--- a/arch/powerpc/include/asm/processor.h
+++ b/arch/powerpc/include/asm/processor.h
@@ -132,6 +132,12 @@  struct debug_reg {
 
 struct thread_struct {
 	unsigned long	ksp;		/* Kernel stack pointer */
+#if defined(CONFIG_VMAP_STACK) && defined(CONFIG_PPC_BOOK3S_64)
+	// Kernel stack base addresses in vmalloc and linear mappings
+	// Used for swapping to linear map in real mode code
+	unsigned long	ksp_vmalloc_base;
+	unsigned long	ksp_linear_base;
+#endif /* CONFIG_VMAP_STACK && CONFIG_PPC_BOOK3S_64 */
 
 #ifdef CONFIG_PPC64
 	unsigned long	ksp_vsid;
diff --git a/arch/powerpc/kernel/asm-offsets.c b/arch/powerpc/kernel/asm-offsets.c
index 4ce2a4aa3985..46ace958d3ce 100644
--- a/arch/powerpc/kernel/asm-offsets.c
+++ b/arch/powerpc/kernel/asm-offsets.c
@@ -99,6 +99,10 @@  int main(void)
 #endif
 
 	OFFSET(KSP, thread_struct, ksp);
+#ifdef CONFIG_VMAP_STACK
+	OFFSET(KSP_VMALLOC_BASE, thread_struct, ksp_vmalloc_base);
+	OFFSET(KSP_LINEAR_BASE, thread_struct, ksp_linear_base);
+#endif /* CONFIG_VMAP_STACK */
 	OFFSET(PT_REGS, thread_struct, regs);
 #ifdef CONFIG_BOOKE
 	OFFSET(THREAD_NORMSAVES, thread_struct, normsave[0]);
@@ -181,6 +185,10 @@  int main(void)
 	OFFSET(PACAPACAINDEX, paca_struct, paca_index);
 	OFFSET(PACAPROCSTART, paca_struct, cpu_start);
 	OFFSET(PACAKSAVE, paca_struct, kstack);
+#if defined(CONFIG_VMAP_STACK) && defined(CONFIG_PPC_BOOK3S_64)
+	OFFSET(PACAKSTACK_VMALLOC_BASE, paca_struct, kstack_vmalloc_base);
+	OFFSET(PACAKSTACK_LINEAR_BASE, paca_struct, kstack_linear_base);
+#endif /* CONFIG_VMAP_STACK && CONFIG_PPC_BOOK3S_64 */
 	OFFSET(PACACURRENT, paca_struct, __current);
 	DEFINE(PACA_THREAD_INFO, offsetof(struct paca_struct, __current) +
 				 offsetof(struct task_struct, thread_info));
diff --git a/arch/powerpc/kernel/entry_64.S b/arch/powerpc/kernel/entry_64.S
index af25db6e0205..cd9e56b25934 100644
--- a/arch/powerpc/kernel/entry_64.S
+++ b/arch/powerpc/kernel/entry_64.S
@@ -253,6 +253,13 @@  END_FTR_SECTION_IFCLR(CPU_FTR_ARCH_207S)
 	mr	r1,r8		/* start using new stack pointer */
 	std	r7,PACAKSAVE(r13)
 
+#if defined(CONFIG_VMAP_STACK) && defined(CONFIG_PPC_BOOK3S_64)
+	ld	r8,KSP_LINEAR_BASE(r4)
+	std	r8,PACAKSTACK_LINEAR_BASE(r13)
+	ld	r8,KSP_VMALLOC_BASE(r4)
+	std	r8,PACAKSTACK_VMALLOC_BASE(r13)
+#endif /* CONFIG_VMAP_STACK && CONFIG_PPC_BOOK3S_64 */
+
 	ld	r6,_CCR(r1)
 	mtcrf	0xFF,r6
 
diff --git a/arch/powerpc/kernel/process.c b/arch/powerpc/kernel/process.c
index 67da147fe34d..07917726c629 100644
--- a/arch/powerpc/kernel/process.c
+++ b/arch/powerpc/kernel/process.c
@@ -1782,6 +1782,10 @@  int copy_thread(struct task_struct *p, const struct kernel_clone_args *args)
 	kregs = (struct pt_regs *) sp;
 	sp -= STACK_FRAME_OVERHEAD;
 	p->thread.ksp = sp;
+#if defined(CONFIG_VMAP_STACK) && defined(CONFIG_PPC_BOOK3S_64)
+	p->thread.ksp_vmalloc_base = sp & ~(THREAD_SIZE - 1);
+	p->thread.ksp_linear_base = (u64)__va(vmalloc_to_pfn((void *)sp) << PAGE_SHIFT);
+#endif /* CONFIG_VMAP_STACK && CONFIG_PPC_BOOK3S_64 */
 #ifdef CONFIG_HAVE_HW_BREAKPOINT
 	for (i = 0; i < nr_wp_slots(); i++)
 		p->thread.ptrace_bps[i] = NULL;
diff --git a/arch/powerpc/kernel/smp.c b/arch/powerpc/kernel/smp.c
index 0da6e59161cd..466ccab5adb8 100644
--- a/arch/powerpc/kernel/smp.c
+++ b/arch/powerpc/kernel/smp.c
@@ -60,6 +60,7 @@ 
 #include <asm/ftrace.h>
 #include <asm/kup.h>
 #include <asm/fadump.h>
+#include <asm/book3s/64/stack.h>
 
 #ifdef DEBUG
 #include <asm/udbg.h>
@@ -1250,6 +1251,12 @@  static void cpu_idle_thread_init(unsigned int cpu, struct task_struct *idle)
 	paca_ptrs[cpu]->__current = idle;
 	paca_ptrs[cpu]->kstack = (unsigned long)task_stack_page(idle) +
 				 THREAD_SIZE - STACK_FRAME_OVERHEAD;
+#if defined(CONFIG_VMAP_STACK) && defined(CONFIG_PPC_BOOK3S_64)
+	paca_ptrs[cpu]->kstack_linear_base = is_vmalloc_addr((void *)paca_ptrs[cpu]->kstack) ?
+		vmalloc_to_phys((void *)(paca_ptrs[cpu]->kstack)) :
+		paca_ptrs[cpu]->kstack;
+	paca_ptrs[cpu]->kstack_vmalloc_base = paca_ptrs[cpu]->kstack & (THREAD_SIZE - 1);
+#endif // CONFIG_VMAP_STACK && CONFIG_PPC_BOOK3S_64
 #endif
 	task_thread_info(idle)->cpu = cpu;
 	secondary_current = current_set[cpu] = idle;
diff --git a/arch/powerpc/xmon/xmon.c b/arch/powerpc/xmon/xmon.c
index f51c882bf902..236287c4a231 100644
--- a/arch/powerpc/xmon/xmon.c
+++ b/arch/powerpc/xmon/xmon.c
@@ -2697,6 +2697,10 @@  static void dump_one_paca(int cpu)
 	DUMP(p, __current, "%-*px");
 	DUMP(p, kstack, "%#-*llx");
 	printf(" %-*s = 0x%016llx\n", 25, "kstack_base", p->kstack & ~(THREAD_SIZE - 1));
+#if defined(CONFIG_VMAP_STACK) && defined(CONFIG_PPC_BOOK3S_64)
+	DUMP(p, kstack_linear_base, "%#-*llx");
+	DUMP(p, kstack_vmalloc_base, "%#-*llx");
+#endif
 #ifdef CONFIG_STACKPROTECTOR
 	DUMP(p, canary, "%#-*lx");
 #endif