From 7a13beb213266cbf6f15ddbbef5bfca274086bd3 Mon Sep 17 00:00:00 2001
From: Anup Patel <anup.patel@wdc.com>
Date: Wed, 6 Nov 2019 16:24:35 +0530
Subject: firmware: Add preferred boot HART field in struct fw_dynamic_info

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    | 10 ++++++++++
 firmware/fw_dynamic.S | 42 ++++++++++++++++++++++++++++++++++++++++++
 firmware/fw_jump.S    | 13 +++++++++++++
 firmware/fw_payload.S | 13 +++++++++++++
 4 files changed, 78 insertions(+)

(limited to 'firmware')

diff --git a/firmware/fw_base.S b/firmware/fw_base.S
index 375c158..0797cc0 100644
--- a/firmware/fw_base.S
+++ b/firmware/fw_base.S
@@ -46,6 +46,16 @@
 	.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
diff --git a/firmware/fw_dynamic.S b/firmware/fw_dynamic.S
index cf97b60..ba9394d 100644
--- a/firmware/fw_dynamic.S
+++ b/firmware/fw_dynamic.S
@@ -17,6 +17,32 @@ _bad_dynamic_info:
 	wfi
 	j	_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
@@ -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
diff --git a/firmware/fw_jump.S b/firmware/fw_jump.S
index cdf1f41..84391c9 100644
--- a/firmware/fw_jump.S
+++ b/firmware/fw_jump.S
@@ -9,6 +9,19 @@
 
 #include "fw_base.S"
 
+	.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
diff --git a/firmware/fw_payload.S b/firmware/fw_payload.S
index 01dce20..9f4e0ec 100644
--- a/firmware/fw_payload.S
+++ b/firmware/fw_payload.S
@@ -9,6 +9,19 @@
 
 #include "fw_base.S"
 
+	.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
-- 
cgit v1.2.3