firmware: Add preferred boot HART field in struct fw_dynamic_info
authorAnup Patel <anup.patel@wdc.com>
Wed, 6 Nov 2019 10:54:35 +0000 (16:24 +0530)
committerAnup Patel <anup@brainfault.org>
Fri, 15 Nov 2019 12:11:18 +0000 (17:41 +0530)
It has been reported that link address range of previous booting stage
(such as U-Boot SPL) can overlap the link address rage of FW_DYNAMIC.

This means self-relocation in FW_DYNAMIC can potentially corrupt
previous booting stage if any of the secondary HART enter FW_DYNAMIC
before primary HART.

To tackle this, we add preferred boot HART field (i.e boot_hart) in
struct fw_dyanmic_info. We use this field to force secondary HARTs
into relocation wait loop till preferred/primary boot HART enters
FW_DYNAMIC completes self-relocation. If preferred boot HART is not
available then we fall back to relocation lottery approach.

Signed-off-by: Anup Patel <anup.patel@wdc.com>
Reviewed-by: Atish Patra <atish.patra@wdc.com>
firmware/fw_base.S
firmware/fw_dynamic.S
firmware/fw_jump.S
firmware/fw_payload.S
include/sbi/fw_dynamic.h

index 375c158..0797cc0 100644 (file)
        .globl _start
        .globl _start_warm
 _start:
+       /* Find preferred boot HART id */
+       MOV_3R  s0, a0, s1, a1, s2, a2
+       call    fw_boot_hart
+       add     a6, a0, zero
+       MOV_3R  a0, s0, a1, s1, a2, s2
+       li      a7, -1
+       beq     a6, a7, _try_lottery
+       /* Jump to relocation wait loop if we are not boot hart */
+       bne     a0, a6, _wait_relocate_copy_done
+_try_lottery:
        /* Jump to relocation wait loop if we don't get relocation lottery */
        la      a6, _relocate_lottery
        li      a7, 1
index cf97b60..ba9394d 100644 (file)
@@ -19,6 +19,32 @@ _bad_dynamic_info:
 
        .align 3
        .section .entry, "ax", %progbits
+       .global fw_boot_hart
+       /*
+        * This function is called very early even before
+        * fw_save_info() is called.
+        * We can only use a0, a1, and a2 registers here.
+        * The boot HART id should be returned in 'a0'.
+        */
+fw_boot_hart:
+       /* Sanity checks */
+       li      a1, FW_DYNAMIC_INFO_MAGIC_VALUE
+       REG_L   a0, FW_DYNAMIC_INFO_MAGIC_OFFSET(a2)
+       bne     a0, a1, _bad_dynamic_info
+       li      a1, FW_DYNAMIC_INFO_VERSION_MAX
+       REG_L   a0, FW_DYNAMIC_INFO_VERSION_OFFSET(a2)
+       bgt     a0, a1, _bad_dynamic_info
+
+       /* Read boot HART id */
+       li      a1, 0x2
+       blt     a0, a1, 2f
+       REG_L   a0, FW_DYNAMIC_INFO_BOOT_HART_OFFSET(a2)
+       ret
+2:     li      a0, -1
+       ret
+
+       .align 3
+       .section .entry, "ax", %progbits
        .global fw_save_info
        /*
         * We can only use a0, a1, a2, a3, and a4 registers here.
@@ -27,14 +53,19 @@ _bad_dynamic_info:
         * Nothing to be returned here.
         */
 fw_save_info:
+       /* Save next arg1 in 'a1' */
        la      a4, _dynamic_next_arg1
        REG_S   a1, (a4)
+
+       /* Sanity checks */
        li      a4, FW_DYNAMIC_INFO_MAGIC_VALUE
        REG_L   a3, FW_DYNAMIC_INFO_MAGIC_OFFSET(a2)
        bne     a3, a4, _bad_dynamic_info
        li      a4, FW_DYNAMIC_INFO_VERSION_MAX
        REG_L   a3, FW_DYNAMIC_INFO_VERSION_OFFSET(a2)
        bgt     a3, a4, _bad_dynamic_info
+
+       /* Save version == 0x1 fields */
        la      a4, _dynamic_next_addr
        REG_L   a3, FW_DYNAMIC_INFO_NEXT_ADDR_OFFSET(a2)
        REG_S   a3, (a4)
@@ -44,6 +75,15 @@ fw_save_info:
        la      a4, _dynamic_options
        REG_L   a3, FW_DYNAMIC_INFO_OPTIONS_OFFSET(a2)
        REG_S   a3, (a4)
+
+       /* Save version == 0x2 fields */
+       li      a4, 0x2
+       REG_L   a3, FW_DYNAMIC_INFO_VERSION_OFFSET(a2)
+       blt     a3, a4, 2f
+       la      a4, _dynamic_boot_hart
+       REG_L   a3, FW_DYNAMIC_INFO_BOOT_HART_OFFSET(a2)
+       REG_S   a3, (a4)
+2:
        ret
 
        .align 3
@@ -116,3 +156,5 @@ _dynamic_next_mode:
        RISCV_PTR PRV_S
 _dynamic_options:
        RISCV_PTR 0x0
+_dynamic_boot_hart:
+       RISCV_PTR -1
index cdf1f41..84391c9 100644 (file)
 
        .align 3
        .section .entry, "ax", %progbits
+       .global fw_boot_hart
+       /*
+        * This function is called very early even before
+        * fw_save_info() is called.
+        * We can only use a0, a1, and a2 registers here.
+        * The boot HART id should be returned in 'a0'.
+        */
+fw_boot_hart:
+       li      a0, -1
+       ret
+
+       .align 3
+       .section .entry, "ax", %progbits
        .global fw_save_info
        /*
         * We can only use a0, a1, a2, a3, and a4 registers here.
index 01dce20..9f4e0ec 100644 (file)
 
        .align 3
        .section .entry, "ax", %progbits
+       .global fw_boot_hart
+       /*
+        * This function is called very early even before
+        * fw_save_info() is called.
+        * We can only use a0, a1, and a2 registers here.
+        * The boot HART id should be returned in 'a0'.
+        */
+fw_boot_hart:
+       li      a0, -1
+       ret
+
+       .align 3
+       .section .entry, "ax", %progbits
        .global fw_save_info
        /*
         * We can only use a0, a1, a2, a3, and a4 registers here.
index 695dc29..3c08831 100644 (file)
 #define FW_DYNAMIC_INFO_MAGIC_OFFSET           (0 * __SIZEOF_POINTER__)
 /** Offset of version member in fw_dynamic_info */
 #define FW_DYNAMIC_INFO_VERSION_OFFSET         (1 * __SIZEOF_POINTER__)
-/** Offset of next_addr member in fw_dynamic_info */
+/** Offset of next_addr member in fw_dynamic_info (version >= 1) */
 #define FW_DYNAMIC_INFO_NEXT_ADDR_OFFSET       (2 * __SIZEOF_POINTER__)
-/** Offset of next_mode member in fw_dynamic_info */
+/** Offset of next_mode member in fw_dynamic_info  (version >= 1) */
 #define FW_DYNAMIC_INFO_NEXT_MODE_OFFSET       (3 * __SIZEOF_POINTER__)
-/** Offset of options member in fw_dynamic_info */
+/** Offset of options member in fw_dynamic_info  (version >= 1) */
 #define FW_DYNAMIC_INFO_OPTIONS_OFFSET         (4 * __SIZEOF_POINTER__)
+/** Offset of boot_hart member in fw_dynamic_info  (version >= 2) */
+#define FW_DYNAMIC_INFO_BOOT_HART_OFFSET       (5 * __SIZEOF_POINTER__)
 
 /** Expected value of info magic ('OSBI' ascii string in hex) */
 #define FW_DYNAMIC_INFO_MAGIC_VALUE            0x4942534f
 
 /** Maximum supported info version */
-#define FW_DYNAMIC_INFO_VERSION_MAX            0x1
+#define FW_DYNAMIC_INFO_VERSION_MAX            0x2
 
 /** Possible next mode values */
 #define FW_DYNAMIC_INFO_NEXT_MODE_U            0x0
@@ -54,6 +56,22 @@ struct fw_dynamic_info {
        unsigned long next_mode;
        /** Options for OpenSBI library */
        unsigned long options;
+       /**
+        * Preferred boot HART id
+        *
+        * It is possible that the previous booting stage uses same link
+        * address as the FW_DYNAMIC firmware. In this case, the relocation
+        * lottery mechanism can potentially overwrite the previous booting
+        * stage while other HARTs are still running in the previous booting
+        * stage leading to boot-time crash. To avoid this boot-time crash,
+        * the previous booting stage can specify last HART that will jump
+        * to the FW_DYNAMIC firmware as the preferred boot HART.
+        *
+        * To avoid specifying a preferred boot HART, the previous booting
+        * stage can set it to -1UL which will force the FW_DYNAMIC firmware
+        * to use the relocation lottery mechanism.
+        */
+       unsigned long boot_hart;
 } __packed;
 
 #endif