On 08/31/2013 05:12 PM, Yu Chen wrote:
>From 1ccf579b871dfd5938ce958f729361a203485c74 Mon Sep 17 00:00:00 2001
From: Yu Chen <chenyu...@gmail.com>
Date: Sat, 31 Aug 2013 23:52:31 +0800
Subject: [PATCH]  powerpc/85xx: Wakeup kexec smp slave cpus in second kernel

In current 85xx smp kexec implementation,master cpu reset slave cpus
by mpic_reset_core,
before jump to second kernel.In order to wake slave cpus up in second
kernel,we debug
this patch on p2041rdb.

What problem causes that you do the modification? I am just curious as kexec feature always is fine on our
P2041RDB board.:-)

Wei

The main principle of this patch,is to get slave cpus polling for flag
to change,
thus waiting for master cpu to set it with non-zero cpu number(see misc_32.S).
This flag is placed in kexec control page,so it would not be
overlapped when copying kimage.
The master cpu put flag's physical address in r28 as a parameter
passed to second kernel,
so the latter knows how to wake slave cpus up in smp_85xx_kick_cpu.
The pseudo-code may be like:
void slave_cpu_spin(void)
{
         int cpu = smp_processor_id();
         while (*kexec_poll != cpu)
                 ;
         /*slave wakeup and jump*/
         jump(*(kexec_poll+1));
}

void master_cpu_wakeup(unsigned long *kexec_poll, int cpu)
{
         *(kexec_poll+1) = __early_start;
         mb();
         *kexec_poll = cpu;
}

However,after applied this patch,we got some kernel exception during
booting second kernel,
I'm not sure if it's caused by improper treament of cache,or tlb,or
other.So I put this
patch here hoping someone can check and review it.

Signed-off-by: Yu Chen <chenyu...@gmail.com>
---
  arch/powerpc/kernel/head_fsl_booke.S |    7 ++
  arch/powerpc/kernel/misc_32.S        |   66 +++++++++++++-
  arch/powerpc/platforms/85xx/smp.c    |  166 ++++++++++++++++++++++++++++++----
  3 files changed, 222 insertions(+), 17 deletions(-)
  mode change 100644 => 100755 arch/powerpc/kernel/head_fsl_booke.S
  mode change 100644 => 100755 arch/powerpc/kernel/misc_32.S
  mode change 100644 => 100755 arch/powerpc/platforms/85xx/smp.c

diff --git a/arch/powerpc/kernel/head_fsl_booke.S
b/arch/powerpc/kernel/head_fsl_booke.S
old mode 100644
new mode 100755
index d10a7ca..63c8392
--- a/arch/powerpc/kernel/head_fsl_booke.S
+++ b/arch/powerpc/kernel/head_fsl_booke.S
@@ -178,6 +178,13 @@ _ENTRY(__early_start)
       * This is where the main kernel code starts.
       */

+#if defined(CONFIG_KEXEC) && defined(CONFIG_SMP)
+    /* r28 contain position where slave cpus spin*/
+    lis    r1,kexec_poll_phy@h
+    ori    r1,r1,kexec_poll_phy@l
+    stw    r28,0(r1)
+#endif
+
      /* ptr to current */
      lis    r2,init_task@h
      ori    r2,r2,init_task@l
diff --git a/arch/powerpc/kernel/misc_32.S b/arch/powerpc/kernel/misc_32.S
old mode 100644
new mode 100755
index e469f30..d9eefc2
--- a/arch/powerpc/kernel/misc_32.S
+++ b/arch/powerpc/kernel/misc_32.S
@@ -120,7 +120,7 @@ _GLOBAL(reloc_got2)
      addi    r4,r4,1b@l
      subf    r0,r4,r0
      add    r7,r0,r7
-2:    lwz    r0,0(r7)
+    2:    lwz    r0,0(r7)
      add    r0,r0,r3
      stw    r0,0(r7)
      addi    r7,r7,4
@@ -692,6 +692,7 @@ _GLOBAL(__main)
      blr

  #ifdef CONFIG_KEXEC
+#define KEXEC_MAGIC 0xdeadbeef
      /*
       * Must be relocatable PIC code callable as a C function.
       */
@@ -707,6 +708,16 @@ relocate_new_kernel:
      mr    r30, r4
      mr    r31, r5

+#ifdef CONFIG_SMP
+    bl    1f
+1:    mflr    r8
+    addi    r8,r8,kexec_flag-1b
+    lis     r7,PAGE_OFFSET@h
+    ori     r7,r7,PAGE_OFFSET@l
+    /*r28 contain slave cpu spin physical address */
+    subf    r28, r7, r8
+#endif
+
  #define ENTRY_MAPPING_KEXEC_SETUP
  #include "fsl_booke_entry_mapping.S"
  #undef ENTRY_MAPPING_KEXEC_SETUP
@@ -1172,4 +1183,57 @@ relocate_new_kernel_end:
      .globl relocate_new_kernel_size
  relocate_new_kernel_size:
      .long relocate_new_kernel_end - relocate_new_kernel
+#ifdef CONFIG_FSL_BOOKE
+    /**
+    * Slave cpus wait for kexec_flag to change
+    */
+    .globl relocate_smp_cpu_offset
+relocate_smp_cpu_offset:
+    .long relocate_smp_cpu_wait-relocate_new_kernel
+
+    .globl relocate_smp_cpu_wait
+relocate_smp_cpu_wait:
+
+    bl    1f
+1:    mflr    r5
+    addi    r5,r5,kexec_flag-1b
+    /*see if anyone calls me?*/
+    mfspr   r24,SPRN_PIR
+99:    lwz    r4,4(r5)
+    cmpw    r4,r24
+    msync
+    bne        99b
+
+    msync
+    /*r4 contains jump address*/
+    lwz    r4,8(r5)
+    msync
+    lis    r5,MSR_KERNEL@h
+    ori    r5,r5,MSR_KERNEL@l
+    msync
+    isync
+    mtspr    SPRN_SRR1, r5
+    mtspr    SPRN_SRR0, r4
+    msync
+    isync
+    rfi
+    isync
+1:    b    1b
+
+    /**
+    * kexec_flag indicates a kexec magic
+    * kexec_flag+4 bytes supposed to be set with cpu number
+    * kexec_flag+8 countain addr for slave cpu to jump into
+    */
+    .globl kexec_flag
+kexec_flag:
+    .long   KEXEC_MAGIC
+    .long    0
+    .long    0
+relocate_smp_cpu_wait_end:
+    .globl relocate_smp_cpu_size
+relocate_smp_cpu_size:
+    .long relocate_smp_cpu_wait_end-relocate_smp_cpu_wait
+#endif
+
  #endif
diff --git a/arch/powerpc/platforms/85xx/smp.c
b/arch/powerpc/platforms/85xx/smp.c
old mode 100644
new mode 100755
index 5ced4f5..c4f5c4c
--- a/arch/powerpc/platforms/85xx/smp.c
+++ b/arch/powerpc/platforms/85xx/smp.c
@@ -140,6 +140,70 @@ static inline u32 read_spin_table_addr_l(void *spin_table)
          (ulong)spin_table + sizeof(struct epapr_spin_table));
      return in_be32(&((struct epapr_spin_table *)spin_table)->addr_l);
  }
+#ifdef CONFIG_KEXEC
+
+#define KEXEC_MAGIC 0xdeadbeef
+#define KEXEC_RESERVE_LIMIT 0x10
+unsigned long kexec_poll_phy;
+extern void reserve_kexec_bootmem(unsigned long poll_phy, int size);
+
+/*
+ * Reserved bootmem for slave cpus kexec spin area.
+ */
+void mpc85xx_smp_reserve_kexec(void)
+{
+    unsigned long kexec_poll_virt;
+    unsigned long *kexec_magic_virt;
+
+    if (!kexec_poll_phy ||
+            kexec_poll_phy >= __max_low_memory)
+        return;
+
+    kexec_poll_virt = (unsigned long)phys_to_virt(kexec_poll_phy);
+    kexec_magic_virt = (unsigned long *)kexec_poll_virt;
+
+    if (*kexec_magic_virt == KEXEC_MAGIC)
+        reserve_kexec_bootmem(kexec_poll_phy, KEXEC_RESERVE_LIMIT);
+}
+
+/*
+ * Kick slave cpus from kexec spin area.
+ */
+int mpc85xx_smp_kick_kexec_cpus(int nr)
+{
+    unsigned long  kexec_poll_virt;
+    unsigned long *kexec_flag_virt;
+    unsigned long *kexec_magic_virt;
+    unsigned long *kexec_jump_virt;
+
+    /*verify accessible*/
+    if (!kexec_poll_phy ||
+            kexec_poll_phy >= __max_low_memory)
+        return -EBUSY;
+
+    kexec_poll_virt = (unsigned long)phys_to_virt(kexec_poll_phy);
+
+    kexec_magic_virt = (unsigned long *)kexec_poll_virt;
+    kexec_flag_virt = (unsigned long *)kexec_poll_virt + 1;
+    kexec_jump_virt = (unsigned long *)kexec_poll_virt + 2;
+
+    /*verify a valid kexec kick*/
+    if (*kexec_magic_virt == KEXEC_MAGIC) {
+        flush_dcache_range((ulong)kexec_poll_virt,
+        (ulong)kexec_poll_virt + L1_CACHE_BYTES-1);
+        *kexec_jump_virt = (unsigned long)__early_start;
+        mb();
+        /*kick cpu[nr] up*/
+        *kexec_flag_virt = nr;
+        mb();
+        flush_dcache_range((ulong)kexec_poll_virt,
+        (ulong)kexec_poll_virt + L1_CACHE_BYTES-1);
+
+        return 0;
+    }
+    return -EBUSY;
+}
+#endif

  static int smp_85xx_kick_cpu(int nr)
  {
@@ -181,6 +245,10 @@ static int smp_85xx_kick_cpu(int nr)

      local_irq_save(flags);
  #ifdef CONFIG_PPC32
+#ifdef CONFIG_KEXEC
+    if (!mpc85xx_smp_kick_kexec_cpus(nr))
+        goto kexec_kick_done;
+#endif
  #ifdef CONFIG_HOTPLUG_CPU
      /* Corresponding to generic_set_cpu_dead() */
      generic_set_cpu_up(nr);
@@ -225,7 +293,9 @@ static int smp_85xx_kick_cpu(int nr)
      out_be32(&spin_table->pir, hw_cpu);
      out_be32(&spin_table->addr_l, __pa(__early_start));
      flush_spin_table(spin_table);
-
+#ifdef CONFIG_KEXEC
+kexec_kick_done:
+#endif
      /* Wait a bit for the CPU to ack. */
      if (!spin_event_timeout(__secondary_hold_acknowledge == hw_cpu,
                      10000, 100)) {
@@ -266,7 +336,13 @@ struct smp_ops_t smp_85xx_ops = {
  };

  #ifdef CONFIG_KEXEC
+
  atomic_t kexec_down_cpus = ATOMIC_INIT(0);
+atomic_t kexec_ready_to_reboot = ATOMIC_INIT(0);
+atomic_t kexec_slave_finish = ATOMIC_INIT(0);
+unsigned long wait_code_buffer;
+static struct kimage *save_image;
+extern const unsigned int relocate_smp_cpu_size;

  void mpc85xx_smp_kexec_cpu_down(int crash_shutdown, int secondary)
  {
@@ -274,8 +350,29 @@ void mpc85xx_smp_kexec_cpu_down(int
crash_shutdown, int secondary)

      if (secondary) {
          atomic_inc(&kexec_down_cpus);
-        /* loop forever */
-        while (1);
+        mb();
+
+        if (crash_shutdown) {
+            /* loop forever */
+            while (1)
+                ;
+        } else {
+            while (!atomic_read(&kexec_ready_to_reboot))
+                cpu_relax();
+            /*flush destination*/
+            if (save_image)
+                mpc85xx_smp_flush_dcache_kexec(save_image, 1);
+
+            flush_icache_range(wait_code_buffer,
+                wait_code_buffer + relocate_smp_cpu_size);
+            flush_dcache_range(wait_code_buffer,
+                wait_code_buffer + relocate_smp_cpu_size);
+
+            atomic_inc(&kexec_slave_finish);
+
+            ((void (*)(void)) wait_code_buffer)();
+            /* NOTREACHED */
+        }
      }
  }

@@ -285,13 +382,23 @@ static void mpc85xx_smp_kexec_down(void *arg)
          ppc_md.kexec_cpu_down(0,1);
  }

-static void map_and_flush(unsigned long paddr)
+static void map_and_flush(unsigned long paddr, int atomic)
  {
      struct page *page = pfn_to_page(paddr >> PAGE_SHIFT);
-    unsigned long kaddr  = (unsigned long)kmap(page);
+    unsigned long kaddr;
+
+    if (atomic)
+        kaddr  = (unsigned long)kmap_atomic(page);
+    else
+        kaddr  = (unsigned long)kmap(page);

      flush_dcache_range(kaddr, kaddr + PAGE_SIZE);
-    kunmap(page);
+    flush_icache_range(kaddr, kaddr + PAGE_SIZE);
+
+    if (atomic)
+        kunmap_atomic((void *)kaddr);
+    else
+        kunmap(page);
  }

  /**
@@ -300,7 +407,7 @@ static void map_and_flush(unsigned long paddr)
   * are performed out of an overabundance of caution as interrupts are not
   * disabled yet and we can switch cores
   */
-static void mpc85xx_smp_flush_dcache_kexec(struct kimage *image)
+static void mpc85xx_smp_flush_dcache_kexec(struct kimage *image, int atomic)
  {
      kimage_entry_t *ptr, entry;
      unsigned long paddr;
@@ -312,18 +419,18 @@ static void
mpc85xx_smp_flush_dcache_kexec(struct kimage *image)
               ptr = (entry & IND_INDIRECTION) ?
                  phys_to_virt(entry & PAGE_MASK) : ptr + 1) {
              if (!(entry & IND_DESTINATION)) {
-                map_and_flush(entry);
+                map_and_flush(entry, atomic);
              }
          }
          /* flush out last IND_DONE page */
-        map_and_flush(entry);
+        map_and_flush(entry, atomic);
      } else {
          /* crash type kexec images are copied to the crash region */
          for (i = 0; i < image->nr_segments; i++) {
              struct kexec_segment *seg = &image->segment[i];
              for (paddr = seg->mem; paddr < seg->mem + seg->memsz;
                   paddr += PAGE_SIZE) {
-                map_and_flush(paddr);
+                map_and_flush(paddr, atomic);
              }
          }
      }
@@ -335,13 +442,18 @@ static void
mpc85xx_smp_flush_dcache_kexec(struct kimage *image)

  static void mpc85xx_smp_machine_kexec(struct kimage *image)
  {
+    extern const unsigned char  relocate_smp_cpu_wait[];
+    extern const unsigned int relocate_smp_cpu_offset;
      int timeout = INT_MAX;
      int i, num_cpus = num_present_cpus();

      mpc85xx_smp_flush_dcache_kexec(image);

-    if (image->type == KEXEC_TYPE_DEFAULT)
+    if (image->type == KEXEC_TYPE_DEFAULT) {
+        save_image = image;
+        mb();
          smp_call_function(mpc85xx_smp_kexec_down, NULL, 0);
+    }

      while ( (atomic_read(&kexec_down_cpus) != (num_cpus - 1)) &&
          ( timeout > 0 ) )
@@ -352,12 +464,34 @@ static void mpc85xx_smp_machine_kexec(struct
kimage *image)
      if ( !timeout )
          printk(KERN_ERR "Unable to bring down secondary cpu(s)");

-    for_each_online_cpu(i)
-    {
-        if ( i == smp_processor_id() ) continue;
-        mpic_reset_core(i);
-    }
+    if (image->type == KEXEC_TYPE_DEFAULT) {

+        wait_code_buffer =
+        (unsigned long)page_address(image->control_code_page)+
+                relocate_smp_cpu_offset;
+
+        /* copy slave cpu spin code to the control code page */
+        memcpy((void *)wait_code_buffer, relocate_smp_cpu_wait,
+                        relocate_smp_cpu_size);
+        atomic_set(&kexec_ready_to_reboot, 1);
+        mb();
+        timeout = INT_MAX;
+
+        while ((atomic_read(&kexec_slave_finish) != (num_cpus-1)) &&
+            (timeout > 0))
+            timeout--;
+
+        if (!timeout)
+            printk(KERN_ERR "Unable to wait for secondary cpu(s) to
flush caches\n");
+
+        } else {
+        for_each_online_cpu(i)
+        {
+            if (i == smp_processor_id())
+                continue;
+            mpic_reset_core(i);
+        }
+    }
      default_machine_kexec(image);
  }
  #endif /* CONFIG_KEXEC */

_______________________________________________
Linuxppc-dev mailing list
Linuxppc-dev@lists.ozlabs.org
https://lists.ozlabs.org/listinfo/linuxppc-dev

Reply via email to