Skip to content

Commit eeffa17

Browse files
committed
smp: Allow switching the boot (primary) CPU core
M3 and later Apple silicon SoCs start m1n1 on the first perfomance core. The cluster layout remained unchanged with an efficiency core cluster first. This causes an annoying but mostly cosmetic issue in Linux. Linux assigns CPU index 0 to the boot CPU. This results in a mismatch of logical and physical CPU core order. As seen with the Radxa Orion O6 this will at least generate support questions but is also annoying for CPU pinning. It's likely to result in pinning to mixture of performance and efficiency cores. While functionality it should not matter to which secondary CPU core control is passed the only expected use case is switching to CPU core 0. Signed-off-by: Janne Grunau <[email protected]>
1 parent 7f65686 commit eeffa17

7 files changed

Lines changed: 138 additions & 1 deletion

File tree

proxyclient/m1n1/proxy.py

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -572,6 +572,7 @@ class M1N1Proxy(Reloadable):
572572
P_SMP_CALL_SYNC_EL1 = 0x508
573573
P_SMP_CALL_EL0 = 0x509
574574
P_SMP_CALL_SYNC_EL0 = 0x50a
575+
P_SMP_SWITCH_BOOT_CPU = 0x50b
575576

576577
P_HEAPBLOCK_ALLOC = 0x600
577578
P_MALLOC = 0x601
@@ -1004,6 +1005,8 @@ def smp_call_sync_el0(self, cpu, addr, *args):
10041005
if len(args) > 3:
10051006
raise ValueError("Too many arguments")
10061007
return self.request(self.P_SMP_CALL_SYNC_EL0, cpu, addr, *args)
1008+
def smp_switch_boot_cpu(self, cpu):
1009+
return self.request(self.P_SMP_SWITCH_BOOT_CPU, cpu)
10071010

10081011
def heapblock_alloc(self, size):
10091012
return self.request(self.P_HEAPBLOCK_ALLOC, size)

src/payload.c

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -342,6 +342,13 @@ int payload_run(void)
342342
printf("Failed to kboot set %s='%s'\n", chosen[i], val);
343343
}
344344

345+
if (boot_cpu_idx != 0) {
346+
int boot_cpu = boot_cpu_idx;
347+
int index = smp_switch_boot_cpu(0);
348+
if (boot_cpu != index)
349+
printf("Switched boot CPU from %d to %d (%d)\n", boot_cpu, index, boot_cpu_idx);
350+
}
351+
345352
if (kboot_prepare_dt(fdt)) {
346353
printf("Failed to prepare FDT!\n");
347354
return -1;

src/proxy.c

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -374,6 +374,9 @@ int proxy_process(ProxyRequest *request, ProxyReply *reply)
374374
request->args[3], request->args[4]);
375375
reply->retval = smp_wait(request->args[0]);
376376
break;
377+
case P_SMP_SWITCH_BOOT_CPU:
378+
reply->retval = smp_switch_boot_cpu(request->args[0]);
379+
break;
377380

378381
case P_HEAPBLOCK_ALLOC:
379382
reply->retval = (u64)heapblock_alloc(request->args[0]);

src/proxy.h

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -92,6 +92,7 @@ typedef enum {
9292
P_SMP_CALL_EL1_SYNC,
9393
P_SMP_CALL_EL0,
9494
P_SMP_CALL_EL0_SYNC,
95+
P_SMP_SWITCH_BOOT_CPU,
9596

9697
P_HEAPBLOCK_ALLOC = 0x600, // Heap and memory management ops
9798
P_MALLOC,

src/smp.c

Lines changed: 89 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,8 @@
55
#include "aic.h"
66
#include "aic_regs.h"
77
#include "cpu_regs.h"
8+
#include "exception.h"
9+
#include "fb.h"
810
#include "malloc.h"
911
#include "memory.h"
1012
#include "pmgr.h"
@@ -108,7 +110,6 @@ void smp_secondary_prep_el3(void)
108110
return;
109111
}
110112

111-
112113
static void smp_prepare_cpu(int index)
113114
{
114115
memset(&spin_table[index], 0, sizeof(struct spin_table));
@@ -420,6 +421,93 @@ void smp_stop_secondaries(bool deep_sleep)
420421
}
421422
}
422423

424+
extern void cpu_reset(void) __attribute__((noreturn));
425+
extern void *smp_switch_boot_cpu_entry(int cpu_index);
426+
extern int smp_switch_boot_cpu_exit(void *prev_stack, u64 saved_sp);
427+
428+
static u64 switch_boot_cpu_init_new(int cpu_index, int old_index, u64 saved_sp)
429+
{
430+
int i;
431+
void *prev_stack;
432+
433+
// wait for the previous previous boot CPU to be available as secondary
434+
for (i = 0; i < 100; i++) {
435+
sysop("dmb ld");
436+
if (spin_table[old_index].flag)
437+
break;
438+
udelay(1000);
439+
}
440+
441+
if (i >= 100)
442+
printf("Previous boot CPU %d failed to start as secondary!\n", old_index);
443+
else
444+
printf(" Started.\n");
445+
446+
// restore _reset_stacks from secondary init of previous boot cpu
447+
_reset_stack = dummy_stack + DUMMY_STACK_SIZE;
448+
_reset_stack_el1 = dummy_stack_el1 + DUMMY_STACK_SIZE;
449+
450+
prev_stack = secondary_stacks[cpu_index];
451+
secondary_stacks[cpu_index] = dummy_stack;
452+
453+
// setup current CPU as boot CPU
454+
if (in_el2())
455+
msr(TPIDR_EL2, boot_cpu_idx);
456+
else
457+
msr(TPIDR_EL1, boot_cpu_idx);
458+
459+
// clear spin table of new boot CPU
460+
memset(&spin_table[boot_cpu_idx], 0, sizeof(struct spin_table));
461+
spin_table[boot_cpu_idx].mpidr = mrs(MPIDR_EL1) & 0xFFFFFF;
462+
463+
exception_initialize();
464+
465+
mmu_init();
466+
fb_set_active(true);
467+
468+
smp_switch_boot_cpu_exit(prev_stack, saved_sp);
469+
__builtin_unreachable();
470+
return -1;
471+
}
472+
473+
void smp_do_switch_boot_cpu(int cpu_index, u64 saved_sp)
474+
{
475+
int old_index = boot_cpu_idx;
476+
477+
printf("Switching boot CPU from %d to %d\n", old_index, cpu_index);
478+
479+
// disable frame buffer until the new CPU has called mmu_init()
480+
fb_set_active(false);
481+
482+
smp_call3(cpu_index, switch_boot_cpu_init_new, cpu_index, old_index, saved_sp);
483+
484+
// switch the boot CPU so the old boot CPU resets as secondary
485+
boot_cpu_idx = cpu_index;
486+
boot_cpu_mpidr = spin_table[cpu_index].mpidr;
487+
smp_prepare_cpu(old_index);
488+
489+
cpu_reset();
490+
__builtin_unreachable();
491+
}
492+
493+
int smp_switch_boot_cpu(int cpu_index)
494+
{
495+
if (cpu_index == boot_cpu_idx)
496+
return cpu_index;
497+
498+
if (!smp_is_alive(cpu_index)) {
499+
printf("Trying to switch to offline CPU %d\n", cpu_index);
500+
return -1;
501+
}
502+
503+
// Call asm helper function to capture callee saved state and pass `sp` to the new boot CPU.
504+
// This function will return on the new boot CPU.
505+
void *old_stack = smp_switch_boot_cpu_entry(cpu_index);
506+
free(old_stack);
507+
508+
return cpu_index;
509+
}
510+
423511
void smp_send_ipi(int cpu)
424512
{
425513
if (cpu >= MAX_CPUS)

src/smp.h

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -19,6 +19,8 @@ void smp_secondary_prep_el3(void);
1919
void smp_start_secondaries(void);
2020
void smp_stop_secondaries(bool deep_sleep);
2121

22+
int smp_switch_boot_cpu(int cpu_index);
23+
2224
#define smp_call0(i, f) smp_call4(i, f, 0, 0, 0, 0)
2325
#define smp_call1(i, f, a) smp_call4(i, f, a, 0, 0, 0)
2426
#define smp_call2(i, f, a, b) smp_call4(i, f, a, b, 0, 0)

src/utils_asm.S

Lines changed: 33 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -172,3 +172,36 @@ _deep_wfi_helper:
172172
ldp x30, x0, [sp], #16
173173

174174
ret
175+
176+
.extern smp_do_switch_boot_cpu
177+
178+
.globl smp_switch_boot_cpu_entry
179+
.type smp_switch_boot_cpu_entry, @function
180+
smp_switch_boot_cpu_entry:
181+
stp x29, x30, [sp, #-16]!
182+
mov x29, sp
183+
stp x27, x28, [sp, #-16]!
184+
stp x25, x26, [sp, #-16]!
185+
stp x23, x24, [sp, #-16]!
186+
stp x21, x22, [sp, #-16]!
187+
stp x19, x20, [sp, #-16]!
188+
stp x17, x18, [sp, #-16]!
189+
190+
mov x1, sp
191+
bl smp_do_switch_boot_cpu
192+
// unreachable
193+
b .
194+
195+
.globl smp_switch_boot_cpu_exit
196+
.type smp_switch_boot_cpu_exit, @function
197+
smp_switch_boot_cpu_exit:
198+
mov sp, x1
199+
ldp x17, x18, [sp], #16
200+
ldp x19, x20, [sp], #16
201+
ldp x21, x22, [sp], #16
202+
ldp x23, x24, [sp], #16
203+
ldp x25, x26, [sp], #16
204+
ldp x27, x28, [sp], #16
205+
ldp x29, x30, [sp], #16
206+
// passes argument in x0 as return value
207+
ret // looks like a return from smp_switch_boot_cpu_entry on the new boot cpu

0 commit comments

Comments
 (0)