Files
lk/arch/arm64/mp.c
Travis Geiselbrecht 91128ad729 [arch][arm64] clean up how secondary cpus are initialized and tracked
- Add a percpu structure for each cpu, akin to x86-64 and riscv. Pointed
  to by x18, which is now reserved for this in the kernel. Tweaked
  exception and context switch routines to leave x18 alone.
- Remove the cpu-trapping spinlock logic that is unused in mainline,
  probably. (Can add a new version of it back if it's necessary).
- Switch fdtwalk helper to using the newer, cleaner way of initializing
  secondaries using the PSCI CPU_ON argument that should be pretty
  standard on modern implementations. (Possibly an issue with old
  firmware).
- Remove the notion of computing the cpu ID from the Affinity levels,
  which doesn't really work properly on modern ARM CPUs which more or
  less abandoned the logical meaning of AFFn.
2025-10-12 19:47:33 -07:00

153 lines
4.3 KiB
C

/*
* Copyright (c) 2014 Travis Geiselbrecht
*
* Use of this source code is governed by a MIT-style
* license that can be found in the LICENSE file or at
* https://opensource.org/licenses/MIT
*/
#include "arch/arm64/mp.h"
#include <arch/atomic.h>
#include <arch/mp.h>
#include <arch/ops.h>
#include <assert.h>
#include <lk/err.h>
#include <lk/init.h>
#include <lk/main.h>
#include <lk/trace.h>
#include <platform/interrupts.h>
#include <stdlib.h>
#include <string.h>
#include "arm64_priv.h"
#if WITH_DEV_INTERRUPT_ARM_GIC
#include <dev/interrupt/arm_gic.h>
#elif PLATFORM_BCM28XX
/* bcm28xx has a weird custom interrupt controller for MP */
extern void bcm28xx_send_ipi(uint irq, uint cpu_mask);
#else
#error need other implementation of interrupt controller that can ipi
#endif
#define LOCAL_TRACE 0
#define GIC_IPI_BASE (14)
// percpu structures for the boot cpu and secondaries
static struct arm64_percpu boot_percpu;
static struct arm64_percpu *secondary_percpu;
static uint secondaries_to_init = 0;
status_t arch_mp_send_ipi(mp_cpu_mask_t target, mp_ipi_t ipi) {
LTRACEF("target 0x%x, ipi %u\n", target, ipi);
#if WITH_DEV_INTERRUPT_ARM_GIC
uint gic_ipi_num = ipi + GIC_IPI_BASE;
/* filter out targets outside of the range of cpus we care about */
target &= ((1UL << SMP_MAX_CPUS) - 1);
if (target != 0) {
LTRACEF("target 0x%x, gic_ipi %u\n", target, gic_ipi_num);
arm_gic_sgi(gic_ipi_num, ARM_GIC_SGI_FLAG_NS, target);
}
#elif PLATFORM_BCM28XX
/* filter out targets outside of the range of cpus we care about */
target &= ((1UL << SMP_MAX_CPUS) - 1);
if (target != 0) {
bcm28xx_send_ipi(ipi, target);
}
#endif
return NO_ERROR;
}
static enum handler_return arm_ipi_generic_handler(void *arg) {
LTRACEF("cpu %u, arg %p\n", arch_curr_cpu_num(), arg);
return INT_NO_RESCHEDULE;
}
static enum handler_return arm_ipi_reschedule_handler(void *arg) {
LTRACEF("cpu %u, arg %p\n", arch_curr_cpu_num(), arg);
return mp_mbx_reschedule_irq();
}
void arm64_mp_init_percpu(void) {
register_int_handler(MP_IPI_GENERIC + GIC_IPI_BASE, &arm_ipi_generic_handler, 0);
register_int_handler(MP_IPI_RESCHEDULE + GIC_IPI_BASE, &arm_ipi_reschedule_handler, 0);
// unmask_interrupt(MP_IPI_GENERIC + GIC_IPI_BASE);
// unmask_interrupt(MP_IPI_RESCHEDULE + GIC_IPI_BASE);
}
// Special case, called from start.S code on the boot cpu, which willJ always be numbered 0
// called from assembly
void arm64_init_boot_percpu(void);
void arm64_init_boot_percpu(void) {
arm64_set_percpu(&boot_percpu);
boot_percpu.cpu_num = 0;
boot_percpu.mpidr = ARM64_READ_SYSREG(mpidr_el1);
}
static void arm64_init_secondary_percpu(uint cpu_num) {
// If we're out of range, just hang
if (cpu_num > secondaries_to_init) {
for (;;) {
__asm__ volatile("wfi");
}
}
struct arm64_percpu *percpu = &secondary_percpu[cpu_num - 1];
arm64_set_percpu(percpu);
percpu->cpu_num = cpu_num;
percpu->mpidr = ARM64_READ_SYSREG(mpidr_el1);
}
void arm64_set_secondary_cpu_count(int count) {
secondaries_to_init = count;
DEBUG_ASSERT(secondary_percpu == NULL);
// clamp the secondary cpu count to SMP_MAX_CPUS - 1
if (secondaries_to_init > (SMP_MAX_CPUS - 1)) {
dprintf(INFO, "ARM64: clamping secondary cpu count from %d to %d\n", secondaries_to_init, SMP_MAX_CPUS - 1);
secondaries_to_init = SMP_MAX_CPUS - 1;
}
// Allocate percpu structures for the secondaries
if (secondaries_to_init > 0) {
const size_t len = sizeof(struct arm64_percpu) * secondaries_to_init;
secondary_percpu = memalign(CACHE_LINE, len);
DEBUG_ASSERT(secondary_percpu);
memset(secondary_percpu, 0, len);
}
}
void arm64_mp_init(void) {
arm64_mp_init_percpu();
}
/* called from assembly */
void arm64_secondary_entry(ulong);
void arm64_secondary_entry(ulong asm_cpu_num) {
arm64_init_secondary_percpu(asm_cpu_num);
uint cpu = arch_curr_cpu_num();
if (cpu != asm_cpu_num) {
return;
}
arm64_early_init_percpu();
/* run early secondary cpu init routines up to the threading level */
lk_init_level(LK_INIT_FLAG_SECONDARY_CPUS, LK_INIT_LEVEL_EARLIEST, LK_INIT_LEVEL_THREADING - 1);
arm64_mp_init_percpu();
LTRACEF("cpu num %d\n", cpu);
lk_secondary_cpu_entry();
}