From ac38164856f61101885f960273f31f8e982f89d1 Mon Sep 17 00:00:00 2001 From: Stefan Butz Date: Wed, 15 Jul 2026 19:16:19 +0200 Subject: [PATCH 1/2] riscv64: Reduce unsafe code in devicetree --- src/arch/riscv64/kernel/devicetree.rs | 311 +++++++++++++------------- 1 file changed, 154 insertions(+), 157 deletions(-) diff --git a/src/arch/riscv64/kernel/devicetree.rs b/src/arch/riscv64/kernel/devicetree.rs index 4c1f3905ee..5b8b99628f 100644 --- a/src/arch/riscv64/kernel/devicetree.rs +++ b/src/arch/riscv64/kernel/devicetree.rs @@ -109,179 +109,176 @@ pub fn init_drivers(handlers: &mut InterruptHandlerMap) { if let Some(fdt) = env::fdt() { debug!("Init drivers using devicetree"); - unsafe { - // Init PLIC first - if let Some(plic_node) = fdt.find_compatible(&["sifive,plic-1.0.0"]) { - debug!("Found interrupt controller"); - let plic_region = plic_node - .reg() - .expect("Reg property for PLIC not found in FDT") - .next() - .unwrap(); - - let plic_region_start = PhysAddr::from(plic_region.starting_address.addr()); - debug!( - "Init PLIC at {:p}, size: {:x}", - plic_region_start, - plic_region.size.unwrap() - ); - assert!( - plic_region.size.unwrap() - < usize::try_from(paging::HugePageSize::SIZE).unwrap() - ); - - paging::identity_map::(plic_region_start); - - // TODO: Determine correct context via devicetree and allow more than one context - let context = match PLATFORM_MODEL { + // Init PLIC first + if let Some(plic_node) = fdt.find_compatible(&["sifive,plic-1.0.0"]) { + debug!("Found interrupt controller"); + let plic_region = plic_node + .reg() + .expect("Reg property for PLIC not found in FDT") + .next() + .unwrap(); + + let plic_region_start = PhysAddr::from(plic_region.starting_address.addr()); + debug!( + "Init PLIC at {:p}, size: {:x}", + plic_region_start, + plic_region.size.unwrap() + ); + assert!( + plic_region.size.unwrap() < usize::try_from(paging::HugePageSize::SIZE).unwrap() + ); + + paging::identity_map::(plic_region_start); + + // TODO: Determine correct context via devicetree and allow more than one context + let context = unsafe { + match PLATFORM_MODEL { Model::Virt | Model::Unknown => 1, Model::Fux40 => 2, - }; - init_plic(plic_region.starting_address, context); + } + }; + init_plic(plic_region.starting_address, context); + } + + // Init GEM + #[cfg(all(feature = "gem-net", not(feature = "pci")))] + if let Some(gem_node) = fdt.find_compatible(&["sifive,fu540-c000-gem"]) { + debug!("Found Ethernet controller"); + + let gem_region = gem_node + .reg() + .expect("reg property for GEM not found in FDT") + .next() + .unwrap(); + let irq = gem_node + .interrupts() + .expect("interrupts property for GEM not found in FDT") + .next() + .unwrap(); + let mac = gem_node + .property("local-mac-address") + .expect("local-mac-address property for GEM not found in FDT") + .value; + debug!("Local MAC address: {mac:x?}"); + let mut phy_addr = u32::MAX; + + let phy_node = gem_node + .children() + .next() + .expect("GEM node has no child node (i. e. ethernet-phy)"); + if phy_node.name.contains("ethernet-phy") { + phy_addr = phy_node + .property("reg") + .expect("reg property for ethernet-phy not found in FDT") + .as_usize() + .unwrap() as u32; + } else { + warn!("Expected ethernet-phy node, found something else"); } - // Init GEM - #[cfg(all(feature = "gem-net", not(feature = "pci")))] - if let Some(gem_node) = fdt.find_compatible(&["sifive,fu540-c000-gem"]) { - debug!("Found Ethernet controller"); - - let gem_region = gem_node - .reg() - .expect("reg property for GEM not found in FDT") - .next() - .unwrap(); - let irq = gem_node - .interrupts() - .expect("interrupts property for GEM not found in FDT") - .next() - .unwrap(); - let mac = gem_node - .property("local-mac-address") - .expect("local-mac-address property for GEM not found in FDT") - .value; - debug!("Local MAC address: {mac:x?}"); - let mut phy_addr = u32::MAX; - - let phy_node = gem_node - .children() - .next() - .expect("GEM node has no child node (i. e. ethernet-phy)"); - if phy_node.name.contains("ethernet-phy") { - phy_addr = phy_node - .property("reg") - .expect("reg property for ethernet-phy not found in FDT") - .as_usize() - .unwrap() as u32; - } else { - warn!("Expected ethernet-phy node, found something else"); - } + let gem_region_start = PhysAddr::from(gem_region.starting_address.expose_provenance()); + debug!("Init GEM at {gem_region_start:p}, irq: {irq}, phy_addr: {phy_addr}"); + assert!( + gem_region.size.unwrap() < usize::try_from(paging::HugePageSize::SIZE).unwrap() + ); + paging::identity_map::(gem_region_start); + match gem::init_device( + VirtAddr::new(gem_region_start.as_u64()), + irq.try_into().unwrap(), + phy_addr, + <[u8; 6]>::try_from(mac).expect("MAC with invalid length"), + handlers, + ) { + Ok(drv) => *NETWORK_DEVICE.lock() = Some(drv), + Err(err) => error!("Could not initialize GEM driver: {err}"), + } + } - let gem_region_start = - PhysAddr::from(gem_region.starting_address.expose_provenance()); - debug!("Init GEM at {gem_region_start:p}, irq: {irq}, phy_addr: {phy_addr}"); - assert!( - gem_region.size.unwrap() < usize::try_from(paging::HugePageSize::SIZE).unwrap() - ); - paging::identity_map::(gem_region_start); - match gem::init_device( - VirtAddr::new(gem_region_start.as_u64()), - irq.try_into().unwrap(), - phy_addr, - <[u8; 6]>::try_from(mac).expect("MAC with invalid length"), - handlers, - ) { - Ok(drv) => *NETWORK_DEVICE.lock() = Some(drv), - Err(err) => error!("Could not initialize GEM driver: {err}"), - } + // Init virtio-mmio + #[cfg(all(feature = "virtio", not(feature = "pci")))] + if let Some(virtio_node) = fdt.find_compatible(&["virtio,mmio"]) { + debug!("Found virtio mmio device"); + let virtio_region = virtio_node + .reg() + .expect("reg property for virtio mmio not found in FDT") + .next() + .unwrap(); + let irq = virtio_node + .interrupts() + .expect("interrupts property for virtio mmio not found in FDT") + .next() + .unwrap(); + + let virtio_region_start = + PhysAddr::from(virtio_region.starting_address.expose_provenance()); + + debug!("Init virtio_mmio at {virtio_region_start:p}, irq: {irq}"); + assert!( + virtio_region.size.unwrap() < usize::try_from(paging::HugePageSize::SIZE).unwrap() + ); + paging::identity_map::(virtio_region_start); + + // Verify the first register value to find out if this is really an MMIO magic-value. + let ptr = virtio_region.starting_address as *mut DeviceRegisters; + let mmio = unsafe { VolatileRef::new(NonNull::new(ptr).unwrap()) }; + + let magic = mmio.as_ptr().magic_value().read().to_ne(); + let version = mmio.as_ptr().version().read().to_ne(); + + const MMIO_MAGIC_VALUE: u32 = 0x7472_6976; + if magic != MMIO_MAGIC_VALUE { + error!("It's not a MMIO-device at {mmio:p}"); } - // Init virtio-mmio - #[cfg(all(feature = "virtio", not(feature = "pci")))] - if let Some(virtio_node) = fdt.find_compatible(&["virtio,mmio"]) { - debug!("Found virtio mmio device"); - let virtio_region = virtio_node - .reg() - .expect("reg property for virtio mmio not found in FDT") - .next() - .unwrap(); - let irq = virtio_node - .interrupts() - .expect("interrupts property for virtio mmio not found in FDT") - .next() - .unwrap(); - - let virtio_region_start = - PhysAddr::from(virtio_region.starting_address.expose_provenance()); - - debug!("Init virtio_mmio at {virtio_region_start:p}, irq: {irq}"); - assert!( - virtio_region.size.unwrap() - < usize::try_from(paging::HugePageSize::SIZE).unwrap() - ); - paging::identity_map::(virtio_region_start); - - // Verify the first register value to find out if this is really an MMIO magic-value. - let ptr = virtio_region.starting_address as *mut DeviceRegisters; - let mmio = VolatileRef::new(NonNull::new(ptr).unwrap()); - - let magic = mmio.as_ptr().magic_value().read().to_ne(); - let version = mmio.as_ptr().version().read().to_ne(); - - const MMIO_MAGIC_VALUE: u32 = 0x7472_6976; - if magic != MMIO_MAGIC_VALUE { - error!("It's not a MMIO-device at {mmio:p}"); - } + if version != 2 { + warn!("Found a legacy device, which isn't supported"); + return; + } - if version != 2 { - warn!("Found a legacy device, which isn't supported"); - return; - } + // We found a MMIO-device (whose 512-bit address in this structure). + trace!("Found a MMIO-device at {mmio:p}"); - // We found a MMIO-device (whose 512-bit address in this structure). - trace!("Found a MMIO-device at {mmio:p}"); + // Verify the device-ID to find the network card + let id = mmio.as_ptr().device_id().read(); - // Verify the device-ID to find the network card - let id = mmio.as_ptr().device_id().read(); + if cfg!(debug_assertions) { + use free_list::PageRange; - if cfg!(debug_assertions) { - use free_list::PageRange; + use crate::mm::{FrameAlloc, PageRangeAllocator}; - use crate::mm::{FrameAlloc, PageRangeAllocator}; + let start = virtio_region.starting_address.addr(); + let len = virtio_region.size.unwrap(); + let frame_range = PageRange::from_start_len(start, len).unwrap(); - let start = virtio_region.starting_address.addr(); - let len = virtio_region.size.unwrap(); - let frame_range = PageRange::from_start_len(start, len).unwrap(); + FrameAlloc::allocate_at(frame_range).unwrap_err(); + } - FrameAlloc::allocate_at(frame_range).unwrap_err(); - } + debug!("Found virtio {id:?} at {mmio:p}"); - debug!("Found virtio {id:?} at {mmio:p}"); - - match mmio_virtio::init_device(mmio, irq.try_into().unwrap(), handlers) { - #[cfg(feature = "virtio-console")] - Ok(VirtioDriver::Console(drv)) => { - register_driver(MmioDriver::VirtioConsole( - hermit_sync::InterruptSpinMutex::new(*drv), - )); - } - #[cfg(feature = "virtio-fs")] - Ok(VirtioDriver::Fs(drv)) => { - register_driver(MmioDriver::VirtioFs( - hermit_sync::InterruptSpinMutex::new(*drv), - )); - } - #[cfg(feature = "virtio-net")] - Ok(VirtioDriver::Net(drv)) => { - *NETWORK_DEVICE.lock() = Some(*drv); - } - #[cfg(feature = "virtio-vsock")] - Ok(VirtioDriver::Vsock(drv)) => { - register_driver(MmioDriver::VirtioVsock( - hermit_sync::InterruptSpinMutex::new(*drv), - )); - } - Err(err) => error!("Could not initialize virtio-mmio device: {err}"), + match mmio_virtio::init_device(mmio, irq.try_into().unwrap(), handlers) { + #[cfg(feature = "virtio-console")] + Ok(VirtioDriver::Console(drv)) => { + register_driver(MmioDriver::VirtioConsole( + hermit_sync::InterruptSpinMutex::new(*drv), + )); + } + #[cfg(feature = "virtio-fs")] + Ok(VirtioDriver::Fs(drv)) => { + register_driver(MmioDriver::VirtioFs(hermit_sync::InterruptSpinMutex::new( + *drv, + ))); + } + #[cfg(feature = "virtio-net")] + Ok(VirtioDriver::Net(drv)) => { + *NETWORK_DEVICE.lock() = Some(*drv); + } + #[cfg(feature = "virtio-vsock")] + Ok(VirtioDriver::Vsock(drv)) => { + register_driver(MmioDriver::VirtioVsock( + hermit_sync::InterruptSpinMutex::new(*drv), + )); } + Err(err) => error!("Could not initialize virtio-mmio device: {err}"), } } } From 0d3c76cf8afe95e7fc8c9d6d1209695def8cf74a Mon Sep 17 00:00:00 2001 From: Stefan Butz Date: Sat, 25 Jul 2026 11:25:45 +0200 Subject: [PATCH 2/2] riscv64: Support multiple virtio mmio devices --- src/arch/riscv64/kernel/devicetree.rs | 9 ++++++++- 1 file changed, 8 insertions(+), 1 deletion(-) diff --git a/src/arch/riscv64/kernel/devicetree.rs b/src/arch/riscv64/kernel/devicetree.rs index 5b8b99628f..c33e507924 100644 --- a/src/arch/riscv64/kernel/devicetree.rs +++ b/src/arch/riscv64/kernel/devicetree.rs @@ -196,8 +196,15 @@ pub fn init_drivers(handlers: &mut InterruptHandlerMap) { // Init virtio-mmio #[cfg(all(feature = "virtio", not(feature = "pci")))] - if let Some(virtio_node) = fdt.find_compatible(&["virtio,mmio"]) { + for virtio_node in fdt.all_nodes() { + let is_virtio_mmio = virtio_node + .compatible() + .is_some_and(|c| c.all().any(|x| x == "virtio,mmio")); + if !is_virtio_mmio { + continue; + } debug!("Found virtio mmio device"); + let virtio_region = virtio_node .reg() .expect("reg property for virtio mmio not found in FDT")