arm64: add a spin-table implementation for Apple Silicon

The M1 has no EL3, so we're limited to a spin-table implementation if we
want to eventually use bhyve on it.  Implement spin-table now, but note
that we still prefer PSCI where possible.

Reviewed by:	mmel
Differential Revision:	https://reviews.freebsd.org/D34661
This commit is contained in:
Kyle Evans 2022-11-11 13:50:29 -06:00
parent 3a194eac3c
commit 753a23ac15

View File

@ -489,12 +489,54 @@ cpu_mp_probe(void)
return (1);
}
static int
enable_cpu_psci(uint64_t target_cpu, vm_paddr_t entry, u_int cpuid)
{
int err;
err = psci_cpu_on(target_cpu, entry, cpuid);
if (err != PSCI_RETVAL_SUCCESS) {
/*
* Panic here if INVARIANTS are enabled and PSCI failed to
* start the requested CPU. psci_cpu_on() returns PSCI_MISSING
* to indicate we are unable to use it to start the given CPU.
*/
KASSERT(err == PSCI_MISSING ||
(mp_quirks & MP_QUIRK_CPULIST) == MP_QUIRK_CPULIST,
("Failed to start CPU %u (%lx), error %d\n",
cpuid, target_cpu, err));
return (EINVAL);
}
return (0);
}
static int
enable_cpu_spin(uint64_t cpu, vm_paddr_t entry, vm_paddr_t release_paddr)
{
vm_paddr_t *release_addr;
release_addr = pmap_mapdev(release_paddr, sizeof(*release_addr));
if (release_addr == NULL)
return (ENOMEM);
*release_addr = entry;
pmap_unmapdev(release_addr, sizeof(*release_addr));
__asm __volatile(
"dsb sy \n"
"sev \n"
::: "memory");
return (0);
}
/*
* Starts a given CPU. If the CPU is already running, i.e. it is the boot CPU,
* do nothing. Returns true if the CPU is present and running.
*/
static bool
start_cpu(u_int cpuid, uint64_t target_cpu, int domain)
start_cpu(u_int cpuid, uint64_t target_cpu, int domain, vm_paddr_t release_addr)
{
struct pcpu *pcpup;
vm_size_t size;
@ -530,18 +572,19 @@ start_cpu(u_int cpuid, uint64_t target_cpu, int domain)
printf("Starting CPU %u (%lx)\n", cpuid, target_cpu);
pa = pmap_extract(kernel_pmap, (vm_offset_t)mpentry);
err = psci_cpu_on(target_cpu, pa, cpuid);
if (err != PSCI_RETVAL_SUCCESS) {
/*
* Panic here if INVARIANTS are enabled and PSCI failed to
* start the requested CPU. psci_cpu_on() returns PSCI_MISSING
* to indicate we are unable to use it to start the given CPU.
*/
KASSERT(err == PSCI_MISSING ||
(mp_quirks & MP_QUIRK_CPULIST) == MP_QUIRK_CPULIST,
("Failed to start CPU %u (%lx), error %d\n",
cpuid, target_cpu, err));
/*
* A limited set of hardware we support can only do spintables and
* remain useful, due to lack of EL3. Thus, we'll usually fall into the
* PSCI branch here.
*/
MPASS(release_addr == 0 || !psci_present);
if (release_addr != 0)
err = enable_cpu_spin(target_cpu, pa, release_addr);
else
err = enable_cpu_psci(target_cpu, pa, cpuid);
if (err != 0) {
pcpu_destroy(pcpup);
dpcpu[cpuid - 1] = NULL;
kmem_free(bootstacks[cpuid], MP_BOOTSTACK_SIZE);
@ -583,7 +626,7 @@ madt_handler(ACPI_SUBTABLE_HEADER *entry, void *arg)
if (vm_ndomains > 1)
domain = acpi_pxm_get_cpu_locality(intr->Uid);
#endif
if (start_cpu(id, intr->ArmMpidr, domain)) {
if (start_cpu(id, intr->ArmMpidr, domain, 0)) {
MPASS(cpuid_to_pcpu[id] != NULL);
cpuid_to_pcpu[id]->pc_acpi_id = intr->Uid;
/*
@ -630,10 +673,27 @@ cpu_init_acpi(void)
#endif
#ifdef FDT
/*
* Failure is indicated by failing to populate *release_addr.
*/
static void
populate_release_addr(phandle_t node, vm_paddr_t *release_addr)
{
pcell_t buf[2];
if (OF_getencprop(node, "cpu-release-addr", buf, sizeof(buf)) !=
sizeof(buf))
return;
*release_addr = (((uintptr_t)buf[0] << 32) | buf[1]);
}
static boolean_t
start_cpu_fdt(u_int id, phandle_t node, u_int addr_size, pcell_t *reg)
{
uint64_t target_cpu;
vm_paddr_t release_addr;
char *enable_method;
int domain;
int cpuid;
@ -648,7 +708,32 @@ start_cpu_fdt(u_int id, phandle_t node, u_int addr_size, pcell_t *reg)
else
cpuid = fdt_cpuid;
if (!start_cpu(cpuid, target_cpu, 0))
/*
* If PSCI is present, we'll always use that -- the cpu_on method is
* mandated in both v0.1 and v0.2. We'll check the enable-method if
* we don't have PSCI and use spin table if it's provided.
*/
release_addr = 0;
if (!psci_present && cpuid != 0) {
if (OF_getprop_alloc(node, "enable-method",
(void **)&enable_method) <= 0)
return (FALSE);
if (strcmp(enable_method, "spin-table") != 0) {
OF_prop_free(enable_method);
return (FALSE);
}
OF_prop_free(enable_method);
populate_release_addr(node, &release_addr);
if (release_addr == 0) {
printf("Failed to fetch release address for CPU %u",
cpuid);
return (FALSE);
}
}
if (!start_cpu(cpuid, target_cpu, 0, release_addr))
return (FALSE);
/*