Skip to content

Commit

Permalink
use linux arguments to determine mmio address of the NIC
Browse files Browse the repository at this point in the history
Firecracker parse the linux argument virtio_mmio.device to the
kernel, which specifies the mmio addresses for VirtIO devices.
This patch uses these arguments to find the network interface.wq:
  • Loading branch information
stlankes committed Nov 2, 2023
1 parent 4f6142f commit 17a72fb
Show file tree
Hide file tree
Showing 2 changed files with 103 additions and 3 deletions.
101 changes: 98 additions & 3 deletions src/arch/x86_64/kernel/mmio.rs
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
use alloc::string::String;
use alloc::vec::Vec;
use core::{ptr, str};

Expand All @@ -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;

Expand All @@ -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::<u32>().unwrap();

trace!(
"try to detect MMIO device at physical address {:#X}",
current_address
);

let mut flags = PageTableEntryFlags::empty();
flags.normal().writable();
paging::map::<BasePageSize>(
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::<MmioRegisterLayout>(
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 =
Expand Down Expand Up @@ -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);
Expand Down
5 changes: 5 additions & 0 deletions src/env.rs
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,7 @@ struct Cli {
freq: Option<u16>,
env_vars: HashMap<String, String, RandomState>,
args: Vec<String>,
#[cfg(all(not(feature = "pci"), any(feature = "tcp", feature = "udp")))]
mmio: Vec<String>,
}

Expand All @@ -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();
Expand All @@ -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());
Expand Down Expand Up @@ -92,6 +95,7 @@ impl Default for Cli {
freq,
env_vars,
args,
#[cfg(all(not(feature = "pci"), any(feature = "tcp", feature = "udp")))]
mmio,
}
}
Expand All @@ -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()
}

0 comments on commit 17a72fb

Please sign in to comment.