From 1e4537a28434b4d4f94d0b5cd1963aa9abca637e Mon Sep 17 00:00:00 2001 From: Allen Aboytes Date: Fri, 26 Jan 2024 20:20:36 -0800 Subject: [PATCH 1/8] Register secondary cores --- src/kernel/src/machine/arm/virt/processor.rs | 27 ++++++++++---------- 1 file changed, 13 insertions(+), 14 deletions(-) diff --git a/src/kernel/src/machine/arm/virt/processor.rs b/src/kernel/src/machine/arm/virt/processor.rs index fee70da6..fa33cfd7 100644 --- a/src/kernel/src/machine/arm/virt/processor.rs +++ b/src/kernel/src/machine/arm/virt/processor.rs @@ -47,21 +47,20 @@ pub fn enumerate_cpus() -> u32 { // enumerate the cpus using a device tree for cpu in devicetree().cpus() { - emerglogln!("found cpu {}", cpu.ids().first()); - if core_id == cpu.ids().first() as u32 { - // For now we assume a single core, the boot core, and - // return it's ID to the scheduling system - crate::processor::register(core_id, core_id); - // set the enable method to turn on the CPU core - if let Some(enable) = cpu.property("enable-method") { - emerglogln!("\tenable = {:?}", enable.as_str()); - let core = unsafe { - crate::processor::get_processor_mut(core_id) - }; - core.arch.boot = BootMethod::from_str(enable.as_str().unwrap()).unwrap(); - } + let cpu_id = cpu.ids().first() as u32; + emerglogln!("found cpu {}", cpu_id); + // For now we assume a single core, the boot core, and + // return it's ID to the scheduling system + crate::processor::register(cpu_id, core_id); + // set the enable method to turn on the CPU core + if let Some(enable) = cpu.property("enable-method") { + emerglogln!("\tenable = {:?}", enable.as_str()); + let core = unsafe { + crate::processor::get_processor_mut(cpu_id) + }; + // set the arch-sepecific boot protocol + core.arch.boot = BootMethod::from_str(enable.as_str().unwrap()).unwrap(); } - // TODO: register other processors so we can start them up } core_id From 71ddd31542e6fa56c9a32a00d65ec0058e1567d3 Mon Sep 17 00:00:00 2001 From: Allen Aboytes Date: Fri, 26 Jan 2024 21:06:58 -0800 Subject: [PATCH 2/8] Add machine specific boot module --- src/kernel/src/arch/aarch64/mod.rs | 4 +- src/kernel/src/machine/arm/common/boot/mod.rs | 44 +++++++++++++++++++ .../src/machine/arm/common/boot/psci.rs | 0 src/kernel/src/machine/arm/common/mod.rs | 1 + src/kernel/src/machine/arm/virt/processor.rs | 34 +------------- 5 files changed, 49 insertions(+), 34 deletions(-) create mode 100644 src/kernel/src/machine/arm/common/boot/mod.rs create mode 100644 src/kernel/src/machine/arm/common/boot/psci.rs diff --git a/src/kernel/src/arch/aarch64/mod.rs b/src/kernel/src/arch/aarch64/mod.rs index 54005c51..54f9c7d8 100644 --- a/src/kernel/src/arch/aarch64/mod.rs +++ b/src/kernel/src/arch/aarch64/mod.rs @@ -113,6 +113,6 @@ pub fn debug_shutdown(_code: u32) { /// Start up a CPU. /// # Safety /// The tcb_base and kernel stack must both be valid memory regions for each thing. -pub unsafe fn poke_cpu(_cpu: u32, _tcb_base: crate::memory::VirtAddr, _kernel_stack: *mut u8) { - todo!("start up a cpu") +pub unsafe fn poke_cpu(cpu: u32, tcb_base: crate::memory::VirtAddr, kernel_stack: *mut u8) { + crate::machine::processor::poke_cpu(cpu, tcb_base, kernel_stack); } diff --git a/src/kernel/src/machine/arm/common/boot/mod.rs b/src/kernel/src/machine/arm/common/boot/mod.rs new file mode 100644 index 00000000..65610c8e --- /dev/null +++ b/src/kernel/src/machine/arm/common/boot/mod.rs @@ -0,0 +1,44 @@ +/// The method of starting a CPU on ARM devices is machine specific +/// and usually implemented by the firmware. + +use core::str::FromStr; + +use crate::memory::VirtAddr; + +#[derive(Debug, Default, Copy, Clone, PartialEq)] +pub enum BootMethod { + Psci, + SpinTable, + #[default] + Unknown, +} + +impl BootMethod { + fn as_str(&self) -> &'static str { + match self { + Self::Psci => "psci", + Self::SpinTable => "spintable", + Self::Unknown => "unknown", + } + } +} + +impl FromStr for BootMethod { + type Err = (); + + // Required method + fn from_str(s: &str) -> Result { + match s { + "psci" => Ok(BootMethod::Psci), + "spin-table" => Ok(BootMethod::SpinTable), + _ => Err(()) + } + } +} + +/// Start up a CPU. +/// # Safety +/// The tcb_base and kernel stack must both be valid memory regions for each thing. +pub unsafe fn poke_cpu(_cpu: u32, _tcb_base: VirtAddr, _kernel_stack: *mut u8) { + todo!("start a core") +} \ No newline at end of file diff --git a/src/kernel/src/machine/arm/common/boot/psci.rs b/src/kernel/src/machine/arm/common/boot/psci.rs new file mode 100644 index 00000000..e69de29b diff --git a/src/kernel/src/machine/arm/common/mod.rs b/src/kernel/src/machine/arm/common/mod.rs index c37b78f9..cdc02d95 100644 --- a/src/kernel/src/machine/arm/common/mod.rs +++ b/src/kernel/src/machine/arm/common/mod.rs @@ -1,3 +1,4 @@ +pub mod boot; pub mod gicv2; pub mod mmio; pub mod uart; diff --git a/src/kernel/src/machine/arm/virt/processor.rs b/src/kernel/src/machine/arm/virt/processor.rs index fa33cfd7..43b9db52 100644 --- a/src/kernel/src/machine/arm/virt/processor.rs +++ b/src/kernel/src/machine/arm/virt/processor.rs @@ -5,36 +5,8 @@ use registers::interfaces::Readable; use crate::machine::info::devicetree; -#[derive(Debug, Default, Copy, Clone, PartialEq)] -pub enum BootMethod { - Psci, - SpinTable, - #[default] - Unknown, -} - -impl BootMethod { - fn as_str(&self) -> &'static str { - match self { - Self::Psci => "psci", - Self::SpinTable => "spintable", - Self::Unknown => "unknown", - } - } -} - -impl FromStr for BootMethod { - type Err = (); - - // Required method - fn from_str(s: &str) -> Result { - match s { - "psci" => Ok(BootMethod::Psci), - "spin-table" => Ok(BootMethod::SpinTable), - _ => Err(()) - } - } -} +// re-export boot module +pub use super::super::common::boot::*; pub fn enumerate_cpus() -> u32 { // MT bit means lowest level is logical cores (SMT) @@ -49,8 +21,6 @@ pub fn enumerate_cpus() -> u32 { for cpu in devicetree().cpus() { let cpu_id = cpu.ids().first() as u32; emerglogln!("found cpu {}", cpu_id); - // For now we assume a single core, the boot core, and - // return it's ID to the scheduling system crate::processor::register(cpu_id, core_id); // set the enable method to turn on the CPU core if let Some(enable) = cpu.property("enable-method") { From d564a788a1ba15577cd26e2164776a25cdfebad7 Mon Sep 17 00:00:00 2001 From: Allen Aboytes Date: Mon, 29 Jan 2024 15:04:05 -0800 Subject: [PATCH 3/8] Add PSCI support to turn on cores --- Cargo.lock | 7 + src/kernel/Cargo.toml | 1 + src/kernel/src/arch/aarch64/processor.rs | 4 +- src/kernel/src/machine/arm/common/boot/mod.rs | 87 ++++++++- .../src/machine/arm/common/boot/psci.rs | 170 ++++++++++++++++++ src/kernel/src/machine/arm/virt/processor.rs | 2 + .../target-spec/aarch64-unknown-none.json | 2 +- 7 files changed, 267 insertions(+), 6 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index fd9f89ca..7964ef04 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -2540,6 +2540,12 @@ version = "1.11.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "942b4a808e05215192e39f4ab80813e599068285906cc91aa64f923db842bd5a" +[[package]] +name = "smccc" +version = "0.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "617d17f088ec733e5a6b86da6ce4cce1414e6e856d6061c16dda51cceae6f68c" + [[package]] name = "socket2" version = "0.4.10" @@ -3028,6 +3034,7 @@ dependencies = [ "nonoverlapping_interval_tree", "object", "slabmalloc", + "smccc", "syscall_encode", "tar-no-std", "tock-registers", diff --git a/src/kernel/Cargo.toml b/src/kernel/Cargo.toml index eb4c9638..10dc63d2 100755 --- a/src/kernel/Cargo.toml +++ b/src/kernel/Cargo.toml @@ -40,6 +40,7 @@ acpi = "4.1.1" arm64 = { package = "aarch64-cpu", version = "9.3.1" } registers = { package = "tock-registers", version = "0.8.x" } fdt = "0.1.5" +smccc = "0.1.1" [dependencies.lazy_static] version = "1.0" diff --git a/src/kernel/src/arch/aarch64/processor.rs b/src/kernel/src/arch/aarch64/processor.rs index 6e1b549a..c5ff2ad0 100755 --- a/src/kernel/src/arch/aarch64/processor.rs +++ b/src/kernel/src/arch/aarch64/processor.rs @@ -4,7 +4,7 @@ use arm64::registers::TPIDR_EL1; use registers::interfaces::{Readable, Writeable}; use crate::{ - machine::processor::BootMethod, + machine::processor::{BootMethod, BootArgs}, memory::VirtAddr, processor::Processor, once::Once, @@ -57,6 +57,8 @@ pub fn get_topology() -> Vec<(usize, bool)> { #[derive(Default, Debug)] pub struct ArchProcessor { pub boot: BootMethod, + pub args: BootArgs, + pub mpidr: u64, } pub fn halt_and_wait() { diff --git a/src/kernel/src/machine/arm/common/boot/mod.rs b/src/kernel/src/machine/arm/common/boot/mod.rs index 65610c8e..e3ca046e 100644 --- a/src/kernel/src/machine/arm/common/boot/mod.rs +++ b/src/kernel/src/machine/arm/common/boot/mod.rs @@ -1,10 +1,17 @@ /// The method of starting a CPU on ARM devices is machine specific /// and usually implemented by the firmware. +mod psci; + use core::str::FromStr; -use crate::memory::VirtAddr; +use arm64::registers::{PAR_EL1, Readable}; + +use twizzler_abi::upcall::MemoryAccessKind; +use crate::memory::{VirtAddr, PhysAddr}; + +/// Possible boot protocols used to start a CPU. #[derive(Debug, Default, Copy, Clone, PartialEq)] pub enum BootMethod { Psci, @@ -36,9 +43,81 @@ impl FromStr for BootMethod { } } +/// The arguments needed to start a CPU. +#[derive(Debug, Default, Copy, Clone)] +pub struct BootArgs { + /// System-wide ID of this CPU core + cpu: u32, + /// TCB base use for TLS data + tcb_base: u64, + /// The stack of this kernel thread + kernel_stack: u64, + /// The entry point of this CPU core + entry: u64, + // system register state used to start core + mair: u64, + ttbr1: u64, + ttbr0: u64, + tcr: u64, + sctlr: u64, + spsr: u64, + cpacr: u64, +} + /// Start up a CPU. /// # Safety /// The tcb_base and kernel stack must both be valid memory regions for each thing. -pub unsafe fn poke_cpu(_cpu: u32, _tcb_base: VirtAddr, _kernel_stack: *mut u8) { - todo!("start a core") -} \ No newline at end of file +pub unsafe fn poke_cpu(cpu: u32, tcb_base: VirtAddr, kernel_stack: *mut u8) { + let core = unsafe { + crate::processor::get_processor_mut(cpu) + }; + logln!("starting {} with {}", core.id, core.arch.boot.as_str()); + + match core.arch.boot { + BootMethod::Psci => psci::boot_core(core, tcb_base, kernel_stack), + _ => unimplemented!("boot method: {}", core.arch.boot.as_str()) + } +} + +// Translate a virtual to a physical address if it is mapped in with the desired access rights +fn translate(va: VirtAddr, access: MemoryAccessKind) -> Option { + if !va.is_kernel() { + unimplemented!("address is in user memory: {:?}", va) + } + unsafe { + // AT , + // : + // - S1,E1,R/W (stage 1, EL1, R or Write) + // : address + match access { + MemoryAccessKind::Read => core::arch::asm!( + "AT S1E1R, {}", + in(reg) va.raw(), + options(nostack, nomem), + ), + // given the way address translation works + // writeable implies readable ... + MemoryAccessKind::Write => core::arch::asm!( + "AT S1E1W, {}", + in(reg) va.raw(), + options(nostack, nomem), + ), + _ => unimplemented!("translation for {:?}", access) + } + } + // PAR_EL1 holds result of AT instruction + // - FST: fault status info + // - PA: output address + logln!("{:?} -> {} {:#018x}", va, PAR_EL1.matches_all(PAR_EL1::F::TranslationSuccessfull), PAR_EL1.read(PAR_EL1::PA)); + if PAR_EL1.matches_all(PAR_EL1::F::TranslationSuccessfull) { + let pa = unsafe { + // PAR_EL1.PA returns bits 47:12 + let base_phys = PAR_EL1.read(PAR_EL1::PA) << 12; + // the lower 12 bit offset resides in the VA + let block_offset = va.raw() & 0xFFF; + PhysAddr::new_unchecked(base_phys | block_offset) + }; + return Some(pa) + } + None +} diff --git a/src/kernel/src/machine/arm/common/boot/psci.rs b/src/kernel/src/machine/arm/common/boot/psci.rs index e69de29b..4df2bf25 100644 --- a/src/kernel/src/machine/arm/common/boot/psci.rs +++ b/src/kernel/src/machine/arm/common/boot/psci.rs @@ -0,0 +1,170 @@ +// TODO: add a link to the spec + +use arm64::registers::{SCTLR_EL1, MAIR_EL1, TTBR1_EL1, TTBR0_EL1, TCR_EL1, SPSR_EL1}; +use arm64::registers::Readable; +use smccc::psci::cpu_on; + +use crate::{ + machine::info::devicetree, + memory::{VirtAddr, PhysAddr}, + processor::Processor, +}; + +use twizzler_abi::upcall::MemoryAccessKind; + +use super::{BootArgs, translate}; + +unsafe fn psci_secondary_entry(context_id: &BootArgs) -> ! { + // TODO: manually set the configuration of registers + + // we need the lower half of memory identity mapped + // this is because we are using physical addresses here + // and when we turn on the mmu we still need to access + // instructions and other data in lower memory + core::arch::asm!( + // set up the system registers needed by address translation + "msr mair_el1, {}", + "msr ttbr0_el1, {}", + "msr ttbr1_el1, {}", + "msr tcr_el1, {}", + // ensure that all of these instructions commit + "isb", + // allow the use of FP instructions + "msr cpacr_el1, {}", + // set the entry point address (virtual) + "msr elr_el1, {}", + // set the stack pointer (virtual) + // TODO: set this and then use aarch64 cpu stuff + // TODO: verify if the way the stack grows is right + "msr sp_el0, {}", + // configure the execution state for EL1 + "msr spsr_el1, {}", + // enable the MMU and caches + "msr sctlr_el1, {}", + // ensure that all other instructions commit + // before executing other code with virtual + // memory on + "isb", + // return to address specified in elr_el1 + "eret", + in(reg) context_id.mair, + in(reg) context_id.ttbr0, + in(reg) context_id.ttbr1, + in(reg) context_id.tcr, + in(reg) context_id.cpacr, + in(reg) context_id.entry, + in(reg) context_id.kernel_stack, + in(reg) context_id.spsr, + in(reg) context_id.sctlr, + options(noreturn, nostack), + ); +} + +/// At this point we expect the MMU to be turned on +/// and paging to be functional. The executing environment +/// should be set up so we can execute safe Rust code. +fn rust_secondary_entry(args: &BootArgs) -> ! { + // after mmu is on test if we can read a VA + let ctx_pa = unsafe { PhysAddr::new_unchecked(args as *const _ as u64) }; + let args_va = ctx_pa.kernel_vaddr().as_ptr::(); + + unsafe { + core::arch::asm!( + // set the stack pointer (virtual) + // "mov sp, {1}", + "mov x11, {}", + "mov x12, 0xBBBB", + in(reg) (*args_va).tcb_base, + // in(reg) (*args_va).kernel_stack, + ); + } + logln!("hello from core!!"); + + // call the generic secondary cpu entry point + crate::processor::secondary_entry(args.cpu, + VirtAddr::new(args.tcb_base).unwrap(), + args.kernel_stack as *mut u8, + ); + + // TODO: clean up values of registers saved after boot here + // TODO: remove smp mappings, needs TLB coherence across cores +} + +pub unsafe fn boot_core(core: &mut Processor, tcb_base: VirtAddr, kernel_stack: *mut u8) { + // we will issue a CPU_ON to turn on the cpu core + // first we will add the necessary arguments needed + // by PSCI's CPU_ON function + + // pass cpu id, this is this core's MPIDR_EL1 value + // TODO: ensure the right bits are 0 + let cpu_id = core.arch.mpidr; + // pass secondary entry point (physical address) + let entry_va = VirtAddr::new(psci_secondary_entry as u64) + .expect("invalid entry point address"); + logln!("entry address: {:?}", entry_va); + let entry_pa = translate(entry_va, MemoryAccessKind::Read) + .expect("entry point is not mapped"); + logln!("entry pa: {:?}", entry_pa); + // pass Context ID which in our implementation is the boot args + // needed to start the CPU core. The Context ID is gaurenteed to + // be passed as an argument to the entry point we specify. + let context_id = &core.arch.args as *const _ as u64; + let ctx_pa = translate(VirtAddr::new(context_id).unwrap(), MemoryAccessKind::Write) + .expect("context ID is not mapped"); + logln!("context id: {:#x}, {:?}", context_id, ctx_pa); + + // Here we pass in the necessary arguments to start the CPU + + // - MAIR, TTBR1, TCR + let cpacr: u64; + core::arch::asm!( + "mrs {}, CPACR_EL1", + out(reg) cpacr, + ); + logln!("MAIR:{:#x}, TTBR1:{:#x}, TCR:{:#x}, SCTLR:{:#x}, CPACR: {:#x}", + MAIR_EL1.get(), + TTBR1_EL1.get(), + TCR_EL1.get(), + SCTLR_EL1.get(), + cpacr + ); + + // Register state needed by low level code to setup an environment + // suitable for executing Rust code in the kernel. + core.arch.args.mair = MAIR_EL1.get(); + core.arch.args.ttbr1 = TTBR1_EL1.get(); + core.arch.args.ttbr0 = TTBR0_EL1.get(); + core.arch.args.tcr = TCR_EL1.get(); + core.arch.args.sctlr = SCTLR_EL1.get(); + core.arch.args.spsr = SPSR_EL1.get(); + core.arch.args.entry = rust_secondary_entry as u64; + core.arch.args.cpacr = cpacr; + + // Things needed by the generic kernel code used to initialize this CPU core. + core.arch.args.cpu = core.id; + core.arch.args.tcb_base = tcb_base.raw(); + core.arch.args.kernel_stack = kernel_stack as u64; + + // get the method from the psci root node + let method = { + let psci_info = devicetree() + .find_node("/psci") + .expect("no psci node"); + psci_info + .property("method") + .expect("no method property") + .as_str() + .expect("failed to convert to string") + }; + logln!("method: {} hcv? {}", method, method == "hvc"); + + // here we assume 64 bit calling convention, in the future + // we should check if this is different + logln!("booting: {:#x} {:?} {:?}", cpu_id, entry_pa, ctx_pa); + let boot_err = match method { + "hvc" => cpu_on::(cpu_id, entry_pa.into(), ctx_pa.into()), + _ => todo!("SMCCC calling convention needed by PSCI") + }; + // let boot_err = smccc::psci::cpu_on::(cpu_id, entry_pa.into(), ctx_pa.into()); + logln!("boot: {:?}", boot_err); +} diff --git a/src/kernel/src/machine/arm/virt/processor.rs b/src/kernel/src/machine/arm/virt/processor.rs index 43b9db52..7bfba537 100644 --- a/src/kernel/src/machine/arm/virt/processor.rs +++ b/src/kernel/src/machine/arm/virt/processor.rs @@ -30,6 +30,8 @@ pub fn enumerate_cpus() -> u32 { }; // set the arch-sepecific boot protocol core.arch.boot = BootMethod::from_str(enable.as_str().unwrap()).unwrap(); + // save the MPIDR_EL1 value found used for boot + core.arch.mpidr = cpu_id as u64; } } diff --git a/src/kernel/target-spec/aarch64-unknown-none.json b/src/kernel/target-spec/aarch64-unknown-none.json index e5fcae28..1541ba80 100644 --- a/src/kernel/target-spec/aarch64-unknown-none.json +++ b/src/kernel/target-spec/aarch64-unknown-none.json @@ -3,7 +3,7 @@ "arch": "aarch64", "data-layout": "e-m:e-i8:8:32-i16:16:32-i64:64-i128:128-n32:64-S128", "disable-redzone": true, - "features": "+strict-align,-neon,-fp-armv8,+tpidr-el1", + "features": "+strict-align,-neon,-fp-armv8,+tpidr-el1,+v8a", "linker": "rust-lld", "linker-flavor": "ld.lld", "llvm-target": "aarch64-unknown-none", From b583fb5cec8389cc3ebea08219ff10d653821ce4 Mon Sep 17 00:00:00 2001 From: Allen Aboytes Date: Mon, 5 Feb 2024 10:44:14 -0800 Subject: [PATCH 4/8] Only call `init_secondary` for secondary cores --- src/kernel/src/arch/aarch64/interrupt.rs | 8 ++--- src/kernel/src/arch/aarch64/mod.rs | 43 ++++++++++++++++++++++-- src/kernel/src/arch/aarch64/processor.rs | 6 ++-- src/kernel/src/main.rs | 1 + src/kernel/src/processor.rs | 2 +- 5 files changed, 48 insertions(+), 12 deletions(-) diff --git a/src/kernel/src/arch/aarch64/interrupt.rs b/src/kernel/src/arch/aarch64/interrupt.rs index 97e3c98d..d346eaf1 100644 --- a/src/kernel/src/arch/aarch64/interrupt.rs +++ b/src/kernel/src/arch/aarch64/interrupt.rs @@ -179,13 +179,11 @@ impl Drop for DynamicInterrupt { } } -pub fn init_interrupts() { - // we don't want to use logln since it enables interrupts - // in the future we should not use logging until mm us up - emerglogln!("[arch::interrupt] initializing interrupts"); - +pub fn init_interrupts() { let cpu = current_processor(); + emerglogln!("[arch::interrupt] processor {} initializing interrupts", cpu.id); + // initialize interrupt controller if cpu.is_bsp() { INTERRUPT_CONTROLLER.configure_global(); diff --git a/src/kernel/src/arch/aarch64/mod.rs b/src/kernel/src/arch/aarch64/mod.rs index 54f9c7d8..f365bb6e 100644 --- a/src/kernel/src/arch/aarch64/mod.rs +++ b/src/kernel/src/arch/aarch64/mod.rs @@ -79,9 +79,46 @@ pub fn init(boot_info: &B) { } pub fn init_secondary() { - // TODO: Initialize secondary processors: - // - set up exception handling - // - configure the local CPU interrupt controller interface + // initialize exceptions by setting up our exception vectors + exception::init(); + + // check if SPSel is already set to use SP_EL1 + let spsel: InMemoryRegister = InMemoryRegister::new(SPSel.get()); + if spsel.matches_all(SPSel::SP::EL0) { + // make it so that we use SP_EL1 in the kernel + // when taking an exception. + spsel.write(SPSel::SP::ELx); + let sp: u64; + unsafe { + core::arch::asm!( + // save the stack pointer from before + "mov {0}, sp", + // change usage of sp from SP_EL0 to SP_EL1 + "msr spsel, {1}", + // set current stack pointer to previous, + // sp is now aliased to SP_EL1 + "mov sp, {0}", + // scrub the value stored in SP_EL0 + // "msr sp_el0, xzr", + out(reg) sp, + in(reg) spsel.get(), + ); + } + + // make it so that the boot stack is in higher half memory + if !VirtAddr::new(sp).unwrap().is_kernel() { + unsafe { + // we convert it to higher memory that has r/w permissions + let new_sp = PhysAddr::new_unchecked(sp).kernel_vaddr().raw(); + core::arch::asm!( + "mov sp, {}", + in(reg) new_sp, + ); + } + } + } + // initialize the (local) settings for the interrupt controller + init_interrupts(); } pub fn start_clock(_statclock_hz: u64, _stat_cb: fn(Nanoseconds)) { diff --git a/src/kernel/src/arch/aarch64/processor.rs b/src/kernel/src/arch/aarch64/processor.rs index c5ff2ad0..d9d72ea0 100755 --- a/src/kernel/src/arch/aarch64/processor.rs +++ b/src/kernel/src/arch/aarch64/processor.rs @@ -1,6 +1,6 @@ use alloc::vec::Vec; -use arm64::registers::TPIDR_EL1; +use arm64::registers::{TPIDR_EL1, MPIDR_EL1}; use registers::interfaces::{Readable, Writeable}; use crate::{ @@ -49,8 +49,8 @@ pub fn get_topology() -> Vec<(usize, bool)> { // using something like information in MPIDR_EL1, // Device Tree, or ACPI - // For now we simply return a single core, the boot core. - alloc::vec![(*BOOT_CORE_ID.wait() as usize, false)] + // For now we simply return a the ID of this core. + alloc::vec![((MPIDR_EL1.get() & 0xff) as usize, true)] } // arch specific implementation of processor specific state diff --git a/src/kernel/src/main.rs b/src/kernel/src/main.rs index 12284f1b..393e72eb 100755 --- a/src/kernel/src/main.rs +++ b/src/kernel/src/main.rs @@ -107,6 +107,7 @@ fn kernel_main(boot_info: &mut B) -> ! { let bsp_id = arch::processor::enumerate_cpus(); processor::init_cpu(image::get_tls(), bsp_id); arch::init_interrupts(); + #[cfg(target_arch = "x86_64")] arch::init_secondary(); initrd::init(boot_info.get_modules()); logln!("[kernel::cpu] booting secondary CPUs"); diff --git a/src/kernel/src/processor.rs b/src/kernel/src/processor.rs index dadc4c81..83581a5b 100644 --- a/src/kernel/src/processor.rs +++ b/src/kernel/src/processor.rs @@ -322,12 +322,12 @@ pub static NR_CPUS: AtomicUsize = AtomicUsize::new(1); static CPU_MAIN_BARRIER: AtomicBool = AtomicBool::new(false); pub fn secondary_entry(id: u32, tcb_base: VirtAddr, kernel_stack_base: *mut u8) -> ! { crate::arch::processor::init(tcb_base); - arch::init_secondary(); unsafe { BOOT_KERNEL_STACK = kernel_stack_base; CPU_ID = id; CURRENT_PROCESSOR = &**ALL_PROCESSORS[id as usize].as_ref().unwrap(); } + arch::init_secondary(); let topo_path = arch::processor::get_topology(); current_processor().set_topology(topo_path); current_processor() From e68643c7ad1fb865d5377915b11f50bc639c797e Mon Sep 17 00:00:00 2001 From: Allen Aboytes Date: Mon, 5 Feb 2024 10:44:59 -0800 Subject: [PATCH 5/8] Make `Mutex` only call scheduler if there is a current thread --- src/kernel/src/mutex.rs | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/src/kernel/src/mutex.rs b/src/kernel/src/mutex.rs index efed25a0..beaf3129 100644 --- a/src/kernel/src/mutex.rs +++ b/src/kernel/src/mutex.rs @@ -127,7 +127,9 @@ impl Mutex { }; arch::processor::spin_wait_iteration(); core::hint::spin_loop(); - sched::schedule(reinsert); + if current_thread.is_some() { + sched::schedule(reinsert); + } crate::interrupt::set(istate); } From 16b6a824203cacbd2b358773a0b744fc5d92c07e Mon Sep 17 00:00:00 2001 From: Allen Aboytes Date: Mon, 5 Feb 2024 11:16:40 -0800 Subject: [PATCH 6/8] Make cores spin and wait --- src/kernel/src/arch/aarch64/processor.rs | 20 +++++++++++++++++--- 1 file changed, 17 insertions(+), 3 deletions(-) diff --git a/src/kernel/src/arch/aarch64/processor.rs b/src/kernel/src/arch/aarch64/processor.rs index d9d72ea0..d185c9f3 100755 --- a/src/kernel/src/arch/aarch64/processor.rs +++ b/src/kernel/src/arch/aarch64/processor.rs @@ -1,6 +1,8 @@ use alloc::vec::Vec; +use core::sync::atomic::{AtomicBool, Ordering::SeqCst}; use arm64::registers::{TPIDR_EL1, MPIDR_EL1}; +use arm64::asm::{wfe, sev}; use registers::interfaces::{Readable, Writeable}; use crate::{ @@ -8,6 +10,7 @@ use crate::{ memory::VirtAddr, processor::Processor, once::Once, + current_processor, }; #[allow(unused_imports)] // DEBUG @@ -59,17 +62,28 @@ pub struct ArchProcessor { pub boot: BootMethod, pub args: BootArgs, pub mpidr: u64, + pub wait_flag: AtomicBool, } pub fn halt_and_wait() { /* TODO: spin a bit */ - /* TODO: actually put the cpu into deeper and deeper sleep */ - todo!() + /* TODO: actually put the cpu into deeper and deeper sleep, see PSCI */ + let core = current_processor(); + // set the wait condition + core.arch.wait_flag.store(true, SeqCst); + + // wait until someone wakes us up + while core.arch.wait_flag.load(SeqCst) { + wfe(); + } } impl Processor { pub fn wakeup(&self, _signal: bool) { - todo!() + // remove the wait condition + self.arch.wait_flag.store(false, SeqCst); + // wakeup the processor + sev(); } } From 2208089e62734c0f858361069daaff8eebfd60ff Mon Sep 17 00:00:00 2001 From: Allen Aboytes Date: Mon, 5 Feb 2024 11:38:12 -0800 Subject: [PATCH 7/8] Cleanup test code and unnecessary logs --- src/kernel/src/machine/arm/common/boot/mod.rs | 2 - .../src/machine/arm/common/boot/psci.rs | 40 +++---------------- src/kernel/src/machine/arm/virt/processor.rs | 2 - 3 files changed, 6 insertions(+), 38 deletions(-) diff --git a/src/kernel/src/machine/arm/common/boot/mod.rs b/src/kernel/src/machine/arm/common/boot/mod.rs index e3ca046e..5bfbd35f 100644 --- a/src/kernel/src/machine/arm/common/boot/mod.rs +++ b/src/kernel/src/machine/arm/common/boot/mod.rs @@ -71,7 +71,6 @@ pub unsafe fn poke_cpu(cpu: u32, tcb_base: VirtAddr, kernel_stack: *mut u8) { let core = unsafe { crate::processor::get_processor_mut(cpu) }; - logln!("starting {} with {}", core.id, core.arch.boot.as_str()); match core.arch.boot { BootMethod::Psci => psci::boot_core(core, tcb_base, kernel_stack), @@ -108,7 +107,6 @@ fn translate(va: VirtAddr, access: MemoryAccessKind) -> Option { // PAR_EL1 holds result of AT instruction // - FST: fault status info // - PA: output address - logln!("{:?} -> {} {:#018x}", va, PAR_EL1.matches_all(PAR_EL1::F::TranslationSuccessfull), PAR_EL1.read(PAR_EL1::PA)); if PAR_EL1.matches_all(PAR_EL1::F::TranslationSuccessfull) { let pa = unsafe { // PAR_EL1.PA returns bits 47:12 diff --git a/src/kernel/src/machine/arm/common/boot/psci.rs b/src/kernel/src/machine/arm/common/boot/psci.rs index 4df2bf25..641b0d11 100644 --- a/src/kernel/src/machine/arm/common/boot/psci.rs +++ b/src/kernel/src/machine/arm/common/boot/psci.rs @@ -64,28 +64,11 @@ unsafe fn psci_secondary_entry(context_id: &BootArgs) -> ! { /// and paging to be functional. The executing environment /// should be set up so we can execute safe Rust code. fn rust_secondary_entry(args: &BootArgs) -> ! { - // after mmu is on test if we can read a VA - let ctx_pa = unsafe { PhysAddr::new_unchecked(args as *const _ as u64) }; - let args_va = ctx_pa.kernel_vaddr().as_ptr::(); - - unsafe { - core::arch::asm!( - // set the stack pointer (virtual) - // "mov sp, {1}", - "mov x11, {}", - "mov x12, 0xBBBB", - in(reg) (*args_va).tcb_base, - // in(reg) (*args_va).kernel_stack, - ); - } - logln!("hello from core!!"); - // call the generic secondary cpu entry point crate::processor::secondary_entry(args.cpu, VirtAddr::new(args.tcb_base).unwrap(), args.kernel_stack as *mut u8, ); - // TODO: clean up values of registers saved after boot here // TODO: remove smp mappings, needs TLB coherence across cores } @@ -101,33 +84,22 @@ pub unsafe fn boot_core(core: &mut Processor, tcb_base: VirtAddr, kernel_stack: // pass secondary entry point (physical address) let entry_va = VirtAddr::new(psci_secondary_entry as u64) .expect("invalid entry point address"); - logln!("entry address: {:?}", entry_va); let entry_pa = translate(entry_va, MemoryAccessKind::Read) .expect("entry point is not mapped"); - logln!("entry pa: {:?}", entry_pa); // pass Context ID which in our implementation is the boot args // needed to start the CPU core. The Context ID is gaurenteed to // be passed as an argument to the entry point we specify. let context_id = &core.arch.args as *const _ as u64; let ctx_pa = translate(VirtAddr::new(context_id).unwrap(), MemoryAccessKind::Write) .expect("context ID is not mapped"); - logln!("context id: {:#x}, {:?}", context_id, ctx_pa); // Here we pass in the necessary arguments to start the CPU - - // - MAIR, TTBR1, TCR + let cpacr: u64; core::arch::asm!( "mrs {}, CPACR_EL1", out(reg) cpacr, ); - logln!("MAIR:{:#x}, TTBR1:{:#x}, TCR:{:#x}, SCTLR:{:#x}, CPACR: {:#x}", - MAIR_EL1.get(), - TTBR1_EL1.get(), - TCR_EL1.get(), - SCTLR_EL1.get(), - cpacr - ); // Register state needed by low level code to setup an environment // suitable for executing Rust code in the kernel. @@ -156,15 +128,15 @@ pub unsafe fn boot_core(core: &mut Processor, tcb_base: VirtAddr, kernel_stack: .as_str() .expect("failed to convert to string") }; - logln!("method: {} hcv? {}", method, method == "hvc"); // here we assume 64 bit calling convention, in the future // we should check if this is different - logln!("booting: {:#x} {:?} {:?}", cpu_id, entry_pa, ctx_pa); - let boot_err = match method { + let boot_result = match method { "hvc" => cpu_on::(cpu_id, entry_pa.into(), ctx_pa.into()), _ => todo!("SMCCC calling convention needed by PSCI") }; - // let boot_err = smccc::psci::cpu_on::(cpu_id, entry_pa.into(), ctx_pa.into()); - logln!("boot: {:?}", boot_err); + // Booting up the core is asynchronous and the call only returns OK if the signal was sent + if boot_result.is_err() { + panic!("failed to start CPU core {}", core.id); + } } diff --git a/src/kernel/src/machine/arm/virt/processor.rs b/src/kernel/src/machine/arm/virt/processor.rs index 7bfba537..42c095a5 100644 --- a/src/kernel/src/machine/arm/virt/processor.rs +++ b/src/kernel/src/machine/arm/virt/processor.rs @@ -20,11 +20,9 @@ pub fn enumerate_cpus() -> u32 { // enumerate the cpus using a device tree for cpu in devicetree().cpus() { let cpu_id = cpu.ids().first() as u32; - emerglogln!("found cpu {}", cpu_id); crate::processor::register(cpu_id, core_id); // set the enable method to turn on the CPU core if let Some(enable) = cpu.property("enable-method") { - emerglogln!("\tenable = {:?}", enable.as_str()); let core = unsafe { crate::processor::get_processor_mut(cpu_id) }; From d7eba7c039edb52f09e752e7c2b93f6656f1f2e6 Mon Sep 17 00:00:00 2001 From: Allen Aboytes Date: Wed, 7 Feb 2024 09:21:32 -0800 Subject: [PATCH 8/8] Add PSCI documentation --- src/kernel/src/machine/arm/common/boot/psci.rs | 10 ++++++++-- 1 file changed, 8 insertions(+), 2 deletions(-) diff --git a/src/kernel/src/machine/arm/common/boot/psci.rs b/src/kernel/src/machine/arm/common/boot/psci.rs index 641b0d11..90d8bb0a 100644 --- a/src/kernel/src/machine/arm/common/boot/psci.rs +++ b/src/kernel/src/machine/arm/common/boot/psci.rs @@ -1,4 +1,8 @@ -// TODO: add a link to the spec +/// Power State Coordination Interface (PSCI) is a standard interface for power management. +/// +/// A full explanation of interfaces for power management can be found in the +/// "Arm Power State Coordination Interface Platform Design Document": +/// https://developer.arm.com/documentation/den0022/f/ use arm64::registers::{SCTLR_EL1, MAIR_EL1, TTBR1_EL1, TTBR0_EL1, TCR_EL1, SPSR_EL1}; use arm64::registers::Readable; @@ -14,6 +18,8 @@ use twizzler_abi::upcall::MemoryAccessKind; use super::{BootArgs, translate}; +// According to Section 6.4 the MMU and caches are disabled +// and software must set the EL1h stack pointer unsafe fn psci_secondary_entry(context_id: &BootArgs) -> ! { // TODO: manually set the configuration of registers @@ -76,7 +82,7 @@ fn rust_secondary_entry(args: &BootArgs) -> ! { pub unsafe fn boot_core(core: &mut Processor, tcb_base: VirtAddr, kernel_stack: *mut u8) { // we will issue a CPU_ON to turn on the cpu core // first we will add the necessary arguments needed - // by PSCI's CPU_ON function + // by PSCI's CPU_ON function (Section 5.6) // pass cpu id, this is this core's MPIDR_EL1 value // TODO: ensure the right bits are 0