diff --git a/guest_image/guest.dts b/guest_image/guest.dts index 0b5a40f..61a4f79 100644 --- a/guest_image/guest.dts +++ b/guest_image/guest.dts @@ -1432,7 +1432,7 @@ }; chosen { stdout-path = "serial0"; - bootargs = "earlycon console=ttyS0,115200 rootwait rw locale.LANG=en_US.UTF-8"; + bootargs = "console=ttyS0,115200 root=PARTUUID=b0f77ad6-36cd-4a99-a8c0-31d73649aa09 rootfstype=ext4 rootwait rw earlycon initcall_debug loglevel=8 debug earlyprintk=sbi selinux=0 LANG=en_US.UTF-8 single "; linux,initrd-start = <0x0 0xafae2000>; linux,initrd-end = <0x0 0xaffffe22>; }; diff --git a/hikami_core/src/device.rs b/hikami_core/src/device.rs index 2264d23..918fd8f 100644 --- a/hikami_core/src/device.rs +++ b/hikami_core/src/device.rs @@ -1,5 +1,6 @@ //! Devices data +pub mod aclint; mod axi_sdc; pub mod clint; mod initrd; @@ -249,7 +250,10 @@ pub struct Devices { pub plic: plic::Plic, /// clint: Core Local INTerrupt - pub clint: clint::Clint, + pub clint: Option, + + /// aclint: Advanced Core Local INTerrupt + pub aclint: Option, /// RTC: Real Time Clock. pub rtc: Option, @@ -293,9 +297,14 @@ impl Devices { clint: clint::Clint::try_new( root_page_table_addr, &device_tree, - &["sifive,clint0", "riscv,clint0", "thead,c900-aclint-mtimer"], - ) - .expect("clint is not found in fdt"), + &["sifive,clint0", "riscv,clint0"], + ), + aclint: aclint::Aclint::try_new_aclint( + root_page_table_addr, + &device_tree, + &["thead,c900-aclint-mswi"], + &["thead,c900-aclint-mtimer"], + ), rtc: rtc::Rtc::try_new(root_page_table_addr, &device_tree, &["google,goldfish-rtc"]), pci: pci::Pci::try_new( root_page_table_addr, diff --git a/hikami_core/src/device/aclint.rs b/hikami_core/src/device/aclint.rs new file mode 100644 index 0000000..372e0d0 --- /dev/null +++ b/hikami_core/src/device/aclint.rs @@ -0,0 +1,148 @@ +//! ACLINT: *A*dvanced *C*ore *L*ocal *Int*errupt + +use super::{MmioDevice, PTE_FLAGS_FOR_DEVICE}; +use crate::memmap::{ + GuestPhysicalAddress, HostPhysicalAddress, MemoryMap, page_table::constants::PAGE_SIZE, +}; + +use alloc::vec::Vec; +use fdt::{Fdt, standard_nodes::MemoryRegion}; + +/// MSWI: Machine level SoftWare Interrupt device +/// +/// The MSWI device provides machine-level IPI functionality for a set of HARTs on a RISC-V platform. +/// It has an IPI register (MSIP) for each HART connected to the MSWI device. +#[derive(Debug)] +pub struct Mswi { + /// Memory maps for memory mapped register. + register_map_regions: Vec, +} +impl MmioDevice for Mswi { + fn try_new( + root_page_table_addr: HostPhysicalAddress, + device_tree: &Fdt, + compatibles: &[&str], + ) -> Option { + let clint_node = device_tree.find_compatible(compatibles)?; + let register_map_regions: Vec = clint_node.reg().unwrap().collect(); + + Self::create_page_table(root_page_table_addr, ®ister_map_regions, clint_node.name); + + Some(Mswi { + register_map_regions, + }) + } + + fn memmap(&self) -> Vec { + self.register_map_regions + .clone() + .into_iter() + .map(MemoryMap::from) + .collect() + } +} + +/// MTIMER: Machine level TIMER device +/// +/// The MTIMER device provides machine-level timer functionality for a set of HARTs on a RISC-V platform. +/// It has a single fixed-frequency monotonic time counter (MTIME) register and a time compare register (MTIMECMP) for each HART connected to the MTIMER device. +/// A MTIMER device not connected to any HART should only have a MTIME register and no MTIMECMP registers. +#[derive(Debug)] +pub struct Mtimer { + /// Memory maps for memory mapped register. + register_map_regions: Vec, +} +impl MmioDevice for Mtimer { + fn try_new( + root_page_table_addr: HostPhysicalAddress, + device_tree: &Fdt, + compatibles: &[&str], + ) -> Option { + let clint_node = device_tree.find_compatible(compatibles)?; + let register_map_regions: Vec = clint_node.reg().unwrap().collect(); + + Self::create_page_table(root_page_table_addr, ®ister_map_regions, clint_node.name); + + Some(Mtimer { + register_map_regions, + }) + } + + fn memmap(&self) -> Vec { + self.register_map_regions + .clone() + .into_iter() + .map(|region| { + let mut size = region.size.unwrap(); + let mut virt_start = GuestPhysicalAddress(region.starting_address as usize); + let mut phys_start = HostPhysicalAddress(region.starting_address as usize); + + // change memory map region if region size is less than the page size. + if phys_start % PAGE_SIZE != 0 && size < PAGE_SIZE { + size = PAGE_SIZE; + virt_start = virt_start - (virt_start % PAGE_SIZE); + phys_start = phys_start - (phys_start % PAGE_SIZE); + } + + MemoryMap::new( + virt_start..virt_start + size, + phys_start..phys_start + size, + &PTE_FLAGS_FOR_DEVICE, + ) + }) + .collect() + } +} + +#[allow(clippy::doc_markdown)] +/// ACLINT: Advanced Core Local INTerrupt +/// Local interrupt controller +#[derive(Debug)] +pub struct Aclint { + /// MSWI + mswi: Mswi, + /// MTIMER + mtimer: Mtimer, +} + +impl Aclint { + pub fn try_new_aclint( + root_page_table_addr: HostPhysicalAddress, + device_tree: &Fdt, + mswi_compatibles: &[&str], + mtimer_compatibles: &[&str], + ) -> Option { + let mswi_node = device_tree.find_compatible(mswi_compatibles)?; + let mtimer_node = device_tree.find_compatible(mtimer_compatibles)?; + let mut register_map_regions: Vec = mswi_node.reg().unwrap().collect(); + register_map_regions.append(&mut mtimer_node.reg().unwrap().collect()); + + Self::create_page_table( + root_page_table_addr, + ®ister_map_regions, + mtimer_node.name, + ); + + Some(Aclint { + mswi: Mswi::try_new(root_page_table_addr, device_tree, mswi_compatibles)?, + mtimer: Mtimer::try_new(root_page_table_addr, device_tree, mtimer_compatibles)?, + }) + } +} + +impl MmioDevice for Aclint { + fn try_new( + _root_page_table_addr: HostPhysicalAddress, + _device_tree: &Fdt, + _compatibles: &[&str], + ) -> Option { + unreachable!("Use `Aclint::try_new_aclint` instead"); + } + + fn memmap(&self) -> Vec { + let mut memory_maps = self.mswi.memmap(); + memory_maps.append(&mut self.mtimer.memmap()); + + memory_maps + } +}