Skip to content

Commit 7c43455

Browse files
committed
elfloader/risc-v: rework multi core handling
- build code around SBI HSM extension - Ensure DTB is always passed to primary core boot - Drop variable hsm_exists, pass information as parameter - Print more log messages Signed-off-by: Axel Heider <axelheider@gmx.de>
1 parent 4f67016 commit 7c43455

2 files changed

Lines changed: 143 additions & 82 deletions

File tree

elfloader-tool/src/arch-riscv/boot.c

Lines changed: 30 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -166,11 +166,10 @@ static int map_kernel_window(struct image_info *kernel_info)
166166
return 0;
167167
}
168168

169-
int hsm_exists = 0; /* assembly startup code will initialise this */
170-
171169
#if CONFIG_MAX_NUM_NODES > 1
172170

173-
extern void secondary_harts(word_t hart_id, word_t core_id);
171+
/* entry if secondary harts are started via SBI HSM extension */
172+
extern void hsm_start_secondary_core(word_t hart_id, word_t core_id);
174173

175174
int secondary_go = 0;
176175
int next_logical_core_id = 1; /* incremented by assembly code */
@@ -318,13 +317,22 @@ NORETURN void boot_hart(word_t hart_id, word_t core_id)
318317
UNREACHABLE();
319318
}
320319

321-
void main(word_t hart_id, void *bootloader_dtb)
320+
void main(word_t hart_id, void *bootloader_dtb, word_t hsm_exists)
322321
{
323322
/* Printing uses SBI, so there is no need to initialize any UART. */
324-
printf("ELF-loader started on (HART %"PRIu_word") (NODES %d)\n",
325-
hart_id, (unsigned int)CONFIG_MAX_NUM_NODES);
326-
327-
printf(" paddr=[%p..%p]\n", _text, _end - 1);
323+
printf("ELF-loader started on hart %"PRIu_word"\n", hart_id);
324+
printf(" MAX_NUM_NODES: %u, SBI HSM extension: %s\n",
325+
(unsigned int)CONFIG_MAX_NUM_NODES,
326+
hsm_exists ? "available" : "missing");
327+
printf(" phys area of binary: [%p..%p]\n", _text, _end - 1);
328+
printf(" DTB from bootloader: %p\n", bootloader_dtb);
329+
330+
if (hart_id != CONFIG_FIRST_HART_ID) {
331+
printf("ERROR: ELF-loader not is running on FIRST_HART_ID (%d)\n",
332+
(unsigned int)CONFIG_FIRST_HART_ID);
333+
abort();
334+
UNREACHABLE();
335+
}
328336

329337
/* Load the ELF images and setup the MMU tables. */
330338
int ret = run_elfloader(bootloader_dtb);
@@ -352,16 +360,24 @@ void main(word_t hart_id, void *bootloader_dtb)
352360
*/
353361
printf("no HSM extension, let's hope secondary cores have been started\n");
354362
} else {
355-
/* Start all cores */
363+
/* If we are running on a platform with SBI HSM extension support, no
364+
* other hart is running. The system start in a random hart, but the
365+
* assembly startup code has done the migration to the designated
366+
* primary hart already. The global variable logical_core_id must be
367+
* untpuched here, otherwise something is badly wrong.
368+
*/
369+
if (1 != next_logical_core_id) {
370+
printf("ERROR: logical core IDs have been assigned already\n");
371+
abort();
372+
UNREACHABLE();
373+
}
356374
for (int i = 0; i < CONFIG_MAX_NUM_NODES; i++) {
357375
word_t remote_hart_id = i + 1; /* hart IDs start at 1 */
358376
if (remote_hart_id != hart_id) {
359-
/* The remote's hart ID is passed as custom parameter, but this
360-
* value is not used anywhere at the moment.
361-
*/
377+
/* start a secondary core and pass a unique logical core ID */
362378
sbi_hsm_ret_t ret = sbi_hart_start(remote_hart_id,
363-
secondary_harts,
364-
remote_hart_id);
379+
hsm_start_secondary_core,
380+
next_logical_core_id++);
365381
if (SBI_SUCCESS != ret.code) {
366382
printf("ERROR: could not start hart %"PRIu_word", failure"
367383
" (%d, %d)\n", remote_hart_id, ret.code, ret.data);

elfloader-tool/src/arch-riscv/crt0.S

Lines changed: 113 additions & 68 deletions
Original file line numberDiff line numberDiff line change
@@ -10,7 +10,6 @@
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
@@ -49,15 +48,9 @@
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

Comments
 (0)