Skip to content

Commit d24200f

Browse files
authored
Merge pull request #258 from ryanbreen/feat/vmware-fixes
feat: VMware mouse + networking fixes
2 parents 45e654c + 0b1503b commit d24200f

17 files changed

Lines changed: 533 additions & 186 deletions

File tree

kernel/src/arch_impl/aarch64/boot.S

Lines changed: 13 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -813,15 +813,17 @@ secondary_el1_init:
813813
isb
814814

815815
// Set up per-CPU boot stack (physical addresses, before MMU)
816-
// Stack top = 0x4100_0000 + (cpu_id + 1) * 0x20_0000
816+
// Stack top = SMP_STACK_BASE_PHYS + (cpu_id + 1) * 0x20_0000
817+
// SMP_STACK_BASE_PHYS is set by CPU 0 Rust code to (ram_base + 0x01000000).
817818
// This gives each CPU a 2MB stack region:
818-
// CPU 1: 0x4120_0000 (top) .. 0x4100_0000
819-
// CPU 2: 0x4140_0000 (top) .. 0x4120_0000
820-
// CPU 3: 0x4160_0000 (top) .. 0x4140_0000
819+
// CPU 1: base+0x0020_0000 (top) .. base
820+
// CPU 2: base+0x0040_0000 (top) .. base+0x0020_0000
821+
// CPU 3: base+0x0060_0000 (top) .. base+0x0040_0000
821822
mov x0, x19 // cpu_id
822823
add x0, x0, #1 // cpu_id + 1
823824
lsl x0, x0, #21 // * 0x20_0000 (2MB)
824-
ldr x1, =0x41000000 // base of per-CPU stack region
825+
ldr x1, =SMP_STACK_BASE_PHYS
826+
ldr x1, [x1] // x1 = actual stack base (set by CPU 0)
825827
add x0, x0, x1
826828
mov sp, x0
827829

@@ -987,6 +989,9 @@ SMP_MAIR_PTR:
987989
.global SMP_TCR_PTR
988990
SMP_TCR_PTR:
989991
.quad SMP_TCR_PHYS
992+
.global SMP_STACK_BASE_PTR
993+
SMP_STACK_BASE_PTR:
994+
.quad SMP_STACK_BASE_PHYS
990995

991996
// -----------------------------------------------------------------------------
992997
// Boot-time BSS (low): page tables + boot stack
@@ -1043,6 +1048,9 @@ SMP_MAIR_PHYS:
10431048
.global SMP_TCR_PHYS
10441049
SMP_TCR_PHYS:
10451050
.skip 8
1051+
.global SMP_STACK_BASE_PHYS
1052+
SMP_STACK_BASE_PHYS:
1053+
.skip 8
10461054

10471055
.balign 16
10481056
.global __boot_stack_bottom

kernel/src/arch_impl/aarch64/constants.rs

Lines changed: 17 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -205,20 +205,25 @@ pub const STACK_GUARD_SIZE: usize = PAGE_SIZE;
205205

206206
/// Base address for per-CPU kernel stacks region (ARM64).
207207
/// Uses a region within the HHDM (higher-half direct map) that is mapped
208-
/// by the boot page tables. Placed at physical 0x4100_0000 (16MB into RAM
209-
/// after kernel) to stay within typical 512MB QEMU RAM configs.
208+
/// by the boot page tables. Placed at ram_base + 0x0100_0000 (16MB into RAM
209+
/// after kernel) to stay within typical 512MB RAM configs.
210210
///
211-
/// QEMU virt RAM layout: physical 0x4000_0000 (1GB mark) for N MB
212-
/// With 512MB RAM: physical 0x4000_0000 to 0x6000_0000
211+
/// RAM layout (relative to ram_base):
212+
/// - +0x0000_0000 - +0x0100_0000: Kernel image (~16MB)
213+
/// - +0x0100_0000 - +0x0200_0000: Per-CPU stacks (16MB for 8 CPUs)
214+
/// - +0x0200_0000 - end: Heap and dynamic allocations
213215
///
214-
/// Stack layout in RAM:
215-
/// - 0x4000_0000 - 0x4100_0000: Kernel image (~16MB)
216-
/// - 0x4100_0000 - 0x4200_0000: Per-CPU stacks (16MB for 8 CPUs)
217-
/// - 0x4200_0000 - 0x6000_0000: Heap and dynamic allocations
218-
///
219-
/// Virtual: 0xFFFF_0000_4100_0000
220-
/// Physical: 0x4100_0000
221-
pub const PERCPU_STACK_REGION_BASE: u64 = HHDM_BASE + 0x4100_0000;
216+
/// Platform-dependent physical base:
217+
/// - QEMU/Parallels (ram at 0x4000_0000): physical 0x4100_0000
218+
/// - VMware (ram at 0x8000_0000): physical 0x8100_0000
219+
#[inline]
220+
pub fn percpu_stack_region_base() -> u64 {
221+
HHDM_BASE + 0x4100_0000 + crate::platform_config::ram_base_offset()
222+
}
223+
224+
/// Legacy constant for compile-time contexts (diagnostics). Uses the default
225+
/// QEMU/Parallels base. Runtime code should use percpu_stack_region_base().
226+
pub const PERCPU_STACK_REGION_BASE_DEFAULT: u64 = HHDM_BASE + 0x4100_0000;
222227

223228
/// Maximum number of CPUs supported on ARM64.
224229
/// Limited to 8 to keep stack region within 512MB RAM constraint.

kernel/src/arch_impl/aarch64/context_switch.rs

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -681,7 +681,7 @@ fn setup_idle_return_locked(
681681
.and_then(|t| t.kernel_stack_top.map(|v| v.as_u64()))
682682
.unwrap_or_else(|| {
683683
let cpu_id64 = cpu_id as u64;
684-
0xFFFF_0000_0000_0000u64 + 0x4100_0000 + (cpu_id64 + 1) * 0x20_0000
684+
super::constants::percpu_stack_region_base() + (cpu_id64 + 1) * 0x20_0000
685685
});
686686

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

kernel/src/arch_impl/aarch64/exception.rs

Lines changed: 11 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -29,9 +29,9 @@ use crate::arch_impl::traits::PerCpuOps;
2929
fn set_idle_stack_for_eret() {
3030
use crate::arch_impl::aarch64::percpu::Aarch64PerCpu;
3131

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

236236
// Classify which stack region the frame is on
237237
let frame_addr = frame as u64;
238+
let boot_stack_base = super::constants::percpu_stack_region_base();
239+
let boot_stack_end = boot_stack_base + 0x0100_0000;
238240
const HHDM_BASE_DIAG: u64 = 0xFFFF_0000_0000_0000;
239-
const BOOT_STACK_BASE: u64 = HHDM_BASE_DIAG + 0x4100_0000;
240-
const BOOT_STACK_END: u64 = HHDM_BASE_DIAG + 0x4200_0000;
241241
const KSTACK_BASE: u64 = HHDM_BASE_DIAG + 0x5200_0000;
242242
const KSTACK_END: u64 = HHDM_BASE_DIAG + 0x5400_0000;
243-
if frame_addr >= BOOT_STACK_BASE && frame_addr < BOOT_STACK_END {
243+
if frame_addr >= boot_stack_base && frame_addr < boot_stack_end {
244244
raw_uart_str("\n STACK=boot_cpu");
245-
let offset_from_base = frame_addr - BOOT_STACK_BASE;
245+
let offset_from_base = frame_addr - boot_stack_base;
246246
let boot_cpu = offset_from_base / 0x20_0000;
247247
raw_uart_dec(boot_cpu);
248248
} else if frame_addr >= KSTACK_BASE && frame_addr < KSTACK_END {
@@ -586,14 +586,14 @@ pub extern "C" fn handle_sync_exception(frame: *mut Aarch64ExceptionFrame, esr:
586586

587587
// Stack classification
588588
let frame_addr = frame_ref as *const _ as u64;
589+
let boot_stack_base = super::constants::percpu_stack_region_base();
590+
let boot_stack_end = boot_stack_base + 0x0100_0000;
589591
const HHDM_BASE_DIAG: u64 = 0xFFFF_0000_0000_0000;
590-
const BOOT_STACK_BASE: u64 = HHDM_BASE_DIAG + 0x4100_0000;
591-
const BOOT_STACK_END: u64 = HHDM_BASE_DIAG + 0x4200_0000;
592592
const KSTACK_BASE: u64 = HHDM_BASE_DIAG + 0x5200_0000;
593593
const KSTACK_END: u64 = HHDM_BASE_DIAG + 0x5400_0000;
594-
if frame_addr >= BOOT_STACK_BASE && frame_addr < BOOT_STACK_END {
594+
if frame_addr >= boot_stack_base && frame_addr < boot_stack_end {
595595
raw_uart_str("\n STACK=boot_cpu");
596-
let offset_from_base = frame_addr - BOOT_STACK_BASE;
596+
let offset_from_base = frame_addr - boot_stack_base;
597597
let boot_cpu = offset_from_base / 0x20_0000;
598598
raw_uart_dec(boot_cpu);
599599
} else if frame_addr >= KSTACK_BASE && frame_addr < KSTACK_END {
@@ -610,7 +610,7 @@ pub extern "C" fn handle_sync_exception(frame: *mut Aarch64ExceptionFrame, esr:
610610

611611
// OUTER FRAME: Read the frame 272 bytes above (if on a valid stack)
612612
let outer_frame_addr = frame_addr + 272;
613-
if outer_frame_addr + 272 <= BOOT_STACK_END
613+
if outer_frame_addr + 272 <= boot_stack_end
614614
|| (outer_frame_addr >= KSTACK_BASE && outer_frame_addr + 272 <= KSTACK_END)
615615
{
616616
let outer = outer_frame_addr as *const u64;

kernel/src/arch_impl/aarch64/smp.rs

Lines changed: 79 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -15,8 +15,9 @@ use core::sync::atomic::{AtomicBool, AtomicU64, Ordering};
1515
/// Maximum number of CPUs supported.
1616
pub const MAX_CPUS: usize = 8;
1717

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

2122
extern "C" {
2223
/// Physical address of secondary_cpu_entry, stored in .rodata by boot.S.
@@ -37,6 +38,11 @@ extern "C" {
3738
static SMP_TTBR1_PTR: u64;
3839
static SMP_MAIR_PTR: u64;
3940
static SMP_TCR_PTR: u64;
41+
42+
/// Pointer to SMP_STACK_BASE_PHYS (in .bss.boot). CPU 0 writes the
43+
/// physical base address of the per-CPU stack region here before PSCI CPU_ON.
44+
/// On QEMU/Parallels: 0x4100_0000; on VMware: 0x8100_0000.
45+
static SMP_STACK_BASE_PTR: u64;
4046
}
4147

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

131+
/// Set the physical base address of the per-CPU stack region.
132+
/// Must be called before `release_cpu()`.
133+
///
134+
/// The stack base is `ram_base + 0x0100_0000` (16MB into RAM).
135+
/// On QEMU/Parallels (ram at 0x40000000): 0x4100_0000.
136+
/// On VMware (ram at 0x80000000): 0x8100_0000.
137+
pub fn set_stack_base_phys(addr: u64) {
138+
unsafe {
139+
let phys = core::ptr::read_volatile(&SMP_STACK_BASE_PTR);
140+
let virt = phys + 0xFFFF_0000_0000_0000u64;
141+
let ptr = virt as *mut u64;
142+
core::ptr::write_volatile(ptr, addr);
143+
core::arch::asm!(
144+
"dc cvac, {addr}",
145+
"dsb ish",
146+
addr = in(reg) ptr,
147+
options(nostack),
148+
);
149+
}
150+
}
151+
125152
/// Number of CPUs currently online (starts at 1 for the boot CPU).
126153
static CPUS_ONLINE: AtomicU64 = AtomicU64::new(1);
127154

@@ -160,6 +187,38 @@ fn psci_cpu_on(target_cpu: u64, entry_point: u64, context_id: u64) -> i64 {
160187
ret
161188
}
162189

190+
/// PSCI CPU_ON with 32-bit function ID via HVC.
191+
fn psci_cpu_on_32(target_cpu: u64, entry_point: u64, context_id: u64) -> i64 {
192+
let ret: i64;
193+
unsafe {
194+
core::arch::asm!(
195+
"hvc #0",
196+
inout("x0") PSCI_CPU_ON_32 => ret,
197+
in("x1") target_cpu,
198+
in("x2") entry_point,
199+
in("x3") context_id,
200+
options(nomem, nostack),
201+
);
202+
}
203+
ret
204+
}
205+
206+
/// PSCI CPU_ON with 64-bit function ID via SMC (EL3 firmware conduit).
207+
fn psci_cpu_on_smc(target_cpu: u64, entry_point: u64, context_id: u64) -> i64 {
208+
let ret: i64;
209+
unsafe {
210+
core::arch::asm!(
211+
"smc #0",
212+
inout("x0") PSCI_CPU_ON_64 => ret,
213+
in("x1") target_cpu,
214+
in("x2") entry_point,
215+
in("x3") context_id,
216+
options(nomem, nostack),
217+
);
218+
}
219+
ret
220+
}
221+
163222
/// Release a secondary CPU using PSCI CPU_ON.
164223
///
165224
/// The CPU will start executing at `secondary_cpu_entry` in boot.S,
@@ -182,12 +241,21 @@ pub fn release_cpu(cpu_id: usize) -> i64 {
182241
// Context ID: pass cpu_id so the new CPU knows who it is
183242
let context_id = cpu_id as u64;
184243

185-
let ret = psci_cpu_on(target_mpidr, entry_phys, context_id);
244+
// Try 64-bit PSCI CPU_ON via HVC first (standard for ARM64 hypervisors)
245+
let mut ret = psci_cpu_on(target_mpidr, entry_phys, context_id);
246+
247+
// If 64-bit failed, try 32-bit function ID (some hypervisors only support this)
248+
if ret != 0 {
249+
crate::serial_println!("[smp] CPU {}: HVC64 failed (ret={}), trying HVC32...", cpu_id, ret);
250+
ret = psci_cpu_on_32(target_mpidr, entry_phys, context_id);
251+
}
252+
253+
// Note: SMC conduit not attempted — on VMware (EL1 guest, no EL3),
254+
// SMC would trap to EL2 and likely fault. HVC is the correct conduit.
186255

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

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

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

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

283350
// Boot stack addresses — must match boot.S layout.
284-
// HHDM_BASE + STACK_REGION_BASE + (cpu_id + 1) * STACK_SIZE
285-
const HHDM_BASE: u64 = 0xFFFF_0000_0000_0000;
286-
const STACK_REGION_BASE: u64 = 0x4100_0000;
351+
let stack_base = super::constants::percpu_stack_region_base();
287352
const STACK_SIZE: u64 = 0x20_0000; // 2MB per CPU
288-
let boot_stack_top = VirtAddr::new(HHDM_BASE + STACK_REGION_BASE + ((cpu_id as u64) + 1) * STACK_SIZE);
289-
let boot_stack_bottom = VirtAddr::new(HHDM_BASE + STACK_REGION_BASE + (cpu_id as u64) * STACK_SIZE);
353+
let boot_stack_top = VirtAddr::new(stack_base + ((cpu_id as u64) + 1) * STACK_SIZE);
354+
let boot_stack_bottom = VirtAddr::new(stack_base + (cpu_id as u64) * STACK_SIZE);
290355
let dummy_tls = VirtAddr::zero();
291356

292357
let mut idle_task = Box::new(Thread::new(

kernel/src/arch_impl/aarch64/timer_interrupt.rs

Lines changed: 6 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -280,9 +280,13 @@ pub extern "C" fn timer_interrupt_handler() {
280280
crate::drivers::usb::ehci::poll_keyboard();
281281
// Poll XHCI USB HID events (needed when PCI interrupt routing isn't available)
282282
crate::drivers::usb::xhci::poll_hid_events();
283-
// Poll VirtIO net PCI for incoming packets (PCI INTx routing not wired up)
283+
// Poll network RX for incoming packets (PCI INTx routing not wired up)
284+
// Covers both VirtIO net PCI (Parallels) and e1000 (VMware)
284285
// Throttle to every 50th tick (~20Hz at 1000Hz timer) to avoid overhead
285-
if crate::drivers::virtio::net_pci::is_initialized() && _count % 50 == 0 {
286+
if (crate::drivers::virtio::net_pci::is_initialized()
287+
|| crate::drivers::e1000::is_initialized())
288+
&& _count % 50 == 0
289+
{
286290
crate::task::softirqd::raise_softirq(crate::task::softirqd::SoftirqType::NetRx);
287291
}
288292
}

0 commit comments

Comments
 (0)