From 25472de89ee3b98cd466f69d1f419f943dcbcb0f Mon Sep 17 00:00:00 2001
From: Anup Patel <anup.patel@wdc.com>
Date: Mon, 29 Apr 2019 10:44:15 +0530
Subject: firmware: Allow firmwares to provide next mode and options

This patch extends existing firmwares (i.e. fw_jump and fw_payload)
to explicitly provide next mode and options to fw_base.

We also introduce fw_save_info() which is called by fw_base very
early on boot HART. This function can be used by existing firmwares
(i.e. fw_jump and fw_payload) to save information passed by previous
booting stage.

Overall, this is a preparatory patch for implementing fw_dynamic.

Signed-off-by: Anup Patel <anup.patel@wdc.com>
Reviewed-by: Alistair Francis <alistair.francis@wdc.com>
Reviewed-by: Atish Patra <atish.patra@wdc.com>
---
 firmware/fw_base.S    | 67 +++++++++++++++++++++++++++++++++++++++------------
 firmware/fw_jump.S    | 50 +++++++++++++++++++++++++++++++++++---
 firmware/fw_payload.S | 56 +++++++++++++++++++++++++++++++++++++-----
 3 files changed, 148 insertions(+), 25 deletions(-)

(limited to 'firmware')

diff --git a/firmware/fw_base.S b/firmware/fw_base.S
index 7897314..0d7cf69 100644
--- a/firmware/fw_base.S
+++ b/firmware/fw_base.S
@@ -13,6 +13,20 @@
 #include <sbi/sbi_scratch.h>
 #include <sbi/sbi_trap.h>
 
+.macro	MOV_3R __d0, __s0, __d1, __s1, __d2, __s2
+	add	\__d0, \__s0, zero
+	add	\__d1, \__s1, zero
+	add	\__d2, \__s2, zero
+.endm
+
+.macro	MOV_5R __d0, __s0, __d1, __s1, __d2, __s2, __d3, __s3, __d4, __s4
+	add	\__d0, \__s0, zero
+	add	\__d1, \__s1, zero
+	add	\__d2, \__s2, zero
+	add	\__d3, \__s3, zero
+	add	\__d4, \__s4, zero
+.endm
+
 	.align 3
 	.section .entry, "ax", %progbits
 	.globl _start
@@ -25,9 +39,15 @@ _start:
 	csrr	a6, CSR_MHARTID
 	blt	zero, a6, _wait_for_boot_hart
 
+	/* Reset all registers for boot HART */
 	li	ra, 0
 	call	_reset_regs
 
+	/* Allow main firmware to save info */
+	MOV_5R	s0, a0, s1, a1, s2, a2, s3, a3, s4, a4
+	call	fw_save_info
+	MOV_5R	a0, s0, a1, s1, a2, s2, a3, s3, a4, s4
+
 	/* Preload HART details
 	 * s7 -> HART Count
 	 * s8 -> HART Stack Size
@@ -59,6 +79,7 @@ _scratch_init:
 	sub	tp, tp, a5
 
 	/* Initialize scratch space */
+	/* Store fw_start and fw_size in scratch space */
 	la	a4, _fw_start
 	la	a5, _fw_end
 	mul	t0, s7, s8
@@ -66,27 +87,44 @@ _scratch_init:
 	sub	a5, a5, a4
 	REG_S	a4, SBI_SCRATCH_FW_START_OFFSET(tp)
 	REG_S	a5, SBI_SCRATCH_FW_SIZE_OFFSET(tp)
-	/* Note: fw_next_arg1() uses a0, a1, and ra */
+	/* Store next arg1 in scratch space */
+	MOV_3R	s0, a0, s1, a1, s2, a2
 	call	fw_next_arg1
 	REG_S	a0, SBI_SCRATCH_NEXT_ARG1_OFFSET(tp)
-	/* Note: fw_next_addr() uses a0, a1, and ra */
+	MOV_3R	a0, s0, a1, s1, a2, s2
+	/* Store next address in scratch space */
+	MOV_3R	s0, a0, s1, a1, s2, a2
 	call	fw_next_addr
 	REG_S	a0, SBI_SCRATCH_NEXT_ADDR_OFFSET(tp)
-	li	a4, PRV_S
-	REG_S	a4, SBI_SCRATCH_NEXT_MODE_OFFSET(tp)
+	MOV_3R	a0, s0, a1, s1, a2, s2
+	/* Store next mode in scratch space */
+	MOV_3R	s0, a0, s1, a1, s2, a2
+	call	fw_next_mode
+	REG_S	a0, SBI_SCRATCH_NEXT_MODE_OFFSET(tp)
+	MOV_3R	a0, s0, a1, s1, a2, s2
+	/* Store warm_boot address in scratch space */
 	la	a4, _start_warm
 	REG_S	a4, SBI_SCRATCH_WARMBOOT_ADDR_OFFSET(tp)
+	/* Store platform address in scratch space */
 	la	a4, platform
 	REG_S	a4, SBI_SCRATCH_PLATFORM_ADDR_OFFSET(tp)
+	/* Store hartid-to-scratch function address in scratch space */
 	la	a4, _hartid_to_scratch
 	REG_S	a4, SBI_SCRATCH_HARTID_TO_SCRATCH_OFFSET(tp)
+	/* Clear tmp0 in scratch space */
 	REG_S	zero, SBI_SCRATCH_TMP0_OFFSET(tp)
+	/* Store firmware options in scratch space */
+	MOV_3R	s0, a0, s1, a1, s2, a2
 #ifdef FW_OPTIONS
 	li	a4, FW_OPTIONS
-	REG_S	a4, SBI_SCRATCH_OPTIONS_OFFSET(tp)
 #else
-	REG_S	zero, SBI_SCRATCH_OPTIONS_OFFSET(tp)
+	add	a4, zero, zero
 #endif
+	call	fw_options
+	or	a4, a4, a0
+	REG_S	a4, SBI_SCRATCH_OPTIONS_OFFSET(tp)
+	MOV_3R	a0, s0, a1, s1, a2, s2
+	/* Move to next scratch space */
 	add	t1, t1, t2
 	blt	t1, s7, _scratch_init
 
@@ -99,12 +137,10 @@ _bss_zero:
 	blt	a4, a5, _bss_zero
 
 	/* Override pervious arg1 */
-	add	s0, a0, zero
-	add	s1, a1, zero
+	MOV_3R	s0, a0, s1, a1, s2, a2
 	call	fw_prev_arg1
 	add	t1, a0, zero
-	add	a0, s0, zero
-	add	a1, s1, zero
+	MOV_3R	a0, s0, a1, s1, a2, s2
 	beqz	t1, _prev_arg1_override_done
 	add	a1, t1, zero
 _prev_arg1_override_done:
@@ -122,13 +158,12 @@ _prev_arg1_override_done:
 	li	a3, ~(__SIZEOF_POINTER__ - 1)
 	li	a4, 0xff
 	/* t1 = destination FDT start address */
-	add	s0, a0, zero
-	add	s1, a1, zero
+	MOV_3R	s0, a0, s1, a1, s2, a2
 	call	fw_next_arg1
 	add	t1, a0, zero
-	add	a0, s0, zero
-	add	a1, s1, zero
+	MOV_3R	a0, s0, a1, s1, a2, s2
 	beqz	t1, _fdt_reloc_done
+	beq	t1, a1, _fdt_reloc_done
 	and	t1, t1, a3
 	/* t0 = source FDT start address */
 	add	t0, a1, zero
@@ -188,6 +223,7 @@ _wait_for_boot_hart:
 	beqz	a5, _wait_for_boot_hart
 
 _start_warm:
+	/* Reset all registers for non-boot HARTs */
 	li	ra, 0
 	call	_reset_regs
 
@@ -430,7 +466,7 @@ _reset_regs:
 
 	/* flush the instruction cache */
 	fence.i
-	/* Reset all registers except ra, a0,a1 */
+	/* Reset all registers except ra, a0, a1 and a2 */
 	li sp, 0
 	li gp, 0
 	li tp, 0
@@ -439,7 +475,6 @@ _reset_regs:
 	li t2, 0
 	li s0, 0
 	li s1, 0
-	li a2, 0
 	li a3, 0
 	li a4, 0
 	li a5, 0
diff --git a/firmware/fw_jump.S b/firmware/fw_jump.S
index 4b71863..cdf1f41 100644
--- a/firmware/fw_jump.S
+++ b/firmware/fw_jump.S
@@ -9,19 +9,37 @@
 
 #include "fw_base.S"
 
+	.align 3
+	.section .entry, "ax", %progbits
+	.global fw_save_info
+	/*
+	 * We can only use a0, a1, a2, a3, and a4 registers here.
+	 * The a0, a1, and a2 registers will be same as passed by
+	 * previous booting stage.
+	 * Nothing to be returned here.
+	 */
+fw_save_info:
+	ret
+
 	.align 3
 	.section .entry, "ax", %progbits
 	.global fw_prev_arg1
+	/*
+	 * We can only use a0, a1, and a2 registers here.
+	 * The previous arg1 should be returned in 'a0'.
+	 */
 fw_prev_arg1:
-	/* We return previous arg1 in 'a0' */
 	add	a0, zero, zero
 	ret
 
 	.align 3
 	.section .entry, "ax", %progbits
 	.global fw_next_arg1
+	/*
+	 * We can only use a0, a1, and a2 registers here.
+	 * The next arg1 should be returned in 'a0'.
+	 */
 fw_next_arg1:
-	/* We return next arg1 in 'a0' */
 #ifdef FW_JUMP_FDT_ADDR
 	li	a0, FW_JUMP_FDT_ADDR
 #else
@@ -32,12 +50,38 @@ fw_next_arg1:
 	.align 3
 	.section .entry, "ax", %progbits
 	.global fw_next_addr
+	/*
+	 * We can only use a0, a1, and a2 registers here.
+	 * The next address should be returned in 'a0'.
+	 */
 fw_next_addr:
-	/* We return next address in 'a0' */
 	la	a0, _jump_addr
 	REG_L	a0, (a0)
 	ret
 
+	.align 3
+	.section .entry, "ax", %progbits
+	.global fw_next_mode
+	/*
+	 * We can only use a0, a1, and a2 registers here.
+	 * The next address should be returned in 'a0'
+	 */
+fw_next_mode:
+	li	a0, PRV_S
+	ret
+
+	.align 3
+	.section .entry, "ax", %progbits
+	.global fw_options
+	/*
+	 * We can only use a0, a1, and a2 registers here.
+	 * The 'a4' register will have default options.
+	 * The next address should be returned in 'a0'.
+	 */
+fw_options:
+	add	a0, zero, zero
+	ret
+
 #ifndef FW_JUMP_ADDR
 #error "Must define FW_JUMP_ADDR"
 #endif
diff --git a/firmware/fw_payload.S b/firmware/fw_payload.S
index b86b63c..4b4527a 100644
--- a/firmware/fw_payload.S
+++ b/firmware/fw_payload.S
@@ -9,11 +9,26 @@
 
 #include "fw_base.S"
 
-	.align 4
+	.align 3
+	.section .entry, "ax", %progbits
+	.global fw_save_info
+	/*
+	 * We can only use a0, a1, a2, a3, and a4 registers here.
+	 * The a0, a1, and a2 registers will be same as passed by
+	 * previous booting stage.
+	 * Nothing to be returned here.
+	 */
+fw_save_info:
+	ret
+
+	.align 3
 	.section .entry, "ax", %progbits
 	.global fw_prev_arg1
+	/*
+	 * We can only use a0, a1, and a2 registers here.
+	 * The previous arg1 should be returned in 'a0'.
+	 */
 fw_prev_arg1:
-	/* We return previous arg1 in 'a0' */
 #ifdef FW_PAYLOAD_FDT_PATH
 	la	a0, fdt_bin
 #else
@@ -21,11 +36,14 @@ fw_prev_arg1:
 #endif
 	ret
 
-	.align 4
+	.align 3
 	.section .entry, "ax", %progbits
 	.global fw_next_arg1
+	/*
+	 * We can only use a0, a1, and a2 registers here.
+	 * The next arg1 should be returned in 'a0'.
+	 */
 fw_next_arg1:
-	/* We return next arg1 in 'a0' */
 #ifdef FW_PAYLOAD_FDT_ADDR
 	li	a0, FW_PAYLOAD_FDT_ADDR
 #else
@@ -33,14 +51,40 @@ fw_next_arg1:
 #endif
 	ret
 
-	.align 4
+	.align 3
 	.section .entry, "ax", %progbits
 	.global fw_next_addr
+	/*
+	 * We can only use a0, a1, and a2 registers here.
+	 * The next address should be returned in 'a0'.
+	 */
 fw_next_addr:
-	/* We return next address in 'a0' */
 	la	a0, payload_bin
 	ret
 
+	.align 3
+	.section .entry, "ax", %progbits
+	.global fw_next_mode
+	/*
+	 * We can only use a0, a1, and a2 registers here.
+	 * The next address should be returned in 'a0'.
+	 */
+fw_next_mode:
+	li	a0, PRV_S
+	ret
+
+	.align 3
+	.section .entry, "ax", %progbits
+	.global fw_options
+	/*
+	 * We can only use a0, a1, and a2 registers here.
+	 * The 'a4' register will have default options.
+	 * The next address should be returned in 'a0'.
+	 */
+fw_options:
+	add	a0, zero, zero
+	ret
+
 #ifdef FW_PAYLOAD_FDT_PATH
 	.align 4
 	.section .text, "ax", %progbits
-- 
cgit v1.2.3