[9/9] hw/arm/raspi: Provide spin-loop code for AArch64 CPUs

Message ID 20180313153458.26822-10-peter.maydell@linaro.org
State Superseded
Headers show
Series
  • raspi3: various fixes for Linux booting
Related show

Commit Message

Peter Maydell March 13, 2018, 3:34 p.m.
The raspi3 has AArch64 CPUs, which means that our smpboot
code for keeping the secondary CPUs in a pen needs to have
a version for A64 as well as A32. Without this, the
secondary CPUs go into an infinite loop of taking undefined
instruction exceptions.

Signed-off-by: Peter Maydell <peter.maydell@linaro.org>

---
 hw/arm/raspi.c | 41 ++++++++++++++++++++++++++++++++++++++++-
 1 file changed, 40 insertions(+), 1 deletion(-)

-- 
2.16.2

Comments

Philippe Mathieu-Daudé March 15, 2018, 12:31 p.m. | #1
On 03/13/2018 04:34 PM, Peter Maydell wrote:
> The raspi3 has AArch64 CPUs, which means that our smpboot

> code for keeping the secondary CPUs in a pen needs to have

> a version for A64 as well as A32. Without this, the

> secondary CPUs go into an infinite loop of taking undefined

> instruction exceptions.

> 

> Signed-off-by: Peter Maydell <peter.maydell@linaro.org>

> ---

>  hw/arm/raspi.c | 41 ++++++++++++++++++++++++++++++++++++++++-

>  1 file changed, 40 insertions(+), 1 deletion(-)

> 

> diff --git a/hw/arm/raspi.c b/hw/arm/raspi.c

> index ae15997669..06f1e08ca9 100644

> --- a/hw/arm/raspi.c

> +++ b/hw/arm/raspi.c

> @@ -27,6 +27,7 @@

>  #define BOARDSETUP_ADDR (MVBAR_ADDR + 0x20) /* board setup code */

>  #define FIRMWARE_ADDR_2 0x8000 /* Pi 2 loads kernel.img here by default */

>  #define FIRMWARE_ADDR_3 0x80000 /* Pi 3 loads kernel.img here by default */

> +#define SPINTABLE_ADDR  0xd8 /* Pi 3 bootloader spintable */

>  

>  /* Table of Linux board IDs for different Pi versions */

>  static const int raspi_boardid[] = {[1] = 0xc42, [2] = 0xc43, [3] = 0xc44};

> @@ -63,6 +64,40 @@ static void write_smpboot(ARMCPU *cpu, const struct arm_boot_info *info)

>                         info->smp_loader_start);

>  }

>  

> +static void write_smpboot64(ARMCPU *cpu, const struct arm_boot_info *info)

> +{

> +    /* Unlike the AArch32 version we don't need to call the board setup hook.

> +     * The mechanism for doing the spin-table is also entirely different.

> +     * We must have four 64-bit fields at absolute addresses

> +     * 0xd8, 0xe0, 0xe8, 0xf0 in RAM, which are the flag variables for

> +     * our CPUs, and which we must ensure are zero initialized before

> +     * the primary CPU goes into the kernel. We put these variables inside

> +     * a rom blob, so that the reset for ROM contents zeroes them for us.

> +     */


Checking with Linux doc/Documentation/arm64/booting.txt and
arch/arm/boot/dts/bcm2837.dtsi.

> +    static const uint32_t smpboot[] = {

> +        0xd2801b05, /*        mov     x5, 0xd8 */


x5 = &spintable;

> +        0xd53800a6, /*        mrs     x6, mpidr_el1 */

> +        0x924004c6, /*        and     x6, x6, #0x3 */


x6 = clusterid;

> +        0xd503205f, /* spin:  wfe */

> +        0xf86678a4, /*        ldr     x4, [x5,x6,lsl #3] */


x4 = &spintable[clusterid];

> +        0xb4ffffc4, /*        cbz     x4, spin */


loop if !x4 ...

> +        0xd2800000, /*        mov     x0, #0x0 */

> +        0xd2800001, /*        mov     x1, #0x0 */

> +        0xd2800002, /*        mov     x2, #0x0 */

> +        0xd2800003, /*        mov     x3, #0x0 */

> +        0xd61f0080, /*        br      x4 */


... else jump()

> +    };

> +

> +    static const uint64_t spintables[] = {

> +        0, 0, 0, 0


the "naturally-aligned 64-bit zero-initalised memory"

> +    };

> +

> +    rom_add_blob_fixed("raspi_smpboot", smpboot, sizeof(smpboot),

> +                       info->smp_loader_start);

> +    rom_add_blob_fixed("raspi_spintables", spintables, sizeof(spintables),

> +                       SPINTABLE_ADDR);


Reviewed-by: Philippe Mathieu-Daudé <f4bug@amsat.org>


> +}

> +

>  static void write_board_setup(ARMCPU *cpu, const struct arm_boot_info *info)

>  {

>      arm_write_secure_board_setup_dummy_smc(cpu, info, MVBAR_ADDR);

> @@ -99,7 +134,11 @@ static void setup_boot(MachineState *machine, int version, size_t ram_size)

>      /* Pi2 and Pi3 requires SMP setup */

>      if (version >= 2) {

>          binfo.smp_loader_start = SMPBOOT_ADDR;

> -        binfo.write_secondary_boot = write_smpboot;

> +        if (version == 2) {

> +            binfo.write_secondary_boot = write_smpboot;

> +        } else {

> +            binfo.write_secondary_boot = write_smpboot64;

> +        }

>          binfo.secondary_cpu_reset_hook = reset_secondary;

>      }

>  

>

Patch

diff --git a/hw/arm/raspi.c b/hw/arm/raspi.c
index ae15997669..06f1e08ca9 100644
--- a/hw/arm/raspi.c
+++ b/hw/arm/raspi.c
@@ -27,6 +27,7 @@ 
 #define BOARDSETUP_ADDR (MVBAR_ADDR + 0x20) /* board setup code */
 #define FIRMWARE_ADDR_2 0x8000 /* Pi 2 loads kernel.img here by default */
 #define FIRMWARE_ADDR_3 0x80000 /* Pi 3 loads kernel.img here by default */
+#define SPINTABLE_ADDR  0xd8 /* Pi 3 bootloader spintable */
 
 /* Table of Linux board IDs for different Pi versions */
 static const int raspi_boardid[] = {[1] = 0xc42, [2] = 0xc43, [3] = 0xc44};
@@ -63,6 +64,40 @@  static void write_smpboot(ARMCPU *cpu, const struct arm_boot_info *info)
                        info->smp_loader_start);
 }
 
+static void write_smpboot64(ARMCPU *cpu, const struct arm_boot_info *info)
+{
+    /* Unlike the AArch32 version we don't need to call the board setup hook.
+     * The mechanism for doing the spin-table is also entirely different.
+     * We must have four 64-bit fields at absolute addresses
+     * 0xd8, 0xe0, 0xe8, 0xf0 in RAM, which are the flag variables for
+     * our CPUs, and which we must ensure are zero initialized before
+     * the primary CPU goes into the kernel. We put these variables inside
+     * a rom blob, so that the reset for ROM contents zeroes them for us.
+     */
+    static const uint32_t smpboot[] = {
+        0xd2801b05, /*        mov     x5, 0xd8 */
+        0xd53800a6, /*        mrs     x6, mpidr_el1 */
+        0x924004c6, /*        and     x6, x6, #0x3 */
+        0xd503205f, /* spin:  wfe */
+        0xf86678a4, /*        ldr     x4, [x5,x6,lsl #3] */
+        0xb4ffffc4, /*        cbz     x4, spin */
+        0xd2800000, /*        mov     x0, #0x0 */
+        0xd2800001, /*        mov     x1, #0x0 */
+        0xd2800002, /*        mov     x2, #0x0 */
+        0xd2800003, /*        mov     x3, #0x0 */
+        0xd61f0080, /*        br      x4 */
+    };
+
+    static const uint64_t spintables[] = {
+        0, 0, 0, 0
+    };
+
+    rom_add_blob_fixed("raspi_smpboot", smpboot, sizeof(smpboot),
+                       info->smp_loader_start);
+    rom_add_blob_fixed("raspi_spintables", spintables, sizeof(spintables),
+                       SPINTABLE_ADDR);
+}
+
 static void write_board_setup(ARMCPU *cpu, const struct arm_boot_info *info)
 {
     arm_write_secure_board_setup_dummy_smc(cpu, info, MVBAR_ADDR);
@@ -99,7 +134,11 @@  static void setup_boot(MachineState *machine, int version, size_t ram_size)
     /* Pi2 and Pi3 requires SMP setup */
     if (version >= 2) {
         binfo.smp_loader_start = SMPBOOT_ADDR;
-        binfo.write_secondary_boot = write_smpboot;
+        if (version == 2) {
+            binfo.write_secondary_boot = write_smpboot;
+        } else {
+            binfo.write_secondary_boot = write_smpboot64;
+        }
         binfo.secondary_cpu_reset_hook = reset_secondary;
     }