diff --git a/src/arch/x86_64/kernel/mmio.rs b/src/arch/x86_64/kernel/mmio.rs index 4aa9fa9ba4..003d5a4704 100644 --- a/src/arch/x86_64/kernel/mmio.rs +++ b/src/arch/x86_64/kernel/mmio.rs @@ -1,3 +1,4 @@ +use alloc::string::String; use alloc::vec::Vec; use core::{ptr, str}; @@ -11,6 +12,7 @@ use crate::arch::x86_64::mm::{paging, PhysAddr}; use crate::drivers::net::virtio_net::VirtioNetDriver; use crate::drivers::virtio::transport::mmio as mmio_virtio; use crate::drivers::virtio::transport::mmio::{DevId, MmioRegisterLayout, VirtioDriver}; +use crate::env; pub const MAGIC_VALUE: u32 = 0x74726976; @@ -34,9 +36,90 @@ impl MmioDriver { } } -/// Tries to find the network device within the specified address range. -/// Returns a reference to it within the Ok() if successful or an Err() on failure. -pub fn detect_network() -> Result<&'static mut MmioRegisterLayout, &'static str> { +fn check_linux_args( + linux_mmio: &'static [String], +) -> Result<&'static mut MmioRegisterLayout, &'static str> { + let virtual_address = + crate::arch::mm::virtualmem::allocate(BasePageSize::SIZE as usize).unwrap(); + + for arg in linux_mmio { + trace!("check linux parameter: {}", arg); + + match arg.trim().trim_matches(char::from(0)).strip_prefix("4K@") { + Some(arg) => { + let v: Vec<&str> = arg.trim().split(':').collect(); + let without_prefix = v[0].trim_start_matches("0x"); + let current_address = usize::from_str_radix(without_prefix, 16).unwrap(); + let irq: u32 = v[1].parse::().unwrap(); + + trace!( + "try to detect MMIO device at physical address {:#X}", + current_address + ); + + let mut flags = PageTableEntryFlags::empty(); + flags.normal().writable(); + paging::map::( + virtual_address, + PhysAddr::from(current_address.align_down(BasePageSize::SIZE as usize)), + 1, + flags, + ); + + // Verify the first register value to find out if this is really an MMIO magic-value. + let mmio = unsafe { + &mut *(ptr::from_exposed_addr_mut::( + virtual_address.as_usize() + | (current_address & (BasePageSize::SIZE as usize - 1)), + )) + }; + + let magic = mmio.get_magic_value(); + let version = mmio.get_version(); + + if magic != MAGIC_VALUE { + trace!("It's not a MMIO-device at {mmio:p}"); + continue; + } + + if version != 2 { + trace!("Found a legacy device, which isn't supported"); + continue; + } + + // 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.get_device_id(); + + if id != DevId::VIRTIO_DEV_ID_NET { + trace!("It's not a network card at {mmio:p}"); + continue; + } + + info!("Found network card at {mmio:p}, irq {}", irq); + + crate::arch::mm::physicalmem::reserve( + PhysAddr::from(current_address.align_down(BasePageSize::SIZE as usize)), + BasePageSize::SIZE as usize, + ); + + return Ok(mmio); + } + _ => { + warn!("Inavlid prefix in {}", arg); + } + } + } + + // frees obsolete virtual memory region for MMIO devices + crate::arch::mm::virtualmem::deallocate(virtual_address, BasePageSize::SIZE as usize); + + Err("Network card not found!") +} + +fn guess_mmio() -> Result<&'static mut MmioRegisterLayout, &'static str> { // Trigger page mapping in the first iteration! let mut current_page = 0; let virtual_address = @@ -112,6 +195,18 @@ pub fn detect_network() -> Result<&'static mut MmioRegisterLayout, &'static str> Err("Network card not found!") } +/// Tries to find the network device within the specified address range. +/// Returns a reference to it within the Ok() if successful or an Err() on failure. +pub fn detect_network() -> Result<&'static mut MmioRegisterLayout, &'static str> { + let linux_mmio = env::mmio(); + + if linux_mmio.len() > 0 { + check_linux_args(linux_mmio) + } else { + guess_mmio() + } +} + pub(crate) fn register_driver(drv: MmioDriver) { unsafe { MMIO_DRIVERS.push(drv); diff --git a/src/env.rs b/src/env.rs index 721149edb4..ff99cee5dc 100644 --- a/src/env.rs +++ b/src/env.rs @@ -26,6 +26,7 @@ struct Cli { freq: Option, env_vars: HashMap, args: Vec, + #[cfg(all(not(feature = "pci"), any(feature = "tcp", feature = "udp")))] mmio: Vec, } @@ -42,6 +43,7 @@ impl Default for Cli { RandomState::with_seeds(0, 0, 0, 0), ); let mut args = Vec::new(); + #[cfg(all(not(feature = "pci"), any(feature = "tcp", feature = "udp")))] let mut mmio = Vec::new(); let words = shell_words::split(kernel::args().unwrap_or_default()).unwrap(); @@ -54,6 +56,7 @@ impl Default for Cli { }) }; while let Some(word) = words.next() { + #[cfg(all(not(feature = "pci"), any(feature = "tcp", feature = "udp")))] if word.as_str().starts_with("virtio_mmio.device=") { let v: Vec<&str> = word.as_str().split('=').collect(); mmio.push(v[1].to_string()); @@ -92,6 +95,7 @@ impl Default for Cli { freq, env_vars, args, + #[cfg(all(not(feature = "pci"), any(feature = "tcp", feature = "udp")))] mmio, } } @@ -117,6 +121,7 @@ pub fn args() -> &'static [String] { } /// Returns the configuration of all mmio devices +#[cfg(all(not(feature = "pci"), any(feature = "tcp", feature = "udp")))] pub fn mmio() -> &'static [String] { CLI.get().unwrap().mmio.as_slice() }