FABRIC-3.10.8 MC-1b: aarch64's second core online and parked
Cores from the DTB /cpus or, on QEMU (ACPI, no DTB), the MADT GICC entries; the MADT lookup is now shared (hal/acpi.c) with amd64. PSCI CPU_ON through the discovered conduit into ap_entry.S, which copies the boot core's MMU/control state, sets TPIDR_EL1 and parks. Core 1 online at EL1 with its own block; its vCPU thread 0.0% on the host. QEMU aarch64 -smp 2 -name debug-threads=on. 3-ISA acceptance clean. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
co-authored by
Claude Opus 5.5
parent
24bde47481
commit
3a2ba5149f
@@ -874,3 +874,44 @@ The ledger reads pulled 319488 / returned 316703 on all three. That is the K-3 b
|
||||
- **Not as named in advance:** the host-side check (core 1's vCPU thread near 0% in `ps -L`) could not be made. QEMU names its threads per vCPU only with `-name debug-threads=on`, which is not on the command line. The park-wake counter (0 wakes) is the evidence of record. Adding `-name debug-threads=on` is a candidate for MC-1b's run line.
|
||||
|
||||
**MC-1a is complete.** amd64 now boots two cores, and the second is parked with interrupts masked. Next: MC-1b (aarch64, PSCI `CPU_ON`).
|
||||
|
||||
### 5.2 MC-1b — aarch64's second core online and parked (2026-09-26)
|
||||
|
||||
**Go:** Captain Bob, 2026-09-26, *"keep going"*.
|
||||
|
||||
**Change.**
|
||||
- **Enumeration** (`arch/aarch64/smp.c`): the devicetree's `/cpus` children with `device_type = "cpu"` (their `reg`, per `#address-cells`) when the firmware hands one over, as on the RPi5. Otherwise, the ACPI MADT's enabled GICC entries (type 0x0B, MPIDR at +68).
|
||||
- QEMU's AAVMF hands over ACPI and no DTB (`PSCI: no DTB -- using HVC`), so on QEMU the cores come from the MADT.
|
||||
- The hardware id is the MPIDR affinity (Aff3..Aff0). The boot core's is read from `MPIDR_EL1`.
|
||||
- **The MADT lookup is now shared** (`hal/acpi.c`, `acpi_find_table()`). amd64's own copy in `arch/amd64/smp.c` is removed and uses it.
|
||||
- **Start:** PSCI `CPU_ON` (SMC64 0xC4000003) through the conduit `arch.c` already discovers (`aarch64_psci_cpu_on()`; HVC on QEMU). Entry is `ap_entry.S`, with x0 = the core's boot block, heap-allocated and kept.
|
||||
- The block, the core's per-core block, and the first 256 bytes of the entry code are cleaned to the point of coherency (`dc cvac`, `dsb sy`) before the call, because the core starts with its MMU and caches off. TCG does not model this; the RPi5 will.
|
||||
- A non-zero PSCI return is printed and the start refused.
|
||||
- **`ap_entry.S`:**
|
||||
- It masks DAIF and copies the boot core's MAIR, TCR, TTBR0/1, VBAR and CPACR (FP/SIMD on). Then it does `tlbi vmalle1` and `ic iallu`, and writes SCTLR, which turns the MMU and caches on at the same identity-mapped address.
|
||||
- It sets TPIDR_EL1 to its block, loads its stack, records CurrentEL in its boot block, and calls `sk_smp_secondary_main()`.
|
||||
- It references no symbol: every address comes from the block, because the file is also assembled for the PE/COFF loader.
|
||||
- **Park:** `msr daifset, #0xf` and `wfi`, each wake counted.
|
||||
- **`arch_smp_arrival()`** is a per-ISA phrase in the online line: "EL1" on aarch64 (from the recorded CurrentEL), "long mode" on amd64.
|
||||
- **QEMU:** `-smp 2 -name debug-threads=on` on the `make qemu` aarch64 line only. The thread names make the host-side check possible.
|
||||
|
||||
**Evidence** (clean builds; the source diff is identical to the saved stage diff):
|
||||
|
||||
| ISA | log | SMP lines | standard sequence |
|
||||
|---|---|---|---|
|
||||
| aarch64 (`-smp 2`) | `logs/20260926-124518` | `SMP: 2 core(s) found`; `SMP: core 1 online: hw id 0x1, block 0x0000000048000130, per-CPU register 0x0000000048000130 (its own block), EL1, 226 us, parked`; `SMP: 2 core(s) online` | K-10 hashes, G A=40 B=40 byte-identical, HHH, all ok `3`, ledger unchanged, 0 B idle, 0 / 0; **0 park-wake lines, 0 `LOCK:` lines** |
|
||||
| amd64 (`-smp 2`) | `logs/20260926-125631` | as MC-1a, now with "long mode": `core 1 online: hw id 0x1, ... (its own block), long mode, 500 us, parked` | same; 0 park wakes, 0 `LOCK:` |
|
||||
| riscv64 | `logs/20260926-130637` | 1 core found, 1 online | same |
|
||||
|
||||
**Host side, aarch64** (`ps -L` on the QEMU process after O-A, then 20 s later):
|
||||
|
||||
| thread | %CPU | CPU time | 20 s later |
|
||||
|---|---|---|---|
|
||||
| `CPU 0/TCG` | 63.5 | 5:10 | 63.8, 5:24 |
|
||||
| `CPU 1/TCG` | **0.0** | **0:00** | **0.0, 0:00** |
|
||||
|
||||
Core 1 is halted, not spinning.
|
||||
|
||||
**Found, not changed:** core 0's vCPU burns about 64% of a host CPU while the system is idle. The boot core's idle loop (the console poll and idle point) polls rather than halting. This predates MC-1. MC-3's criterion "idle worker cores halted, not spinning" applies to the new cores; whether core 0 should halt when idle too is a separate question for MC-3's design.
|
||||
|
||||
**MC-1b is complete.** Next: MC-1c (riscv64, SBI HSM `hart_start`).
|
||||
|
||||
@@ -485,6 +485,9 @@ endif
|
||||
ifeq ($(ARCH),riscv64)
|
||||
LOADER_ASM += $(KERNEL_SRC)/arch/$(ARCH)/kernel_entry.S
|
||||
endif
|
||||
ifeq ($(ARCH),aarch64)
|
||||
LOADER_ASM += $(KERNEL_SRC)/arch/$(ARCH)/ap_entry.S
|
||||
endif
|
||||
|
||||
LOADER_ARCH_SRCS := \
|
||||
$(KERNEL_SRC)/arch/$(ARCH)/arch.c \
|
||||
@@ -502,6 +505,10 @@ ifeq ($(ARCH),riscv64)
|
||||
LOADER_ARCH_SRCS += \
|
||||
$(KERNEL_SRC)/arch/$(ARCH)/plic.c
|
||||
endif
|
||||
ifeq ($(ARCH),aarch64)
|
||||
LOADER_ARCH_SRCS += \
|
||||
$(KERNEL_SRC)/arch/$(ARCH)/smp.c
|
||||
endif
|
||||
else
|
||||
LOADER_SRCS_BASE := \
|
||||
$(KERNEL_SRC)/boot/uefi_loader.c \
|
||||
@@ -950,6 +957,8 @@ else ifeq ($(ARCH),aarch64)
|
||||
qemu-system-aarch64 \
|
||||
-machine virt \
|
||||
-cpu cortex-a57 \
|
||||
-smp 2 \
|
||||
-name debug-threads=on \
|
||||
$$(if [ -n "$$AAVMF_VARS" ]; then \
|
||||
echo "-drive if=pflash,format=raw,readonly=on,file=$$AAVMF_CODE -drive if=pflash,format=raw,file=$$AAVMF_VARS"; \
|
||||
else \
|
||||
|
||||
@@ -0,0 +1,32 @@
|
||||
/*
|
||||
StarForth — Steady-State Virtual Machine Runtime
|
||||
|
||||
Copyright (c) 2023–2025 Robert A. James
|
||||
All rights reserved.
|
||||
|
||||
This file is part of the StarForth project.
|
||||
|
||||
Licensed under the StarForth License, Version 1.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
*/
|
||||
|
||||
/**
|
||||
* acpi.h - Finding an ACPI table from the RSDP (FABRIC-3.10.8 MC-1b).
|
||||
*
|
||||
* Shared by the core enumeration of amd64 (MADT Local APIC entries) and
|
||||
* aarch64 (MADT GICC entries, when the firmware hands over ACPI rather
|
||||
* than a devicetree, as QEMU's AAVMF does).
|
||||
*/
|
||||
#ifndef STARKERNEL_ACPI_H
|
||||
#define STARKERNEL_ACPI_H
|
||||
|
||||
#include <stdint.h>
|
||||
|
||||
/* The first XSDT-listed table whose 4-byte signature is `sig`, or NULL.
|
||||
* `rsdp` is BootInfo->acpi_table (ACPI 2.0+ RSDP); NULL-safe. */
|
||||
const uint8_t *acpi_find_table(const void *rsdp, const char *sig);
|
||||
|
||||
/* A table's length field (bytes 4..7 of its header). */
|
||||
uint32_t acpi_table_length(const uint8_t *table);
|
||||
|
||||
#endif /* STARKERNEL_ACPI_H */
|
||||
@@ -60,4 +60,8 @@ int arch_smp_start(BootInfo *boot_info, sk_percpu_t *pc, void *stack_top);
|
||||
/* One park step: interrupts stay masked; returns when the core wakes. */
|
||||
void arch_smp_park_once(void);
|
||||
|
||||
/* Once `pc` is online: what the ISA can say about how it arrived (the
|
||||
* exception level, the privilege mode), as a short phrase for its line. */
|
||||
const char *arch_smp_arrival(const sk_percpu_t *pc);
|
||||
|
||||
#endif /* STARKERNEL_SMP_H */
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
@@ -0,0 +1,70 @@
|
||||
/*
|
||||
* ap_entry.S (aarch64) -- secondary-core entry, FABRIC-3.10.8 MC-1b.
|
||||
*
|
||||
* PSCI CPU_ON lands here at EL1 with the MMU and caches off and
|
||||
* x0 = the core's boot block (arch/aarch64/smp.c: a64_boot_t; the kernel
|
||||
* is identity-mapped, so its address is physical too). The boot core has
|
||||
* cleaned the block to the point of coherency, since these first loads
|
||||
* bypass the caches.
|
||||
*
|
||||
* The core takes the boot core's own translation and control state --
|
||||
* MAIR, TCR, TTBR0/1, VBAR, CPACR (FP/SIMD: kernel C uses it) -- then
|
||||
* SCTLR, which turns the MMU and caches on at this same, identity-mapped
|
||||
* address. It sets TPIDR_EL1 to its per-core block before any C code,
|
||||
* records CurrentEL, and calls sk_smp_secondary_main(block) on its own
|
||||
* stack. No symbol is referenced: every address comes from the block
|
||||
* (this file is also assembled for the PE/COFF loader).
|
||||
*/
|
||||
|
||||
.section .text
|
||||
.balign 4
|
||||
|
||||
.equ B_MAIR, 0
|
||||
.equ B_TCR, 8
|
||||
.equ B_TTBR0, 16
|
||||
.equ B_TTBR1, 24
|
||||
.equ B_VBAR, 32
|
||||
.equ B_CPACR, 40
|
||||
.equ B_SCTLR, 48
|
||||
.equ B_PERCPU, 56
|
||||
.equ B_STACK, 64
|
||||
.equ B_ENTRY, 72
|
||||
.equ B_EL, 80
|
||||
|
||||
.global sk_ap_entry_a64
|
||||
sk_ap_entry_a64:
|
||||
msr daifset, #0xf
|
||||
mov x19, x0
|
||||
ldr x1, [x19, #B_MAIR]
|
||||
msr mair_el1, x1
|
||||
ldr x1, [x19, #B_TCR]
|
||||
msr tcr_el1, x1
|
||||
ldr x1, [x19, #B_TTBR0]
|
||||
msr ttbr0_el1, x1
|
||||
ldr x1, [x19, #B_TTBR1]
|
||||
msr ttbr1_el1, x1
|
||||
ldr x1, [x19, #B_VBAR]
|
||||
msr vbar_el1, x1
|
||||
ldr x1, [x19, #B_CPACR]
|
||||
msr cpacr_el1, x1
|
||||
isb
|
||||
tlbi vmalle1
|
||||
dsb nsh
|
||||
ic iallu
|
||||
dsb nsh
|
||||
isb
|
||||
ldr x1, [x19, #B_SCTLR]
|
||||
msr sctlr_el1, x1
|
||||
isb
|
||||
ldr x1, [x19, #B_PERCPU]
|
||||
msr tpidr_el1, x1
|
||||
ldr x1, [x19, #B_STACK]
|
||||
mov sp, x1
|
||||
mrs x2, CurrentEL
|
||||
str x2, [x19, #B_EL]
|
||||
ldr x3, [x19, #B_ENTRY]
|
||||
ldr x0, [x19, #B_PERCPU]
|
||||
blr x3
|
||||
1:
|
||||
wfi
|
||||
b 1b
|
||||
@@ -192,6 +192,27 @@ void aarch64_psci_conduit_init(const void *dtb)
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief PSCI CPU_ON (SMC64, 0xC4000003) through the discovered conduit.
|
||||
*
|
||||
* FABRIC-3.10.8 MC-1b: starts core @p mpidr at physical @p entry with
|
||||
* x0 = @p context_id. Returns PSCI's status (0 = SUCCESS, -4 ALREADY_ON,
|
||||
* -2 INVALID_PARAMETERS, ...).
|
||||
*/
|
||||
int64_t aarch64_psci_cpu_on(uint64_t mpidr, uint64_t entry, uint64_t context_id)
|
||||
{
|
||||
register uint64_t x0 __asm__("x0") = 0xC4000003ull;
|
||||
register uint64_t x1 __asm__("x1") = mpidr;
|
||||
register uint64_t x2 __asm__("x2") = entry;
|
||||
register uint64_t x3 __asm__("x3") = context_id;
|
||||
if (s_psci_use_smc) {
|
||||
__asm__ volatile ("smc #0" : "+r"(x0), "+r"(x1), "+r"(x2), "+r"(x3) :: "memory");
|
||||
} else {
|
||||
__asm__ volatile ("hvc #0" : "+r"(x0), "+r"(x1), "+r"(x2), "+r"(x3) :: "memory");
|
||||
}
|
||||
return (int64_t)x0;
|
||||
}
|
||||
|
||||
void arch_cold_reset(void)
|
||||
{
|
||||
arch_disable_interrupts();
|
||||
|
||||
@@ -0,0 +1,208 @@
|
||||
/*
|
||||
StarForth — Steady-State Virtual Machine Runtime
|
||||
|
||||
Copyright (c) 2023–2025 Robert A. James
|
||||
All rights reserved.
|
||||
|
||||
This file is part of the StarForth project.
|
||||
|
||||
Licensed under the StarForth License, Version 1.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
*/
|
||||
|
||||
/**
|
||||
* smp.c (aarch64) - secondary-core bring-up, FABRIC-3.10.8 MC-1b.
|
||||
*
|
||||
* Cores come from the devicetree's /cpus when the firmware hands one over
|
||||
* (the Raspberry Pi 5), otherwise from the ACPI MADT's GICC entries (QEMU's
|
||||
* AAVMF hands over ACPI and no DTB). The hardware id is the MPIDR affinity
|
||||
* (Aff3..Aff0). Each core is started with PSCI CPU_ON through the conduit
|
||||
* arch.c discovered, into ap_entry.S, with x0 = its boot block.
|
||||
*/
|
||||
|
||||
#include "starkernel/smp.h"
|
||||
#include "starkernel/acpi.h"
|
||||
#include "starkernel/fdt.h"
|
||||
#include "starkernel/kmalloc.h"
|
||||
#include "starkernel/console.h"
|
||||
#include "freestanding/stdio.h"
|
||||
#include "uefi.h"
|
||||
|
||||
extern void sk_ap_entry_a64(void);
|
||||
extern int64_t aarch64_psci_cpu_on(uint64_t mpidr, uint64_t entry, uint64_t context_id);
|
||||
|
||||
#define MPIDR_AFF_MASK 0xFF00FFFFFFull /* Aff3 [39:32], Aff2..Aff0 [23:0] */
|
||||
|
||||
/* The core's boot block (ap_entry.S, B_* offsets). Heap-allocated per core
|
||||
* and kept: the core reads it with its MMU off, then writes B_EL. */
|
||||
typedef struct {
|
||||
uint64_t mair, tcr, ttbr0, ttbr1, vbar, cpacr, sctlr;
|
||||
uint64_t percpu, stack, entry;
|
||||
volatile uint64_t el;
|
||||
} a64_boot_t;
|
||||
|
||||
static uint64_t be_cells(const uint8_t *p, uint32_t cells)
|
||||
{
|
||||
uint64_t v = 0;
|
||||
uint32_t i;
|
||||
for (i = 0; i < cells * 4u; i++) v = (v << 8) | p[i];
|
||||
return v;
|
||||
}
|
||||
|
||||
static int id_seen(const uint64_t *ids, uint32_t n, uint64_t id)
|
||||
{
|
||||
uint32_t i;
|
||||
for (i = 0; i < n; i++) if (ids[i] == id) return 1;
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void add_id(uint64_t *ids, uint32_t cap, uint32_t *n, uint64_t id)
|
||||
{
|
||||
if (cap) {
|
||||
if (id_seen(ids, *n < cap ? *n : cap, id)) return;
|
||||
if (*n < cap) ids[*n] = id;
|
||||
}
|
||||
(*n)++;
|
||||
}
|
||||
|
||||
static uint32_t enumerate_dtb(const void *dtb, uint64_t *ids, uint32_t cap)
|
||||
{
|
||||
const void *cpus = fdt_find_node_by_name(dtb, "cpus");
|
||||
const void *c = (const void *)0;
|
||||
const uint8_t *ac;
|
||||
uint32_t len, cells = 1, n = 0;
|
||||
|
||||
if (!cpus) return 0;
|
||||
ac = (const uint8_t *)fdt_find_prop_in_node(dtb, cpus, "#address-cells", &len);
|
||||
if (ac && len == 4) cells = (uint32_t)be_cells(ac, 1);
|
||||
if (cells < 1 || cells > 2) cells = 1;
|
||||
while ((c = fdt_next_child_node(dtb, cpus, c)) != (const void *)0) {
|
||||
const char *type = (const char *)fdt_find_prop_in_node(dtb, c, "device_type", &len);
|
||||
const uint8_t *reg;
|
||||
if (!type || len < 4 || type[0] != 'c' || type[1] != 'p' || type[2] != 'u' || type[3] != 0) continue;
|
||||
reg = (const uint8_t *)fdt_find_prop_in_node(dtb, c, "reg", &len);
|
||||
if (!reg || len < cells * 4u) continue;
|
||||
add_id(ids, cap, &n, be_cells(reg, cells) & MPIDR_AFF_MASK);
|
||||
}
|
||||
return n;
|
||||
}
|
||||
|
||||
/* MADT type 0x0B (GICC): flags at +12 (bit 0 enabled), MPIDR at +68. */
|
||||
static uint32_t enumerate_madt(const void *rsdp, uint64_t *ids, uint32_t cap)
|
||||
{
|
||||
const uint8_t *madt = acpi_find_table(rsdp, "APIC");
|
||||
const uint8_t *p, *end;
|
||||
uint32_t n = 0;
|
||||
|
||||
if (!madt) return 0;
|
||||
end = madt + acpi_table_length(madt);
|
||||
for (p = madt + 44; p + 2 <= end; p += p[1]) {
|
||||
uint32_t flags;
|
||||
uint64_t mpidr;
|
||||
uint32_t k;
|
||||
if (p[1] == 0 || p + p[1] > end) break;
|
||||
if (p[0] != 0x0B || p[1] < 76) continue;
|
||||
flags = (uint32_t)p[12] | ((uint32_t)p[13] << 8) | ((uint32_t)p[14] << 16) | ((uint32_t)p[15] << 24);
|
||||
if (!(flags & 1u)) continue;
|
||||
mpidr = 0;
|
||||
for (k = 0; k < 8; k++) mpidr |= (uint64_t)p[68 + k] << (8 * k);
|
||||
add_id(ids, cap, &n, mpidr & MPIDR_AFF_MASK);
|
||||
}
|
||||
return n;
|
||||
}
|
||||
|
||||
uint64_t arch_smp_boot_hw_id(void)
|
||||
{
|
||||
uint64_t m;
|
||||
__asm__ volatile ("mrs %0, mpidr_el1" : "=r"(m));
|
||||
return m & MPIDR_AFF_MASK;
|
||||
}
|
||||
|
||||
uint32_t arch_smp_enumerate(BootInfo *boot_info, uint64_t *hw_ids, uint32_t cap)
|
||||
{
|
||||
uint32_t n = 0;
|
||||
if (boot_info && fdt_valid(boot_info->dtb)) n = enumerate_dtb(boot_info->dtb, hw_ids, cap);
|
||||
if (n == 0 && boot_info) n = enumerate_madt(boot_info->acpi_table, hw_ids, cap);
|
||||
if (n == 0) {
|
||||
if (cap) hw_ids[0] = arch_smp_boot_hw_id();
|
||||
return 1;
|
||||
}
|
||||
return n;
|
||||
}
|
||||
|
||||
void arch_smp_park_once(void)
|
||||
{
|
||||
__asm__ volatile ("msr daifset, #0xf\n\twfi" ::: "memory");
|
||||
}
|
||||
|
||||
/* Clean [p, p+len) to the point of coherency: the core reads it with its
|
||||
* MMU and caches off. */
|
||||
static void clean_to_poc(const void *p, uint64_t len)
|
||||
{
|
||||
uint64_t a = (uint64_t)(uintptr_t)p & ~63ull, end = (uint64_t)(uintptr_t)p + len;
|
||||
for (; a < end; a += 64) __asm__ volatile ("dc cvac, %0" :: "r"(a) : "memory");
|
||||
__asm__ volatile ("dsb sy" ::: "memory");
|
||||
}
|
||||
|
||||
/* Each started core's block, found again by arch_smp_arrival(): keyed by
|
||||
* the per-core block, so a list (heap), not a table. */
|
||||
typedef struct a64_started { sk_percpu_t *pc; a64_boot_t *b; struct a64_started *next; } a64_started_t;
|
||||
static a64_started_t *g_started;
|
||||
|
||||
int arch_smp_start(BootInfo *boot_info, sk_percpu_t *pc, void *stack_top)
|
||||
{
|
||||
a64_boot_t *b;
|
||||
a64_started_t *s;
|
||||
int64_t rc;
|
||||
uint64_t v;
|
||||
(void)boot_info;
|
||||
|
||||
b = (a64_boot_t *)kmalloc(sizeof(a64_boot_t));
|
||||
s = (a64_started_t *)kmalloc(sizeof(a64_started_t));
|
||||
if (!b || !s) {
|
||||
if (b) kfree(b);
|
||||
if (s) kfree(s);
|
||||
return -1;
|
||||
}
|
||||
__asm__ volatile ("mrs %0, mair_el1" : "=r"(v)); b->mair = v;
|
||||
__asm__ volatile ("mrs %0, tcr_el1" : "=r"(v)); b->tcr = v;
|
||||
__asm__ volatile ("mrs %0, ttbr0_el1" : "=r"(v)); b->ttbr0 = v;
|
||||
__asm__ volatile ("mrs %0, ttbr1_el1" : "=r"(v)); b->ttbr1 = v;
|
||||
__asm__ volatile ("mrs %0, vbar_el1" : "=r"(v)); b->vbar = v;
|
||||
__asm__ volatile ("mrs %0, cpacr_el1" : "=r"(v)); b->cpacr = v;
|
||||
__asm__ volatile ("mrs %0, sctlr_el1" : "=r"(v)); b->sctlr = v;
|
||||
b->percpu = (uint64_t)(uintptr_t)pc;
|
||||
b->stack = (uint64_t)(uintptr_t)stack_top & ~15ull;
|
||||
b->entry = (uint64_t)(uintptr_t)&sk_smp_secondary_main;
|
||||
b->el = 0;
|
||||
s->pc = pc; s->b = b; s->next = g_started; g_started = s;
|
||||
|
||||
clean_to_poc(b, sizeof(*b));
|
||||
clean_to_poc(pc, sizeof(*pc));
|
||||
clean_to_poc((const void *)&sk_ap_entry_a64, 256);
|
||||
|
||||
rc = aarch64_psci_cpu_on(pc->hw_id, (uint64_t)(uintptr_t)&sk_ap_entry_a64, (uint64_t)(uintptr_t)b);
|
||||
if (rc != 0) {
|
||||
char line[96];
|
||||
snprintf(line, sizeof line, "SMP: PSCI CPU_ON returned %lld", (long long)rc);
|
||||
console_println(line);
|
||||
return -1;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
const char *arch_smp_arrival(const sk_percpu_t *pc)
|
||||
{
|
||||
a64_started_t *s;
|
||||
for (s = g_started; s; s = s->next) {
|
||||
if (s->pc == pc) {
|
||||
switch ((s->b->el >> 2) & 3u) {
|
||||
case 1: return "EL1";
|
||||
case 2: return "EL2";
|
||||
case 3: return "EL3";
|
||||
default: return "EL0?";
|
||||
}
|
||||
}
|
||||
}
|
||||
return "?";
|
||||
}
|
||||
@@ -27,6 +27,7 @@
|
||||
#include "starkernel/apic.h"
|
||||
#include "starkernel/timer.h"
|
||||
#include "starkernel/console.h"
|
||||
#include "starkernel/acpi.h" /* MC-1b: the MADT lookup, shared with aarch64 */
|
||||
#include "uefi.h"
|
||||
|
||||
extern char sk_ap_tramp_start[], sk_ap_tramp_end[];
|
||||
@@ -49,48 +50,6 @@ typedef struct __attribute__((packed)) {
|
||||
|
||||
/* ---- MADT ---------------------------------------------------------------- */
|
||||
|
||||
typedef struct __attribute__((packed)) {
|
||||
uint8_t signature[8];
|
||||
uint8_t checksum;
|
||||
uint8_t oem_id[6];
|
||||
uint8_t revision;
|
||||
uint32_t rsdt_address;
|
||||
uint32_t length;
|
||||
uint64_t xsdt_address;
|
||||
} smp_rsdp_t;
|
||||
|
||||
typedef struct __attribute__((packed)) {
|
||||
uint8_t signature[4];
|
||||
uint32_t length;
|
||||
uint8_t rest[28];
|
||||
} smp_sdt_t;
|
||||
|
||||
static int sig_eq(const uint8_t *p, const char *s, int n)
|
||||
{
|
||||
int i;
|
||||
for (i = 0; i < n; i++) if (p[i] != (uint8_t)s[i]) return 0;
|
||||
return 1;
|
||||
}
|
||||
|
||||
static const uint8_t *find_madt(void *rsdp_ptr)
|
||||
{
|
||||
const smp_rsdp_t *r = (const smp_rsdp_t *)rsdp_ptr;
|
||||
const smp_sdt_t *xsdt;
|
||||
const uint64_t *ent;
|
||||
uint32_t n, i;
|
||||
|
||||
if (!r || !sig_eq(r->signature, "RSD PTR ", 8) || r->revision < 2 || !r->xsdt_address) return 0;
|
||||
xsdt = (const smp_sdt_t *)(uintptr_t)r->xsdt_address;
|
||||
if (!sig_eq(xsdt->signature, "XSDT", 4) || xsdt->length <= 36u) return 0;
|
||||
n = (xsdt->length - 36u) / 8u;
|
||||
ent = (const uint64_t *)((const uint8_t *)xsdt + 36u);
|
||||
for (i = 0; i < n; i++) {
|
||||
const smp_sdt_t *sdt = (const smp_sdt_t *)(uintptr_t)ent[i];
|
||||
if (sdt && sig_eq(sdt->signature, "APIC", 4)) return (const uint8_t *)sdt;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int id_seen(const uint64_t *ids, uint32_t n, uint64_t id)
|
||||
{
|
||||
uint32_t i;
|
||||
@@ -102,7 +61,7 @@ static int id_seen(const uint64_t *ids, uint32_t n, uint64_t id)
|
||||
* id fits xAPIC (<= 255) and was not already listed. */
|
||||
uint32_t arch_smp_enumerate(BootInfo *boot_info, uint64_t *hw_ids, uint32_t cap)
|
||||
{
|
||||
const uint8_t *madt = find_madt(boot_info ? boot_info->acpi_table : (void *)0);
|
||||
const uint8_t *madt = acpi_find_table(boot_info ? boot_info->acpi_table : (void *)0, "APIC");
|
||||
const uint8_t *p, *end;
|
||||
uint32_t n = 0, pass;
|
||||
|
||||
@@ -110,7 +69,7 @@ uint32_t arch_smp_enumerate(BootInfo *boot_info, uint64_t *hw_ids, uint32_t cap)
|
||||
if (cap) hw_ids[0] = apic_id();
|
||||
return 1;
|
||||
}
|
||||
end = madt + ((const smp_sdt_t *)madt)->length;
|
||||
end = madt + acpi_table_length(madt);
|
||||
for (pass = 0; pass < 2; pass++) {
|
||||
for (p = madt + 44; p + 2 <= end; p += p[1]) {
|
||||
uint64_t id;
|
||||
@@ -148,6 +107,12 @@ void arch_smp_park_once(void)
|
||||
__asm__ volatile ("cli; hlt" ::: "memory");
|
||||
}
|
||||
|
||||
const char *arch_smp_arrival(const sk_percpu_t *pc)
|
||||
{
|
||||
(void)pc;
|
||||
return "long mode"; /* the trampoline's only way to reach C */
|
||||
}
|
||||
|
||||
/* ---- the trampoline page ------------------------------------------------- */
|
||||
|
||||
static uint64_t g_tramp_page; /* 0 = not chosen yet */
|
||||
|
||||
@@ -0,0 +1,61 @@
|
||||
/*
|
||||
StarForth — Steady-State Virtual Machine Runtime
|
||||
|
||||
Copyright (c) 2023–2025 Robert A. James
|
||||
All rights reserved.
|
||||
|
||||
This file is part of the StarForth project.
|
||||
|
||||
Licensed under the StarForth License, Version 1.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
*/
|
||||
|
||||
/**
|
||||
* acpi.c - Finding an ACPI table from the RSDP (FABRIC-3.10.8 MC-1b).
|
||||
* See starkernel/acpi.h. Moved out of arch/amd64/smp.c (MC-1a) when
|
||||
* aarch64 needed the same MADT lookup.
|
||||
*/
|
||||
|
||||
#include "starkernel/acpi.h"
|
||||
|
||||
static int sig_eq(const uint8_t *p, const char *s, int n)
|
||||
{
|
||||
int i;
|
||||
for (i = 0; i < n; i++) if (p[i] != (uint8_t)s[i]) return 0;
|
||||
return 1;
|
||||
}
|
||||
|
||||
static uint32_t le32(const uint8_t *p)
|
||||
{
|
||||
return (uint32_t)p[0] | ((uint32_t)p[1] << 8) | ((uint32_t)p[2] << 16) | ((uint32_t)p[3] << 24);
|
||||
}
|
||||
|
||||
static uint64_t le64(const uint8_t *p)
|
||||
{
|
||||
return (uint64_t)le32(p) | ((uint64_t)le32(p + 4) << 32);
|
||||
}
|
||||
|
||||
uint32_t acpi_table_length(const uint8_t *table)
|
||||
{
|
||||
return table ? le32(table + 4) : 0;
|
||||
}
|
||||
|
||||
const uint8_t *acpi_find_table(const void *rsdp, const char *sig)
|
||||
{
|
||||
const uint8_t *r = (const uint8_t *)rsdp;
|
||||
const uint8_t *xsdt;
|
||||
uint32_t len, n, i;
|
||||
|
||||
/* RSDP: "RSD PTR " at 0, revision at 15, XSDT address at 24 (ACPI 2.0+). */
|
||||
if (!r || !sig_eq(r, "RSD PTR ", 8) || r[15] < 2) return (const uint8_t *)0;
|
||||
xsdt = (const uint8_t *)(uintptr_t)le64(r + 24);
|
||||
if (!xsdt || !sig_eq(xsdt, "XSDT", 4)) return (const uint8_t *)0;
|
||||
len = le32(xsdt + 4);
|
||||
if (len <= 36u) return (const uint8_t *)0;
|
||||
n = (len - 36u) / 8u;
|
||||
for (i = 0; i < n; i++) {
|
||||
const uint8_t *t = (const uint8_t *)(uintptr_t)le64(xsdt + 36u + 8u * i);
|
||||
if (t && sig_eq(t, sig, 4)) return t;
|
||||
}
|
||||
return (const uint8_t *)0;
|
||||
}
|
||||
@@ -64,10 +64,10 @@ void sk_smp_secondary_main(sk_percpu_t *pc)
|
||||
}
|
||||
}
|
||||
|
||||
#if defined(__x86_64__)
|
||||
#if defined(__x86_64__) || defined(__aarch64__)
|
||||
#define SMP_ISA_BRINGS_UP 1
|
||||
#else
|
||||
/* MC-1b (aarch64) and MC-1c (riscv64): not yet -- the boot core only. */
|
||||
/* MC-1c (riscv64): not yet -- the boot core only. */
|
||||
#define SMP_ISA_BRINGS_UP 0
|
||||
uint32_t arch_smp_enumerate(BootInfo *b, uint64_t *ids, uint32_t cap)
|
||||
{
|
||||
@@ -78,11 +78,12 @@ uint32_t arch_smp_enumerate(BootInfo *b, uint64_t *ids, uint32_t cap)
|
||||
uint64_t arch_smp_boot_hw_id(void) { return 0; }
|
||||
int arch_smp_start(BootInfo *b, sk_percpu_t *pc, void *st) { (void)b; (void)pc; (void)st; return -1; }
|
||||
void arch_smp_park_once(void) { }
|
||||
const char *arch_smp_arrival(const sk_percpu_t *pc) { (void)pc; return ""; }
|
||||
#endif
|
||||
|
||||
void sk_smp_init(BootInfo *boot_info)
|
||||
{
|
||||
char line[160], a[19], b[19];
|
||||
char line[200], a[19], b[19];
|
||||
uint32_t n, i, next = 1;
|
||||
uint64_t boot_id, *ids;
|
||||
sk_percpu_t *boot = sk_percpu();
|
||||
@@ -173,10 +174,11 @@ void sk_smp_init(BootInfo *boot_info)
|
||||
char blk[19];
|
||||
hex64(blk, (uint64_t)(uintptr_t)pc);
|
||||
snprintf(line, sizeof line,
|
||||
"SMP: core %u online: hw id %s, block %s, per-CPU register %s (%s), %llu us, parked",
|
||||
"SMP: core %u online: hw id %s, block %s, per-CPU register %s (%s), %s, %llu us, parked",
|
||||
(unsigned)pc->core_id, a, blk, b,
|
||||
pc->reg_readback == (uint64_t)(uintptr_t)pc &&
|
||||
pc->reg_readback != (uint64_t)(uintptr_t)boot ? "its own block" : "WRONG",
|
||||
arch_smp_arrival(pc),
|
||||
(unsigned long long)(hz ? waited / (hz / 1000000u ? hz / 1000000u : 1u) : 0));
|
||||
}
|
||||
console_println(line);
|
||||
|
||||
Reference in New Issue
Block a user