powerpc/kexec: Switch to a static PACA on the way out
authorMatt Evans <matt@ozlabs.org>
Wed, 7 Jul 2010 21:55:37 +0000 (21:55 +0000)
committerBenjamin Herrenschmidt <benh@kernel.crashing.org>
Sat, 31 Jul 2010 04:56:30 +0000 (14:56 +1000)
With dynamic PACAs, the kexecing CPU's PACA won't lie within the kernel
static data and there is a chance that something may stomp it when preparing
to kexec.  This patch switches this final CPU to a static PACA just before
we pull the switch.

Signed-off-by: Matt Evans <matt@ozlabs.org>
Signed-off-by: Benjamin Herrenschmidt <benh@kernel.crashing.org>
arch/powerpc/include/asm/paca.h
arch/powerpc/kernel/machine_kexec_64.c
arch/powerpc/kernel/paca.c
arch/powerpc/kernel/setup_64.c

index 8ce7963..1ff6662 100644 (file)
@@ -146,7 +146,7 @@ struct paca_struct {
 extern struct paca_struct *paca;
 extern __initdata struct paca_struct boot_paca;
 extern void initialise_paca(struct paca_struct *new_paca, int cpu);
-
+extern void setup_paca(struct paca_struct *new_paca);
 extern void allocate_pacas(void);
 extern void free_unused_pacas(void);
 
index de6b70e..022d2f6 100644 (file)
@@ -260,6 +260,12 @@ static void kexec_prepare_cpus(void)
 static union thread_union kexec_stack __init_task_data =
        { };
 
+/*
+ * For similar reasons to the stack above, the kexecing CPU needs to be on a
+ * static PACA; we switch to kexec_paca.
+ */
+struct paca_struct kexec_paca;
+
 /* Our assembly helper, in kexec_stub.S */
 extern NORET_TYPE void kexec_sequence(void *newstack, unsigned long start,
                                        void *image, void *control,
@@ -287,6 +293,20 @@ void default_machine_kexec(struct kimage *image)
        kexec_stack.thread_info.task = current_thread_info()->task;
        kexec_stack.thread_info.flags = 0;
 
+       /* We need a static PACA, too; copy this CPU's PACA over and switch to
+        * it.  Also poison per_cpu_offset to catch anyone using non-static
+        * data.
+        */
+       memcpy(&kexec_paca, get_paca(), sizeof(struct paca_struct));
+       kexec_paca.data_offset = 0xedeaddeadeeeeeeeUL;
+       paca = (struct paca_struct *)RELOC_HIDE(&kexec_paca, 0) -
+               kexec_paca.paca_index;
+       setup_paca(&kexec_paca);
+
+       /* XXX: If anyone does 'dynamic lppacas' this will also need to be
+        * switched to a static version!
+        */
+
        /* Some things are best done in assembly.  Finding globals with
         * a toc is easier in C, so pass in what we can.
         */
index f88acf0..3db8d64 100644 (file)
@@ -105,6 +105,16 @@ void __init initialise_paca(struct paca_struct *new_paca, int cpu)
 #endif /* CONFIG_PPC_STD_MMU_64 */
 }
 
+/* Put the paca pointer into r13 and SPRG_PACA */
+void setup_paca(struct paca_struct *new_paca)
+{
+       local_paca = new_paca;
+       mtspr(SPRN_SPRG_PACA, local_paca);
+#ifdef CONFIG_PPC_BOOK3E
+       mtspr(SPRN_SPRG_TLB_EXFRAME, local_paca->extlb);
+#endif
+}
+
 static int __initdata paca_size;
 
 void __init allocate_pacas(void)
index c352f32..96e662c 100644 (file)
@@ -142,16 +142,6 @@ early_param("smt-enabled", early_smt_enabled);
 #define check_smt_enabled()
 #endif /* CONFIG_SMP */
 
-/* Put the paca pointer into r13 and SPRG_PACA */
-static void __init setup_paca(struct paca_struct *new_paca)
-{
-       local_paca = new_paca;
-       mtspr(SPRN_SPRG_PACA, local_paca);
-#ifdef CONFIG_PPC_BOOK3E
-       mtspr(SPRN_SPRG_TLB_EXFRAME, local_paca->extlb);
-#endif
-}
-
 /*
  * Early initialization entry point. This is called by head.S
  * with MMU translation disabled. We rely on the "feature" of