From 8693f0241111fddcf7e8b904655b82514fac3a70 Mon Sep 17 00:00:00 2001 From: Allen Aboytes Date: Fri, 19 Apr 2024 18:19:10 -0700 Subject: [PATCH 1/6] Add support for CHERI QEMU --- tools/xtask/Cargo.toml | 1 + tools/xtask/src/build.rs | 12 +++++++++++- tools/xtask/src/qemu.rs | 30 +++++++++++++++++++++++------- tools/xtask/src/triple.rs | 11 +++++++++++ 4 files changed, 46 insertions(+), 8 deletions(-) diff --git a/tools/xtask/Cargo.toml b/tools/xtask/Cargo.toml index ae5ea6b8..e6616ca2 100644 --- a/tools/xtask/Cargo.toml +++ b/tools/xtask/Cargo.toml @@ -27,6 +27,7 @@ toml_edit = "0.19.15" tracing = "0.1" tracing-subscriber = "0.3" guess_host_triple = "0.1.3" +home = "0.5.9" [package.metadata] twizzler-build = "xtask" diff --git a/tools/xtask/src/build.rs b/tools/xtask/src/build.rs index de3a7aa5..dcce7261 100644 --- a/tools/xtask/src/build.rs +++ b/tools/xtask/src/build.rs @@ -24,7 +24,7 @@ struct OtherOptions { build_twizzler: bool, } -use crate::{triple::Triple, BuildOptions, CheckOptions, DocOptions, Profile}; +use crate::{triple::{Triple, valid_targets}, BuildOptions, CheckOptions, DocOptions, Profile}; fn locate_packages<'a>(workspace: &'a Workspace, kind: Option<&str>) -> Vec { workspace @@ -225,6 +225,7 @@ fn build_twizzler<'a>( crate::toolchain::set_dynamic(); crate::toolchain::set_cc(); crate::print_status_line("collection: userspace", Some(build_config)); + // let triple = build_config.twz_triple(); // the currently supported build target triples // have a value of "unknown" for the machine, but // we might specify a different value for machine @@ -448,11 +449,20 @@ impl TwizzlerCompilation { } } +fn check_build_target(config: crate::BuildConfig) -> anyhow::Result<()> { + if valid_targets().contains(&(config.arch, config.machine)) { + Ok(()) + } else { + anyhow::bail!("build target is invalid"); + } +} + fn compile( bc: crate::BuildConfig, mode: CompileMode, other_options: &OtherOptions, ) -> anyhow::Result { + check_build_target(bc)?; crate::toolchain::init_for_build( mode.is_doc() || mode.is_check() || !other_options.build_twizzler || true, )?; diff --git a/tools/xtask/src/qemu.rs b/tools/xtask/src/qemu.rs index 1d20a7e4..25b63f7d 100644 --- a/tools/xtask/src/qemu.rs +++ b/tools/xtask/src/qemu.rs @@ -4,23 +4,32 @@ use std::{ process::{Command, ExitStatus}, }; -use crate::{image::ImageInfo, triple::Arch, QemuOptions}; +use crate::{image::ImageInfo, triple::{Arch, Machine}, QemuOptions}; #[derive(Debug)] struct QemuCommand { cmd: Command, arch: Arch, + machine: Machine } impl QemuCommand { pub fn new(cli: &QemuOptions) -> Self { let cmd = match cli.config.arch { - Arch::X86_64 => "qemu-system-x86_64", - Arch::Aarch64 => "qemu-system-aarch64", + Arch::X86_64 => String::from("qemu-system-x86_64"), + Arch::Aarch64 => if cli.config.machine == Machine::Morello { + // all morello software by default is installed in ~/cheri + let mut qemu = home::home_dir().expect("failed to find home directory"); + qemu.push("cheri/output/sdk/bin/qemu-system-morello"); + String::from(qemu.to_str().unwrap()) + } else { + String::from("qemu-system-aarch64") + }, }; Self { - cmd: Command::new(cmd), + cmd: Command::new(&cmd), arch: cli.config.arch, + machine: cli.config.machine, } } @@ -100,9 +109,16 @@ impl QemuCommand { Arch::Aarch64 => { self.cmd.arg("-bios").arg("toolchain/install/OVMF-AA64.fd"); self.cmd.arg("-net").arg("none"); - // use qemu virt machine by default - self.cmd.arg("-machine").arg("virt"); //,gic-version=max"); - self.cmd.arg("-cpu").arg("cortex-a72"); + if self.machine == Machine::Morello { + // TODO: change this to use GICv3, like how Morello uses + self.cmd.arg("-machine").arg("virt,gic-version=2"); + self.cmd.arg("-cpu").arg("morello"); + } else { + // use qemu virt machine by default + // virt uses GICv2 by default + self.cmd.arg("-machine").arg("virt"); + self.cmd.arg("-cpu").arg("cortex-a72"); + } self.cmd.arg("-nographic"); } } diff --git a/tools/xtask/src/triple.rs b/tools/xtask/src/triple.rs index 20adf1a3..354eea24 100644 --- a/tools/xtask/src/triple.rs +++ b/tools/xtask/src/triple.rs @@ -5,6 +5,7 @@ pub enum Machine { Unknown, Rpi3, Virt, + Morello, } #[derive(Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Debug, EnumIter, clap::ValueEnum)] @@ -75,6 +76,7 @@ impl From for String { Machine::Unknown => "unknown", Machine::Rpi3 => "rpi3", Machine::Virt => "virt", + Machine::Morello => "morello", } .to_string() } @@ -163,3 +165,12 @@ pub fn all_possible_platforms() -> Vec { ]; triples } + +pub fn valid_targets() -> Vec<(Arch, Machine)> { + let targets = vec![ + (Arch::X86_64, Machine::Unknown), + (Arch::Aarch64, Machine::Virt), + (Arch::Aarch64, Machine::Morello) + ]; + targets +} From 83ce8009ef5945e18ea83eef0f9113cb9b4d3c60 Mon Sep 17 00:00:00 2001 From: Allen Aboytes Date: Fri, 19 Apr 2024 18:22:59 -0700 Subject: [PATCH 2/6] Add Morello build target --- src/kernel/src/machine/arm/mod.rs | 7 + src/kernel/src/machine/arm/morello/info.rs | 133 ++++++++++++++++++ .../src/machine/arm/morello/interrupt.rs | 67 +++++++++ src/kernel/src/machine/arm/morello/memory.rs | 22 +++ src/kernel/src/machine/arm/morello/mod.rs | 10 ++ .../src/machine/arm/morello/processor.rs | 37 +++++ src/kernel/src/machine/arm/morello/serial.rs | 113 +++++++++++++++ 7 files changed, 389 insertions(+) create mode 100644 src/kernel/src/machine/arm/morello/info.rs create mode 100644 src/kernel/src/machine/arm/morello/interrupt.rs create mode 100644 src/kernel/src/machine/arm/morello/memory.rs create mode 100644 src/kernel/src/machine/arm/morello/mod.rs create mode 100644 src/kernel/src/machine/arm/morello/processor.rs create mode 100644 src/kernel/src/machine/arm/morello/serial.rs diff --git a/src/kernel/src/machine/arm/mod.rs b/src/kernel/src/machine/arm/mod.rs index e5bdb853..486dc973 100644 --- a/src/kernel/src/machine/arm/mod.rs +++ b/src/kernel/src/machine/arm/mod.rs @@ -5,4 +5,11 @@ mod virt; #[cfg(machine = "virt")] pub use virt::*; +/// Morello SDP for CHERI +#[cfg(machine = "morello")] +mod morello; + +#[cfg(machine = "morello")] +pub use morello::*; + mod common; diff --git a/src/kernel/src/machine/arm/morello/info.rs b/src/kernel/src/machine/arm/morello/info.rs new file mode 100644 index 00000000..4e52acee --- /dev/null +++ b/src/kernel/src/machine/arm/morello/info.rs @@ -0,0 +1,133 @@ +use fdt::Fdt; + +use twizzler_abi::device::{MmioInfo, CacheType}; + +use crate::once::Once; +use crate::BootInfo; +use crate::arch::BootInfoSystemTable; + +// We use device tree to describe the hardware on this machine +static FDT: Once> = Once::new(); + +pub fn init(boot_info: &B) { + FDT.call_once(|| { + let dtb = { + // try to find device tree location using the bootloader + let bootloader_dtb_addr = boot_info.get_system_table(BootInfoSystemTable::Dtb); + // otherwise use a static address + if bootloader_dtb_addr.raw() == 0 { + // in the case of QEMU's virt platform, we can use 0x4000_0000 + super::memory::DTB_ADDR.kernel_vaddr() + } else { + bootloader_dtb_addr + } + }; + // should not fail, but it might ... + unsafe { + Fdt::from_ptr(dtb.as_ptr()) + .expect("invalid DTB file, cannot boot") + } + }); +} + +pub fn devicetree() -> &'static Fdt<'static> { + FDT.poll() + .expect("device tree initialization has not been called!!") +} + +// return the clock frequency and the mmio register info +pub fn get_uart_info() -> (usize, MmioInfo) { + let mut mmio = MmioInfo { + length: 0, + cache_type: CacheType::MemoryMappedIO, + info: 0, + }; + let mut clock_freq: usize = 0; + // we use the device tree to retrieve mmio register information + // and other useful configuration info + let chosen = devicetree().chosen(); + if let Some(uart) = chosen.stdout() { + // find the mmio registers + let regs = uart.reg().unwrap().next().unwrap(); + mmio.info = regs.starting_address as u64; + mmio.length = regs.size.unwrap() as u64; + // find the clock information + if let Some(clock_list) = uart.property("clocks") { + let phandle: u32 = { + // TODO: use size cell/address cell info + let mut converter = [0u8; 4]; + let mut phandle = 0; + for (i, v) in clock_list.value.iter().enumerate() { + converter[i % 4] = *v; + if (i + 1) % core::mem::size_of::() == 0 { + // converted value + phandle = u32::from_be_bytes(converter); + break; + } + } + phandle + }; + if let Some(clock) = devicetree().find_phandle(phandle) { + clock_freq = clock.property("clock-frequency").unwrap().as_usize().unwrap(); + } + } + } + (clock_freq, mmio) +} + +// Retrieve the interrupt number for the UART device +pub fn get_uart_interrupt_num() -> Option { + // NOTE: this encoding is specific to GIC interrupt controllers + let chosen = devicetree().chosen(); + if let Some(uart) = chosen.stdout() { + // find the interrupt information + if let Some(inter) = uart.property("interrupts") { + let mut converter = [0u8; 4]; + let mut converted = [0u32; 3]; + // each interrupt property is encoded as a series of 32-bit values + for (i, v) in inter.value.iter().enumerate() { + converter[i % 4] = *v; + if (i + 1) % core::mem::size_of::() == 0 { + // converted value + let val = u32::from_be_bytes(converter); + converted[i % 3] = val; + } + } + // first number is the SPI flag + let is_spi = converted[0] == 1; + // second number is the interrupt + let int_num = if is_spi { converted[1] + 16 } else { converted[1] + 32 }; + // third number is the trigger level + let _trigger = converted[2]; + return Some(int_num) + } + } + None +} + +// return the mmio address info for the distributor and cpu interfaces +// for a gicv2 interrupt controller +pub fn get_gicv2_info() -> (MmioInfo, MmioInfo) { + let mut gicd_mmio = MmioInfo { + length: 0, + cache_type: CacheType::MemoryMappedIO, + info: 0, + }; + let mut gicc_mmio = MmioInfo { + length: 0, + cache_type: CacheType::MemoryMappedIO, + info: 0, + }; + if let Some(gic) = devicetree().find_node("/intc") { + let mut mmio_regs = gic.reg().unwrap(); + // get distributor mmio regs + let regs = mmio_regs.next().unwrap(); + gicd_mmio.info = regs.starting_address as u64; + gicd_mmio.length = regs.size.unwrap() as u64; + // get local cpu interface regs + let regs = mmio_regs.next().unwrap(); + gicc_mmio.info = regs.starting_address as u64; + gicc_mmio.length = regs.size.unwrap() as u64; + } + (gicd_mmio, gicc_mmio) +} diff --git a/src/kernel/src/machine/arm/morello/interrupt.rs b/src/kernel/src/machine/arm/morello/interrupt.rs new file mode 100644 index 00000000..b8c29538 --- /dev/null +++ b/src/kernel/src/machine/arm/morello/interrupt.rs @@ -0,0 +1,67 @@ +use lazy_static::lazy_static; + +use super::super::common::gicv2::GICv2; + +lazy_static! { + /// System-wide reference to the interrupt controller + pub static ref INTERRUPT_CONTROLLER: GICv2 = { + use twizzler_abi::{device::CacheType, object::Protections}; + + use crate::memory::{ + PhysAddr, + pagetables::{ + ContiguousProvider, MappingCursor, MappingSettings, Mapper, + MappingFlags, + }, + }; + use crate::arch::memory::mmio::MMIO_ALLOCATOR; + + // retrive the locations of the MMIO registers + let (distributor_mmio, cpu_interface_mmio) = crate::machine::info::get_gicv2_info(); + // reserve regions of virtual address space for MMIO + let (gicc_mmio_base, gicd_mmio_base) = { + let mut alloc = MMIO_ALLOCATOR.lock(); + let cpu = alloc.alloc(cpu_interface_mmio.length as usize) + .expect("failed to allocate MMIO region"); + let dist = alloc.alloc(distributor_mmio.length as usize) + .expect("failed to allocate MMIO region"); + (cpu, dist) + }; + // configure mapping settings for this region of memory + let gicc_region = MappingCursor::new( + gicc_mmio_base, + cpu_interface_mmio.length as usize, + ); + let mut gicc_phys = ContiguousProvider::new( + unsafe { PhysAddr::new_unchecked(cpu_interface_mmio.info) }, + cpu_interface_mmio.length as usize, + ); + let gicd_region = MappingCursor::new( + gicd_mmio_base, + distributor_mmio.length as usize, + ); + let mut gicd_phys = ContiguousProvider::new( + unsafe { PhysAddr::new_unchecked(distributor_mmio.info) }, + distributor_mmio.length as usize, + ); + // Device memory only prevetns speculative data accesses, so we must not + // make this region executable to prevent speculative instruction accesses. + let settings = MappingSettings::new( + Protections::READ | Protections::WRITE, + CacheType::MemoryMappedIO, + MappingFlags::GLOBAL, + ); + // map in with curent memory context + unsafe { + let mut mapper = Mapper::current(); + mapper.map(gicc_region, &mut gicc_phys, &settings); + mapper.map(gicd_region, &mut gicd_phys, &settings); + } + GICv2::new( + // TODO: might need to lock global distributor state, + // and possibly CPU interface + gicd_mmio_base, + gicc_mmio_base, + ) + }; +} diff --git a/src/kernel/src/machine/arm/morello/memory.rs b/src/kernel/src/machine/arm/morello/memory.rs new file mode 100644 index 00000000..ea431806 --- /dev/null +++ b/src/kernel/src/machine/arm/morello/memory.rs @@ -0,0 +1,22 @@ +use crate::memory::{MemoryRegion, MemoryRegionKind, PhysAddr}; + +pub const DTB_ADDR: PhysAddr = unsafe { + PhysAddr::new_unchecked(0x4000_0000) +}; + +static RESERVED: [MemoryRegion; 1] = [ + MemoryRegion { + // physical base address in QEMU + start: DTB_ADDR, + // TODO: determine this at runtime + length: 0x100000, + kind: MemoryRegionKind::Reserved, + }, +]; + +/// A slice of physical regions of memory that are reserved +/// and should be ignored by the kernel. This list is device specific +/// and may be empty. +pub fn reserved_regions() -> &'static [MemoryRegion] { + &RESERVED +} diff --git a/src/kernel/src/machine/arm/morello/mod.rs b/src/kernel/src/machine/arm/morello/mod.rs new file mode 100644 index 00000000..8f0a4777 --- /dev/null +++ b/src/kernel/src/machine/arm/morello/mod.rs @@ -0,0 +1,10 @@ +pub mod info; +pub mod interrupt; +pub mod memory; +pub mod processor; +pub mod serial; + +pub fn machine_post_init() { + // initialize uart with interrupts + serial::SERIAL.late_init(); +} \ No newline at end of file diff --git a/src/kernel/src/machine/arm/morello/processor.rs b/src/kernel/src/machine/arm/morello/processor.rs new file mode 100644 index 00000000..42c095a5 --- /dev/null +++ b/src/kernel/src/machine/arm/morello/processor.rs @@ -0,0 +1,37 @@ +use core::str::FromStr; + +use arm64::registers::MPIDR_EL1; +use registers::interfaces::Readable; + +use crate::machine::info::devicetree; + +// re-export boot module +pub use super::super::common::boot::*; + +pub fn enumerate_cpus() -> u32 { + // MT bit means lowest level is logical cores (SMT) + // U bit means we are running on a uniprocessor + // combination of aff{3-0} is unique system wide + + // generally affinity 1 is the cluster ID, and + // affinity 0 (bits [7:0]) is the core ID in the cluster + let core_id = (MPIDR_EL1.get() & 0xff) as u32; + + // enumerate the cpus using a device tree + for cpu in devicetree().cpus() { + let cpu_id = cpu.ids().first() as u32; + 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") { + 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(); + // save the MPIDR_EL1 value found used for boot + core.arch.mpidr = cpu_id as u64; + } + } + + core_id +} diff --git a/src/kernel/src/machine/arm/morello/serial.rs b/src/kernel/src/machine/arm/morello/serial.rs new file mode 100644 index 00000000..00c57eb4 --- /dev/null +++ b/src/kernel/src/machine/arm/morello/serial.rs @@ -0,0 +1,113 @@ +use lazy_static::lazy_static; +use twizzler_abi::object::Protections; + +use super::super::common::uart::PL011; + +use crate::memory::{PhysAddr, pagetables::{ + ContiguousProvider, MappingCursor, MappingSettings, Mapper, + MappingFlags, +}}; +use crate::interrupt::{Destination, TriggerMode}; +use crate::arch::memory::mmio::MMIO_ALLOCATOR; + +lazy_static! { + // TODO: add a spinlock here + pub static ref SERIAL: PL011 = { + let (clock_freq, mmio) = crate::machine::info::get_uart_info(); + // the desired virtal address for this region of mmio + let uart_mmio_base = { + MMIO_ALLOCATOR.lock().alloc(mmio.length as usize) + .expect("failed to allocate MMIO region") + }; + // configure mapping settings for this region of memory + let cursor = MappingCursor::new( + uart_mmio_base, + mmio.length as usize, + ); + let mut phys = ContiguousProvider::new( + unsafe { PhysAddr::new_unchecked(mmio.info) }, + mmio.length as usize, + ); + // Device memory only prevetns speculative data accesses, so we must not + // make this region executable to prevent speculative instruction accesses. + let settings = MappingSettings::new( + Protections::READ | Protections::WRITE, + mmio.cache_type, + MappingFlags::GLOBAL, + ); + // map in with curent memory context + unsafe { + let mut mapper = Mapper::current(); + mapper.map(cursor, &mut phys, &settings); + } + + // create instance of the PL011 UART driver + let serial_port = unsafe { + PL011::new(uart_mmio_base.into()) + }; + serial_port.early_init(clock_freq as u32); + serial_port + }; + + pub static ref SERIAL_INT_ID: u32 = { + let int_num = crate::machine::info::get_uart_interrupt_num() + .expect("failed to decode UART interrupt number"); + int_num + }; +} + +impl PL011 { + fn write_str(&self, s: &str) { + for byte in s.bytes() { + self.tx_byte(byte); + } + } + + /// initalize the UART driver early, before interrupts in the system are enabled + fn early_init(&self, clock_freq: u32) { + const BAUD: u32 = 115200; + // configure the UART with the desired baud, given the clock rate + unsafe { + self.init(clock_freq, BAUD); + } + } + + /// intitialize the UART driver after the system has enabled interrupts + pub fn late_init(&self) { + // enable the rx side to use interrupts + unsafe { + self.enable_rx_interrupt(); + } + + crate::arch::set_interrupt( + *SERIAL_INT_ID, + false, + TriggerMode::Edge, + crate::interrupt::PinPolarity::ActiveHigh, + Destination::Bsp, + ); + } +} + +pub fn write(data: &[u8], _flags: crate::log::KernelConsoleWriteFlags) { + // We need the memory management system up and running to use MMIO. + // Other requests to log to the console are ignored. The console is + // initialized lazily on first access. + // + // This means that we cannot and should not ouput logging messages to + // the UART before this happens. Mapping in some memory might require + // allocating physical frames for the page tables. + if crate::memory::is_init() { + unsafe { + SERIAL.write_str(core::str::from_utf8_unchecked(data)); + } + } +} + +pub fn serial_interrupt_handler() { + let byte = SERIAL.rx_byte(); + if let Some(x) = byte { + crate::log::push_input_byte(x); + } + SERIAL.clear_rx_interrupt(); +} From 8bf9695a328eaaee9894737cc5658354f87e8275 Mon Sep 17 00:00:00 2001 From: Allen Aboytes Date: Fri, 19 Apr 2024 23:17:48 -0700 Subject: [PATCH 3/6] Implement basic GICv3 support --- src/kernel/Cargo.toml | 3 + src/kernel/src/machine/arm/common/gicv3.rs | 130 ++++++++++++++++++ src/kernel/src/machine/arm/common/mod.rs | 1 + src/kernel/src/machine/arm/morello/info.rs | 15 ++ .../src/machine/arm/morello/interrupt.rs | 22 ++- tools/xtask/src/qemu.rs | 3 +- 6 files changed, 160 insertions(+), 14 deletions(-) create mode 100644 src/kernel/src/machine/arm/common/gicv3.rs diff --git a/src/kernel/Cargo.toml b/src/kernel/Cargo.toml index e87305cb..5641f589 100755 --- a/src/kernel/Cargo.toml +++ b/src/kernel/Cargo.toml @@ -50,6 +50,9 @@ registers = { package = "tock-registers", version = "0.8.x" } fdt = "0.1.5" smccc = "0.1.1" +[target.'cfg(machine = "morello")'.dependencies] +arm-gic = "0.1.0" + [dependencies.lazy_static] version = "1.0" features = ["spin_no_std"] diff --git a/src/kernel/src/machine/arm/common/gicv3.rs b/src/kernel/src/machine/arm/common/gicv3.rs new file mode 100644 index 00000000..ad6222a8 --- /dev/null +++ b/src/kernel/src/machine/arm/common/gicv3.rs @@ -0,0 +1,130 @@ +/// A Generic Interrupt Controller (GIC) v3 driver interface +/// +/// The full specification can be found here: +/// https://developer.arm.com/documentation/ihi0069/latest +/// +/// A summary of its functionality can be found here: +/// https://developer.arm.com/documentation/198123/0302/ +/// +/// A driver interface for the GICv3 + +use crate::{memory::VirtAddr, spinlock::Spinlock}; +use arm_gic::gicv3::{GicV3, IntId}; + +/// A representation of the Generic Interrupt Controller (GIC) v3 +pub struct GICv3 { + global: Spinlock, +} + +/// This wrapper is needed because the type `GicV3` is not +/// Send or Sync since it's internal implementation uses +/// raw pointers. The workaround is a wrapper type so we +/// are able to use this with `Spinlock` and be able to +/// use its APIs that mutate state in the global distributor. +struct Gicv3Wrapper(GicV3); + +unsafe impl Send for Gicv3Wrapper {} +unsafe impl Sync for Gicv3Wrapper {} + +impl GICv3 { + // The interrupt mask to accept all interrupts regardless of priority. + const ACCEPT_ALL: u8 = 0xff; + + // The highest interrupt priority. Lower means higher priority. + const HIGHEST_PRIORITY: u8 = 0; + + pub fn new(distr_base: VirtAddr, redist_base: VirtAddr) -> Self { + unsafe { + let gic_instance = GicV3::new(distr_base.as_mut_ptr(), redist_base.as_mut_ptr()); + Self { + global: Spinlock::new(Gicv3Wrapper(gic_instance)) + } + } + } + + /// Configures the interrupt controller. At the end of this function + /// the current calling CPU is ready to recieve interrupts. + pub fn configure_local(&self) { + // set the interrupt priority mask to accept all interrupts + self.set_interrupt_mask(Self::ACCEPT_ALL); + // TODO: enable the gic cpu interface. See GICR_WAKER + + // NOTE: handled in the `setup` function. This might not be the best + // crate to use if we want to utilize multiple cores since + // the gicv3 crate touches global and local state during `setup`. + } + + /// Configures global state in the interrupt controller. This should only + /// really be called once during system intialization by the boostrap core. + pub fn configure_global(&self) { + // enable the gic distributor + self.global.lock().0.setup(); + // NOTE: This might not be the best crate to use if we want to + // utilize multiple cores since the gicv3 crate touches global + // and local state during `setup`. + // + // might be OK since the global configs are the same every time. + } + + /// Sets the interrupt priority mask for the current calling CPU. + fn set_interrupt_mask(&self, mask: u8) { + // set the interrupt priority mask that we will accept + GicV3::set_priority_mask(mask); + } + + // Enables the interrupt with a given ID to be routed to CPUs. + pub fn enable_interrupt(&self, int_id: u32) { + self.global.lock().0.enable_interrupt(u32_to_int_id(int_id), true); + } + + /// Programs the interrupt controller to be able to route + /// a given interrupt to a particular core. + pub fn route_interrupt(&self, int_id: u32, _core: u32) { + // route the interrupt to a corresponding core + // self.global.set_interrupt_target(int_id, core); + // TODO: have the priority set to something reasonable + // set the priority for the corresponding interrupt + self.global.lock().0.set_interrupt_priority(u32_to_int_id(int_id), Self::HIGHEST_PRIORITY); + // TODO: edge triggered or level sensitive??? see GICD_ICFGRn + } + + /// Returns the pending interrupt ID from the controller, and + /// acknowledges the interrupt. + pub fn pending_interrupt(&self) -> u32 { + let int_id = GicV3::get_and_acknowledge_interrupt(); + int_id.expect("failed to retrieve interrupt ID").into() + } + + /// Signal the controller that we have serviced the interrupt + pub fn finish_active_interrupt(&self, int_id: u32) { + GicV3::end_interrupt(u32_to_int_id(int_id)) + } + + /// Print the configuration of the GIC + pub fn print_config(&self) { + todo!() + } +} + + +/// The ID of the first Software Generated Interrupt. +const SGI_START: u32 = 0; +/// The ID of the first Private Peripheral Interrupt. +const PPI_START: u32 = 16; +/// The ID of the first Shared Peripheral Interrupt. +const SPI_START: u32 = 32; +/// The first special interrupt ID. +const SPECIAL_START: u32 = 1020; + +fn u32_to_int_id(value: u32) -> IntId { + logln!("[debug] setting int id: {}", value); + if value < PPI_START { + IntId::sgi(PPI_START - value) + } else if value < SPI_START { + IntId::ppi(SPI_START - value) + } else if value < SPECIAL_START { + IntId::spi(SPECIAL_START - value) + } else { + panic!("invalid interrupt id: {}", value); + } +} diff --git a/src/kernel/src/machine/arm/common/mod.rs b/src/kernel/src/machine/arm/common/mod.rs index cdc02d95..80d5b81d 100644 --- a/src/kernel/src/machine/arm/common/mod.rs +++ b/src/kernel/src/machine/arm/common/mod.rs @@ -1,4 +1,5 @@ pub mod boot; pub mod gicv2; +pub mod gicv3; pub mod mmio; pub mod uart; diff --git a/src/kernel/src/machine/arm/morello/info.rs b/src/kernel/src/machine/arm/morello/info.rs index 4e52acee..eb561491 100644 --- a/src/kernel/src/machine/arm/morello/info.rs +++ b/src/kernel/src/machine/arm/morello/info.rs @@ -131,3 +131,18 @@ pub fn get_gicv2_info() -> (MmioInfo, MmioInfo) { } (gicd_mmio, gicc_mmio) } + +/// Return the MMIO info for the GICv3 registers +pub fn get_gicv3_info() -> (MmioInfo, MmioInfo) { + let gicd_mmio = MmioInfo { + length: 0x1000, + cache_type: CacheType::MemoryMappedIO, + info: 0x800_0000, + }; + let gicr_mmio = MmioInfo { + length: 0x00F60000, + cache_type: CacheType::MemoryMappedIO, + info: 0x80A_0000, + }; + (gicd_mmio, gicr_mmio) +} diff --git a/src/kernel/src/machine/arm/morello/interrupt.rs b/src/kernel/src/machine/arm/morello/interrupt.rs index b8c29538..5ee28b1b 100644 --- a/src/kernel/src/machine/arm/morello/interrupt.rs +++ b/src/kernel/src/machine/arm/morello/interrupt.rs @@ -1,10 +1,10 @@ use lazy_static::lazy_static; -use super::super::common::gicv2::GICv2; +use super::super::common::gicv3::GICv3; lazy_static! { /// System-wide reference to the interrupt controller - pub static ref INTERRUPT_CONTROLLER: GICv2 = { + pub static ref INTERRUPT_CONTROLLER: GICv3 = { use twizzler_abi::{device::CacheType, object::Protections}; use crate::memory::{ @@ -17,9 +17,9 @@ lazy_static! { use crate::arch::memory::mmio::MMIO_ALLOCATOR; // retrive the locations of the MMIO registers - let (distributor_mmio, cpu_interface_mmio) = crate::machine::info::get_gicv2_info(); + let (distributor_mmio, cpu_interface_mmio) = crate::machine::info::get_gicv3_info(); // reserve regions of virtual address space for MMIO - let (gicc_mmio_base, gicd_mmio_base) = { + let (gicr_mmio_base, gicd_mmio_base) = { let mut alloc = MMIO_ALLOCATOR.lock(); let cpu = alloc.alloc(cpu_interface_mmio.length as usize) .expect("failed to allocate MMIO region"); @@ -28,11 +28,11 @@ lazy_static! { (cpu, dist) }; // configure mapping settings for this region of memory - let gicc_region = MappingCursor::new( - gicc_mmio_base, + let gicr_region = MappingCursor::new( + gicr_mmio_base, cpu_interface_mmio.length as usize, ); - let mut gicc_phys = ContiguousProvider::new( + let mut gicr_phys = ContiguousProvider::new( unsafe { PhysAddr::new_unchecked(cpu_interface_mmio.info) }, cpu_interface_mmio.length as usize, ); @@ -54,14 +54,12 @@ lazy_static! { // map in with curent memory context unsafe { let mut mapper = Mapper::current(); - mapper.map(gicc_region, &mut gicc_phys, &settings); + mapper.map(gicr_region, &mut gicr_phys, &settings); mapper.map(gicd_region, &mut gicd_phys, &settings); } - GICv2::new( - // TODO: might need to lock global distributor state, - // and possibly CPU interface + GICv3::new( gicd_mmio_base, - gicc_mmio_base, + gicr_mmio_base, ) }; } diff --git a/tools/xtask/src/qemu.rs b/tools/xtask/src/qemu.rs index 25b63f7d..c33e5674 100644 --- a/tools/xtask/src/qemu.rs +++ b/tools/xtask/src/qemu.rs @@ -110,8 +110,7 @@ impl QemuCommand { self.cmd.arg("-bios").arg("toolchain/install/OVMF-AA64.fd"); self.cmd.arg("-net").arg("none"); if self.machine == Machine::Morello { - // TODO: change this to use GICv3, like how Morello uses - self.cmd.arg("-machine").arg("virt,gic-version=2"); + self.cmd.arg("-machine").arg("virt,gic-version=3"); self.cmd.arg("-cpu").arg("morello"); } else { // use qemu virt machine by default From 503fe5217359406cba17190088dac1a94262d8ab Mon Sep 17 00:00:00 2001 From: Allen Aboytes Date: Mon, 22 Apr 2024 08:44:57 -0700 Subject: [PATCH 4/6] Run cargo fmt --- src/kernel/src/machine/arm/common/gicv3.rs | 30 ++++++++----- src/kernel/src/machine/arm/morello/info.rs | 44 ++++++++++--------- .../src/machine/arm/morello/interrupt.rs | 4 +- src/kernel/src/machine/arm/morello/memory.rs | 20 ++++----- src/kernel/src/machine/arm/morello/mod.rs | 2 +- .../src/machine/arm/morello/processor.rs | 7 +-- src/kernel/src/machine/arm/morello/serial.rs | 23 +++++----- tools/xtask/src/build.rs | 7 ++- tools/xtask/src/qemu.rs | 26 ++++++----- tools/xtask/src/triple.rs | 2 +- 10 files changed, 89 insertions(+), 76 deletions(-) diff --git a/src/kernel/src/machine/arm/common/gicv3.rs b/src/kernel/src/machine/arm/common/gicv3.rs index ad6222a8..5b2a233f 100644 --- a/src/kernel/src/machine/arm/common/gicv3.rs +++ b/src/kernel/src/machine/arm/common/gicv3.rs @@ -1,16 +1,17 @@ /// A Generic Interrupt Controller (GIC) v3 driver interface -/// +/// /// The full specification can be found here: /// https://developer.arm.com/documentation/ihi0069/latest /// /// A summary of its functionality can be found here: /// https://developer.arm.com/documentation/198123/0302/ -/// +/// /// A driver interface for the GICv3 -use crate::{memory::VirtAddr, spinlock::Spinlock}; use arm_gic::gicv3::{GicV3, IntId}; +use crate::{memory::VirtAddr, spinlock::Spinlock}; + /// A representation of the Generic Interrupt Controller (GIC) v3 pub struct GICv3 { global: Spinlock, @@ -19,7 +20,7 @@ pub struct GICv3 { /// This wrapper is needed because the type `GicV3` is not /// Send or Sync since it's internal implementation uses /// raw pointers. The workaround is a wrapper type so we -/// are able to use this with `Spinlock` and be able to +/// are able to use this with `Spinlock` and be able to /// use its APIs that mutate state in the global distributor. struct Gicv3Wrapper(GicV3); @@ -37,7 +38,7 @@ impl GICv3 { unsafe { let gic_instance = GicV3::new(distr_base.as_mut_ptr(), redist_base.as_mut_ptr()); Self { - global: Spinlock::new(Gicv3Wrapper(gic_instance)) + global: Spinlock::new(Gicv3Wrapper(gic_instance)), } } } @@ -49,8 +50,8 @@ impl GICv3 { self.set_interrupt_mask(Self::ACCEPT_ALL); // TODO: enable the gic cpu interface. See GICR_WAKER - // NOTE: handled in the `setup` function. This might not be the best - // crate to use if we want to utilize multiple cores since + // NOTE: handled in the `setup` function. This might not be the best + // crate to use if we want to utilize multiple cores since // the gicv3 crate touches global and local state during `setup`. } @@ -59,7 +60,7 @@ impl GICv3 { pub fn configure_global(&self) { // enable the gic distributor self.global.lock().0.setup(); - // NOTE: This might not be the best crate to use if we want to + // NOTE: This might not be the best crate to use if we want to // utilize multiple cores since the gicv3 crate touches global // and local state during `setup`. // @@ -74,17 +75,25 @@ impl GICv3 { // Enables the interrupt with a given ID to be routed to CPUs. pub fn enable_interrupt(&self, int_id: u32) { - self.global.lock().0.enable_interrupt(u32_to_int_id(int_id), true); + self.global + .lock() + .0 + .enable_interrupt(u32_to_int_id(int_id), true); } /// Programs the interrupt controller to be able to route /// a given interrupt to a particular core. pub fn route_interrupt(&self, int_id: u32, _core: u32) { + // TODO: route interrupts (PPIs/SPIs) to cores // route the interrupt to a corresponding core // self.global.set_interrupt_target(int_id, core); + // TODO: have the priority set to something reasonable // set the priority for the corresponding interrupt - self.global.lock().0.set_interrupt_priority(u32_to_int_id(int_id), Self::HIGHEST_PRIORITY); + self.global + .lock() + .0 + .set_interrupt_priority(u32_to_int_id(int_id), Self::HIGHEST_PRIORITY); // TODO: edge triggered or level sensitive??? see GICD_ICFGRn } @@ -106,7 +115,6 @@ impl GICv3 { } } - /// The ID of the first Software Generated Interrupt. const SGI_START: u32 = 0; /// The ID of the first Private Peripheral Interrupt. diff --git a/src/kernel/src/machine/arm/morello/info.rs b/src/kernel/src/machine/arm/morello/info.rs index eb561491..c57fb2b5 100644 --- a/src/kernel/src/machine/arm/morello/info.rs +++ b/src/kernel/src/machine/arm/morello/info.rs @@ -1,10 +1,7 @@ use fdt::Fdt; +use twizzler_abi::device::{CacheType, MmioInfo}; -use twizzler_abi::device::{MmioInfo, CacheType}; - -use crate::once::Once; -use crate::BootInfo; -use crate::arch::BootInfoSystemTable; +use crate::{arch::BootInfoSystemTable, once::Once, BootInfo}; // We use device tree to describe the hardware on this machine static FDT: Once> = Once::new(); @@ -20,13 +17,10 @@ pub fn init(boot_info: &B) { super::memory::DTB_ADDR.kernel_vaddr() } else { bootloader_dtb_addr - } + } }; // should not fail, but it might ... - unsafe { - Fdt::from_ptr(dtb.as_ptr()) - .expect("invalid DTB file, cannot boot") - } + unsafe { Fdt::from_ptr(dtb.as_ptr()).expect("invalid DTB file, cannot boot") } }); } @@ -68,7 +62,11 @@ pub fn get_uart_info() -> (usize, MmioInfo) { phandle }; if let Some(clock) = devicetree().find_phandle(phandle) { - clock_freq = clock.property("clock-frequency").unwrap().as_usize().unwrap(); + clock_freq = clock + .property("clock-frequency") + .unwrap() + .as_usize() + .unwrap(); } } } @@ -96,10 +94,14 @@ pub fn get_uart_interrupt_num() -> Option { // first number is the SPI flag let is_spi = converted[0] == 1; // second number is the interrupt - let int_num = if is_spi { converted[1] + 16 } else { converted[1] + 32 }; + let int_num = if is_spi { + converted[1] + 16 + } else { + converted[1] + 32 + }; // third number is the trigger level let _trigger = converted[2]; - return Some(int_num) + return Some(int_num); } } None @@ -108,15 +110,15 @@ pub fn get_uart_interrupt_num() -> Option { // return the mmio address info for the distributor and cpu interfaces // for a gicv2 interrupt controller pub fn get_gicv2_info() -> (MmioInfo, MmioInfo) { - let mut gicd_mmio = MmioInfo { + let mut gicd_mmio = MmioInfo { length: 0, cache_type: CacheType::MemoryMappedIO, - info: 0, + info: 0, }; - let mut gicc_mmio = MmioInfo { + let mut gicc_mmio = MmioInfo { length: 0, cache_type: CacheType::MemoryMappedIO, - info: 0, + info: 0, }; if let Some(gic) = devicetree().find_node("/intc") { let mut mmio_regs = gic.reg().unwrap(); @@ -134,15 +136,15 @@ pub fn get_gicv2_info() -> (MmioInfo, MmioInfo) { /// Return the MMIO info for the GICv3 registers pub fn get_gicv3_info() -> (MmioInfo, MmioInfo) { - let gicd_mmio = MmioInfo { + let gicd_mmio = MmioInfo { length: 0x1000, cache_type: CacheType::MemoryMappedIO, - info: 0x800_0000, + info: 0x800_0000, }; - let gicr_mmio = MmioInfo { + let gicr_mmio = MmioInfo { length: 0x00F60000, cache_type: CacheType::MemoryMappedIO, - info: 0x80A_0000, + info: 0x80A_0000, }; (gicd_mmio, gicr_mmio) } diff --git a/src/kernel/src/machine/arm/morello/interrupt.rs b/src/kernel/src/machine/arm/morello/interrupt.rs index 5ee28b1b..b404a911 100644 --- a/src/kernel/src/machine/arm/morello/interrupt.rs +++ b/src/kernel/src/machine/arm/morello/interrupt.rs @@ -6,7 +6,7 @@ lazy_static! { /// System-wide reference to the interrupt controller pub static ref INTERRUPT_CONTROLLER: GICv3 = { use twizzler_abi::{device::CacheType, object::Protections}; - + use crate::memory::{ PhysAddr, pagetables::{ @@ -15,7 +15,7 @@ lazy_static! { }, }; use crate::arch::memory::mmio::MMIO_ALLOCATOR; - + // retrive the locations of the MMIO registers let (distributor_mmio, cpu_interface_mmio) = crate::machine::info::get_gicv3_info(); // reserve regions of virtual address space for MMIO diff --git a/src/kernel/src/machine/arm/morello/memory.rs b/src/kernel/src/machine/arm/morello/memory.rs index ea431806..516f9359 100644 --- a/src/kernel/src/machine/arm/morello/memory.rs +++ b/src/kernel/src/machine/arm/morello/memory.rs @@ -1,18 +1,14 @@ use crate::memory::{MemoryRegion, MemoryRegionKind, PhysAddr}; -pub const DTB_ADDR: PhysAddr = unsafe { - PhysAddr::new_unchecked(0x4000_0000) -}; +pub const DTB_ADDR: PhysAddr = unsafe { PhysAddr::new_unchecked(0x4000_0000) }; -static RESERVED: [MemoryRegion; 1] = [ - MemoryRegion { - // physical base address in QEMU - start: DTB_ADDR, - // TODO: determine this at runtime - length: 0x100000, - kind: MemoryRegionKind::Reserved, - }, -]; +static RESERVED: [MemoryRegion; 1] = [MemoryRegion { + // physical base address in QEMU + start: DTB_ADDR, + // TODO: determine this at runtime + length: 0x100000, + kind: MemoryRegionKind::Reserved, +}]; /// A slice of physical regions of memory that are reserved /// and should be ignored by the kernel. This list is device specific diff --git a/src/kernel/src/machine/arm/morello/mod.rs b/src/kernel/src/machine/arm/morello/mod.rs index 8f0a4777..a2397fcb 100644 --- a/src/kernel/src/machine/arm/morello/mod.rs +++ b/src/kernel/src/machine/arm/morello/mod.rs @@ -7,4 +7,4 @@ pub mod serial; pub fn machine_post_init() { // initialize uart with interrupts serial::SERIAL.late_init(); -} \ No newline at end of file +} diff --git a/src/kernel/src/machine/arm/morello/processor.rs b/src/kernel/src/machine/arm/morello/processor.rs index 42c095a5..f1aa5aed 100644 --- a/src/kernel/src/machine/arm/morello/processor.rs +++ b/src/kernel/src/machine/arm/morello/processor.rs @@ -3,10 +3,9 @@ use core::str::FromStr; use arm64::registers::MPIDR_EL1; use registers::interfaces::Readable; -use crate::machine::info::devicetree; - // re-export boot module pub use super::super::common::boot::*; +use crate::machine::info::devicetree; pub fn enumerate_cpus() -> u32 { // MT bit means lowest level is logical cores (SMT) @@ -23,9 +22,7 @@ pub fn enumerate_cpus() -> u32 { 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") { - let core = unsafe { - crate::processor::get_processor_mut(cpu_id) - }; + 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(); // save the MPIDR_EL1 value found used for boot diff --git a/src/kernel/src/machine/arm/morello/serial.rs b/src/kernel/src/machine/arm/morello/serial.rs index 00c57eb4..32ce8a71 100644 --- a/src/kernel/src/machine/arm/morello/serial.rs +++ b/src/kernel/src/machine/arm/morello/serial.rs @@ -1,14 +1,15 @@ use lazy_static::lazy_static; use twizzler_abi::object::Protections; -use super::super::common::uart::PL011; - -use crate::memory::{PhysAddr, pagetables::{ - ContiguousProvider, MappingCursor, MappingSettings, Mapper, - MappingFlags, -}}; -use crate::interrupt::{Destination, TriggerMode}; -use crate::arch::memory::mmio::MMIO_ALLOCATOR; +use super::super::common::uart::PL011; +use crate::{ + arch::memory::mmio::MMIO_ALLOCATOR, + interrupt::{Destination, TriggerMode}, + memory::{ + pagetables::{ContiguousProvider, Mapper, MappingCursor, MappingFlags, MappingSettings}, + PhysAddr, + }, +}; lazy_static! { // TODO: add a spinlock here @@ -42,8 +43,8 @@ lazy_static! { } // create instance of the PL011 UART driver - let serial_port = unsafe { - PL011::new(uart_mmio_base.into()) + let serial_port = unsafe { + PL011::new(uart_mmio_base.into()) }; serial_port.early_init(clock_freq as u32); serial_port @@ -67,7 +68,7 @@ impl PL011 { fn early_init(&self, clock_freq: u32) { const BAUD: u32 = 115200; // configure the UART with the desired baud, given the clock rate - unsafe { + unsafe { self.init(clock_freq, BAUD); } } diff --git a/tools/xtask/src/build.rs b/tools/xtask/src/build.rs index dcce7261..81f526a0 100644 --- a/tools/xtask/src/build.rs +++ b/tools/xtask/src/build.rs @@ -24,7 +24,10 @@ struct OtherOptions { build_twizzler: bool, } -use crate::{triple::{Triple, valid_targets}, BuildOptions, CheckOptions, DocOptions, Profile}; +use crate::{ + triple::{valid_targets, Triple}, + BuildOptions, CheckOptions, DocOptions, Profile, +}; fn locate_packages<'a>(workspace: &'a Workspace, kind: Option<&str>) -> Vec { workspace @@ -454,7 +457,7 @@ fn check_build_target(config: crate::BuildConfig) -> anyhow::Result<()> { Ok(()) } else { anyhow::bail!("build target is invalid"); - } + } } fn compile( diff --git a/tools/xtask/src/qemu.rs b/tools/xtask/src/qemu.rs index c33e5674..460b41da 100644 --- a/tools/xtask/src/qemu.rs +++ b/tools/xtask/src/qemu.rs @@ -4,27 +4,33 @@ use std::{ process::{Command, ExitStatus}, }; -use crate::{image::ImageInfo, triple::{Arch, Machine}, QemuOptions}; +use crate::{ + image::ImageInfo, + triple::{Arch, Machine}, + QemuOptions, +}; #[derive(Debug)] struct QemuCommand { cmd: Command, arch: Arch, - machine: Machine + machine: Machine, } impl QemuCommand { pub fn new(cli: &QemuOptions) -> Self { let cmd = match cli.config.arch { Arch::X86_64 => String::from("qemu-system-x86_64"), - Arch::Aarch64 => if cli.config.machine == Machine::Morello { - // all morello software by default is installed in ~/cheri - let mut qemu = home::home_dir().expect("failed to find home directory"); - qemu.push("cheri/output/sdk/bin/qemu-system-morello"); - String::from(qemu.to_str().unwrap()) - } else { - String::from("qemu-system-aarch64") - }, + Arch::Aarch64 => { + if cli.config.machine == Machine::Morello { + // all morello software by default is installed in ~/cheri + let mut qemu = home::home_dir().expect("failed to find home directory"); + qemu.push("cheri/output/sdk/bin/qemu-system-morello"); + String::from(qemu.to_str().unwrap()) + } else { + String::from("qemu-system-aarch64") + } + } }; Self { cmd: Command::new(&cmd), diff --git a/tools/xtask/src/triple.rs b/tools/xtask/src/triple.rs index 354eea24..90ca78c9 100644 --- a/tools/xtask/src/triple.rs +++ b/tools/xtask/src/triple.rs @@ -170,7 +170,7 @@ pub fn valid_targets() -> Vec<(Arch, Machine)> { let targets = vec![ (Arch::X86_64, Machine::Unknown), (Arch::Aarch64, Machine::Virt), - (Arch::Aarch64, Machine::Morello) + (Arch::Aarch64, Machine::Morello), ]; targets } From ab8aeb2062c6258e867e5031c42ed0a8aa7937e1 Mon Sep 17 00:00:00 2001 From: Allen Aboytes Date: Mon, 12 Aug 2024 14:54:00 -0700 Subject: [PATCH 5/6] Fix interrupt initialization --- Cargo.lock | 49 +++-- src/kernel/src/machine/arm/common/gicv3.rs | 175 ++++++++++++++++-- src/kernel/src/machine/arm/common/mod.rs | 1 + src/kernel/src/machine/arm/morello/info.rs | 27 --- .../src/machine/arm/morello/interrupt.rs | 5 + 5 files changed, 193 insertions(+), 64 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index c0ba2562..6f263f1a 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -171,6 +171,15 @@ dependencies = [ "serde", ] +[[package]] +name = "arm-gic" +version = "0.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "84eab3ef073fa1f664c9173e44f2437ce413e41d0ae8f17edd632a1bbcc89c4d" +dependencies = [ + "bitflags 2.6.0", +] + [[package]] name = "arrayvec" version = "0.5.2" @@ -335,9 +344,9 @@ checksum = "bef38d45163c2f1dde094a7dfd33ccf595c92905c8f8f4fdc18d06fb1037718a" [[package]] name = "bitflags" -version = "2.4.1" +version = "2.6.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "327762f6e5a765692301e5bb513e0d9fef63be86bbc14528052b1cd3e6f03e07" +checksum = "b048fb63fd8b5923fc5aa7b340d8e156aec7ec02f0c78fa8a6ddc2613f6f71de" dependencies = [ "compiler_builtins", "rustc-std-workspace-core", @@ -865,7 +874,7 @@ name = "dynlink" version = "0.1.0" dependencies = [ "anyhow", - "bitflags 2.4.1", + "bitflags 2.6.0", "elf", "humansize", "itertools 0.12.0", @@ -1308,7 +1317,7 @@ version = "3.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "8283e7331b8c93b9756e0cfdbcfb90312852f953c6faf9bf741e684cc3b6ad69" dependencies = [ - "bitflags 2.4.1", + "bitflags 2.6.0", "crc", "log", "uuid", @@ -1412,11 +1421,11 @@ dependencies = [ [[package]] name = "home" -version = "0.5.5" +version = "0.5.9" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5444c27eef6923071f7ebcc33e3444508466a76f7a2b93da00ed6e19f30c1ddb" +checksum = "e3d1354bf6b7235cb4a0576c2619fd4ed18183f689b12b006a0ee7329eeff9a5" dependencies = [ - "windows-sys 0.48.0", + "windows-sys 0.52.0", ] [[package]] @@ -2141,7 +2150,7 @@ version = "0.10.57" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "bac25ee399abb46215765b1cb35bc0212377e58a061560d8b29b024fd0430e7c" dependencies = [ - "bitflags 2.4.1", + "bitflags 2.6.0", "cfg-if 1.0.0", "foreign-types", "libc", @@ -2370,7 +2379,7 @@ name = "polling" version = "3.6.0" source = "git+https://github.com/twizzler-operating-system/polling.git?branch=twizzler#83586e3bbde74954cc24e5d571a5d004a9d00c24" dependencies = [ - "bitflags 2.4.1", + "bitflags 2.6.0", "cfg-if 1.0.0", "concurrent-queue", "hermit-abi 0.3.9", @@ -2625,7 +2634,7 @@ version = "0.38.32" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "65e04861e65f21776e67888bfbea442b3642beaa0138fdb1dd7a84a52dffdb89" dependencies = [ - "bitflags 2.4.1", + "bitflags 2.6.0", "errno 0.3.8", "libc", "linux-raw-sys", @@ -3092,7 +3101,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "d87688750eada7aef6d4a0b82979148980c67aca111f62f4bef82827276f3fc5" dependencies = [ "arrayvec 0.7.4", - "bitflags 2.4.1", + "bitflags 2.6.0", "log", ] @@ -3347,7 +3356,7 @@ checksum = "3528ecfd12c466c6f163363caf2d02a71161dd5e1cc6ae7b34207ea2d42d81ed" name = "twizzler-abi" version = "0.1.0" dependencies = [ - "bitflags 2.4.1", + "bitflags 2.6.0", "bitset-core", "cfg-if 1.0.0", "compiler_builtins", @@ -3380,7 +3389,7 @@ name = "twizzler-driver" version = "0.1.0" dependencies = [ "async-trait", - "bitflags 2.4.1", + "bitflags 2.6.0", "futures", "twizzler-abi", "twizzler-async", @@ -3402,8 +3411,9 @@ dependencies = [ "aarch64-cpu", "acpi", "addr2line 0.16.0", + "arm-gic", "backtracer_core", - "bitflags 2.4.1", + "bitflags 2.6.0", "fdt", "fixedbitset", "hex-literal", @@ -3439,7 +3449,7 @@ version = "0.0.0" name = "twizzler-net" version = "0.1.0" dependencies = [ - "bitflags 2.4.1", + "bitflags 2.6.0", "twizzler-abi", "twizzler-async", "twizzler-object", @@ -3450,7 +3460,7 @@ dependencies = [ name = "twizzler-object" version = "0.1.0" dependencies = [ - "bitflags 2.4.1", + "bitflags 2.6.0", "lazy_static", "twizzler-abi", "twizzler-runtime-api", @@ -3470,7 +3480,7 @@ dependencies = [ name = "twizzler-queue-raw" version = "0.1.0" dependencies = [ - "bitflags 2.4.1", + "bitflags 2.6.0", "twizzler-abi", ] @@ -3478,7 +3488,7 @@ dependencies = [ name = "twizzler-runtime-api" version = "0.1.0" dependencies = [ - "bitflags 2.4.1", + "bitflags 2.6.0", "compiler_builtins", "rustc-std-workspace-core", ] @@ -3488,7 +3498,7 @@ name = "twz-rt" version = "0.1.0" dependencies = [ "atomic", - "bitflags 2.4.1", + "bitflags 2.6.0", "dynlink", "elf", "lazy_static", @@ -4046,6 +4056,7 @@ dependencies = [ "futures", "futures-util", "guess_host_triple", + "home", "indicatif", "ouroboros", "reqwest", diff --git a/src/kernel/src/machine/arm/common/gicv3.rs b/src/kernel/src/machine/arm/common/gicv3.rs index 5b2a233f..062f9f72 100644 --- a/src/kernel/src/machine/arm/common/gicv3.rs +++ b/src/kernel/src/machine/arm/common/gicv3.rs @@ -5,16 +5,15 @@ /// /// A summary of its functionality can be found here: /// https://developer.arm.com/documentation/198123/0302/ -/// -/// A driver interface for the GICv3 - -use arm_gic::gicv3::{GicV3, IntId}; +use arm_gic::gicv3::{GicV3, IntId, SgiTarget}; -use crate::{memory::VirtAddr, spinlock::Spinlock}; +use crate::{current_processor, interrupt::Destination, memory::VirtAddr, spinlock::Spinlock}; /// A representation of the Generic Interrupt Controller (GIC) v3 pub struct GICv3 { global: Spinlock, + distr: VirtAddr, + redist: VirtAddr, } /// This wrapper is needed because the type `GicV3` is not @@ -34,11 +33,18 @@ impl GICv3 { // The highest interrupt priority. Lower means higher priority. const HIGHEST_PRIORITY: u8 = 0; + // used by generic kernel interrupt code + pub const MIN_VECTOR: usize = 0; // *GICD::SGI_ID_RANGE.start() as usize; + pub const MAX_VECTOR: usize = 15; //*GICD::SGI_ID_RANGE.end() as usize; + pub const NUM_VECTORS: usize = 16; + pub fn new(distr_base: VirtAddr, redist_base: VirtAddr) -> Self { unsafe { let gic_instance = GicV3::new(distr_base.as_mut_ptr(), redist_base.as_mut_ptr()); Self { global: Spinlock::new(Gicv3Wrapper(gic_instance)), + distr: distr_base, + redist: redist_base, } } } @@ -98,40 +104,173 @@ impl GICv3 { } /// Returns the pending interrupt ID from the controller, and - /// acknowledges the interrupt. - pub fn pending_interrupt(&self) -> u32 { + /// acknowledges the interrupt. Possibly returing the core ID + /// for an SW-generated interrupt. + pub fn pending_interrupt(&self) -> (u32, Option) { + // GICv2: the IAR register contains the CPUID let int_id = GicV3::get_and_acknowledge_interrupt(); - int_id.expect("failed to retrieve interrupt ID").into() + // handler must read one of the Interrupt Acknowledge Registers (IARs) to get the INTID + // multiple IAR's, IAR1 Used to acknowledge Group 1 interrupts + ( + int_id.expect("failed to retrieve interrupt ID").into(), + None, + ) } /// Signal the controller that we have serviced the interrupt - pub fn finish_active_interrupt(&self, int_id: u32) { + pub fn finish_active_interrupt(&self, int_id: u32, _core: Option) { GicV3::end_interrupt(u32_to_int_id(int_id)) + // software must inform the interrupt controller: priority drop, and deactivation + // ICC_CTLR_ELn.EOImode = 0 means that ... + // - write to ICC_EOIR0_EL1 does deactivation and priority drop + } + + /// Send a software generated interrupt to another core + pub fn send_interrupt(&self, int_id: u32, dest: Destination) { + // SGI is generated by writing to special registers in CPU interface + // ICC_SGI1R_EL1 - current security state for PE + + // SGI's range from 0-15 + if int_id >= PPI_START { + return; + } + + fn sgi_target_list(core: u32) -> SgiTarget { + use arm64::registers::MPIDR_EL1; + use registers::{interfaces::Readable, registers::InMemoryRegister}; + + let mpidr: InMemoryRegister = { + let core = crate::processor::get_processor(core); + InMemoryRegister::new(core.arch.mpidr) + }; + + // target list is 16 bits + // each bit correspongs to a PE in the cluster, + // affinity 0 value == bit number, so to send an SGI to core 0, + // set bit 0 high + let target_list = 1 << mpidr.read(MPIDR_EL1::Aff0); + + SgiTarget::List { + affinity3: mpidr.read(MPIDR_EL1::Aff3) as u8, + affinity2: mpidr.read(MPIDR_EL1::Aff2) as u8, + affinity1: mpidr.read(MPIDR_EL1::Aff1) as u8, + target_list, + } + } + + match dest { + Destination::Single(core) => { + GicV3::send_sgi(u32_to_int_id(int_id), sgi_target_list(core)) + } + Destination::AllButSelf => { + // IRM bit controls if SGI is routed to all but self, or single + // the GICv3 can only send interrupts to a single PE or all but self + let all = SgiTarget::All; + + GicV3::send_sgi(u32_to_int_id(int_id), all) + } + Destination::Bsp | Destination::LowestPriority => { + let bsp_core = current_processor().bsp_id(); + GicV3::send_sgi(u32_to_int_id(int_id), sgi_target_list(bsp_core)) + } + _ => unimplemented!("unsupported SGI destination: {:?}", dest), + }; + } + + /// Check if the interrupt is still pending. + pub fn is_interrupt_pending(&self, _int_id: u32, dest: Destination) -> bool { + // Checking the state of individual INTIDs + // Distributor provides registers for state of each SPI. + // Redistributors provide registers for state of PPIs and SGIs + + // separate registers to report the active state and the pending state + + match dest { + _ => unimplemented!("unsupported SGI destination: {:?}", dest), + } } /// Print the configuration of the GIC pub fn print_config(&self) { - todo!() + emerglogln!("[gicv3] config"); + unsafe { + // is the controller enabled? + let ctlr = read_reg(self.distr.as_ptr(), 0x0); + emerglogln!("[gicv3] GICD_CTLR: {:#x}", ctlr); + emerglogln!("\tEnableGrp: {}", get_bit(ctlr, 0)); + emerglogln!("\tEnableGrp1NS: {}", get_bit(ctlr, 1)); + emerglogln!("\tEnableGrp1S: {}", get_bit(ctlr, 2)); + emerglogln!("\tARE_S: {}", get_bit(ctlr, 4)); + emerglogln!("\tARE_NS: {}", get_bit(ctlr, 5)); + emerglogln!("\tDS: {}", get_bit(ctlr, 6)); + let mut icc: u64; + core::arch::asm!("mrs {}, icc_igrpen0_el1", out(reg) icc); + emerglogln!("[gicv3] ICC_IGRPEN0_EL1: {}", get_bit(icc as u32, 0)); + core::arch::asm!("mrs {}, icc_igrpen1_el1", out(reg) icc); + emerglogln!("[gicv3] ICC_IGRPEN1_EL1: {}", get_bit(icc as u32, 0)); + // how many interrupt numbers?? + + // which interrupts are enabled? + emerglogln!("[gicv3] SGI/PPI enable"); + let renable = read_reg(self.redist.as_ptr(), 0x0100); + // this covers SGIs and PPIs. only makes sense with affinity routing. + emerglogln!("[gicv3] GICR_ISENABLER0: {:#x}", renable); + + // let denable = read_reg(self.distr.as_ptr(), 0x0100); + emerglogln!( + "[gicv3] GICD_ISENABLER0: {:#x}", + read_reg(self.distr.as_ptr(), 0x0100) + ); + emerglogln!( + "[gicv3] GICD_ISENABLER1: {:#x}", + read_reg(self.distr.as_ptr(), 0x0100 + 4 * 1) + ); + // emerglogln!("[gicv3] GICD_ISENABLER1: {:#x}", denable); + // emerglogln!("[gicv3] GICD_ISENABLER: {:#x}", denable); + // emerglogln!("[gicv3] GICD_ISENABLER: {:#x}", denable); + } } } -/// The ID of the first Software Generated Interrupt. +fn get_bit(reg: u32, bit: usize) -> u32 { + (reg >> bit) & 0x1 +} + +/// Write a value to a single register. Registers are 32-bits wide. +unsafe fn write_reg(base: *mut u32, reg_off: usize, value: u32) { + // let reg = (base + register as usize) as *mut u32; + let reg = (base as *mut u8).add(reg_off) as *mut u32; + reg.write_volatile(value as u32) +} + +/// Read a value to a single register. Registers are 32-bits wide. +unsafe fn read_reg(base: *const u32, reg_off: usize) -> u32 { + // let reg = (self.base + register as usize) as *const u32; + let reg = (base as *const u8).add(reg_off) as *const u32; + reg.read_volatile() +} + +// The ranges for the Interrupt IDs are derived from table 2-1. + +/// The ID of the first Software Generated Interrupt. SGI ranges from 0-15. const SGI_START: u32 = 0; -/// The ID of the first Private Peripheral Interrupt. +/// The ID of the first Private Peripheral Interrupt. PPI ranges from 16-31. +/// Extended PPI range from 1056-1119. const PPI_START: u32 = 16; -/// The ID of the first Shared Peripheral Interrupt. +/// The ID of the first Shared Peripheral Interrupt. SPI ranges from 32-1019. +/// Extended SPI range from 4096-5119. const SPI_START: u32 = 32; -/// The first special interrupt ID. +/// The first special interrupt ID. Special interrupt numbers range from 1020-1023. const SPECIAL_START: u32 = 1020; +// TODO: is this mapping off?? fn u32_to_int_id(value: u32) -> IntId { - logln!("[debug] setting int id: {}", value); if value < PPI_START { - IntId::sgi(PPI_START - value) + IntId::sgi(value) } else if value < SPI_START { - IntId::ppi(SPI_START - value) + IntId::ppi(value - PPI_START) } else if value < SPECIAL_START { - IntId::spi(SPECIAL_START - value) + IntId::spi(value - SPI_START) } else { panic!("invalid interrupt id: {}", value); } diff --git a/src/kernel/src/machine/arm/common/mod.rs b/src/kernel/src/machine/arm/common/mod.rs index 80d5b81d..976025a9 100644 --- a/src/kernel/src/machine/arm/common/mod.rs +++ b/src/kernel/src/machine/arm/common/mod.rs @@ -1,5 +1,6 @@ pub mod boot; pub mod gicv2; +#[cfg(machine = "morello")] pub mod gicv3; pub mod mmio; pub mod uart; diff --git a/src/kernel/src/machine/arm/morello/info.rs b/src/kernel/src/machine/arm/morello/info.rs index c57fb2b5..1fc9dda6 100644 --- a/src/kernel/src/machine/arm/morello/info.rs +++ b/src/kernel/src/machine/arm/morello/info.rs @@ -107,33 +107,6 @@ pub fn get_uart_interrupt_num() -> Option { None } -// return the mmio address info for the distributor and cpu interfaces -// for a gicv2 interrupt controller -pub fn get_gicv2_info() -> (MmioInfo, MmioInfo) { - let mut gicd_mmio = MmioInfo { - length: 0, - cache_type: CacheType::MemoryMappedIO, - info: 0, - }; - let mut gicc_mmio = MmioInfo { - length: 0, - cache_type: CacheType::MemoryMappedIO, - info: 0, - }; - if let Some(gic) = devicetree().find_node("/intc") { - let mut mmio_regs = gic.reg().unwrap(); - // get distributor mmio regs - let regs = mmio_regs.next().unwrap(); - gicd_mmio.info = regs.starting_address as u64; - gicd_mmio.length = regs.size.unwrap() as u64; - // get local cpu interface regs - let regs = mmio_regs.next().unwrap(); - gicc_mmio.info = regs.starting_address as u64; - gicc_mmio.length = regs.size.unwrap() as u64; - } - (gicd_mmio, gicc_mmio) -} - /// Return the MMIO info for the GICv3 registers pub fn get_gicv3_info() -> (MmioInfo, MmioInfo) { let gicd_mmio = MmioInfo { diff --git a/src/kernel/src/machine/arm/morello/interrupt.rs b/src/kernel/src/machine/arm/morello/interrupt.rs index b404a911..e23ec127 100644 --- a/src/kernel/src/machine/arm/morello/interrupt.rs +++ b/src/kernel/src/machine/arm/morello/interrupt.rs @@ -2,6 +2,11 @@ use lazy_static::lazy_static; use super::super::common::gicv3::GICv3; +// used by generic kernel interrupt code +pub const MIN_VECTOR: usize = GICv3::MIN_VECTOR; +pub const MAX_VECTOR: usize = GICv3::MAX_VECTOR; +pub const NUM_VECTORS: usize = GICv3::NUM_VECTORS; + lazy_static! { /// System-wide reference to the interrupt controller pub static ref INTERRUPT_CONTROLLER: GICv3 = { From 9daaadfb704355f1fc544a44b4121eedc8d8d2eb Mon Sep 17 00:00:00 2001 From: Allen Aboytes Date: Thu, 15 Aug 2024 17:23:33 -0700 Subject: [PATCH 6/6] Cleanup and comments --- src/kernel/src/machine/arm/common/gicv3.rs | 4 ---- src/kernel/src/machine/arm/morello/info.rs | 3 ++- 2 files changed, 2 insertions(+), 5 deletions(-) diff --git a/src/kernel/src/machine/arm/common/gicv3.rs b/src/kernel/src/machine/arm/common/gicv3.rs index 062f9f72..08877a48 100644 --- a/src/kernel/src/machine/arm/common/gicv3.rs +++ b/src/kernel/src/machine/arm/common/gicv3.rs @@ -225,9 +225,6 @@ impl GICv3 { "[gicv3] GICD_ISENABLER1: {:#x}", read_reg(self.distr.as_ptr(), 0x0100 + 4 * 1) ); - // emerglogln!("[gicv3] GICD_ISENABLER1: {:#x}", denable); - // emerglogln!("[gicv3] GICD_ISENABLER: {:#x}", denable); - // emerglogln!("[gicv3] GICD_ISENABLER: {:#x}", denable); } } } @@ -263,7 +260,6 @@ const SPI_START: u32 = 32; /// The first special interrupt ID. Special interrupt numbers range from 1020-1023. const SPECIAL_START: u32 = 1020; -// TODO: is this mapping off?? fn u32_to_int_id(value: u32) -> IntId { if value < PPI_START { IntId::sgi(value) diff --git a/src/kernel/src/machine/arm/morello/info.rs b/src/kernel/src/machine/arm/morello/info.rs index 1fc9dda6..87ef372a 100644 --- a/src/kernel/src/machine/arm/morello/info.rs +++ b/src/kernel/src/machine/arm/morello/info.rs @@ -19,7 +19,8 @@ pub fn init(boot_info: &B) { bootloader_dtb_addr } }; - // should not fail, but it might ... + // this should not fail, but it might due a bad magic value + // in the FDT header or the fact that a NULL pointer is passed in. unsafe { Fdt::from_ptr(dtb.as_ptr()).expect("invalid DTB file, cannot boot") } }); }