Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
18 changes: 13 additions & 5 deletions kernel/src/arch_impl/aarch64/boot.S
Original file line number Diff line number Diff line change
Expand Up @@ -813,15 +813,17 @@ secondary_el1_init:
isb

// Set up per-CPU boot stack (physical addresses, before MMU)
// Stack top = 0x4100_0000 + (cpu_id + 1) * 0x20_0000
// Stack top = SMP_STACK_BASE_PHYS + (cpu_id + 1) * 0x20_0000
// SMP_STACK_BASE_PHYS is set by CPU 0 Rust code to (ram_base + 0x01000000).
// This gives each CPU a 2MB stack region:
// CPU 1: 0x4120_0000 (top) .. 0x4100_0000
// CPU 2: 0x4140_0000 (top) .. 0x4120_0000
// CPU 3: 0x4160_0000 (top) .. 0x4140_0000
// CPU 1: base+0x0020_0000 (top) .. base
// CPU 2: base+0x0040_0000 (top) .. base+0x0020_0000
// CPU 3: base+0x0060_0000 (top) .. base+0x0040_0000
mov x0, x19 // cpu_id
add x0, x0, #1 // cpu_id + 1
lsl x0, x0, #21 // * 0x20_0000 (2MB)
ldr x1, =0x41000000 // base of per-CPU stack region
ldr x1, =SMP_STACK_BASE_PHYS
ldr x1, [x1] // x1 = actual stack base (set by CPU 0)
add x0, x0, x1
mov sp, x0

Expand Down Expand Up @@ -987,6 +989,9 @@ SMP_MAIR_PTR:
.global SMP_TCR_PTR
SMP_TCR_PTR:
.quad SMP_TCR_PHYS
.global SMP_STACK_BASE_PTR
SMP_STACK_BASE_PTR:
.quad SMP_STACK_BASE_PHYS

// -----------------------------------------------------------------------------
// Boot-time BSS (low): page tables + boot stack
Expand Down Expand Up @@ -1043,6 +1048,9 @@ SMP_MAIR_PHYS:
.global SMP_TCR_PHYS
SMP_TCR_PHYS:
.skip 8
.global SMP_STACK_BASE_PHYS
SMP_STACK_BASE_PHYS:
.skip 8

.balign 16
.global __boot_stack_bottom
Expand Down
29 changes: 17 additions & 12 deletions kernel/src/arch_impl/aarch64/constants.rs
Original file line number Diff line number Diff line change
Expand Up @@ -205,20 +205,25 @@ pub const STACK_GUARD_SIZE: usize = PAGE_SIZE;

/// Base address for per-CPU kernel stacks region (ARM64).
/// Uses a region within the HHDM (higher-half direct map) that is mapped
/// by the boot page tables. Placed at physical 0x4100_0000 (16MB into RAM
/// after kernel) to stay within typical 512MB QEMU RAM configs.
/// by the boot page tables. Placed at ram_base + 0x0100_0000 (16MB into RAM
/// after kernel) to stay within typical 512MB RAM configs.
///
/// QEMU virt RAM layout: physical 0x4000_0000 (1GB mark) for N MB
/// With 512MB RAM: physical 0x4000_0000 to 0x6000_0000
/// RAM layout (relative to ram_base):
/// - +0x0000_0000 - +0x0100_0000: Kernel image (~16MB)
/// - +0x0100_0000 - +0x0200_0000: Per-CPU stacks (16MB for 8 CPUs)
/// - +0x0200_0000 - end: Heap and dynamic allocations
///
/// Stack layout in RAM:
/// - 0x4000_0000 - 0x4100_0000: Kernel image (~16MB)
/// - 0x4100_0000 - 0x4200_0000: Per-CPU stacks (16MB for 8 CPUs)
/// - 0x4200_0000 - 0x6000_0000: Heap and dynamic allocations
///
/// Virtual: 0xFFFF_0000_4100_0000
/// Physical: 0x4100_0000
pub const PERCPU_STACK_REGION_BASE: u64 = HHDM_BASE + 0x4100_0000;
/// Platform-dependent physical base:
/// - QEMU/Parallels (ram at 0x4000_0000): physical 0x4100_0000
/// - VMware (ram at 0x8000_0000): physical 0x8100_0000
#[inline]
pub fn percpu_stack_region_base() -> u64 {
HHDM_BASE + 0x4100_0000 + crate::platform_config::ram_base_offset()
}

/// Legacy constant for compile-time contexts (diagnostics). Uses the default
/// QEMU/Parallels base. Runtime code should use percpu_stack_region_base().
pub const PERCPU_STACK_REGION_BASE_DEFAULT: u64 = HHDM_BASE + 0x4100_0000;

/// Maximum number of CPUs supported on ARM64.
/// Limited to 8 to keep stack region within 512MB RAM constraint.
Expand Down
4 changes: 2 additions & 2 deletions kernel/src/arch_impl/aarch64/context_switch.rs
Original file line number Diff line number Diff line change
Expand Up @@ -681,7 +681,7 @@ fn setup_idle_return_locked(
.and_then(|t| t.kernel_stack_top.map(|v| v.as_u64()))
.unwrap_or_else(|| {
let cpu_id64 = cpu_id as u64;
0xFFFF_0000_0000_0000u64 + 0x4100_0000 + (cpu_id64 + 1) * 0x20_0000
super::constants::percpu_stack_region_base() + (cpu_id64 + 1) * 0x20_0000
});

// Clear all general purpose registers for clean state
Expand Down Expand Up @@ -1232,7 +1232,7 @@ fn setup_idle_return_arm64(frame: &mut Aarch64ExceptionFrame) {
.flatten()
.unwrap_or_else(|| {
let cpu_id = Aarch64PerCpu::cpu_id() as u64;
let boot_stack_top = 0xFFFF_0000_0000_0000u64 + 0x4100_0000 + (cpu_id + 1) * 0x20_0000;
let boot_stack_top = super::constants::percpu_stack_region_base() + (cpu_id + 1) * 0x20_0000;
boot_stack_top
});

Expand Down
22 changes: 11 additions & 11 deletions kernel/src/arch_impl/aarch64/exception.rs
Original file line number Diff line number Diff line change
Expand Up @@ -29,9 +29,9 @@ use crate::arch_impl::traits::PerCpuOps;
fn set_idle_stack_for_eret() {
use crate::arch_impl::aarch64::percpu::Aarch64PerCpu;

// Boot stack: HHDM_BASE + 0x4100_0000 + (cpu_id + 1) * 0x20_0000
let cpu_id = Aarch64PerCpu::cpu_id() as u64;
let idle_stack = 0xFFFF_0000_0000_0000u64 + 0x4100_0000 + (cpu_id + 1) * 0x20_0000;
let stack_base = super::constants::percpu_stack_region_base();
let idle_stack = stack_base + (cpu_id + 1) * 0x20_0000;
unsafe {
Aarch64PerCpu::set_user_rsp_scratch(idle_stack);
}
Expand Down Expand Up @@ -235,14 +235,14 @@ pub extern "C" fn handle_sync_exception(frame: *mut Aarch64ExceptionFrame, esr:

// Classify which stack region the frame is on
let frame_addr = frame as u64;
let boot_stack_base = super::constants::percpu_stack_region_base();
let boot_stack_end = boot_stack_base + 0x0100_0000;
const HHDM_BASE_DIAG: u64 = 0xFFFF_0000_0000_0000;
const BOOT_STACK_BASE: u64 = HHDM_BASE_DIAG + 0x4100_0000;
const BOOT_STACK_END: u64 = HHDM_BASE_DIAG + 0x4200_0000;
const KSTACK_BASE: u64 = HHDM_BASE_DIAG + 0x5200_0000;
const KSTACK_END: u64 = HHDM_BASE_DIAG + 0x5400_0000;
if frame_addr >= BOOT_STACK_BASE && frame_addr < BOOT_STACK_END {
if frame_addr >= boot_stack_base && frame_addr < boot_stack_end {
raw_uart_str("\n STACK=boot_cpu");
let offset_from_base = frame_addr - BOOT_STACK_BASE;
let offset_from_base = frame_addr - boot_stack_base;
let boot_cpu = offset_from_base / 0x20_0000;
raw_uart_dec(boot_cpu);
} else if frame_addr >= KSTACK_BASE && frame_addr < KSTACK_END {
Expand Down Expand Up @@ -586,14 +586,14 @@ pub extern "C" fn handle_sync_exception(frame: *mut Aarch64ExceptionFrame, esr:

// Stack classification
let frame_addr = frame_ref as *const _ as u64;
let boot_stack_base = super::constants::percpu_stack_region_base();
let boot_stack_end = boot_stack_base + 0x0100_0000;
const HHDM_BASE_DIAG: u64 = 0xFFFF_0000_0000_0000;
const BOOT_STACK_BASE: u64 = HHDM_BASE_DIAG + 0x4100_0000;
const BOOT_STACK_END: u64 = HHDM_BASE_DIAG + 0x4200_0000;
const KSTACK_BASE: u64 = HHDM_BASE_DIAG + 0x5200_0000;
const KSTACK_END: u64 = HHDM_BASE_DIAG + 0x5400_0000;
if frame_addr >= BOOT_STACK_BASE && frame_addr < BOOT_STACK_END {
if frame_addr >= boot_stack_base && frame_addr < boot_stack_end {
raw_uart_str("\n STACK=boot_cpu");
let offset_from_base = frame_addr - BOOT_STACK_BASE;
let offset_from_base = frame_addr - boot_stack_base;
let boot_cpu = offset_from_base / 0x20_0000;
raw_uart_dec(boot_cpu);
} else if frame_addr >= KSTACK_BASE && frame_addr < KSTACK_END {
Expand All @@ -610,7 +610,7 @@ pub extern "C" fn handle_sync_exception(frame: *mut Aarch64ExceptionFrame, esr:

// OUTER FRAME: Read the frame 272 bytes above (if on a valid stack)
let outer_frame_addr = frame_addr + 272;
if outer_frame_addr + 272 <= BOOT_STACK_END
if outer_frame_addr + 272 <= boot_stack_end
|| (outer_frame_addr >= KSTACK_BASE && outer_frame_addr + 272 <= KSTACK_END)
{
let outer = outer_frame_addr as *const u64;
Expand Down
93 changes: 79 additions & 14 deletions kernel/src/arch_impl/aarch64/smp.rs
Original file line number Diff line number Diff line change
Expand Up @@ -15,8 +15,9 @@ use core::sync::atomic::{AtomicBool, AtomicU64, Ordering};
/// Maximum number of CPUs supported.
pub const MAX_CPUS: usize = 8;

/// PSCI function IDs (SMCCC compliant, 64-bit).
/// PSCI function IDs (SMCCC compliant).
const PSCI_CPU_ON_64: u64 = 0xC400_0003;
const PSCI_CPU_ON_32: u64 = 0x8400_0003;

extern "C" {
/// Physical address of secondary_cpu_entry, stored in .rodata by boot.S.
Expand All @@ -37,6 +38,11 @@ extern "C" {
static SMP_TTBR1_PTR: u64;
static SMP_MAIR_PTR: u64;
static SMP_TCR_PTR: u64;

/// Pointer to SMP_STACK_BASE_PHYS (in .bss.boot). CPU 0 writes the
/// physical base address of the per-CPU stack region here before PSCI CPU_ON.
/// On QEMU/Parallels: 0x4100_0000; on VMware: 0x8100_0000.
static SMP_STACK_BASE_PTR: u64;
}

/// Write CPU 0's actual MMU configuration to .bss.boot variables so
Expand Down Expand Up @@ -122,6 +128,27 @@ pub fn set_uart_phys(addr: u64) {
}
}

/// Set the physical base address of the per-CPU stack region.
/// Must be called before `release_cpu()`.
///
/// The stack base is `ram_base + 0x0100_0000` (16MB into RAM).
/// On QEMU/Parallels (ram at 0x40000000): 0x4100_0000.
/// On VMware (ram at 0x80000000): 0x8100_0000.
pub fn set_stack_base_phys(addr: u64) {
unsafe {
let phys = core::ptr::read_volatile(&SMP_STACK_BASE_PTR);
let virt = phys + 0xFFFF_0000_0000_0000u64;
let ptr = virt as *mut u64;
core::ptr::write_volatile(ptr, addr);
core::arch::asm!(
"dc cvac, {addr}",
"dsb ish",
addr = in(reg) ptr,
options(nostack),
);
}
}

/// Number of CPUs currently online (starts at 1 for the boot CPU).
static CPUS_ONLINE: AtomicU64 = AtomicU64::new(1);

Expand Down Expand Up @@ -160,6 +187,38 @@ fn psci_cpu_on(target_cpu: u64, entry_point: u64, context_id: u64) -> i64 {
ret
}

/// PSCI CPU_ON with 32-bit function ID via HVC.
fn psci_cpu_on_32(target_cpu: u64, entry_point: u64, context_id: u64) -> i64 {
let ret: i64;
unsafe {
core::arch::asm!(
"hvc #0",
inout("x0") PSCI_CPU_ON_32 => ret,
in("x1") target_cpu,
in("x2") entry_point,
in("x3") context_id,
options(nomem, nostack),
);
}
ret
}

/// PSCI CPU_ON with 64-bit function ID via SMC (EL3 firmware conduit).
fn psci_cpu_on_smc(target_cpu: u64, entry_point: u64, context_id: u64) -> i64 {
let ret: i64;
unsafe {
core::arch::asm!(
"smc #0",
inout("x0") PSCI_CPU_ON_64 => ret,
in("x1") target_cpu,
in("x2") entry_point,
in("x3") context_id,
options(nomem, nostack),
);
}
ret
}

/// Release a secondary CPU using PSCI CPU_ON.
///
/// The CPU will start executing at `secondary_cpu_entry` in boot.S,
Expand All @@ -182,12 +241,21 @@ pub fn release_cpu(cpu_id: usize) -> i64 {
// Context ID: pass cpu_id so the new CPU knows who it is
let context_id = cpu_id as u64;

let ret = psci_cpu_on(target_mpidr, entry_phys, context_id);
// Try 64-bit PSCI CPU_ON via HVC first (standard for ARM64 hypervisors)
let mut ret = psci_cpu_on(target_mpidr, entry_phys, context_id);

// If 64-bit failed, try 32-bit function ID (some hypervisors only support this)
if ret != 0 {
crate::serial_println!("[smp] CPU {}: HVC64 failed (ret={}), trying HVC32...", cpu_id, ret);
ret = psci_cpu_on_32(target_mpidr, entry_phys, context_id);
}

// Note: SMC conduit not attempted — on VMware (EL1 guest, no EL3),
// SMC would trap to EL2 and likely fault. HVC is the correct conduit.

if ret != 0 {
// PSCI error — emit raw UART error indicator
raw_uart_char(b'E');
raw_uart_char(b'0' + cpu_id as u8);
crate::serial_println!("[smp] PSCI CPU_ON failed for CPU {}: ret={} (MPIDR={:#x} entry={:#x})",
cpu_id, ret, target_mpidr, entry_phys);
}

ret
Expand Down Expand Up @@ -233,14 +301,13 @@ pub extern "C" fn secondary_cpu_entry_rust(cpu_id: u64) -> ! {
crate::per_cpu_aarch64::init_cpu(cpu_id as usize);

// Set kernel stack top for this CPU.
// boot.S sets SP to 0x41000000 + (cpu_id+1)*0x200000 (physical),
// boot.S sets SP to SMP_STACK_BASE_PHYS + (cpu_id+1)*0x200000 (physical),
// then adds KERNEL_VIRT_BASE after enabling MMU.
// This value is critical: when a user thread runs on this CPU and an
// exception occurs, the kernel needs to switch to this stack.
const HHDM_BASE: u64 = 0xFFFF_0000_0000_0000;
const STACK_REGION_BASE: u64 = 0x4100_0000;
let stack_base = super::constants::percpu_stack_region_base();
const STACK_SIZE: u64 = 0x20_0000; // 2MB per CPU
let kernel_stack_top = HHDM_BASE + STACK_REGION_BASE + (cpu_id + 1) * STACK_SIZE;
let kernel_stack_top = stack_base + (cpu_id + 1) * STACK_SIZE;
crate::per_cpu_aarch64::set_kernel_stack_top(kernel_stack_top);

// Initialize GIC CPU interface (GICC registers are banked per-CPU)
Expand Down Expand Up @@ -281,12 +348,10 @@ fn create_and_register_idle_thread(cpu_id: usize) {
use crate::memory::arch_stub::VirtAddr;

// Boot stack addresses — must match boot.S layout.
// HHDM_BASE + STACK_REGION_BASE + (cpu_id + 1) * STACK_SIZE
const HHDM_BASE: u64 = 0xFFFF_0000_0000_0000;
const STACK_REGION_BASE: u64 = 0x4100_0000;
let stack_base = super::constants::percpu_stack_region_base();
const STACK_SIZE: u64 = 0x20_0000; // 2MB per CPU
let boot_stack_top = VirtAddr::new(HHDM_BASE + STACK_REGION_BASE + ((cpu_id as u64) + 1) * STACK_SIZE);
let boot_stack_bottom = VirtAddr::new(HHDM_BASE + STACK_REGION_BASE + (cpu_id as u64) * STACK_SIZE);
let boot_stack_top = VirtAddr::new(stack_base + ((cpu_id as u64) + 1) * STACK_SIZE);
let boot_stack_bottom = VirtAddr::new(stack_base + (cpu_id as u64) * STACK_SIZE);
let dummy_tls = VirtAddr::zero();

let mut idle_task = Box::new(Thread::new(
Expand Down
8 changes: 6 additions & 2 deletions kernel/src/arch_impl/aarch64/timer_interrupt.rs
Original file line number Diff line number Diff line change
Expand Up @@ -280,9 +280,13 @@ pub extern "C" fn timer_interrupt_handler() {
crate::drivers::usb::ehci::poll_keyboard();
// Poll XHCI USB HID events (needed when PCI interrupt routing isn't available)
crate::drivers::usb::xhci::poll_hid_events();
// Poll VirtIO net PCI for incoming packets (PCI INTx routing not wired up)
// Poll network RX for incoming packets (PCI INTx routing not wired up)
// Covers both VirtIO net PCI (Parallels) and e1000 (VMware)
// Throttle to every 50th tick (~20Hz at 1000Hz timer) to avoid overhead
if crate::drivers::virtio::net_pci::is_initialized() && _count % 50 == 0 {
if (crate::drivers::virtio::net_pci::is_initialized()
|| crate::drivers::e1000::is_initialized())
&& _count % 50 == 0
{
crate::task::softirqd::raise_softirq(crate::task::softirqd::SoftirqType::NetRx);
}
}
Expand Down
Loading
Loading