use alloc::vec::Vec;
use core::str;
use align_address::Align;
use hermit_sync::{without_interrupts, InterruptTicketMutex};
use crate::arch::x86_64::mm::paging::{
BasePageSize, PageSize, PageTableEntryFlags, PageTableEntryFlagsExt,
};
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};
pub const MAGIC_VALUE: u32 = 0x74726976;
pub const MMIO_START: usize = 0x00000000c0000000;
pub const MMIO_END: usize = 0x00000000c0000fff;
const IRQ_NUMBER: u8 = 12;
static mut MMIO_DRIVERS: Vec<MmioDriver> = Vec::new();
pub(crate) enum MmioDriver {
VirtioNet(InterruptTicketMutex<VirtioNetDriver>),
}
impl MmioDriver {
#[allow(unreachable_patterns)]
fn get_network_driver(&self) -> Option<&InterruptTicketMutex<VirtioNetDriver>> {
match self {
Self::VirtioNet(drv) => Some(drv),
_ => None,
}
}
}
pub fn detect_network() -> Result<&'static mut MmioRegisterLayout, &'static str> {
let mut current_page = 0;
let virtual_address =
crate::arch::mm::virtualmem::allocate(BasePageSize::SIZE as usize).unwrap();
for current_address in (MMIO_START..MMIO_END).step_by(512) {
trace!(
"try to detect MMIO device at physical address {:#X}",
current_address
);
if current_address / BasePageSize::SIZE as usize > current_page {
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,
);
current_page = current_address / BasePageSize::SIZE as usize;
}
let mmio = unsafe {
&mut *((virtual_address.as_usize()
| (current_address & (BasePageSize::SIZE as usize - 1)))
as *mut MmioRegisterLayout)
};
let magic = mmio.get_magic_value();
let version = mmio.get_version();
if magic != MAGIC_VALUE {
trace!("It's not a MMIO-device at {:#X}", mmio as *const _ as usize);
continue;
}
if version != 2 {
trace!("Found a legacy device, which isn't supported");
continue;
}
trace!("Found a MMIO-device at {:#X}", mmio as *const _ as usize);
let id = mmio.get_device_id();
if id != DevId::VIRTIO_DEV_ID_NET {
trace!(
"It's not a network card at {:#X}",
mmio as *const _ as usize
);
continue;
}
info!("Found network card at {:#X}", mmio as *const _ as usize);
crate::arch::mm::physicalmem::reserve(
PhysAddr::from(current_address.align_down(BasePageSize::SIZE as usize)),
BasePageSize::SIZE as usize,
);
return Ok(mmio);
}
crate::arch::mm::virtualmem::deallocate(virtual_address, BasePageSize::SIZE as usize);
Err("Network card not found!")
}
pub(crate) fn register_driver(drv: MmioDriver) {
unsafe {
MMIO_DRIVERS.push(drv);
}
}
pub(crate) fn get_network_driver() -> Option<&'static InterruptTicketMutex<VirtioNetDriver>> {
unsafe { MMIO_DRIVERS.iter().find_map(|drv| drv.get_network_driver()) }
}
pub(crate) fn init_drivers() {
without_interrupts(|| {
if let Ok(mmio) = detect_network() {
warn!(
"Found MMIO device, but we guess the interrupt number {}!",
IRQ_NUMBER
);
if let Ok(VirtioDriver::Network(drv)) = mmio_virtio::init_device(mmio, IRQ_NUMBER) {
register_driver(MmioDriver::VirtioNet(InterruptTicketMutex::new(drv)))
}
} else {
warn!("Unable to find mmio device");
}
});
}