1010.extern main
1111.extern __global_pointer$
1212.extern elfloader_stack
13- .extern hsm_exists
1413#if CONFIG_MAX_NUM_NODES > 1
1514.extern boot_hart
1615.extern next_logical_core_id
4948.global _start
5049_start:
5150
52- .option push
53- .option norelax
54- 1 :auipc gp, %pcrel_hi(__global_pointer$)
55- addi gp, gp, %pcrel_lo(1b)
56- .option pop
57-
5851 /* save the parameters passed */
5952 mv s0, a0 /* preserve a0 (hart id) in s0 */
60- mv s2 , a1 /* preserve a1 (dtb) in s2 */
53+ mv s1 , a1 /* preserve a1 (dtb) in s1 */
6154
6255#ifdef CONFIG_IMAGE_BINARY
6356/* Clear the BSS before we get to do anything more specific */
@@ -74,27 +67,86 @@ _start:
7467 li a6 , SBI_EXT_BASE_PROBE_EXT
7568 li a0 , SBI_HSM_BASE
7669 ecall /* call SBI to probe for HSM extension */
77- mv a2 , a0 /* move SBI call generic return code to s2 as we need a0 */
78- mv a0 , s0 /* restore a0 to hold hart ID passed by the boot loader */
79- bnez a2 , _start1 /* goto _start1 if SBI did not return SBI_SUCCESS (0) */
80- beqz a1 , _start1 /* goto _start1 if HSM extension is missing */
81-
82- /* Update global bool variable to tell boot code the HSM extension exists. */
83- la t1, hsm_exists
84- li t2, 1
85- amoadd.w t1, t2, (t1)
86-
87- /* Check if we are on CONFIG_FIRST_HART_ID */
88- li s1, CONFIG_FIRST_HART_ID
89- beq a0 , s1, _start1 /* goto _start1 if we are on CONFIG_FIRST_HART_ID */
90-
91- /* Use HSM extension to start hart CONFIG_FIRST_HART_ID. */
92- hsm_switch_hart:
70+ seqz t0, a0 /* t0 = (a0 == 0) to check SBI returned SBI_SUCCESS (0) */
71+ snez t1, a1 /* t1 = (a1 != 0) to HSM extension exist */
72+ and a2 , t0, t1 /* a2 = 1 if HSM extension is available, otherwise 0 */
73+ li t0, CONFIG_FIRST_HART_ID
74+ bne s0 , t0, start_on_secondary
75+ mv a0 , s0 /* restore a0 to hold hart ID */
76+ mv a1 , s1 /* restore a1 to hold DTB passed on entry */
77+ boot_on_primary:
78+ /* We end up here, we are running on the designated primary hart, which might
79+ * not be hart ID 0. The register setup is:
80+ * a0: hart ID from SBI, this must be CONFIG_FIRST_HART_ID
81+ * a1: DTB
82+ * a2: HSM extension exists flag
83+ */
84+ la sp , (elfloader_stack + BIT(CONFIG_KERNEL_STACK_BITS))
85+ la t1, main
86+ enter_c_world:
87+ /* if we end up here, assembly startup if finished and control will be handed
88+ * over to C code. Registers a0-n and sp must be set up, t1 holds the address
89+ * of the C function to call. We avoid using t0 (x5), because this is a
90+ * designated additional link register that would make this technically a call
91+ * and not a jump.
92+ */
93+ .option push
94+ .option norelax
95+ 1 :auipc gp, %pcrel_hi(__global_pointer$)
96+ addi gp, gp, %pcrel_lo(1b)
97+ .option pop
98+ jr t1
99+
100+ /*----------------------------------------------------------------------------*/
101+ hsm_start_primary_core:
102+ /* SBI has started us on a designated secondary hart, so we used the SBI HSM
103+ * extension to switch to the designated primary hart. The secondary hart is
104+ * shut down here, so we can bring is up via the HSM extension when needed.
105+ * The register setup is:
106+ * a0: hard ID
107+ * a1: custom parameter: DTB from bootloader
108+ */
109+ li a2 , 1 /* remember that the HSM extension is available */
110+ j boot_on_primary
111+
112+ /*----------------------------------------------------------------------------*/
113+ #if CONFIG_MAX_NUM_NODES > 1
114+ .global hsm_start_secondary_core
115+ hsm_start_secondary_core:
116+ /* We enter here when the ELF-Loader starts a secondary hart via the SBI HSM
117+ * extension. All we have to do here is se up a stack and jump to the C code.
118+ * The register setup is:
119+ * a0: hard ID
120+ * a1: custom parameter: logical core ID
121+ */
122+ /* setup stack based on the logical ID */
123+ addi t0, a0 , 1 /* increment by one because we need to set sp to the end */
124+ slli t0, t0, CONFIG_KERNEL_STACK_BITS /* t0 *= BIT(CONFIG_KERNEL_STACK_BITS) */
125+ la sp , elfloader_stack
126+ add sp , sp , t0
127+ /* prepare C code entry with paramters: a0 = hard ID, a1 = logical core ID */
128+ la t1, boot_hart
129+ j enter_c_world
130+
131+ #endif /* CONFIG_MAX_NUM_NODES > 1 */
132+
133+ /*----------------------------------------------------------------------------*/
134+ start_on_secondary:
135+ /* We end up here if the startup code has detected that SBI has started us on
136+ * a hart that is not the designated primary hart. Try to switch to the
137+ * primary hart and continue the boot process there. This must be supported
138+ * even if CONFIG_MAX_NUM_NODES is set to 1. The register setup is:
139+ * s0: hard ID
140+ * s1: DTB passed from SBI
141+ * a2: HSM extension exists flag
142+ */
143+ beqz a2 , no_hsm_start_secondary
144+ /* Try to bring up the primary hart via the HSM extension */
93145 li a7 , SBI_HSM_BASE
94146 li a6 , SBI_HSM_BASE_HART_START
95147 li a0 , CONFIG_FIRST_HART_ID /* hart id to start */
96- la a1 , _start1 /* where to start the hart */
97- li a2 , 0 /* logical hart_id to be passed in a1 when new hart starts */
148+ la a1 , hsm_start_primary_core /* where to start the hart */
149+ mv a2 , s1 /* custom parameter passed in a1 is the DTB */
98150 ecall /* call SBI to start hart FIRST_HART_ID */
99151 /* Stop current hart, the boot code may bring it up again when needed. */
100152 li a7 , SBI_HSM_BASE
@@ -104,51 +156,44 @@ hsm_switch_hart_error:
104156 wfi
105157 j hsm_switch_hart_error
106158
107- _start1: /* a0 must hold current hard ID passed by bootloader */
159+ /*----------------------------------------------------------------------------*/
160+ no_hsm_start_secondary:
161+ /* We end up here if we are not starting in the designated primary core and SBI
162+ * does no implement the HSM extension, so we can't switch to the designated
163+ * primary hart. Lokkls like we are running on a legacy platform where all
164+ * harts start in parallel. The register setup is:
165+ * s0: hard ID
166+ * s1: DTB passed from SBI
167+ * a2: HSM extension exists flag
168+ */
108169
109- .option push
110- .option norelax
111- 1 :auipc gp, %pcrel_hi(__global_pointer$)
112- addi gp, gp, %pcrel_lo(1b)
113- .option pop
114-
115- li s0, CONFIG_FIRST_HART_ID
116- bne a0 , s0, secondary_harts
170+ #if CONFIG_MAX_NUM_NODES > 1
117171
118- la sp , (elfloader_stack + BIT(CONFIG_KERNEL_STACK_BITS))
119- / * The C code expects the registers to be set up as:
120- * a0 = hart id
121- * a1 = dtb
172+ / * Simulate an SBI HSM extension entry, where a0 holds the hart ID and a1 a
173+ * custom value, which is the logical core ID in our usage. Determine it from
174+ * an atomic increment operation on the global variable next_logical_core_id,
175+ * what we use as our ID is the value it had before incrementing it.
122176 */
123- mv a1 , s2 /* restore dtb passed on entry */
124- la s0, main
125- jr s0
126-
127-
128- .global secondary_harts
129- secondary_harts:
177+ mv a0 , s0 /* restore a0 with hart ID */
178+ la t0, next_logical_core_id
179+ li t1, 1
180+ amoadd.w a1 , t1, (t0) / * a1 is set to old value of next_logical_core_id */
181+ / * The logical core ID is valid only less than CONFIG_MAX_NUM_NODES. */
182+ li t0, CONFIG_MAX_NUM_NODES
183+ blt a1 , t0, hsm_start_secondary_core
130184
131- .option push
132- .option norelax
133- 1 :auipc gp, %pcrel_hi(__global_pointer$)
134- addi gp, gp, %pcrel_lo(1b)
135- .option pop
136-
137- #if CONFIG_MAX_NUM_NODES > 1
138- la a1 , next_logical_core_id
139- li t2, 1
140- amoadd.w a1 , t2, (a1 )
141- /* now a1 has the logical core id */
142- li t2, CONFIG_MAX_NUM_NODES
143- bge a1 , t2, spin_hart
144- /* setup the core specific stack pointer */
145- la sp , elfloader_stack
146- addi t0, a1 , 1 /* increment by one because we need to set sp to the end */
147- slli t0, t0, CONFIG_KERNEL_STACK_BITS /* t0 = t0 * BIT(CONFIG_KERNEL_STACK_BITS) */
148- add sp , sp , t0
149- la s0, boot_hart
150- jr s0
151185#endif
152- spin_hart:
186+
187+ /* If we arrive here, this hart cannot be used because the number of supported
188+ * secondary hart has been exeeded. Maybe multi core support is not even
189+ * enabled at all. Here is no SBI HSM extension to turn off this hart, so all
190+ * we can do is spinning over a WFI. However, this is not guaranteed to work
191+ * forever, because the memory where the ELF loader keeps the loop can be
192+ * reused and overwritten by the kernel. This will lead to undefined behavior,
193+ * as we don't know what the new contents will be. If we are lucky, the loop
194+ * keeps running from a hart specific instruction cache, so the new memory
195+ * contents are ignored because no synchronization is triggered.
196+ */
197+ secondary_hart_wfi_loop:
153198 wfi
154- j spin_hart
199+ j secondary_hart_wfi_loop
0 commit comments