|
5 | 5 | #include "aic.h" |
6 | 6 | #include "aic_regs.h" |
7 | 7 | #include "cpu_regs.h" |
| 8 | +#include "exception.h" |
| 9 | +#include "fb.h" |
8 | 10 | #include "malloc.h" |
9 | 11 | #include "memory.h" |
10 | 12 | #include "pmgr.h" |
@@ -108,7 +110,6 @@ void smp_secondary_prep_el3(void) |
108 | 110 | return; |
109 | 111 | } |
110 | 112 |
|
111 | | - |
112 | 113 | static void smp_prepare_cpu(int index) |
113 | 114 | { |
114 | 115 | memset(&spin_table[index], 0, sizeof(struct spin_table)); |
@@ -420,6 +421,93 @@ void smp_stop_secondaries(bool deep_sleep) |
420 | 421 | } |
421 | 422 | } |
422 | 423 |
|
| 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 | + |
423 | 511 | void smp_send_ipi(int cpu) |
424 | 512 | { |
425 | 513 | if (cpu >= MAX_CPUS) |
|
0 commit comments