diff --git a/Cargo.lock b/Cargo.lock index dfb6e6c7e..ce1fa111b 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -660,7 +660,6 @@ dependencies = [ "component", "jinux-frame", "jinux-util", - "jinux-virtio", "lazy_static", "log", "spin 0.9.8", @@ -717,7 +716,6 @@ dependencies = [ "jinux-frame", "jinux-rights", "jinux-util", - "jinux-virtio", "lazy_static", "log", "spin 0.9.8", @@ -728,12 +726,16 @@ dependencies = [ name = "jinux-network" version = "0.1.0" dependencies = [ + "align_ext", + "bitflags 1.3.2", + "bytes", "component", + "int-to-c-enum", "jinux-frame", "jinux-rights", "jinux-util", - "jinux-virtio", "log", + "pod", "ringbuf", "smoltcp", "spin 0.9.8", @@ -779,6 +781,7 @@ dependencies = [ "jinux-rights-proc", "jinux-time", "jinux-util", + "jinux-virtio", "keyable-arc", "lazy_static", "lending-iterator", @@ -828,13 +831,18 @@ dependencies = [ "bytes", "component", "int-to-c-enum", + "jinux-block", "jinux-frame", + "jinux-input", + "jinux-network", "jinux-rights", "jinux-util", "log", "pod", + "smoltcp", "spin 0.9.8", "typeflags-util", + "virtio-input-decoder", ] [[package]] diff --git a/framework/jinux-frame/src/bus/pci/capability/msix.rs b/framework/jinux-frame/src/bus/pci/capability/msix.rs index 6e61125fa..86c8c4fb7 100644 --- a/framework/jinux-frame/src/bus/pci/capability/msix.rs +++ b/framework/jinux-frame/src/bus/pci/capability/msix.rs @@ -6,7 +6,6 @@ use crate::{ common_device::PciCommonDevice, device_info::PciDeviceLocation, }, - sync::{SpinLock, SpinLockGuard}, trap::IrqAllocateHandle, vm::VmIo, }; @@ -25,20 +24,19 @@ pub struct CapabilityMsixData { pending_table_bar: Arc, table_offset: usize, pending_table_offset: usize, - irq_allocate_handles: SpinLock>>, + irqs: Vec>, } impl Clone for CapabilityMsixData { fn clone(&self) -> Self { - let handles = self.irq_allocate_handles.lock(); - let new_vec = handles.clone().to_vec(); + let new_vec = self.irqs.clone().to_vec(); Self { loc: self.loc.clone(), ptr: self.ptr.clone(), table_size: self.table_size.clone(), table_bar: self.table_bar.clone(), pending_table_bar: self.pending_table_bar.clone(), - irq_allocate_handles: SpinLock::new(new_vec), + irqs: new_vec, table_offset: self.table_offset, pending_table_offset: self.pending_table_offset, } @@ -122,7 +120,7 @@ impl CapabilityMsixData { table_size: (dev.location().read16(cap_ptr + 2) & 0b11_1111_1111) + 1, table_bar, pending_table_bar: pba_bar, - irq_allocate_handles: SpinLock::new(irq_allocate_handles), + irqs: irq_allocate_handles, table_offset: table_offset, pending_table_offset: pba_offset, } @@ -133,7 +131,7 @@ impl CapabilityMsixData { (self.loc.read16(self.ptr + 2) & 0b11_1111_1111) + 1 } - pub fn set_interrupt_vector(&self, handle: IrqAllocateHandle, index: u16) { + pub fn set_interrupt_vector(&mut self, handle: IrqAllocateHandle, index: u16) { if index >= self.table_size { return; } @@ -144,10 +142,7 @@ impl CapabilityMsixData { &(handle.num() as u32), ) .unwrap(); - let old_handles = core::mem::replace( - &mut self.irq_allocate_handles.lock()[index as usize], - Some(handle), - ); + let old_handles = core::mem::replace(&mut self.irqs[index as usize], Some(handle)); // Enable this msix vector self.table_bar .io_mem() @@ -155,8 +150,8 @@ impl CapabilityMsixData { .unwrap(); } - pub fn interrupt_handles(&self) -> SpinLockGuard<'_, Vec>> { - self.irq_allocate_handles.lock() + pub fn irq_mut(&mut self, index: usize) -> Option<&mut IrqAllocateHandle> { + self.irqs[index].as_mut() } } diff --git a/kernel/main.rs b/kernel/main.rs index 55aea1cc3..5f750438c 100644 --- a/kernel/main.rs +++ b/kernel/main.rs @@ -27,7 +27,7 @@ pub fn jinux_main() -> ! { fn panic(info: &PanicInfo) -> ! { use jinux_frame::{exit_qemu, QemuExitCode}; - println!("[panic]:{:?}", info); + println!("[panic]:{:#?}", info); jinux_frame::panic_handler(); exit_qemu(QemuExitCode::Failed); } diff --git a/services/comps/block/Cargo.toml b/services/comps/block/Cargo.toml index 9c239281a..99e0d2303 100644 --- a/services/comps/block/Cargo.toml +++ b/services/comps/block/Cargo.toml @@ -9,7 +9,6 @@ edition = "2021" bitflags = "1.3" spin = "0.9.4" jinux-frame = { path = "../../../framework/jinux-frame" } -jinux-virtio = { path = "../virtio" } jinux-util = { path = "../../libs/jinux-util" } component = { path = "../../libs/comp-sys/component" } log = "0.4" diff --git a/services/comps/block/src/lib.rs b/services/comps/block/src/lib.rs index 7f87f1074..730387fcd 100644 --- a/services/comps/block/src/lib.rs +++ b/services/comps/block/src/lib.rs @@ -1,72 +1,65 @@ -//! The block device of jinux +//! The block devices of jinux #![no_std] #![forbid(unsafe_code)] #![feature(fn_traits)] -mod virtio; - extern crate alloc; use core::any::Any; +use core::fmt::Debug; -use alloc::string::ToString; +use alloc::collections::BTreeMap; +use alloc::string::String; use alloc::sync::Arc; +use alloc::vec::Vec; use component::init_component; use component::ComponentInitError; -use jinux_virtio::VirtioDeviceType; +use jinux_frame::sync::SpinLock; use spin::Once; -use virtio::VirtioBlockDevice; pub const BLK_SIZE: usize = 512; -pub trait BlockDevice: Send + Sync + Any { - fn init(&self) {} +pub trait BlockDevice: Send + Sync + Any + Debug { fn read_block(&self, block_id: usize, buf: &mut [u8]); fn write_block(&self, block_id: usize, buf: &[u8]); fn handle_irq(&self); } -pub static BLK_COMPONENT: Once = Once::new(); +pub fn register_device(name: String, device: Arc) { + COMPONENT.get().unwrap().devices.lock().insert(name, device); +} + +pub fn get_device(str: &String) -> Option> { + COMPONENT.get().unwrap().devices.lock().get(str).cloned() +} + +pub fn all_devices() -> Vec<(String, Arc)> { + let lock = COMPONENT.get().unwrap().devices.lock(); + let mut vec = Vec::new(); + for (name, device) in lock.iter() { + vec.push((name.clone(), device.clone())); + } + vec +} + +static COMPONENT: Once = Once::new(); #[init_component] -fn blk_component_init() -> Result<(), ComponentInitError> { - let a = BLKComponent::init()?; - BLK_COMPONENT.call_once(|| a); +fn component_init() -> Result<(), ComponentInitError> { + let a = Component::init()?; + COMPONENT.call_once(|| a); Ok(()) } -pub struct BLKComponent { - /// Input device map, key is the irq number, value is the Input device - blk_device: Arc, +#[derive(Debug)] +struct Component { + devices: SpinLock>>, } -impl BLKComponent { +impl Component { pub fn init() -> Result { - let virtio = jinux_virtio::VIRTIO_COMPONENT.get().unwrap(); - let devices = virtio.get_device(VirtioDeviceType::Block); - // FIXME: deal with multiple block devices - if let Some(device) = devices.into_iter().next() { - let v_device = VirtioBlockDevice::new(device); - return Ok(Self { - blk_device: Arc::new(v_device), - }); - } - Err(ComponentInitError::UninitializedDependencies( - "Virtio".to_string(), - )) - } - - pub const fn name() -> &'static str { - "Block device" - } - // 0~65535 - pub const fn priority() -> u16 { - 8192 - } -} - -impl BLKComponent { - pub fn get_device(self: &Self) -> Arc { - self.blk_device.clone() + Ok(Self { + devices: SpinLock::new(BTreeMap::new()), + }) } } diff --git a/services/comps/block/src/virtio.rs b/services/comps/block/src/virtio.rs deleted file mode 100644 index eca98db95..000000000 --- a/services/comps/block/src/virtio.rs +++ /dev/null @@ -1,54 +0,0 @@ -//! Block device based on Virtio - -use jinux_frame::sync::Mutex; -use jinux_frame::{io_mem::IoMem, trap::TrapFrame}; -use jinux_pci::msix::MSIX; -use jinux_util::safe_ptr::SafePtr; -use jinux_virtio::{device::block::device::BLKDevice, PCIVirtioDevice, VirtioPciCommonCfg}; -use log::debug; - -use crate::{BlockDevice, BLK_COMPONENT}; - -pub struct VirtioBlockDevice { - blk_device: Mutex, - pub common_cfg: SafePtr, - _msix: MSIX, -} - -impl BlockDevice for VirtioBlockDevice { - fn read_block(&self, block_id: usize, buf: &mut [u8]) { - self.blk_device.lock().read_block(block_id, buf); - } - - /// it is blocking now - fn write_block(&self, block_id: usize, buf: &[u8]) { - self.blk_device.lock().write_block(block_id, buf); - } - - fn handle_irq(&self) { - debug!("block device handle irq"); - } -} - -impl VirtioBlockDevice { - pub(crate) fn new(mut virtio_device: PCIVirtioDevice) -> Self { - fn handle_block_device(_: &TrapFrame) { - BLK_COMPONENT.get().unwrap().blk_device.handle_irq() - } - fn config_space_change(_: &TrapFrame) { - debug!("block device config space change"); - } - virtio_device.register_interrupt_functions(&config_space_change, &handle_block_device); - let blk_device = Mutex::new(match virtio_device.device { - jinux_virtio::device::VirtioDevice::Block(blk) => blk, - _ => { - panic!("Error when creating new block device, the input device is other type of virtio device"); - } - }); - Self { - blk_device, - common_cfg: virtio_device.common_cfg, - _msix: virtio_device.msix, - } - } -} diff --git a/services/comps/input/Cargo.toml b/services/comps/input/Cargo.toml index bcb054b97..9726dad1b 100644 --- a/services/comps/input/Cargo.toml +++ b/services/comps/input/Cargo.toml @@ -9,7 +9,6 @@ edition = "2021" bitflags = "1.3" spin = "0.9.4" jinux-frame = { path = "../../../framework/jinux-frame" } -jinux-virtio = { path = "../virtio" } jinux-util = { path = "../../libs/jinux-util" } jinux-rights = { path = "../../libs/jinux-rights" } component = { path = "../../libs/comp-sys/component" } diff --git a/services/comps/input/src/lib.rs b/services/comps/input/src/lib.rs index d6ec790b5..896a3c5e0 100644 --- a/services/comps/input/src/lib.rs +++ b/services/comps/input/src/lib.rs @@ -1,12 +1,11 @@ -//! The input device of jinux +//! The input devices of jinux #![no_std] #![forbid(unsafe_code)] #![feature(fn_traits)] -mod virtio; - extern crate alloc; use core::any::Any; +use core::fmt::Debug; use alloc::collections::BTreeMap; use alloc::string::String; @@ -14,79 +13,51 @@ use alloc::sync::Arc; use alloc::vec::Vec; use component::init_component; use component::ComponentInitError; -use jinux_frame::sync::Mutex; -use jinux_virtio::VirtioDeviceType; +use jinux_frame::sync::SpinLock; use spin::Once; -use virtio::VirtioInputDevice; use virtio_input_decoder::DecodeType; -pub trait INPUTDevice: Send + Sync + Any { +pub trait InputDevice: Send + Sync + Any + Debug { fn handle_irq(&self) -> Option<()>; fn register_callbacks(&self, function: &'static (dyn Fn(DecodeType) + Send + Sync)); - fn name(&self) -> &String; } -pub static INPUT_COMPONENT: Once = Once::new(); +pub fn register_device(name: String, device: Arc) { + COMPONENT.get().unwrap().devices.lock().insert(name, device); +} + +pub fn get_device(str: &String) -> Option> { + COMPONENT.get().unwrap().devices.lock().get(str).cloned() +} + +pub fn all_devices() -> Vec<(String, Arc)> { + let lock = COMPONENT.get().unwrap().devices.lock(); + let mut vec = Vec::new(); + for (name, device) in lock.iter() { + vec.push((name.clone(), device.clone())); + } + vec +} + +static COMPONENT: Once = Once::new(); #[init_component] -fn input_component_init() -> Result<(), ComponentInitError> { - let a = INPUTComponent::init()?; - INPUT_COMPONENT.call_once(|| a); +fn component_init() -> Result<(), ComponentInitError> { + let a = Component::init()?; + COMPONENT.call_once(|| a); Ok(()) } -pub struct INPUTComponent { - /// Input device map, key is the irq number, value is the Input device - input_device_map: Mutex>>, +#[derive(Debug)] +struct Component { + devices: SpinLock>>, } -impl INPUTComponent { +impl Component { pub fn init() -> Result { - let mut input_device_map: BTreeMap> = BTreeMap::new(); - let virtio = jinux_virtio::VIRTIO_COMPONENT.get().unwrap(); - let devices = virtio.get_device(VirtioDeviceType::Input); - for device in devices { - let (v_device, irq_num) = VirtioInputDevice::new(device); - input_device_map.insert(irq_num, Arc::new(v_device)); - } Ok(Self { - input_device_map: Mutex::new(input_device_map), + devices: SpinLock::new(BTreeMap::new()), }) } - - pub const fn name() -> &'static str { - "Input Device" - } - // 0~65535 - pub const fn priority() -> u16 { - 8192 - } -} - -impl INPUTComponent { - fn call(self: &Self, irq_number: u8) -> Result<(), InputDeviceHandleError> { - // FIXME: use Result instead - let binding = self.input_device_map.lock(); - let device = binding - .get(&irq_number) - .ok_or(InputDeviceHandleError::DeviceNotExists)?; - device.handle_irq(); - Ok(()) - } - - pub fn get_input_device(self: &Self) -> Vec> { - self.input_device_map - .lock() - .iter() - .map(|(_, device)| device.clone()) - .collect::>>() - } -} - -#[allow(dead_code)] -#[derive(Debug)] -enum InputDeviceHandleError { - DeviceNotExists, - Unknown, } diff --git a/services/comps/input/src/virtio.rs b/services/comps/input/src/virtio.rs deleted file mode 100644 index 03f9f7b93..000000000 --- a/services/comps/input/src/virtio.rs +++ /dev/null @@ -1,126 +0,0 @@ -//! Input device based on Virtio - -use alloc::{string::String, sync::Arc, vec::Vec}; -use jinux_frame::io_mem::IoMem; -use jinux_frame::offset_of; -use jinux_frame::sync::Mutex; -use jinux_frame::trap::TrapFrame; -use jinux_pci::msix::MSIX; -use jinux_util::field_ptr; -use jinux_util::safe_ptr::SafePtr; -use jinux_virtio::device::input::device::InputProp; -use jinux_virtio::VirtioPciCommonCfg; -use jinux_virtio::{ - device::input::{device::InputDevice, InputConfigSelect}, - PCIVirtioDevice, -}; -use log::{debug, info}; -use virtio_input_decoder::{DecodeType, Decoder}; - -use crate::INPUTDevice; -pub struct VirtioInputDevice { - input_device: InputDevice, - _common_cfg: SafePtr, - _msix: Mutex, - name: String, - callbacks: Mutex>>, -} - -impl VirtioInputDevice { - /// Create a new Virtio Input Device, return value contains the irq number it will use - pub(crate) fn new(virtio_device: PCIVirtioDevice) -> (Self, u8) { - let input_device = match virtio_device.device { - jinux_virtio::device::VirtioDevice::Input(dev) => dev, - _ => { - panic!("Error when creating new input device, the input device is other type of virtio device"); - } - }; - let mut raw_name: [u8; 128] = [0; 128]; - input_device.query_config_select(InputConfigSelect::IdName, 0, &mut raw_name); - let name = String::from_utf8(raw_name.to_vec()).unwrap(); - info!("input device name:{}", name); - - let mut prop: [u8; 128] = [0; 128]; - input_device.query_config_select(InputConfigSelect::PropBits, 0, &mut prop); - - let input_prop = InputProp::from_bits(prop[0]).unwrap(); - debug!("input device prop:{:?}", input_prop); - - fn handle_input(frame: &TrapFrame) { - debug!("in handle input"); - let input_component = crate::INPUT_COMPONENT.get().unwrap(); - input_component.call(frame.trap_num as u8).unwrap(); - } - fn config_space_change(_: &TrapFrame) { - debug!("input device config space change"); - } - - let common_cfg = virtio_device.common_cfg; - let mut msix = virtio_device.msix; - - let config_msix_vector = field_ptr!(&common_cfg, VirtioPciCommonCfg, config_msix_vector) - .read() - .unwrap() as usize; - - let mut event_irq_number = 0; - for i in 0..msix.table_size as usize { - let msix = msix.table.get_mut(i).unwrap(); - if !msix.irq_handle.is_empty() { - panic!("msix already have irq functions"); - } - if config_msix_vector == i { - msix.irq_handle.on_active(config_space_change); - } else { - event_irq_number = msix.irq_handle.num(); - msix.irq_handle.on_active(handle_input); - } - } - - ( - Self { - input_device, - _common_cfg: common_cfg, - _msix: Mutex::new(msix), - name, - callbacks: Mutex::new(Vec::new()), - }, - event_irq_number, - ) - } -} - -impl INPUTDevice for VirtioInputDevice { - fn handle_irq(&self) -> Option<()> { - let input = &self.input_device; - // one interrupt may contains serval input, so it should loop - loop { - let event = input.pop_pending_event()?; - let dtype = match Decoder::decode( - event.event_type as usize, - event.code as usize, - event.value as usize, - ) { - Ok(dtype) => dtype, - Err(_) => return Some(()), - }; - let lock = self.callbacks.lock(); - for callback in lock.iter() { - callback.call((dtype,)); - } - match dtype { - virtio_input_decoder::DecodeType::Key(key, r#type) => { - info!("{:?} {:?}", key, r#type); - } - virtio_input_decoder::DecodeType::Mouse(mouse) => info!("{:?}", mouse), - } - } - } - - fn register_callbacks(&self, function: &'static (dyn Fn(DecodeType) + Send + Sync)) { - self.callbacks.lock().push(Arc::new(function)) - } - - fn name(&self) -> &String { - &self.name - } -} diff --git a/services/comps/network/Cargo.toml b/services/comps/network/Cargo.toml index 7e3c13af1..214f10794 100644 --- a/services/comps/network/Cargo.toml +++ b/services/comps/network/Cargo.toml @@ -8,9 +8,13 @@ edition = "2021" [dependencies] component = { path = "../../libs/comp-sys/component" } jinux-frame = { path = "../../../framework/jinux-frame" } -jinux-virtio = { path = "../virtio" } jinux-util = { path = "../../libs/jinux-util" } jinux-rights = { path = "../../libs/jinux-rights" } +align_ext = { path = "../../../framework/libs/align_ext" } +int-to-c-enum = { path = "../../libs/int-to-c-enum" } +bytes = { version = "1.4.0", default-features = false } +pod = { git = "https://github.com/jinzhao-dev/pod", rev = "71e59ec" } +bitflags = "1.3" spin = "0.9.4" ringbuf = { version = "0.3.2", default-features = false, features = ["alloc"] } log = "0.4" diff --git a/services/comps/virtio/src/device/network/buffer.rs b/services/comps/network/src/buffer.rs similarity index 66% rename from services/comps/virtio/src/device/network/buffer.rs rename to services/comps/network/src/buffer.rs index 3e73be249..0769b40fb 100644 --- a/services/comps/virtio/src/device/network/buffer.rs +++ b/services/comps/network/src/buffer.rs @@ -1,25 +1,29 @@ +use core::mem::size_of; + use align_ext::AlignExt; use bytes::BytesMut; use pod::Pod; -use crate::device::network::header::VIRTIO_NET_HDR_LEN; - -use super::header::VirtioNetHdr; - /// Buffer for receive packet #[derive(Debug)] pub struct RxBuffer { /// Packet Buffer, length align 8. buf: BytesMut, + /// Header len + header_len: usize, /// Packet len packet_len: usize, } impl RxBuffer { - pub fn new(len: usize) -> Self { + pub fn new(len: usize, header_len: usize) -> Self { let len = len.align_up(8); let buf = BytesMut::zeroed(len); - Self { buf, packet_len: 0 } + Self { + buf, + packet_len: 0, + header_len, + } } pub const fn packet_len(&self) -> usize { @@ -40,18 +44,19 @@ impl RxBuffer { /// Packet payload slice, which is inner buffer excluding VirtioNetHdr. pub fn packet(&self) -> &[u8] { - debug_assert!(VIRTIO_NET_HDR_LEN + self.packet_len <= self.buf.len()); - &self.buf[VIRTIO_NET_HDR_LEN..VIRTIO_NET_HDR_LEN + self.packet_len] + debug_assert!(self.header_len + self.packet_len <= self.buf.len()); + &self.buf[self.header_len..self.header_len + self.packet_len] } /// Mutable packet payload slice. pub fn packet_mut(&mut self) -> &mut [u8] { - debug_assert!(VIRTIO_NET_HDR_LEN + self.packet_len <= self.buf.len()); - &mut self.buf[VIRTIO_NET_HDR_LEN..VIRTIO_NET_HDR_LEN + self.packet_len] + debug_assert!(self.header_len + self.packet_len <= self.buf.len()); + &mut self.buf[self.header_len..self.header_len + self.packet_len] } - pub fn virtio_net_header(&self) -> VirtioNetHdr { - VirtioNetHdr::from_bytes(&self.buf[..VIRTIO_NET_HDR_LEN]) + pub fn header(&self) -> H { + debug_assert_eq!(size_of::(), self.header_len); + H::from_bytes(&self.buf[..size_of::()]) } } diff --git a/services/comps/network/src/driver.rs b/services/comps/network/src/driver.rs index 707810bb0..4fa1b1905 100644 --- a/services/comps/network/src/driver.rs +++ b/services/comps/network/src/driver.rs @@ -1,47 +1,36 @@ use alloc::vec; -use smoltcp::phy::{self, Medium}; +use smoltcp::{phy, time::Instant}; -use crate::VirtioNet; -use jinux_virtio::device::network::{ +use crate::{ buffer::{RxBuffer, TxBuffer}, - device::NetworkDevice, + NetworkDevice, }; -impl phy::Device for VirtioNet { +impl phy::Device for dyn NetworkDevice { type RxToken<'a> = RxToken; type TxToken<'a> = TxToken<'a>; - fn receive( - &mut self, - _timestamp: smoltcp::time::Instant, - ) -> Option<(Self::RxToken<'_>, Self::TxToken<'_>)> { + fn receive(&mut self, _timestamp: Instant) -> Option<(Self::RxToken<'_>, Self::TxToken<'_>)> { if self.can_receive() { - let device = self.device_mut(); - let rx_buffer = device.receive().unwrap(); - Some((RxToken(rx_buffer), TxToken(device))) + let rx_buffer = self.receive().unwrap(); + Some((RxToken(rx_buffer), TxToken(self))) } else { None } } - fn transmit(&mut self, _timestamp: smoltcp::time::Instant) -> Option> { + fn transmit(&mut self, _timestamp: Instant) -> Option> { if self.can_send() { - let device = self.device_mut(); - Some(TxToken(device)) + Some(TxToken(self)) } else { None } } fn capabilities(&self) -> phy::DeviceCapabilities { - let mut caps = phy::DeviceCapabilities::default(); - caps.max_transmission_unit = 1536; - caps.max_burst_size = Some(1); - caps.medium = Medium::Ethernet; - caps + self.capabilities() } } - pub struct RxToken(RxBuffer); impl phy::RxToken for RxToken { @@ -55,7 +44,7 @@ impl phy::RxToken for RxToken { } } -pub struct TxToken<'a>(&'a mut NetworkDevice); +pub struct TxToken<'a>(&'a mut dyn NetworkDevice); impl<'a> phy::TxToken for TxToken<'a> { fn consume(self, len: usize, f: F) -> R diff --git a/services/comps/network/src/lib.rs b/services/comps/network/src/lib.rs index c339f4d6d..6210d9ed8 100644 --- a/services/comps/network/src/lib.rs +++ b/services/comps/network/src/lib.rs @@ -1,59 +1,134 @@ #![no_std] #![forbid(unsafe_code)] #![feature(trait_alias)] +#![feature(fn_traits)] + +pub mod buffer; +pub mod driver; extern crate alloc; +use alloc::boxed::Box; +use alloc::collections::BTreeMap; +use alloc::string::String; use alloc::sync::Arc; use alloc::vec::Vec; +use buffer::RxBuffer; +use buffer::TxBuffer; use component::init_component; use component::ComponentInitError; use core::any::Any; +use core::fmt::Debug; use jinux_frame::sync::SpinLock; -use jinux_virtio::device::network::device::EthernetAddr; -use jinux_virtio::VirtioDeviceType; +use jinux_util::safe_ptr::Pod; +use smoltcp::phy; use spin::Once; -mod driver; -mod virtio; +#[derive(Debug, Clone, Copy, Pod)] +#[repr(C)] +pub struct EthernetAddr(pub [u8; 6]); -pub use virtio::VirtioNet; - -pub trait NetworkDevice: Send + Sync + Any { - fn irq_number(&self) -> u8; - fn name(&self) -> &'static str; - fn mac_addr(&self) -> EthernetAddr; +#[derive(Debug, Clone, Copy)] +pub enum VirtioNetError { + NotReady, + WrongToken, + Unknown, } -pub trait NetDeviceIrqHandler = Fn(u8) + Send + Sync + 'static; +pub trait NetworkDevice: Send + Sync + Any + Debug { + // ================Device Information================= + + fn mac_addr(&self) -> EthernetAddr; + fn capabilities(&self) -> phy::DeviceCapabilities; + + // ================Device Operation=================== + + fn can_receive(&self) -> bool; + fn can_send(&self) -> bool; + /// Receive a packet from network. If packet is ready, returns a RxBuffer containing the packet. + /// Otherwise, return NotReady error. + fn receive(&mut self) -> Result; + /// Send a packet to network. Return until the request completes. + fn send(&mut self, tx_buffer: TxBuffer) -> Result<(), VirtioNetError>; +} + +pub trait NetDeviceIrqHandler = Fn() + Send + Sync + 'static; + +pub fn register_device(name: String, device: Arc>>) { + COMPONENT + .get() + .unwrap() + .devices + .lock() + .insert(name, (Arc::new(SpinLock::new(Vec::new())), device)); +} + +pub fn get_device(str: &String) -> Option>>> { + let lock = COMPONENT.get().unwrap().devices.lock(); + let Some((_, device)) = lock.get(str) else { + return None; + }; + Some(device.clone()) +} + +pub fn register_recv_callback(name: &String, callback: impl NetDeviceIrqHandler) { + let lock = COMPONENT.get().unwrap().devices.lock(); + let Some((callbacks, _)) = lock.get(name) else { + return; + }; + callbacks.lock().push(Arc::new(callback)); +} + +pub fn handle_recv_irq(name: &String) { + let lock = COMPONENT.get().unwrap().devices.lock(); + let Some((callbacks, _)) = lock.get(name) else { + return; + }; + let callbacks = callbacks.clone(); + let lock = callbacks.lock(); + for callback in lock.iter() { + callback.call(()) + } +} + +pub fn all_devices() -> Vec<(String, Arc>>)> { + let lock = COMPONENT.get().unwrap().devices.lock(); + let mut vec = Vec::new(); + for (name, (_, device)) in lock.iter() { + vec.push((name.clone(), device.clone())); + } + vec +} + +static COMPONENT: Once = Once::new(); pub(crate) static NETWORK_IRQ_HANDLERS: Once>>> = Once::new(); #[init_component] fn init() -> Result<(), ComponentInitError> { + let a = Component::init()?; + COMPONENT.call_once(|| a); NETWORK_IRQ_HANDLERS.call_once(|| SpinLock::new(Vec::new())); Ok(()) } -pub fn probe_virtio_net() -> Result { - let network_devices = { - let virtio = jinux_virtio::VIRTIO_COMPONENT.get().unwrap(); - virtio.get_device(VirtioDeviceType::Network) - }; +struct Component { + /// Device list, the key is device name, value is (callbacks, device); + devices: SpinLock< + BTreeMap< + String, + ( + Arc>>>, + Arc>>, + ), + >, + >, +} - // FIXME: deal with multiple net devices - if let Some(device) = network_devices.into_iter().next() { - let virtio_net = VirtioNet::new(device); - return Ok(virtio_net); +impl Component { + pub fn init() -> Result { + Ok(Self { + devices: SpinLock::new(BTreeMap::new()), + }) } - - Err(ComponentInitError::Unknown) -} - -pub fn register_net_device_irq_handler(callback: impl NetDeviceIrqHandler) { - NETWORK_IRQ_HANDLERS - .get() - .unwrap() - .lock_irq_disabled() - .push(Arc::new(callback)) } diff --git a/services/comps/network/src/virtio.rs b/services/comps/network/src/virtio.rs deleted file mode 100644 index 5aa5c415f..000000000 --- a/services/comps/network/src/virtio.rs +++ /dev/null @@ -1,111 +0,0 @@ -use jinux_frame::io_mem::IoMem; -use jinux_frame::offset_of; -use jinux_frame::sync::SpinLock; -use jinux_frame::trap::TrapFrame; -use jinux_pci::msix::MSIX; -use jinux_util::field_ptr; -use jinux_util::safe_ptr::SafePtr; -use jinux_virtio::device::network::device::{self, EthernetAddr}; -use jinux_virtio::PCIVirtioDevice; -use jinux_virtio::VirtioPciCommonCfg; -use log::debug; - -use crate::{NetworkDevice, NETWORK_IRQ_HANDLERS}; - -pub struct VirtioNet { - /// Network Device - device: device::NetworkDevice, - /// Own common cfg to avoid other devices access this frame - _common_cfg: SafePtr, - _msix: SpinLock, - irq_number: u8, -} - -impl NetworkDevice for VirtioNet { - fn irq_number(&self) -> u8 { - self.irq_number - } - - fn name(&self) -> &'static str { - "virtio net" - } - - fn mac_addr(&self) -> EthernetAddr { - self.device.mac_addr() - } -} - -impl VirtioNet { - pub(crate) fn new(virtio_device: PCIVirtioDevice) -> Self { - let device = if let jinux_virtio::device::VirtioDevice::Network(network_device) = - virtio_device.device - { - network_device - } else { - panic!("Invalid device type") - }; - - let common_cfg = virtio_device.common_cfg; - let mut msix = virtio_device.msix; - let config_msix_vector = field_ptr!(&common_cfg, VirtioPciCommonCfg, config_msix_vector) - .read() - .unwrap() as usize; - - let mut network_irq_num = 0; - for i in 0..msix.table_size as usize { - let msix_entry = msix.table.get_mut(i).unwrap(); - if !msix_entry.irq_handle.is_empty() { - panic!("msix already have irq functions"); - } - if config_msix_vector == i { - debug!( - "network config space change irq number = {}", - msix_entry.irq_handle.num() - ); - msix_entry.irq_handle.on_active(config_space_change); - } else { - network_irq_num = msix_entry.irq_handle.num(); - msix_entry.irq_handle.on_active(handle_network_event); - } - } - debug_assert!(network_irq_num != 0); - debug!("Network device irq num = {}", network_irq_num); - let device = VirtioNet { - device, - _common_cfg: common_cfg, - irq_number: network_irq_num, - _msix: SpinLock::new(msix), - }; - device - } - - pub(crate) fn can_receive(&self) -> bool { - self.device.can_receive() - } - - pub(crate) fn can_send(&self) -> bool { - self.device.can_send() - } - - pub(crate) fn device_mut(&mut self) -> &mut device::NetworkDevice { - &mut self.device - } -} - -/// Interrupt handler if network device config space changes -fn config_space_change(_: &TrapFrame) { - debug!("network device config space change"); -} - -/// Interrupt handler if network device receives some packet -fn handle_network_event(trap_frame: &TrapFrame) { - let irq_num = trap_frame.trap_num as u8; - for callback in NETWORK_IRQ_HANDLERS - .get() - .unwrap() - .lock_irq_disabled() - .iter() - { - callback(irq_num); - } -} diff --git a/services/comps/virtio/Cargo.toml b/services/comps/virtio/Cargo.toml index 4d01839b5..c11066410 100644 --- a/services/comps/virtio/Cargo.toml +++ b/services/comps/virtio/Cargo.toml @@ -8,8 +8,12 @@ edition = "2021" [dependencies] bitflags = "1.3" spin = "0.9.4" +virtio-input-decoder = "0.1.4" bytes = { version = "1.4.0", default-features = false } align_ext = { path = "../../../framework/libs/align_ext" } +jinux-input = { path = "../input" } +jinux-block = { path = "../block" } +jinux-network = { path = "../network" } jinux-frame = { path = "../../../framework/jinux-frame" } jinux-util = { path = "../../libs/jinux-util" } jinux-rights = { path = "../../libs/jinux-rights" } @@ -18,5 +22,18 @@ pod = { git = "https://github.com/jinzhao-dev/pod", rev = "71e59ec" } component = { path = "../../libs/comp-sys/component" } log = "0.4" int-to-c-enum = { path = "../../libs/int-to-c-enum" } - +smoltcp = { version = "0.9.1", default-features = false, features = [ + "alloc", + "log", + "medium-ethernet", + "medium-ip", + "proto-dhcpv4", + "proto-ipv4", + "proto-igmp", + "socket-icmp", + "socket-udp", + "socket-tcp", + "socket-raw", + "socket-dhcpv4", +] } [features] diff --git a/services/comps/virtio/src/device/block/device.rs b/services/comps/virtio/src/device/block/device.rs index d5d318ce8..6da51e004 100644 --- a/services/comps/virtio/src/device/block/device.rs +++ b/services/comps/virtio/src/device/block/device.rs @@ -1,65 +1,30 @@ use core::hint::spin_loop; -use alloc::vec::Vec; -use jinux_frame::{io_mem::IoMem, offset_of}; -use jinux_pci::{capability::vendor::virtio::CapabilityVirtioData, util::BAR}; -use jinux_util::{field_ptr, safe_ptr::SafePtr}; +use alloc::{boxed::Box, string::ToString, sync::Arc}; +use jinux_frame::{io_mem::IoMem, sync::SpinLock, trap::TrapFrame}; +use jinux_util::safe_ptr::SafePtr; +use log::info; use pod::Pod; use crate::{ device::block::{BlkReq, BlkResp, ReqType, RespStatus, BLK_SIZE}, device::VirtioDeviceError, - queue::{QueueError, VirtQueue}, - VirtioPciCommonCfg, + queue::VirtQueue, + transport::VirtioTransport, }; -use super::{BLKFeatures, VirtioBLKConfig}; +use super::{BlkFeatures, VirtioBlkConfig}; #[derive(Debug)] -pub struct BLKDevice { - config: SafePtr, - queue: VirtQueue, +pub struct BlockDevice { + config: SafePtr, + queue: SpinLock, + transport: Box, } -impl BLKDevice { - /// Create a new VirtIO-Block driver. - /// msix_vector_left should at least have one element or n elements where n is the virtqueue amount - pub(crate) fn new( - cap: &CapabilityVirtioData, - bars: [Option; 6], - common_cfg: &SafePtr, - notify_base_address: usize, - notify_off_multiplier: u32, - mut msix_vector_left: Vec, - ) -> Result { - let config = VirtioBLKConfig::new(cap, bars); - let num_queues = field_ptr!(common_cfg, VirtioPciCommonCfg, num_queues) - .read() - .unwrap(); - if num_queues != 1 { - return Err(VirtioDeviceError::QueuesAmountDoNotMatch(num_queues, 1)); - } - let queue = VirtQueue::new( - &common_cfg, - 0 as usize, - 128, - notify_base_address as usize, - notify_off_multiplier, - msix_vector_left.pop().unwrap(), - ) - .expect("create virtqueue failed"); - Ok(Self { config, queue }) - } - - /// Negotiate features for the device specified bits 0~23 - pub(crate) fn negotiate_features(features: u64) -> u64 { - let feature = BLKFeatures::from_bits(features).unwrap(); - let support_features = BLKFeatures::from_bits(features).unwrap(); - (feature & support_features).bits - } - +impl BlockDevice { /// read data from block device, this function is blocking - pub fn read_block(&mut self, block_id: usize, buf: &mut [u8]) { + pub fn read(&self, block_id: usize, buf: &mut [u8]) { assert_eq!(buf.len(), BLK_SIZE); let req = BlkReq { type_: ReqType::In as _, @@ -67,24 +32,22 @@ impl BLKDevice { sector: block_id as u64, }; let mut resp = BlkResp::default(); - let token = self - .queue + let mut queue = self.queue.lock_irq_disabled(); + let token = queue .add(&[req.as_bytes()], &[buf, resp.as_bytes_mut()]) .expect("add queue failed"); - self.queue.notify(); - while !self.queue.can_pop() { + queue.notify(); + while !queue.can_pop() { spin_loop(); } - self.queue - .pop_used_with_token(token) - .expect("pop used failed"); + queue.pop_used_with_token(token).expect("pop used failed"); match RespStatus::try_from(resp.status).unwrap() { RespStatus::Ok => {} _ => panic!("io error in block device"), }; } /// write data to block device, this function is blocking - pub fn write_block(&mut self, block_id: usize, buf: &[u8]) { + pub fn write(&self, block_id: usize, buf: &[u8]) { assert_eq!(buf.len(), BLK_SIZE); let req = BlkReq { type_: ReqType::Out as _, @@ -92,17 +55,15 @@ impl BLKDevice { sector: block_id as u64, }; let mut resp = BlkResp::default(); - let token = self - .queue + let mut queue = self.queue.lock_irq_disabled(); + let token = queue .add(&[req.as_bytes(), buf], &[resp.as_bytes_mut()]) .expect("add queue failed"); - self.queue.notify(); - while !self.queue.can_pop() { + queue.notify(); + while !queue.can_pop() { spin_loop(); } - self.queue - .pop_used_with_token(token) - .expect("pop used failed"); + queue.pop_used_with_token(token).expect("pop used failed"); let st = resp.status; match RespStatus::try_from(st).unwrap() { RespStatus::Ok => {} @@ -110,57 +71,63 @@ impl BLKDevice { }; } - pub fn pop_used(&mut self) -> Result<(u16, u32), QueueError> { - self.queue.pop_used() - } - - pub fn pop_used_with_token(&mut self, token: u16) -> Result { - self.queue.pop_used_with_token(token) - } - - /// read data from block device, this function is non-blocking - /// return value is token - pub fn read_block_non_blocking( - &mut self, - block_id: usize, - buf: &mut [u8], - req: &mut BlkReq, - resp: &mut BlkResp, - ) -> u16 { - assert_eq!(buf.len(), BLK_SIZE); - *req = BlkReq { - type_: ReqType::In as _, - reserved: 0, - sector: block_id as u64, + /// Create a new VirtIO-Block driver. + pub(crate) fn init(mut transport: Box) -> Result<(), VirtioDeviceError> { + let config = VirtioBlkConfig::new(transport.as_mut()); + let num_queues = transport.num_queues(); + if num_queues != 1 { + return Err(VirtioDeviceError::QueuesAmountDoNotMatch(num_queues, 1)); + } + let queue = VirtQueue::new(0, 64, transport.as_mut()).expect("create virtqueue failed"); + let mut device = Self { + config, + queue: SpinLock::new(queue), + transport, }; - let token = self - .queue - .add(&[req.as_bytes()], &[buf, resp.as_bytes_mut()]) + + device + .transport + .register_cfg_callback(Box::new(config_space_change)) .unwrap(); - self.queue.notify(); - token + device + .transport + .register_queue_callback(0, Box::new(handle_block_device), false) + .unwrap(); + + fn handle_block_device(_: &TrapFrame) { + jinux_block::get_device(&(super::DEVICE_NAME.to_string())) + .unwrap() + .handle_irq(); + } + + fn config_space_change(_: &TrapFrame) { + info!("Virtio block device config space change"); + } + device.transport.finish_init(); + + jinux_block::register_device(super::DEVICE_NAME.to_string(), Arc::new(device)); + + Ok(()) } - /// write data to block device, this function is non-blocking - /// return value is token - pub fn write_block_non_blocking( - &mut self, - block_id: usize, - buf: &[u8], - req: &mut BlkReq, - resp: &mut BlkResp, - ) -> u16 { - assert_eq!(buf.len(), BLK_SIZE); - *req = BlkReq { - type_: ReqType::Out as _, - reserved: 0, - sector: block_id as u64, - }; - let token = self - .queue - .add(&[req.as_bytes(), buf], &[resp.as_bytes_mut()]) - .expect("add queue failed"); - self.queue.notify(); - token + /// Negotiate features for the device specified bits 0~23 + pub(crate) fn negotiate_features(features: u64) -> u64 { + let feature = BlkFeatures::from_bits(features).unwrap(); + let support_features = BlkFeatures::from_bits(features).unwrap(); + (feature & support_features).bits + } +} + +impl jinux_block::BlockDevice for BlockDevice { + fn read_block(&self, block_id: usize, buf: &mut [u8]) { + self.read(block_id, buf); + } + + fn write_block(&self, block_id: usize, buf: &[u8]) { + self.write(block_id, buf); + } + + fn handle_irq(&self) { + info!("Virtio block device handle irq"); } } diff --git a/services/comps/virtio/src/device/block/mod.rs b/services/comps/virtio/src/device/block/mod.rs index 04e11c57e..b61a210b2 100644 --- a/services/comps/virtio/src/device/block/mod.rs +++ b/services/comps/virtio/src/device/block/mod.rs @@ -1,20 +1,19 @@ pub mod device; -use core::mem::size_of; - use bitflags::bitflags; use int_to_c_enum::TryFromInt; use jinux_frame::io_mem::IoMem; -use jinux_pci::capability::vendor::virtio::CapabilityVirtioData; -use jinux_pci::util::BAR; use jinux_util::safe_ptr::SafePtr; use pod::Pod; +use crate::transport::VirtioTransport; + pub const BLK_SIZE: usize = 512; +pub static DEVICE_NAME: &'static str = "Virtio-Block"; bitflags! { /// features for virtio block device - pub(crate) struct BLKFeatures : u64{ + pub(crate) struct BlkFeatures : u64{ const SIZE_MAX = 1 << 1; const SEG_MAX = 1 << 2; const GEOMETRY = 1 << 4; @@ -78,12 +77,12 @@ pub enum RespStatus { #[derive(Debug, Copy, Clone, Pod)] #[repr(C)] -pub struct VirtioBLKConfig { +pub struct VirtioBlkConfig { capacity: u64, size_max: u64, - geometry: VirtioBLKGeometry, + geometry: VirtioBlkGeometry, blk_size: u32, - topology: VirtioBLKTopology, + topology: VirtioBlkTopology, writeback: u8, unused0: [u8; 3], max_discard_sectors: u32, @@ -97,7 +96,7 @@ pub struct VirtioBLKConfig { #[derive(Debug, Copy, Clone, Pod)] #[repr(C)] -pub struct VirtioBLKGeometry { +pub struct VirtioBlkGeometry { cylinders: u16, heads: u8, sectors: u8, @@ -105,27 +104,16 @@ pub struct VirtioBLKGeometry { #[derive(Debug, Copy, Clone, Pod)] #[repr(C)] -pub struct VirtioBLKTopology { +pub struct VirtioBlkTopology { physical_block_exp: u8, alignment_offset: u8, min_io_size: u16, opt_io_size: u32, } -impl VirtioBLKConfig { - pub(crate) fn new(cap: &CapabilityVirtioData, bars: [Option; 6]) -> SafePtr { - let bar = cap.bar; - let offset = cap.offset; - match bars[bar as usize].expect("Virtio pci block cfg:bar is none") { - BAR::Memory(address, _, _, _) => SafePtr::new( - IoMem::new( - (address as usize + offset as usize) - ..(address as usize + offset as usize + size_of::()), - ) - .unwrap(), - 0, - ), - BAR::IO(_, _) => panic!("Virtio pci block cfg:bar is IO type"), - } +impl VirtioBlkConfig { + pub(self) fn new(transport: &mut dyn VirtioTransport) -> SafePtr { + let memory = transport.device_config_memory(); + SafePtr::new(memory, 0) } } diff --git a/services/comps/virtio/src/device/input/device.rs b/services/comps/virtio/src/device/input/device.rs index 0138cb955..aaae50304 100644 --- a/services/comps/virtio/src/device/input/device.rs +++ b/services/comps/virtio/src/device/input/device.rs @@ -1,15 +1,20 @@ -use crate::{device::VirtioDeviceError, queue::VirtQueue, VirtioPciCommonCfg}; -use alloc::{boxed::Box, vec::Vec}; -use bitflags::bitflags; -use jinux_frame::sync::Mutex; -use jinux_frame::{io_mem::IoMem, offset_of}; -use jinux_pci::{capability::vendor::virtio::CapabilityVirtioData, util::BAR}; -use jinux_util::{field_ptr, safe_ptr::SafePtr}; -use pod::Pod; +use core::fmt::Debug; -use super::{ - InputConfigSelect, InputEvent, VirtioInputConfig, QUEUE_EVENT, QUEUE_SIZE, QUEUE_STATUS, +use crate::{device::VirtioDeviceError, queue::VirtQueue, transport::VirtioTransport}; +use alloc::{ + boxed::Box, + string::{String, ToString}, + sync::Arc, + vec::Vec, }; +use bitflags::bitflags; +use jinux_frame::{io_mem::IoMem, offset_of, sync::SpinLock, trap::TrapFrame}; +use jinux_util::{field_ptr, safe_ptr::SafePtr}; +use log::{debug, info}; +use pod::Pod; +use virtio_input_decoder::{DecodeType, Decoder}; + +use super::{InputConfigSelect, InputEvent, VirtioInputConfig, QUEUE_EVENT, QUEUE_STATUS}; bitflags! { /// The properties of input device. @@ -46,56 +51,31 @@ pub const FF: u8 = 0x15; pub const PWR: u8 = 0x16; pub const FF_STATUS: u8 = 0x17; +const QUEUE_SIZE: u16 = 64; + /// Virtual human interface devices such as keyboards, mice and tablets. /// /// An instance of the virtio device represents one such input device. /// Device behavior mirrors that of the evdev layer in Linux, /// making pass-through implementations on top of evdev easy. -#[derive(Debug)] pub struct InputDevice { config: SafePtr, - event_queue: Mutex, + event_queue: SpinLock, status_queue: VirtQueue, - pub event_buf: Mutex>, + event_buf: SpinLock>, + callbacks: SpinLock>>, + transport: Box, } impl InputDevice { /// Create a new VirtIO-Input driver. /// msix_vector_left should at least have one element or n elements where n is the virtqueue amount - pub fn new( - cap: &CapabilityVirtioData, - bars: [Option; 6], - common_cfg: &SafePtr, - notify_base_address: usize, - notify_off_multiplier: u32, - mut msix_vector_left: Vec, - ) -> Result { - let mut event_buf = Box::new([InputEvent::default(); QUEUE_SIZE]); - let vector_left = msix_vector_left.len(); - let mut next_msix_vector = msix_vector_left.pop().unwrap(); - let mut event_queue = VirtQueue::new( - &common_cfg, - QUEUE_EVENT, - QUEUE_SIZE as u16, - notify_base_address, - notify_off_multiplier, - next_msix_vector, - ) - .expect("create event virtqueue failed"); - next_msix_vector = if vector_left == 1 { - next_msix_vector - } else { - msix_vector_left.pop().unwrap() - }; - let status_queue = VirtQueue::new( - &common_cfg, - QUEUE_STATUS, - QUEUE_SIZE as u16, - notify_base_address, - notify_off_multiplier, - next_msix_vector, - ) - .expect("create status virtqueue failed"); + pub fn init(mut transport: Box) -> Result<(), VirtioDeviceError> { + let mut event_buf = Box::new([InputEvent::default(); QUEUE_SIZE as usize]); + let mut event_queue = VirtQueue::new(QUEUE_EVENT, QUEUE_SIZE, transport.as_mut()) + .expect("create event virtqueue failed"); + let status_queue = VirtQueue::new(QUEUE_STATUS, QUEUE_SIZE, transport.as_mut()) + .expect("create status virtqueue failed"); for (i, event) in event_buf.as_mut().iter_mut().enumerate() { let token = event_queue.add(&[], &[event.as_bytes_mut()]); @@ -109,24 +89,56 @@ impl InputDevice { } } - Ok(Self { - config: VirtioInputConfig::new(cap, bars), - event_queue: Mutex::new(event_queue), + let mut device = Self { + config: VirtioInputConfig::new(transport.as_mut()), + event_queue: SpinLock::new(event_queue), status_queue, - event_buf: Mutex::new(event_buf), - }) - } + event_buf: SpinLock::new(event_buf), + transport, + callbacks: SpinLock::new(Vec::new()), + }; - // /// Acknowledge interrupt and process events. - // pub fn ack_interrupt(&mut self) -> bool { - // self.transport.ack_interrupt() - // } + let mut raw_name: [u8; 128] = [0; 128]; + device.query_config_select(InputConfigSelect::IdName, 0, &mut raw_name); + let name = String::from_utf8(raw_name.to_vec()).unwrap(); + info!("Virtio input device name:{}", name); + + let mut prop: [u8; 128] = [0; 128]; + device.query_config_select(InputConfigSelect::PropBits, 0, &mut prop); + let input_prop = InputProp::from_bits(prop[0]).unwrap(); + debug!("input device prop:{:?}", input_prop); + + fn handle_input(_: &TrapFrame) { + debug!("Handle Virtio input interrupt"); + let device = jinux_input::get_device(&(super::DEVICE_NAME.to_string())).unwrap(); + device.handle_irq().unwrap(); + } + + fn config_space_change(_: &TrapFrame) { + debug!("input device config space change"); + } + + device + .transport + .register_cfg_callback(Box::new(config_space_change)) + .unwrap(); + device + .transport + .register_queue_callback(QUEUE_EVENT, Box::new(handle_input), false) + .unwrap(); + + device.transport.finish_init(); + + jinux_input::register_device(super::DEVICE_NAME.to_string(), Arc::new(device)); + + Ok(()) + } /// Pop the pending event. pub fn pop_pending_event(&self) -> Option { let mut lock = self.event_queue.lock(); if let Ok((token, _)) = lock.pop_used() { - if token >= QUEUE_SIZE as u16 { + if token >= QUEUE_SIZE { return None; } let event = &mut self.event_buf.lock()[token as usize]; @@ -167,3 +179,49 @@ impl InputDevice { 0 } } + +impl jinux_input::InputDevice for InputDevice { + fn handle_irq(&self) -> Option<()> { + // one interrupt may contains serval input, so it should loop + loop { + let event = self.pop_pending_event()?; + let dtype = match Decoder::decode( + event.event_type as usize, + event.code as usize, + event.value as usize, + ) { + Ok(dtype) => dtype, + Err(_) => return Some(()), + }; + let lock = self.callbacks.lock(); + for callback in lock.iter() { + callback.call((dtype,)); + } + match dtype { + virtio_input_decoder::DecodeType::Key(key, r#type) => { + info!("{:?} {:?}", key, r#type); + } + virtio_input_decoder::DecodeType::Mouse(mouse) => info!("{:?}", mouse), + } + } + } + + fn register_callbacks( + &self, + function: &'static (dyn Fn(virtio_input_decoder::DecodeType) + Send + Sync), + ) { + self.callbacks.lock().push(Arc::new(function)) + } +} + +impl Debug for InputDevice { + fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result { + f.debug_struct("InputDevice") + .field("config", &self.config) + .field("event_queue", &self.event_queue) + .field("status_queue", &self.status_queue) + .field("event_buf", &self.event_buf) + .field("transport", &self.transport) + .finish() + } +} diff --git a/services/comps/virtio/src/device/input/mod.rs b/services/comps/virtio/src/device/input/mod.rs index a801f4966..b0936e215 100644 --- a/services/comps/virtio/src/device/input/mod.rs +++ b/services/comps/virtio/src/device/input/mod.rs @@ -25,13 +25,13 @@ // pub mod device; -use core::mem::size_of; - +use crate::transport::VirtioTransport; use jinux_frame::io_mem::IoMem; -use jinux_pci::{capability::vendor::virtio::CapabilityVirtioData, util::BAR}; use jinux_util::safe_ptr::SafePtr; use pod::Pod; +pub static DEVICE_NAME: &'static str = "Virtio-Input"; + /// Select value used for [`VirtIOInput::query_config_select()`]. #[repr(u8)] #[derive(Debug, Clone, Copy)] @@ -72,20 +72,9 @@ pub struct VirtioInputConfig { } impl VirtioInputConfig { - pub(crate) fn new(cap: &CapabilityVirtioData, bars: [Option; 6]) -> SafePtr { - let bar = cap.bar; - let offset = cap.offset; - match bars[bar as usize].expect("Virtio pci block cfg:bar is none") { - BAR::Memory(address, _, _, _) => SafePtr::new( - IoMem::new( - (address as usize + offset as usize) - ..(address as usize + offset as usize + size_of::()), - ) - .unwrap(), - 0, - ), - BAR::IO(_, _) => panic!("Virtio pci block cfg:bar is IO type"), - } + pub(self) fn new(transport: &mut dyn VirtioTransport) -> SafePtr { + let memory = transport.device_config_memory(); + SafePtr::new(memory, 0) } } @@ -121,8 +110,5 @@ pub struct InputEvent { pub value: u32, } -const QUEUE_EVENT: usize = 0; -const QUEUE_STATUS: usize = 1; - -// a parameter that can change -const QUEUE_SIZE: usize = 64; +const QUEUE_EVENT: u16 = 0; +const QUEUE_STATUS: u16 = 1; diff --git a/services/comps/virtio/src/device/mod.rs b/services/comps/virtio/src/device/mod.rs index 6342d86e8..9b314a349 100644 --- a/services/comps/virtio/src/device/mod.rs +++ b/services/comps/virtio/src/device/mod.rs @@ -1,39 +1,36 @@ -use crate::{ - device::block::device::BLKDevice, queue::QueueError, Feature, VirtioDeviceType, - VirtioPciCommonCfg, -}; -use alloc::vec::Vec; -use jinux_frame::io_mem::IoMem; -use jinux_pci::{ - capability::{vendor::virtio::CapabilityVirtioData, Capability}, - util::BAR, -}; -use jinux_util::safe_ptr::SafePtr; - -use self::{input::device::InputDevice, network::device::NetworkDevice}; +use crate::queue::QueueError; +use int_to_c_enum::TryFromInt; pub mod block; pub mod input; pub mod network; -pub(crate) const PCI_VIRTIO_CAP_COMMON_CFG: u8 = 1; -pub(crate) const PCI_VIRTIO_CAP_NOTIFY_CFG: u8 = 2; -pub(crate) const PCI_VIRTIO_CAP_ISR_CFG: u8 = 3; -pub(crate) const PCI_VIRTIO_CAP_DEVICE_CFG: u8 = 4; -pub(crate) const PCI_VIRTIO_CAP_PCI_CFG: u8 = 5; - -pub enum VirtioDevice { - Network(NetworkDevice), - Block(BLKDevice), - Console, - Entropy, - TraditionalMemoryBalloon, - ScsiHost, - GPU, - Input(InputDevice), - Crypto, - Socket, - Unknown, +#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, TryFromInt)] +#[repr(u8)] +pub enum VirtioDeviceType { + Invalid = 0, + Network = 1, + Block = 2, + Console = 3, + Entropy = 4, + TraditionalMemoryBalloon = 5, + IoMemory = 6, + Rpmsg = 7, + ScsiHost = 8, + Transport9P = 9, + Mac80211Wlan = 10, + RprocSerial = 11, + VirtioCAIF = 12, + MemoryBalloon = 13, + GPU = 16, + Timer = 17, + Input = 18, + Socket = 19, + Crypto = 20, + SignalDistribution = 21, + Pstore = 22, + IOMMU = 23, + Memory = 24, } #[derive(Debug)] @@ -52,131 +49,3 @@ impl From for VirtioDeviceError { VirtioDeviceError::QueueUnknownError } } - -pub struct VirtioInfo { - pub device_type: VirtioDeviceType, - pub notify_base_address: u64, - pub notify_off_multiplier: u32, - pub common_cfg_frame_ptr: SafePtr, - pub device_cap_cfg: CapabilityVirtioData, -} - -impl VirtioInfo { - pub(crate) fn new( - device_type: VirtioDeviceType, - bars: [Option; 6], - virtio_cap_list: Vec<&Capability>, - ) -> Result { - let mut notify_base_address = 0; - let mut notify_off_multiplier = 0; - let mut common_cfg_frame_ptr_some = None; - let mut device_cap_cfg = None; - for cap in virtio_cap_list.iter() { - match cap.data { - jinux_pci::capability::CapabilityData::VNDR(vndr_data) => match vndr_data { - jinux_pci::capability::vendor::CapabilityVNDRData::VIRTIO(cap_data) => { - match cap_data.cfg_type { - PCI_VIRTIO_CAP_COMMON_CFG => { - common_cfg_frame_ptr_some = - Some(VirtioPciCommonCfg::new(&cap_data, bars)); - } - PCI_VIRTIO_CAP_NOTIFY_CFG => { - notify_off_multiplier = cap_data.option.unwrap(); - match bars[cap_data.bar as usize] - .expect("initialize PCIDevice failed, notify bar is None") - { - BAR::Memory(address, _, _, _) => { - notify_base_address = address + cap_data.offset as u64; - } - BAR::IO(_, _) => { - panic!("initialize PCIDevice failed, notify bar is IO Type") - } - }; - } - PCI_VIRTIO_CAP_ISR_CFG => {} - PCI_VIRTIO_CAP_DEVICE_CFG => { - device_cap_cfg = Some(cap_data); - } - PCI_VIRTIO_CAP_PCI_CFG => {} - _ => panic!("unsupport cfg, cfg_type:{}", cap_data.cfg_type), - }; - } - }, - _ => { - return Err(VirtioDeviceError::CapabilityListError); - } - } - } - Ok(Self { - notify_base_address, - notify_off_multiplier, - common_cfg_frame_ptr: common_cfg_frame_ptr_some - .ok_or(VirtioDeviceError::CapabilityListError)?, - device_cap_cfg: device_cap_cfg.ok_or(VirtioDeviceError::CapabilityListError)?, - device_type, - }) - } -} - -impl VirtioDevice { - /// call this function after features_ok - pub(crate) fn new( - virtio_info: &VirtioInfo, - bars: [Option; 6], - msix_vector_left: Vec, - ) -> Result { - let device = match virtio_info.device_type { - VirtioDeviceType::Block => VirtioDevice::Block(BLKDevice::new( - &virtio_info.device_cap_cfg, - bars, - &virtio_info.common_cfg_frame_ptr, - virtio_info.notify_base_address as usize, - virtio_info.notify_off_multiplier, - msix_vector_left, - )?), - VirtioDeviceType::Input => VirtioDevice::Input(InputDevice::new( - &virtio_info.device_cap_cfg, - bars, - &virtio_info.common_cfg_frame_ptr, - virtio_info.notify_base_address as usize, - virtio_info.notify_off_multiplier, - msix_vector_left, - )?), - VirtioDeviceType::Network => VirtioDevice::Network(NetworkDevice::new( - &virtio_info.device_cap_cfg, - bars, - &virtio_info.common_cfg_frame_ptr, - virtio_info.notify_base_address as usize, - virtio_info.notify_off_multiplier, - msix_vector_left, - )?), - _ => { - panic!("initialize PCIDevice failed, unsupport Virtio Device Type") - } - }; - Ok(device) - } - - pub(crate) fn negotiate_features(features: u64, device_type: VirtioDeviceType) -> u64 { - let mask = ((1u64 << 24) - 1) | (((1u64 << 24) - 1) << 50); - let device_specified_features = features & mask; - let device_support_features = match device_type { - VirtioDeviceType::Network => { - NetworkDevice::negotiate_features(device_specified_features) - } - VirtioDeviceType::Block => BLKDevice::negotiate_features(device_specified_features), - VirtioDeviceType::Console => todo!(), - VirtioDeviceType::Entropy => todo!(), - VirtioDeviceType::TraditionalMemoryBalloon => todo!(), - VirtioDeviceType::ScsiHost => todo!(), - VirtioDeviceType::GPU => todo!(), - VirtioDeviceType::Input => InputDevice::negotiate_features(device_specified_features), - VirtioDeviceType::Crypto => todo!(), - VirtioDeviceType::Socket => todo!(), - VirtioDeviceType::Unknown => todo!(), - }; - let mut support_feature = Feature::from_bits_truncate(features); - support_feature.remove(Feature::RING_EVENT_IDX); - features & (support_feature.bits | device_support_features) - } -} diff --git a/services/comps/virtio/src/device/network/config.rs b/services/comps/virtio/src/device/network/config.rs index 870c85eb6..71c2d73d7 100644 --- a/services/comps/virtio/src/device/network/config.rs +++ b/services/comps/virtio/src/device/network/config.rs @@ -1,12 +1,10 @@ -use core::mem::size_of; - use bitflags::bitflags; use jinux_frame::io_mem::IoMem; -use jinux_pci::{capability::vendor::virtio::CapabilityVirtioData, util::BAR}; +use jinux_network::EthernetAddr; use jinux_util::safe_ptr::SafePtr; use pod::Pod; -use super::device::EthernetAddr; +use crate::transport::VirtioTransport; bitflags! { /// Virtio Net Feature bits. @@ -73,19 +71,8 @@ pub struct VirtioNetConfig { } impl VirtioNetConfig { - pub(crate) fn new(cap: &CapabilityVirtioData, bars: [Option; 6]) -> SafePtr { - let bar = cap.bar; - let offset = cap.offset; - match bars[bar as usize].expect("Virtio pci net cfg:bar is none") { - BAR::Memory(address, _, _, _) => SafePtr::new( - IoMem::new( - (address as usize + offset as usize) - ..(address as usize + offset as usize + size_of::()), - ) - .unwrap(), - 0, - ), - BAR::IO(_, _) => panic!("Virtio pci net cfg:bar is IO type"), - } + pub(super) fn new(transport: &mut dyn VirtioTransport) -> SafePtr { + let memory = transport.device_config_memory(); + SafePtr::new(memory, 0) } } diff --git a/services/comps/virtio/src/device/network/device.rs b/services/comps/virtio/src/device/network/device.rs index 8cca99bc3..dedb24973 100644 --- a/services/comps/virtio/src/device/network/device.rs +++ b/services/comps/virtio/src/device/network/device.rs @@ -1,34 +1,23 @@ -use core::hint::spin_loop; +use core::{fmt::Debug, hint::spin_loop, mem::size_of}; -use alloc::vec::Vec; -use jinux_frame::{io_mem::IoMem, offset_of}; -use jinux_pci::{capability::vendor::virtio::CapabilityVirtioData, util::BAR}; -use jinux_util::{field_ptr, safe_ptr::SafePtr, slot_vec::SlotVec}; +use alloc::{boxed::Box, string::ToString, sync::Arc, vec::Vec}; +use jinux_frame::{offset_of, sync::SpinLock, trap::TrapFrame}; +use jinux_network::{ + buffer::{RxBuffer, TxBuffer}, + EthernetAddr, NetDeviceIrqHandler, VirtioNetError, +}; +use jinux_util::{field_ptr, slot_vec::SlotVec}; use log::debug; use pod::Pod; +use smoltcp::phy::{DeviceCapabilities, Medium}; use crate::{ device::{network::config::NetworkFeatures, VirtioDeviceError}, queue::{QueueError, VirtQueue}, - VirtioPciCommonCfg, + transport::VirtioTransport, }; -use super::{ - buffer::{RxBuffer, TxBuffer}, - config::VirtioNetConfig, - header::VirtioNetHdr, -}; - -#[derive(Debug, Clone, Copy, Pod)] -#[repr(C)] -pub struct EthernetAddr(pub [u8; 6]); - -#[derive(Debug, Clone, Copy)] -pub enum VirtioNetError { - NotReady, - WrongToken, - Unknown, -} +use super::{config::VirtioNetConfig, header::VirtioNetHdr}; pub struct NetworkDevice { config: VirtioNetConfig, @@ -36,16 +25,8 @@ pub struct NetworkDevice { send_queue: VirtQueue, recv_queue: VirtQueue, rx_buffers: SlotVec, -} - -impl From for VirtioNetError { - fn from(value: QueueError) -> Self { - match value { - QueueError::NotReady => VirtioNetError::NotReady, - QueueError::WrongToken => VirtioNetError::WrongToken, - _ => VirtioNetError::Unknown, - } - } + callbacks: Vec>, + transport: Box, } impl NetworkDevice { @@ -57,33 +38,11 @@ impl NetworkDevice { network_features.bits() } - pub fn new( - cap: &CapabilityVirtioData, - bars: [Option; 6], - common_cfg: &SafePtr, - notify_base_address: usize, - notify_off_multiplier: u32, - mut msix_vector_left: Vec, - ) -> Result { - let virtio_net_config = VirtioNetConfig::new(cap, bars); - let features = { - // select low - field_ptr!(common_cfg, VirtioPciCommonCfg, device_feature_select) - .write(&0u32) - .unwrap(); - let device_feature_low = field_ptr!(common_cfg, VirtioPciCommonCfg, device_feature) - .read() - .unwrap() as u64; - // select high - field_ptr!(common_cfg, VirtioPciCommonCfg, device_feature_select) - .write(&1u32) - .unwrap(); - let device_feature_high = field_ptr!(common_cfg, VirtioPciCommonCfg, device_feature) - .read() - .unwrap() as u64; - let device_feature = device_feature_high << 32 | device_feature_low; - NetworkFeatures::from_bits_truncate(Self::negotiate_features(device_feature)) - }; + pub fn init(mut transport: Box) -> Result<(), VirtioDeviceError> { + let virtio_net_config = VirtioNetConfig::new(transport.as_mut()); + let features = NetworkFeatures::from_bits_truncate(Self::negotiate_features( + transport.device_features(), + )); debug!("virtio_net_config = {:?}", virtio_net_config); debug!("features = {:?}", features); let mac_addr = field_ptr!(&virtio_net_config, VirtioNetConfig, mac) @@ -93,38 +52,14 @@ impl NetworkDevice { .read() .unwrap(); debug!("mac addr = {:x?}, status = {:?}", mac_addr, status); - let (recv_msix_vec, send_msix_vec) = { - if msix_vector_left.len() >= 2 { - let vector1 = msix_vector_left.pop().unwrap(); - let vector2 = msix_vector_left.pop().unwrap(); - (vector1, vector2) - } else { - let vector = msix_vector_left.pop().unwrap(); - (vector, vector) - } - }; - let mut recv_queue = VirtQueue::new( - &common_cfg, - QUEUE_RECV as usize, - QUEUE_SIZE, - notify_base_address, - notify_off_multiplier, - recv_msix_vec, - ) - .expect("creating recv queue fails"); - let send_queue = VirtQueue::new( - &common_cfg, - QUEUE_SEND as usize, - QUEUE_SIZE, - notify_base_address, - notify_off_multiplier, - send_msix_vec, - ) - .expect("create send queue fails"); + let mut recv_queue = VirtQueue::new(QUEUE_RECV, QUEUE_SIZE, transport.as_mut()) + .expect("creating recv queue fails"); + let send_queue = VirtQueue::new(QUEUE_SEND, QUEUE_SIZE, transport.as_mut()) + .expect("create send queue fails"); let mut rx_buffers = SlotVec::new(); for i in 0..QUEUE_SIZE { - let mut rx_buffer = RxBuffer::new(RX_BUFFER_LEN); + let mut rx_buffer = RxBuffer::new(RX_BUFFER_LEN, size_of::()); let token = recv_queue.add(&[], &mut [rx_buffer.buf_mut()])?; assert_eq!(i, token); assert_eq!(rx_buffers.put(rx_buffer) as u16, i); @@ -134,19 +69,48 @@ impl NetworkDevice { debug!("notify receive queue"); recv_queue.notify(); } - - Ok(Self { + let mut device = Self { config: virtio_net_config.read().unwrap(), mac_addr, send_queue, recv_queue, rx_buffers, - }) + transport, + callbacks: Vec::new(), + }; + device.transport.finish_init(); + /// Interrupt handler if network device config space changes + fn config_space_change(_: &TrapFrame) { + debug!("network device config space change"); + } + + /// Interrupt handler if network device receives some packet + fn handle_network_event(_: &TrapFrame) { + jinux_network::handle_recv_irq(&(super::DEVICE_NAME.to_string())); + } + + device + .transport + .register_cfg_callback(Box::new(config_space_change)) + .unwrap(); + device + .transport + .register_queue_callback(QUEUE_RECV, Box::new(handle_network_event), false) + .unwrap(); + + jinux_network::register_device( + super::DEVICE_NAME.to_string(), + Arc::new(SpinLock::new(Box::new(device))), + ); + Ok(()) } /// Add a rx buffer to recv queue fn add_rx_buffer(&mut self, mut rx_buffer: RxBuffer) -> Result<(), VirtioNetError> { - let token = self.recv_queue.add(&[], &mut [rx_buffer.buf_mut()])?; + let token = self + .recv_queue + .add(&[], &mut [rx_buffer.buf_mut()]) + .map_err(queue_to_network_error)?; assert!(self.rx_buffers.put_at(token as usize, rx_buffer).is_none()); if self.recv_queue.should_notify() { self.recv_queue.notify(); @@ -154,30 +118,10 @@ impl NetworkDevice { Ok(()) } - // Acknowledge interrupt - pub fn ack_interrupt(&self) -> bool { - todo!() - } - - /// The mac address - pub fn mac_addr(&self) -> EthernetAddr { - self.mac_addr - } - - /// Send queue is ready - pub fn can_send(&self) -> bool { - self.send_queue.available_desc() >= 2 - } - - /// Receive queue is ready - pub fn can_receive(&self) -> bool { - self.recv_queue.can_pop() - } - /// Receive a packet from network. If packet is ready, returns a RxBuffer containing the packet. /// Otherwise, return NotReady error. - pub fn receive(&mut self) -> Result { - let (token, len) = self.recv_queue.pop_used()?; + fn receive(&mut self) -> Result { + let (token, len) = self.recv_queue.pop_used().map_err(queue_to_network_error)?; debug!("receive packet: token = {}, len = {}", token, len); let mut rx_buffer = self .rx_buffers @@ -186,17 +130,18 @@ impl NetworkDevice { rx_buffer.set_packet_len(len as usize); // FIXME: Ideally, we can reuse the returned buffer without creating new buffer. // But this requires locking device to be compatible with smoltcp interface. - let new_rx_buffer = RxBuffer::new(RX_BUFFER_LEN); + let new_rx_buffer = RxBuffer::new(RX_BUFFER_LEN, size_of::()); self.add_rx_buffer(new_rx_buffer)?; Ok(rx_buffer) } /// Send a packet to network. Return until the request completes. - pub fn send(&mut self, tx_buffer: TxBuffer) -> Result<(), VirtioNetError> { + fn send(&mut self, tx_buffer: TxBuffer) -> Result<(), VirtioNetError> { let header = VirtioNetHdr::default(); let token = self .send_queue - .add(&[header.as_bytes(), tx_buffer.buf()], &mut [])?; + .add(&[header.as_bytes(), tx_buffer.buf()], &mut []) + .map_err(queue_to_network_error)?; if self.send_queue.should_notify() { self.send_queue.notify(); @@ -206,7 +151,7 @@ impl NetworkDevice { spin_loop(); } // Pop out the buffer, so we can reuse the send queue further - let (pop_token, _) = self.send_queue.pop_used()?; + let (pop_token, _) = self.send_queue.pop_used().map_err(queue_to_network_error)?; debug_assert!(pop_token == token); if pop_token != token { return Err(VirtioNetError::WrongToken); @@ -216,6 +161,56 @@ impl NetworkDevice { } } +fn queue_to_network_error(err: QueueError) -> VirtioNetError { + match err { + QueueError::NotReady => VirtioNetError::NotReady, + QueueError::WrongToken => VirtioNetError::WrongToken, + _ => VirtioNetError::Unknown, + } +} + +impl jinux_network::NetworkDevice for NetworkDevice { + fn mac_addr(&self) -> EthernetAddr { + self.mac_addr + } + + fn capabilities(&self) -> DeviceCapabilities { + let mut caps = DeviceCapabilities::default(); + caps.max_transmission_unit = 1536; + caps.max_burst_size = Some(1); + caps.medium = Medium::Ethernet; + caps + } + + fn can_receive(&self) -> bool { + self.recv_queue.can_pop() + } + + fn can_send(&self) -> bool { + self.send_queue.available_desc() >= 2 + } + + fn receive(&mut self) -> Result { + self.receive() + } + + fn send(&mut self, tx_buffer: TxBuffer) -> Result<(), VirtioNetError> { + self.send(tx_buffer) + } +} + +impl Debug for NetworkDevice { + fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result { + f.debug_struct("NetworkDevice") + .field("config", &self.config) + .field("mac_addr", &self.mac_addr) + .field("send_queue", &self.send_queue) + .field("recv_queue", &self.recv_queue) + .field("transport", &self.transport) + .finish() + } +} + const QUEUE_RECV: u16 = 0; const QUEUE_SEND: u16 = 1; diff --git a/services/comps/virtio/src/device/network/mod.rs b/services/comps/virtio/src/device/network/mod.rs index 0e163938d..99b6ec5a3 100644 --- a/services/comps/virtio/src/device/network/mod.rs +++ b/services/comps/virtio/src/device/network/mod.rs @@ -1,4 +1,5 @@ -pub mod buffer; pub mod config; pub mod device; pub mod header; + +pub static DEVICE_NAME: &'static str = "Virtio-Net"; diff --git a/services/comps/virtio/src/lib.rs b/services/comps/virtio/src/lib.rs index 65ad6a4dc..d685dbff1 100644 --- a/services/comps/virtio/src/lib.rs +++ b/services/comps/virtio/src/lib.rs @@ -2,121 +2,86 @@ #![no_std] #![forbid(unsafe_code)] #![allow(dead_code)] +#![feature(fn_traits)] extern crate alloc; use component::init_component; -use core::{mem::size_of, str::FromStr}; -use alloc::{collections::VecDeque, string::String, sync::Arc, vec::Vec}; +use alloc::boxed::Box; use bitflags::bitflags; use component::ComponentInitError; -use device::VirtioDevice; -use jinux_frame::sync::Mutex; -use jinux_frame::{io_mem::IoMem, offset_of, trap::TrapFrame}; -use jinux_pci::{util::BAR, PciDevice}; -use jinux_util::{field_ptr, safe_ptr::SafePtr}; -use log::{debug, info}; -use pod::Pod; -use spin::Once; +use device::{ + block::device::BlockDevice, input::device::InputDevice, network::device::NetworkDevice, + VirtioDeviceType, +}; +use log::{error, warn}; +use transport::{pci::VIRTIO_PCI_DRIVER, DeviceStatus}; -use crate::device::VirtioInfo; -use jinux_pci::{capability::vendor::virtio::CapabilityVirtioData, msix::MSIX}; +use crate::transport::VirtioTransport; pub mod device; pub mod queue; - -pub static VIRTIO_COMPONENT: Once = Once::new(); +mod transport; #[init_component] fn virtio_component_init() -> Result<(), ComponentInitError> { - let a = VIRTIOComponent::init()?; - VIRTIO_COMPONENT.call_once(|| a); + // Find all devices and register them to the corresponding crate + transport::init(); + let pci_driver = VIRTIO_PCI_DRIVER.get().unwrap(); + while let Some(mut transport) = pci_driver.pop_device_tranport() { + // Reset device + transport.set_device_status(DeviceStatus::empty()).unwrap(); + // Set to acknowledge + transport + .set_device_status(DeviceStatus::ACKNOWLEDGE | DeviceStatus::DRIVER) + .unwrap(); + // negotiate features + negotiate_features(&mut transport); + + // change to features ok status + transport + .set_device_status( + DeviceStatus::ACKNOWLEDGE | DeviceStatus::DRIVER | DeviceStatus::FEATURES_OK, + ) + .unwrap(); + let transport = Box::new(transport); + let device_type = transport.device_type(); + let res = match transport.device_type() { + VirtioDeviceType::Block => BlockDevice::init(transport), + VirtioDeviceType::Input => InputDevice::init(transport), + VirtioDeviceType::Network => NetworkDevice::init(transport), + _ => { + warn!("[Virtio]: Found unimplemented device:{:?}", device_type); + Ok(()) + } + }; + if res.is_err() { + error!( + "[Virtio]: Device initialization error:{:?}, device type:{:?}", + res, device_type + ); + } + } Ok(()) } -pub struct VIRTIOComponent { - virtio_devices: Mutex>, -} +fn negotiate_features(transport: &mut dyn VirtioTransport) { + let features = transport.device_features(); -impl VIRTIOComponent { - pub fn init() -> Result { - let pci_devices = - jinux_pci::PCI_COMPONENT - .get() - .ok_or(ComponentInitError::UninitializedDependencies( - String::from_str("PCI").unwrap(), - ))?; - let mut virtio_devices = VecDeque::new(); - for index in 0..pci_devices.device_amount() { - let pci_device = pci_devices.get_pci_devices(index).unwrap(); - if pci_device.id.vendor_id == 0x1af4 { - virtio_devices.push_back(PCIVirtioDevice::new(pci_device)); - } - } - Ok(Self { - virtio_devices: Mutex::new(virtio_devices), - }) - } - - pub const fn name() -> &'static str { - "Virtio" - } - // 0~65535 - pub const fn priority() -> u16 { - 256 - } -} - -impl VIRTIOComponent { - pub fn pop(self: &Self) -> Option { - self.virtio_devices.lock().pop_front() - } - - pub fn get_device(self: &Self, device_type: VirtioDeviceType) -> Vec { - let mut devices = Vec::new(); - let mut lock = self.virtio_devices.lock(); - let len = lock.len(); - for _ in 0..len { - let device = lock.pop_front().unwrap(); - let d_type = VirtioDeviceType::from_virtio_device(&device.device); - if d_type == device_type { - devices.push(device); - } else { - lock.push_back(device); - } - } - devices - } -} - -bitflags! { - /// The device status field. - pub struct DeviceStatus: u8 { - /// Indicates that the guest OS has found the device and recognized it - /// as a valid virtio device. - const ACKNOWLEDGE = 1; - - /// Indicates that the guest OS knows how to drive the device. - const DRIVER = 2; - - /// Indicates that something went wrong in the guest, and it has given - /// up on the device. This could be an internal error, or the driver - /// didn’t like the device for some reason, or even a fatal error - /// during device operation. - const FAILED = 128; - - /// Indicates that the driver has acknowledged all the features it - /// understands, and feature negotiation is complete. - const FEATURES_OK = 8; - - /// Indicates that the driver is set up and ready to drive the device. - const DRIVER_OK = 4; - - /// Indicates that the device has experienced an error from which it - /// can’t recover. - const DEVICE_NEEDS_RESET = 64; - } + let mask = ((1u64 << 24) - 1) | (((1u64 << 24) - 1) << 50); + let device_specified_features = features & mask; + let device_support_features = match transport.device_type() { + VirtioDeviceType::Network => NetworkDevice::negotiate_features(device_specified_features), + VirtioDeviceType::Block => BlockDevice::negotiate_features(device_specified_features), + VirtioDeviceType::Input => InputDevice::negotiate_features(device_specified_features), + _ => device_specified_features, + }; + let mut support_feature = Feature::from_bits_truncate(features); + support_feature.remove(Feature::RING_EVENT_IDX); + transport + .set_driver_features(features & (support_feature.bits | device_support_features)) + .unwrap(); } bitflags! { @@ -142,231 +107,3 @@ bitflags! { const NOTIFICATION_DATA = 1 << 38; } } - -#[derive(Debug, Default, Copy, Clone, Pod)] -#[repr(C)] -pub struct VirtioPciCommonCfg { - device_feature_select: u32, - device_feature: u32, - driver_feature_select: u32, - driver_feature: u32, - pub config_msix_vector: u16, - num_queues: u16, - pub device_status: u8, - config_generation: u8, - - queue_select: u16, - queue_size: u16, - pub queue_msix_vector: u16, - queue_enable: u16, - queue_notify_off: u16, - queue_desc: u64, - queue_driver: u64, - queue_device: u64, -} - -impl VirtioPciCommonCfg { - pub(crate) fn new(cap: &CapabilityVirtioData, bars: [Option; 6]) -> SafePtr { - let bar = cap.bar; - let offset = cap.offset; - match bars[bar as usize].expect("Virtio pci common cfg:bar is none") { - BAR::Memory(address, _, _, _) => { - debug!("common_cfg addr:{:x}", (address as usize + offset as usize)); - SafePtr::new( - IoMem::new( - (address as usize + offset as usize) - ..(address as usize + offset as usize + size_of::()), - ) - .unwrap(), - 0, - ) - } - BAR::IO(first, second) => { - panic!( - "Virtio pci common cfg:bar is IO type, value:{:x}, {:x}", - first, second - ) - } - } - } -} - -#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord)] -pub enum VirtioDeviceType { - Network, - Block, - Console, - Entropy, - TraditionalMemoryBalloon, - ScsiHost, - GPU, - Input, - Crypto, - Socket, - Unknown, -} - -impl VirtioDeviceType { - pub fn from_virtio_device(device: &VirtioDevice) -> Self { - match device { - VirtioDevice::Network(_) => VirtioDeviceType::Network, - VirtioDevice::Block(_) => VirtioDeviceType::Block, - VirtioDevice::Console => VirtioDeviceType::Console, - VirtioDevice::Entropy => VirtioDeviceType::Entropy, - VirtioDevice::TraditionalMemoryBalloon => VirtioDeviceType::TraditionalMemoryBalloon, - VirtioDevice::ScsiHost => VirtioDeviceType::ScsiHost, - VirtioDevice::GPU => VirtioDeviceType::GPU, - VirtioDevice::Input(_) => VirtioDeviceType::Input, - VirtioDevice::Crypto => VirtioDeviceType::Crypto, - VirtioDevice::Socket => VirtioDeviceType::Socket, - VirtioDevice::Unknown => VirtioDeviceType::Unknown, - } - } -} - -pub struct PCIVirtioDevice { - /// common config of one device - pub common_cfg: SafePtr, - pub device: VirtioDevice, - pub msix: MSIX, -} - -impl PCIVirtioDevice { - /// create a new PCI Virtio Device, note that this function will stop with device status features ok - pub fn new(dev: Arc) -> Self { - assert_eq!(dev.id.vendor_id, 0x1af4); - let device_type = match dev.id.device_id { - 0x1000 | 0x1041 => VirtioDeviceType::Network, - 0x1001 | 0x1042 => VirtioDeviceType::Block, - 0x1002 | 0x1043 => VirtioDeviceType::TraditionalMemoryBalloon, - 0x1003 | 0x1044 => VirtioDeviceType::Console, - 0x1004 | 0x1045 => VirtioDeviceType::ScsiHost, - 0x1005 | 0x1046 => VirtioDeviceType::Entropy, - // 0x1009 | 0x104a => VirtioDeviceType::, - 0x1011 | 0x1052 => VirtioDeviceType::Input, - _ => { - panic!("initialize PCIDevice failed, unrecognized Virtio Device Type") - } - }; - info!("PCI device:{:?}", device_type); - let bars = dev.bars; - let loc = dev.loc; - let mut msix = MSIX::default(); - let mut virtio_cap_list = Vec::new(); - for cap in dev.capabilities.iter() { - match &cap.data { - jinux_pci::capability::CapabilityData::VNDR(_) => { - virtio_cap_list.push(cap); - } - jinux_pci::capability::CapabilityData::MSIX(cap_data) => { - msix = MSIX::new(&cap_data, bars, loc, cap.cap_ptr); - } - jinux_pci::capability::CapabilityData::Unknown(id) => { - panic!("unknown capability device:{}", id) - } - _ => { - panic!("PCI Virtio device should not have other type of capability") - } - } - } - // create device - let virtio_info = VirtioInfo::new(device_type, bars, virtio_cap_list).unwrap(); - let mut msix_vector_list: Vec = (0..msix.table_size).collect(); - let config_msix_vector = msix_vector_list.pop().unwrap(); - let common_cfg = &virtio_info.common_cfg_frame_ptr; - - // Reset device - field_ptr!(common_cfg, VirtioPciCommonCfg, device_status) - .write(&0u8) - .unwrap(); - - field_ptr!(common_cfg, VirtioPciCommonCfg, config_msix_vector) - .write(&config_msix_vector) - .unwrap(); - field_ptr!(common_cfg, VirtioPciCommonCfg, device_status) - .write(&(DeviceStatus::ACKNOWLEDGE | DeviceStatus::DRIVER).bits()) - .unwrap(); - // negotiate features - // get the value of device features - field_ptr!(common_cfg, VirtioPciCommonCfg, device_feature_select) - .write(&0u32) - .unwrap(); - let mut low: u32 = field_ptr!(common_cfg, VirtioPciCommonCfg, device_feature) - .read() - .unwrap(); - field_ptr!(common_cfg, VirtioPciCommonCfg, device_feature_select) - .write(&1u32) - .unwrap(); - let mut high: u32 = field_ptr!(common_cfg, VirtioPciCommonCfg, device_feature) - .read() - .unwrap(); - let mut feature = (high as u64) << 32; - feature |= low as u64; - // let the device to negotiate Features - let driver_features = VirtioDevice::negotiate_features(feature, device_type); - // write features back - low = driver_features as u32; - high = (driver_features >> 32) as u32; - field_ptr!(common_cfg, VirtioPciCommonCfg, driver_feature_select) - .write(&0u32) - .unwrap(); - field_ptr!(common_cfg, VirtioPciCommonCfg, driver_feature) - .write(&low) - .unwrap(); - field_ptr!(common_cfg, VirtioPciCommonCfg, driver_feature_select) - .write(&1u32) - .unwrap(); - field_ptr!(common_cfg, VirtioPciCommonCfg, driver_feature) - .write(&high) - .unwrap(); - // change to features ok status - field_ptr!(common_cfg, VirtioPciCommonCfg, device_status) - .write( - &(DeviceStatus::ACKNOWLEDGE | DeviceStatus::DRIVER | DeviceStatus::FEATURES_OK) - .bits(), - ) - .unwrap(); - let device = VirtioDevice::new(&virtio_info, bars, msix_vector_list).unwrap(); - // change to driver ok status - field_ptr!(common_cfg, VirtioPciCommonCfg, device_status) - .write( - &(DeviceStatus::ACKNOWLEDGE - | DeviceStatus::DRIVER - | DeviceStatus::FEATURES_OK - | DeviceStatus::DRIVER_OK) - .bits(), - ) - .unwrap(); - Self { - common_cfg: virtio_info.common_cfg_frame_ptr, - device, - msix, - } - } - - /// register all the interrupt functions, this function should call only once - pub fn register_interrupt_functions( - &mut self, - config_change_function: &'static F, - other_function: &'static T, - ) where - F: Fn(&TrapFrame) + Send + Sync + 'static, - T: Fn(&TrapFrame) + Send + Sync + 'static, - { - let config_msix_vector = - field_ptr!(&self.common_cfg, VirtioPciCommonCfg, config_msix_vector) - .read() - .unwrap() as usize; - for i in 0..self.msix.table_size as usize { - let msix = self.msix.table.get_mut(i).unwrap(); - if !msix.irq_handle.is_empty() { - panic!("function `register_queue_interrupt_functions` called more than one time"); - } - if config_msix_vector == i { - msix.irq_handle.on_active(config_change_function); - } else { - msix.irq_handle.on_active(other_function); - } - } - } -} diff --git a/services/comps/virtio/src/queue.rs b/services/comps/virtio/src/queue.rs index a0cc32199..e19e33b9e 100644 --- a/services/comps/virtio/src/queue.rs +++ b/services/comps/virtio/src/queue.rs @@ -1,12 +1,10 @@ //! Virtqueue -use super::VirtioPciCommonCfg; +use crate::transport::VirtioTransport; + use alloc::vec::Vec; use bitflags::bitflags; -use core::{ - mem::size_of, - sync::atomic::{fence, Ordering}, -}; +use core::sync::atomic::{fence, Ordering}; use jinux_frame::{ io_mem::IoMem, offset_of, @@ -60,101 +58,64 @@ pub struct VirtQueue { impl VirtQueue { /// Create a new VirtQueue. pub(crate) fn new( - cfg: &SafePtr, - idx: usize, + idx: u16, size: u16, - notify_base_address: usize, - notify_off_multiplier: u32, - msix_vector: u16, + transport: &mut dyn VirtioTransport, ) -> Result { - field_ptr!(cfg, VirtioPciCommonCfg, queue_select) - .write(&(idx as u16)) - .unwrap(); - assert_eq!( - field_ptr!(cfg, VirtioPciCommonCfg, queue_select) - .read() - .unwrap(), - idx as u16 - ); if !size.is_power_of_two() { return Err(QueueError::InvalidArgs); } - field_ptr!(cfg, VirtioPciCommonCfg, queue_size) - .write(&size) - .unwrap(); - field_ptr!(cfg, VirtioPciCommonCfg, queue_msix_vector) - .write(&msix_vector) - .unwrap(); - assert_eq!( - field_ptr!(cfg, VirtioPciCommonCfg, queue_msix_vector) - .read() - .unwrap(), - msix_vector - ); - let desc_frame_ptr: SafePtr = SafePtr::new( + let descriptor_ptr: SafePtr = SafePtr::new( VmFrameVec::allocate(&VmAllocOptions::new(1).uninit(false).can_dma(true)) .unwrap() .pop() .unwrap(), 0, ); - let avail_frame_ptr: SafePtr = SafePtr::new( + let avail_ring_ptr: SafePtr = SafePtr::new( VmFrameVec::allocate(&VmAllocOptions::new(1).uninit(false).can_dma(true)) .unwrap() .pop() .unwrap(), 0, ); - let used_frame_ptr: SafePtr = SafePtr::new( + let used_ring_ptr: SafePtr = SafePtr::new( VmFrameVec::allocate(&VmAllocOptions::new(1).uninit(false).can_dma(true)) .unwrap() .pop() .unwrap(), 0, ); - debug!("queue_desc start paddr:{:x?}", desc_frame_ptr.paddr()); - debug!("queue_driver start paddr:{:x?}", avail_frame_ptr.paddr()); - debug!("queue_device start paddr:{:x?}", used_frame_ptr.paddr()); - field_ptr!(cfg, VirtioPciCommonCfg, queue_desc) - .write(&(desc_frame_ptr.paddr() as u64)) - .unwrap(); - field_ptr!(cfg, VirtioPciCommonCfg, queue_driver) - .write(&(avail_frame_ptr.paddr() as u64)) - .unwrap(); - field_ptr!(cfg, VirtioPciCommonCfg, queue_device) - .write(&(used_frame_ptr.paddr() as u64)) + debug!("queue_desc start paddr:{:x?}", descriptor_ptr.paddr()); + debug!("queue_driver start paddr:{:x?}", avail_ring_ptr.paddr()); + debug!("queue_device start paddr:{:x?}", used_ring_ptr.paddr()); + + transport + .set_queue(idx, size, &descriptor_ptr, &avail_ring_ptr, &used_ring_ptr) .unwrap(); let mut descs = Vec::with_capacity(size as usize); - descs.push(desc_frame_ptr); + descs.push(descriptor_ptr); for i in 0..size as usize { let mut desc = descs.get(i).unwrap().clone(); desc.offset(1); descs.push(desc); } - let notify_address = notify_base_address + notify_off_multiplier as usize * idx; - let notify = SafePtr::new( - IoMem::new(notify_address..notify_address + size_of::()).unwrap(), - 0, - ); + + let notify = transport.get_notify_ptr(idx).unwrap(); // Link descriptors together. for i in 0..(size - 1) { let temp = descs.get(i as usize).unwrap(); - field_ptr!(temp, Descriptor, next) - .write(&(i + 1)) - .map_err(|_err| QueueError::InvalidArgs)?; + field_ptr!(temp, Descriptor, next).write(&(i + 1)).unwrap(); } - field_ptr!(&avail_frame_ptr, AvailRing, flags) + field_ptr!(&avail_ring_ptr, AvailRing, flags) .write(&(0u16)) - .map_err(|_err| QueueError::InvalidArgs)?; - field_ptr!(cfg, VirtioPciCommonCfg, queue_enable) - .write(&1u16) .unwrap(); Ok(VirtQueue { descs, - avail: avail_frame_ptr, - used: used_frame_ptr, + avail: avail_ring_ptr, + used: used_ring_ptr, notify, queue_size: size, queue_idx: idx as u32, @@ -342,7 +303,7 @@ impl VirtQueue { #[repr(C, align(16))] #[derive(Debug, Default, Copy, Clone, Pod)] -struct Descriptor { +pub struct Descriptor { addr: u64, len: u32, flags: DescFlags, @@ -383,7 +344,7 @@ impl Default for DescFlags { /// It is only written by the driver and read by the device. #[repr(C, align(2))] #[derive(Debug, Copy, Clone, Pod)] -struct AvailRing { +pub struct AvailRing { flags: u16, /// A driver MUST NOT decrement the idx. idx: u16, @@ -395,7 +356,7 @@ struct AvailRing { /// it is only written to by the device, and read by the driver. #[repr(C, align(4))] #[derive(Debug, Copy, Clone, Pod)] -struct UsedRing { +pub struct UsedRing { // the flag in UsedRing flags: u16, // the next index of the used element in ring array @@ -406,7 +367,7 @@ struct UsedRing { #[repr(C)] #[derive(Debug, Default, Copy, Clone, Pod)] -struct UsedElem { +pub struct UsedElem { id: u32, len: u32, } diff --git a/services/comps/virtio/src/transport/mod.rs b/services/comps/virtio/src/transport/mod.rs new file mode 100644 index 000000000..439123dff --- /dev/null +++ b/services/comps/virtio/src/transport/mod.rs @@ -0,0 +1,131 @@ +use core::fmt::Debug; + +use alloc::boxed::Box; +use jinux_frame::{io_mem::IoMem, trap::TrapFrame, vm::VmFrame}; +use jinux_util::safe_ptr::SafePtr; + +use crate::{ + queue::{AvailRing, Descriptor, UsedRing}, + VirtioDeviceType, +}; + +use self::pci::virtio_pci_init; + +pub mod pci; + +/// The transport of virtio device. Virtio device can use this transport to: +/// 1. Set device status. +/// 2. Negotiate features. +/// 3. Access device config memory. +/// 4. Config virtqueue. +/// 5. Get the interrupt resources allocated to the device. +pub trait VirtioTransport: Sync + Send + Debug { + // ====================Device related APIs======================= + + fn device_type(&self) -> VirtioDeviceType; + + /// Get device features. + fn device_features(&self) -> u64; + + /// Set driver features. + fn set_driver_features(&mut self, features: u64) -> Result<(), VirtioTransportError>; + + /// Get device status. + fn device_status(&self) -> DeviceStatus; + + /// Set device status. + fn set_device_status(&mut self, status: DeviceStatus) -> Result<(), VirtioTransportError>; + + // Set to driver ok status + fn finish_init(&mut self) { + self.set_device_status( + DeviceStatus::ACKNOWLEDGE + | DeviceStatus::DRIVER + | DeviceStatus::FEATURES_OK + | DeviceStatus::DRIVER_OK, + ) + .unwrap(); + } + + /// Get access to the device config memory. + fn device_config_memory(&self) -> IoMem; + + // ====================Virtqueue related APIs==================== + + /// Get the total number of queues + fn num_queues(&self) -> u16; + + /// Set virtqueue information. Some transport may set other necessary information such as MSI-X vector in PCI transport. + fn set_queue( + &mut self, + idx: u16, + queue_size: u16, + descriptor_ptr: &SafePtr, + avail_ring_ptr: &SafePtr, + used_ring_ptr: &SafePtr, + ) -> Result<(), VirtioTransportError>; + + /// The max queue size of one virtqueue. + fn max_queue_size(&self, idx: u16) -> Result; + + /// Get notify pointer of a virtqueue. User should send notification (e.g. write 0 to the pointer) + /// after it add buffers into the corresponding virtqueue. + fn get_notify_ptr(&self, idx: u16) -> Result, VirtioTransportError>; + + // ====================Device interrupt APIs===================== + + /// Register queue interrupt callback. The transport will try to allocate single IRQ line if + /// `single_interrupt` is set. + fn register_queue_callback( + &mut self, + index: u16, + func: Box, + single_interrupt: bool, + ) -> Result<(), VirtioTransportError>; + + /// Register configuration space change interrupt callback. + fn register_cfg_callback( + &mut self, + func: Box, + ) -> Result<(), VirtioTransportError>; +} + +#[derive(Debug, PartialEq, Eq)] +pub enum VirtioTransportError { + DeviceStatusError, + InvalidArgs, + NotEnoughResources, +} + +bitflags::bitflags! { + /// The device status field. + pub struct DeviceStatus: u8 { + /// Indicates that the guest OS has found the device and recognized it + /// as a valid virtio device. + const ACKNOWLEDGE = 1; + + /// Indicates that the guest OS knows how to drive the device. + const DRIVER = 2; + + /// Indicates that something went wrong in the guest, and it has given + /// up on the device. This could be an internal error, or the driver + /// didn’t like the device for some reason, or even a fatal error + /// during device operation. + const FAILED = 128; + + /// Indicates that the driver has acknowledged all the features it + /// understands, and feature negotiation is complete. + const FEATURES_OK = 8; + + /// Indicates that the driver is set up and ready to drive the device. + const DRIVER_OK = 4; + + /// Indicates that the device has experienced an error from which it + /// can’t recover. + const DEVICE_NEEDS_RESET = 64; + } +} + +pub fn init() { + virtio_pci_init(); +} diff --git a/services/comps/virtio/src/transport/pci/capability.rs b/services/comps/virtio/src/transport/pci/capability.rs new file mode 100644 index 000000000..e5c91f362 --- /dev/null +++ b/services/comps/virtio/src/transport/pci/capability.rs @@ -0,0 +1,95 @@ +use alloc::sync::Arc; +use jinux_frame::bus::pci::{ + capability::vendor::CapabilityVndrData, + cfg_space::{Bar, IoBar, MemoryBar}, + common_device::BarManager, +}; + +#[derive(Debug, Clone, PartialEq, Eq, PartialOrd, Ord)] +#[repr(u8)] +pub enum VirtioPciCpabilityType { + CommonCfg = 1, + NotifyCfg = 2, + IsrCfg = 3, + DeviceCfg = 4, + PciCfg = 5, +} + +#[derive(Debug, Clone)] +pub struct VirtioPciCapabilityData { + cfg_type: VirtioPciCpabilityType, + offset: u32, + length: u32, + option: Option, + memory_bar: Option>, + io_bar: Option>, +} + +impl VirtioPciCapabilityData { + pub fn memory_bar(&self) -> &Option> { + &self.memory_bar + } + + pub fn io_bar(&self) -> &Option> { + &self.io_bar + } + + pub fn offset(&self) -> u32 { + self.offset + } + + pub fn length(&self) -> u32 { + self.length + } + + pub fn typ(&self) -> VirtioPciCpabilityType { + self.cfg_type.clone() + } + + pub fn option_value(&self) -> Option { + self.option + } + + pub(super) fn new(bar_manager: &BarManager, vendor_cap: CapabilityVndrData) -> Self { + let cfg_type = vendor_cap.read8(3).unwrap(); + let cfg_type = match cfg_type { + 1 => VirtioPciCpabilityType::CommonCfg, + 2 => VirtioPciCpabilityType::NotifyCfg, + 3 => VirtioPciCpabilityType::IsrCfg, + 4 => VirtioPciCpabilityType::DeviceCfg, + 5 => VirtioPciCpabilityType::PciCfg, + _ => panic!("Unsupport virtio capability type:{:?}", cfg_type), + }; + let bar = vendor_cap.read8(4).unwrap(); + let capability_length = vendor_cap.read8(2).unwrap(); + let offset = vendor_cap.read32(8).unwrap(); + let length = vendor_cap.read32(12).unwrap(); + let option = if capability_length > 0x10 { + Some(vendor_cap.read32(16).unwrap()) + } else { + None + }; + + let mut io_bar = None; + let mut memory_bar = None; + match bar_manager.bar(bar) { + Some(bar) => match bar { + Bar::Memory(memory) => { + memory_bar = Some(memory); + } + Bar::Io(io) => { + io_bar = Some(io); + } + }, + None => {} + }; + Self { + cfg_type, + offset, + length, + option, + memory_bar, + io_bar, + } + } +} diff --git a/services/comps/virtio/src/transport/pci/common_cfg.rs b/services/comps/virtio/src/transport/pci/common_cfg.rs new file mode 100644 index 000000000..adaae7b1f --- /dev/null +++ b/services/comps/virtio/src/transport/pci/common_cfg.rs @@ -0,0 +1,39 @@ +use jinux_frame::io_mem::IoMem; +use jinux_util::safe_ptr::SafePtr; +use pod::Pod; + +use crate::transport::pci::capability::VirtioPciCpabilityType; + +use super::capability::VirtioPciCapabilityData; + +#[derive(Debug, Default, Copy, Clone, Pod)] +#[repr(C)] +pub struct VirtioPciCommonCfg { + pub device_feature_select: u32, + pub device_features: u32, + pub driver_feature_select: u32, + pub driver_features: u32, + pub config_msix_vector: u16, + pub num_queues: u16, + pub device_status: u8, + pub config_generation: u8, + + pub queue_select: u16, + pub queue_size: u16, + pub queue_msix_vector: u16, + pub queue_enable: u16, + pub queue_notify_off: u16, + pub queue_desc: u64, + pub queue_driver: u64, + pub queue_device: u64, +} + +impl VirtioPciCommonCfg { + pub(super) fn new(cap: &VirtioPciCapabilityData) -> SafePtr { + debug_assert!(cap.typ() == VirtioPciCpabilityType::CommonCfg); + SafePtr::new( + cap.memory_bar().as_ref().unwrap().io_mem().clone(), + cap.offset() as usize, + ) + } +} diff --git a/services/comps/virtio/src/transport/pci/device.rs b/services/comps/virtio/src/transport/pci/device.rs new file mode 100644 index 000000000..de15415fd --- /dev/null +++ b/services/comps/virtio/src/transport/pci/device.rs @@ -0,0 +1,331 @@ +use jinux_frame::{ + bus::pci::{ + bus::{PciDevice, PciDriverProbeError}, + capability::CapabilityData, + common_device::PciCommonDevice, + PciDeviceId, + }, + io_mem::IoMem, + offset_of, + trap::TrapFrame, + vm::VmFrame, +}; + +use alloc::{boxed::Box, sync::Arc}; +use core::fmt::Debug; +use jinux_util::{field_ptr, safe_ptr::SafePtr}; +use log::{info, warn}; + +use super::{common_cfg::VirtioPciCommonCfg, msix::VirtioMsixManager}; +use crate::{ + queue::{AvailRing, Descriptor, UsedRing}, + transport::{ + pci::capability::{VirtioPciCapabilityData, VirtioPciCpabilityType}, + DeviceStatus, VirtioTransport, VirtioTransportError, + }, + VirtioDeviceType, +}; + +pub struct VirtioPciNotify { + offset_multiplier: u32, + offset: u32, + io_memory: IoMem, +} + +#[derive(Debug)] +pub struct VirtioPciDevice { + device_id: PciDeviceId, +} + +pub struct VirtioPciTransport { + device_type: VirtioDeviceType, + common_device: PciCommonDevice, + common_cfg: SafePtr, + device_cfg: VirtioPciCapabilityData, + notify: VirtioPciNotify, + msix_manager: VirtioMsixManager, + device: Arc, +} + +impl PciDevice for VirtioPciDevice { + fn device_id(&self) -> PciDeviceId { + self.device_id.clone() + } +} + +impl Debug for VirtioPciTransport { + fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result { + f.debug_struct("PCIVirtioDevice") + .field("common_device", &self.common_device) + .finish() + } +} + +impl VirtioTransport for VirtioPciTransport { + fn device_type(&self) -> VirtioDeviceType { + self.device_type + } + + fn set_queue( + &mut self, + idx: u16, + queue_size: u16, + descriptor_ptr: &SafePtr, + avail_ring_ptr: &SafePtr, + used_ring_ptr: &SafePtr, + ) -> Result<(), VirtioTransportError> { + if idx >= self.num_queues() { + return Err(VirtioTransportError::InvalidArgs); + } + field_ptr!(&self.common_cfg, VirtioPciCommonCfg, queue_select) + .write(&(idx as u16)) + .unwrap(); + debug_assert_eq!( + field_ptr!(&self.common_cfg, VirtioPciCommonCfg, queue_select) + .read() + .unwrap(), + idx as u16 + ); + + field_ptr!(&self.common_cfg, VirtioPciCommonCfg, queue_size) + .write(&queue_size) + .unwrap(); + field_ptr!(&self.common_cfg, VirtioPciCommonCfg, queue_desc) + .write(&(descriptor_ptr.paddr() as u64)) + .unwrap(); + field_ptr!(&self.common_cfg, VirtioPciCommonCfg, queue_driver) + .write(&(avail_ring_ptr.paddr() as u64)) + .unwrap(); + field_ptr!(&self.common_cfg, VirtioPciCommonCfg, queue_device) + .write(&(used_ring_ptr.paddr() as u64)) + .unwrap(); + // Enable queue + field_ptr!(&self.common_cfg, VirtioPciCommonCfg, queue_enable) + .write(&1u16) + .unwrap(); + Ok(()) + } + + fn get_notify_ptr(&self, idx: u16) -> Result, VirtioTransportError> { + if idx >= self.num_queues() { + return Err(VirtioTransportError::InvalidArgs); + } + Ok(SafePtr::new( + self.notify.io_memory.clone(), + (self.notify.offset + self.notify.offset_multiplier * idx as u32) as usize, + )) + } + + fn num_queues(&self) -> u16 { + field_ptr!(&self.common_cfg, VirtioPciCommonCfg, num_queues) + .read() + .unwrap() + } + + fn device_config_memory(&self) -> IoMem { + let mut memory = self + .device_cfg + .memory_bar() + .as_ref() + .unwrap() + .io_mem() + .clone(); + let new_paddr = memory.paddr() + self.device_cfg.offset() as usize; + memory + .resize(new_paddr..(self.device_cfg.length() as usize + new_paddr)) + .unwrap(); + memory + } + + fn device_features(&self) -> u64 { + // select low + field_ptr!(&self.common_cfg, VirtioPciCommonCfg, device_feature_select) + .write(&0u32) + .unwrap(); + let device_feature_low = field_ptr!(&self.common_cfg, VirtioPciCommonCfg, device_features) + .read() + .unwrap(); + // select high + field_ptr!(&self.common_cfg, VirtioPciCommonCfg, device_feature_select) + .write(&1u32) + .unwrap(); + let device_feature_high = field_ptr!(&self.common_cfg, VirtioPciCommonCfg, device_features) + .read() + .unwrap() as u64; + device_feature_high << 32 | device_feature_low as u64 + } + + fn set_driver_features(&mut self, features: u64) -> Result<(), VirtioTransportError> { + let low = features as u32; + let high = (features >> 32) as u32; + field_ptr!(&self.common_cfg, VirtioPciCommonCfg, driver_feature_select) + .write(&0u32) + .unwrap(); + field_ptr!(&self.common_cfg, VirtioPciCommonCfg, driver_features) + .write(&low) + .unwrap(); + field_ptr!(&self.common_cfg, VirtioPciCommonCfg, driver_feature_select) + .write(&1u32) + .unwrap(); + field_ptr!(&self.common_cfg, VirtioPciCommonCfg, driver_features) + .write(&high) + .unwrap(); + Ok(()) + } + + fn device_status(&self) -> DeviceStatus { + let status = field_ptr!(&self.common_cfg, VirtioPciCommonCfg, device_status) + .read() + .unwrap(); + DeviceStatus::from_bits(status).unwrap() + } + + fn set_device_status(&mut self, status: DeviceStatus) -> Result<(), VirtioTransportError> { + field_ptr!(&self.common_cfg, VirtioPciCommonCfg, device_status) + .write(&(status.bits())) + .unwrap(); + Ok(()) + } + + fn max_queue_size(&self, idx: u16) -> Result { + field_ptr!(&self.common_cfg, VirtioPciCommonCfg, queue_select) + .write(&(idx as u16)) + .unwrap(); + debug_assert_eq!( + field_ptr!(&self.common_cfg, VirtioPciCommonCfg, queue_select) + .read() + .unwrap(), + idx as u16 + ); + + Ok(field_ptr!(&self.common_cfg, VirtioPciCommonCfg, queue_size) + .read() + .unwrap()) + } + + fn register_queue_callback( + &mut self, + index: u16, + func: Box, + single_interrupt: bool, + ) -> Result<(), VirtioTransportError> { + if index >= self.num_queues() { + return Err(VirtioTransportError::InvalidArgs); + } + let (vector, irq) = if single_interrupt { + self.msix_manager + .pop_unused_irq() + .ok_or(VirtioTransportError::NotEnoughResources)? + } else { + self.msix_manager.shared_interrupt_irq() + }; + irq.on_active(func); + field_ptr!(&self.common_cfg, VirtioPciCommonCfg, queue_select) + .write(&index) + .unwrap(); + debug_assert_eq!( + field_ptr!(&self.common_cfg, VirtioPciCommonCfg, queue_select) + .read() + .unwrap(), + index + ); + field_ptr!(&self.common_cfg, VirtioPciCommonCfg, queue_msix_vector) + .write(&vector) + .unwrap(); + Ok(()) + } + + fn register_cfg_callback( + &mut self, + func: Box, + ) -> Result<(), VirtioTransportError> { + let (_, irq) = self.msix_manager.config_msix_irq(); + irq.on_active(func); + Ok(()) + } +} + +impl VirtioPciTransport { + pub(super) fn pci_device(&self) -> &Arc { + &self.device + } + + pub(super) fn new( + common_device: PciCommonDevice, + ) -> Result { + let device_type = match common_device.device_id().device_id { + 0x1000 | 0x1041 => VirtioDeviceType::Network, + 0x1001 | 0x1042 => VirtioDeviceType::Block, + 0x1002 | 0x1043 => VirtioDeviceType::TraditionalMemoryBalloon, + 0x1003 | 0x1044 => VirtioDeviceType::Console, + 0x1004 | 0x1045 => VirtioDeviceType::ScsiHost, + 0x1005 | 0x1046 => VirtioDeviceType::Entropy, + 0x1009 | 0x104a => VirtioDeviceType::Transport9P, + 0x1011 | 0x1052 => VirtioDeviceType::Input, + _ => { + warn!( + "Unrecognized virtio-pci device id:{:?}", + common_device.device_id().device_id + ); + return Err((PciDriverProbeError::ConfigurationSpaceError, common_device)); + } + }; + + info!("[Virtio]: Found device:{:?}", device_type); + + let mut msix = None; + let mut notify = None; + let mut common_cfg = None; + let mut device_cfg = None; + for cap in common_device.capabilities().iter() { + match cap.capability_data() { + CapabilityData::Vndr(vendor) => { + let data = + VirtioPciCapabilityData::new(common_device.bar_manager(), vendor.clone()); + match data.typ() { + VirtioPciCpabilityType::CommonCfg => { + common_cfg = Some(VirtioPciCommonCfg::new(&data)); + } + VirtioPciCpabilityType::NotifyCfg => { + notify = Some(VirtioPciNotify { + offset_multiplier: data.option_value().unwrap(), + offset: data.offset(), + io_memory: data.memory_bar().as_ref().unwrap().io_mem().clone(), + }); + } + VirtioPciCpabilityType::IsrCfg => {} + VirtioPciCpabilityType::DeviceCfg => { + device_cfg = Some(data); + } + VirtioPciCpabilityType::PciCfg => {} + } + } + CapabilityData::Msix(data) => { + msix = Some(data.clone()); + } + CapabilityData::Unknown(id) => { + panic!("unknown capability: {}", id) + } + _ => { + panic!("PCI Virtio device should not have other type of capability") + } + } + } + // TODO: Support interrupt without MSI-X + let msix = msix.unwrap(); + let notify = notify.unwrap(); + let common_cfg = common_cfg.unwrap(); + let device_cfg = device_cfg.unwrap(); + let msix_manager = VirtioMsixManager::new(msix); + let device_id = common_device.device_id().clone(); + Ok(Self { + common_device, + common_cfg, + device_cfg, + notify, + msix_manager, + device_type, + device: Arc::new(VirtioPciDevice { device_id }), + }) + } +} diff --git a/services/comps/virtio/src/transport/pci/driver.rs b/services/comps/virtio/src/transport/pci/driver.rs new file mode 100644 index 000000000..6d2ef5889 --- /dev/null +++ b/services/comps/virtio/src/transport/pci/driver.rs @@ -0,0 +1,47 @@ +use alloc::{sync::Arc, vec::Vec}; +use jinux_frame::{ + bus::pci::{ + bus::{PciDevice, PciDriver, PciDriverProbeError}, + common_device::PciCommonDevice, + }, + sync::SpinLock, +}; + +use super::device::VirtioPciTransport; + +#[derive(Debug)] +pub struct VirtioPciDriver { + devices: SpinLock>, +} + +impl VirtioPciDriver { + pub fn num_devices(&self) -> usize { + self.devices.lock().len() + } + + pub fn pop_device_tranport(&self) -> Option { + self.devices.lock().pop() + } + + pub(super) fn new() -> Self { + VirtioPciDriver { + devices: SpinLock::new(Vec::new()), + } + } +} + +impl PciDriver for VirtioPciDriver { + fn probe( + &self, + device: PciCommonDevice, + ) -> Result, (PciDriverProbeError, PciCommonDevice)> { + const VIRTIO_DEVICE_VENDOR_ID: u16 = 0x1af4; + if device.device_id().vendor_id != VIRTIO_DEVICE_VENDOR_ID { + return Err((PciDriverProbeError::DeviceNotMatch, device)); + } + let transport = VirtioPciTransport::new(device)?; + let device = transport.pci_device().clone(); + self.devices.lock().push(transport); + Ok(device) + } +} diff --git a/services/comps/virtio/src/transport/pci/mod.rs b/services/comps/virtio/src/transport/pci/mod.rs new file mode 100644 index 000000000..886e72736 --- /dev/null +++ b/services/comps/virtio/src/transport/pci/mod.rs @@ -0,0 +1,19 @@ +pub mod capability; +pub mod common_cfg; +pub mod device; +pub mod driver; +pub(super) mod msix; + +use alloc::sync::Arc; +use jinux_frame::bus::pci::PCI_BUS; +use spin::Once; + +use self::driver::VirtioPciDriver; + +pub static VIRTIO_PCI_DRIVER: Once> = Once::new(); +pub fn virtio_pci_init() { + VIRTIO_PCI_DRIVER.call_once(|| Arc::new(VirtioPciDriver::new())); + PCI_BUS + .lock() + .register_driver(VIRTIO_PCI_DRIVER.get().unwrap().clone()); +} diff --git a/services/comps/virtio/src/transport/pci/msix.rs b/services/comps/virtio/src/transport/pci/msix.rs new file mode 100644 index 000000000..179b420b1 --- /dev/null +++ b/services/comps/virtio/src/transport/pci/msix.rs @@ -0,0 +1,62 @@ +use alloc::vec::Vec; +use jinux_frame::{bus::pci::capability::msix::CapabilityMsixData, trap::IrqAllocateHandle}; + +pub struct VirtioMsixManager { + config_msix_vector: u16, + /// Shared interrupt vector used by queue. + shared_interrupt_vector: u16, + /// The MSI-X vectors allocated to queue interrupt except `shared_interrupt_vector`. All the + /// vector are considered to be occupied by only one queue. + unused_msix_vectors: Vec, + /// Used MSI-X vectors. + used_msix_vectors: Vec, + msix: CapabilityMsixData, +} + +impl VirtioMsixManager { + pub fn new(mut msix: CapabilityMsixData) -> Self { + let mut msix_vector_list: Vec = (0..msix.table_size()).collect(); + for i in msix_vector_list.iter() { + let allocate_handle = jinux_frame::trap::allocate_irq().unwrap(); + msix.set_interrupt_vector(allocate_handle, *i); + } + let config_msix_vector = msix_vector_list.pop().unwrap(); + let shared_interrupt_vector = msix_vector_list.pop().unwrap(); + Self { + config_msix_vector, + unused_msix_vectors: msix_vector_list, + msix, + shared_interrupt_vector, + used_msix_vectors: Vec::new(), + } + } + + /// Get config space change MSI-X IRQ, this function will return the MSI-X vector and corresponding IRQ. + pub fn config_msix_irq(&mut self) -> (u16, &mut IrqAllocateHandle) { + ( + self.config_msix_vector, + self.msix.irq_mut(self.config_msix_vector as usize).unwrap(), + ) + } + + /// Get shared interrupt IRQ used by virtqueue. If a virtqueue will not send interrupt frequently. + /// Then this virtqueue should use shared interrupt IRQ. + /// This function will return the MSI-X vector and corresponding IRQ. + pub fn shared_interrupt_irq(&mut self) -> (u16, &mut IrqAllocateHandle) { + ( + self.shared_interrupt_vector, + self.msix + .irq_mut(self.shared_interrupt_vector as usize) + .unwrap(), + ) + } + + /// Pop unused vector. If a virtqueue will send interrupt frequently. + /// Then this virtqueue should use the single IRQ that this function provides. + /// this function will return the MSI-X vector and corresponding IRQ. + pub fn pop_unused_irq(&mut self) -> Option<(u16, &mut IrqAllocateHandle)> { + let vector = self.unused_msix_vectors.pop()?; + self.used_msix_vectors.push(vector); + Some((vector, self.msix.irq_mut(vector as usize).unwrap())) + } +} diff --git a/services/libs/jinux-std/Cargo.toml b/services/libs/jinux-std/Cargo.toml index 666cab4c7..31fd7ba26 100644 --- a/services/libs/jinux-std/Cargo.toml +++ b/services/libs/jinux-std/Cargo.toml @@ -13,6 +13,7 @@ jinux-input = { path = "../../comps/input" } jinux-block = { path = "../../comps/block" } jinux-network = { path = "../../comps/network" } jinux-time = { path = "../../comps/time" } +jinux-virtio = { path = "../../comps/virtio" } jinux-rights = { path = "../jinux-rights" } controlled = { path = "../../libs/comp-sys/controlled" } typeflags = { path = "../typeflags" } @@ -25,7 +26,20 @@ virtio-input-decoder = "0.1.4" ascii = { version = "1.1", default-features = false, features = ["alloc"] } intrusive-collections = "0.9.5" time = { version = "0.3", default-features = false, features = ["alloc"] } -smoltcp = { version = "0.9.1", default-features = false, features = ["alloc", "log", "medium-ethernet", "medium-ip", "proto-dhcpv4", "proto-ipv4", "proto-igmp", "socket-icmp", "socket-udp", "socket-tcp", "socket-raw", "socket-dhcpv4"] } +smoltcp = { version = "0.9.1", default-features = false, features = [ + "alloc", + "log", + "medium-ethernet", + "medium-ip", + "proto-dhcpv4", + "proto-ipv4", + "proto-igmp", + "socket-icmp", + "socket-udp", + "socket-tcp", + "socket-raw", + "socket-dhcpv4", +] } # parse elf file xmas-elf = "0.8.0" @@ -35,14 +49,18 @@ bitflags = "1.3" ringbuf = { version = "0.3.2", default-features = false, features = ["alloc"] } keyable-arc = { path = "../keyable-arc" } # unzip initramfs -libflate = { git = "https://github.com/jinzhao-dev/libflate", rev = "b781da6", features = ["no_std"] } +libflate = { git = "https://github.com/jinzhao-dev/libflate", rev = "b781da6", features = [ + "no_std", +] } core2 = { version = "0.4", default_features = false, features = ["alloc"] } lending-iterator = "0.1.7" spin = "0.9.4" vte = "0.10" lru = "0.9.0" log = "0.4" -getrandom = { version = "0.2.10", default-features = false, features = ["rdrand"] } +getrandom = { version = "0.2.10", default-features = false, features = [ + "rdrand", +] } [dependencies.lazy_static] version = "1.0" diff --git a/services/libs/jinux-std/src/driver/mod.rs b/services/libs/jinux-std/src/driver/mod.rs index de9025ad2..3c49bf8ec 100644 --- a/services/libs/jinux-std/src/driver/mod.rs +++ b/services/libs/jinux-std/src/driver/mod.rs @@ -1,27 +1,27 @@ -use jinux_input::INPUT_COMPONENT; use log::info; pub fn init() { // print all the input device to make sure input crate will compile - for comp in INPUT_COMPONENT.get().unwrap().get_input_device() { - info!("input device name:{}", comp.name()); + for (name, _) in jinux_input::all_devices() { + info!("Found Input device, name:{}", name); } } #[allow(unused)] fn block_device_test() { - let block_device = jinux_block::BLK_COMPONENT.get().unwrap().get_device(); - let mut write_buffer = [0u8; 512]; - let mut read_buffer = [0u8; 512]; - info!("write_buffer address:{:x}", write_buffer.as_ptr() as usize); - info!("read_buffer address:{:x}", read_buffer.as_ptr() as usize); - for i in 0..512 { - for byte in write_buffer.iter_mut() { - *byte = i as u8; + for (_, device) in jinux_block::all_devices() { + let mut write_buffer = [0u8; 512]; + let mut read_buffer = [0u8; 512]; + info!("write_buffer address:{:x}", write_buffer.as_ptr() as usize); + info!("read_buffer address:{:x}", read_buffer.as_ptr() as usize); + for i in 0..512 { + for byte in write_buffer.iter_mut() { + *byte = i as u8; + } + device.write_block(i as usize, &write_buffer); + device.read_block(i as usize, &mut read_buffer); + assert_eq!(write_buffer, read_buffer); } - block_device.write_block(i as usize, &write_buffer); - block_device.read_block(i as usize, &mut read_buffer); - assert_eq!(write_buffer, read_buffer); + info!("block device test passed!"); } - info!("block device test passed!"); } diff --git a/services/libs/jinux-std/src/net/iface/virtio.rs b/services/libs/jinux-std/src/net/iface/virtio.rs index 3caac6d4b..d77e249d5 100644 --- a/services/libs/jinux-std/src/net/iface/virtio.rs +++ b/services/libs/jinux-std/src/net/iface/virtio.rs @@ -1,6 +1,7 @@ use crate::prelude::*; use jinux_frame::sync::SpinLock; -use jinux_network::{probe_virtio_net, NetworkDevice, VirtioNet}; +use jinux_network::NetworkDevice; +use jinux_virtio::device::network::DEVICE_NAME; use smoltcp::{ iface::{Config, Routes, SocketHandle, SocketSet}, socket::dhcpv4, @@ -10,7 +11,7 @@ use smoltcp::{ use super::{common::IfaceCommon, internal::IfaceInternal, Iface}; pub struct IfaceVirtio { - driver: SpinLock, + driver: Arc>>, common: IfaceCommon, dhcp_handle: SocketHandle, weak_self: Weak, @@ -18,9 +19,9 @@ pub struct IfaceVirtio { impl IfaceVirtio { pub fn new() -> Arc { - let mut virtio_net = probe_virtio_net().unwrap(); + let mut virtio_net = jinux_network::get_device(&(DEVICE_NAME).to_string()).unwrap(); let interface = { - let mac_addr = virtio_net.mac_addr(); + let mac_addr = virtio_net.lock().mac_addr(); let ip_addr = IpCidr::new(wire::IpAddress::Ipv4(wire::Ipv4Address::UNSPECIFIED), 0); let routes = Routes::new(); let config = { @@ -30,7 +31,7 @@ impl IfaceVirtio { )); config }; - let mut interface = smoltcp::iface::Interface::new(config, &mut virtio_net); + let mut interface = smoltcp::iface::Interface::new(config, &mut **virtio_net.lock()); interface.update_ip_addrs(|ip_addrs| { debug_assert!(ip_addrs.len() == 0); ip_addrs.push(ip_addr).unwrap(); @@ -42,7 +43,7 @@ impl IfaceVirtio { let dhcp_handle = init_dhcp_client(&mut socket_set); drop(socket_set); Arc::new_cyclic(|weak| Self { - driver: SpinLock::new(virtio_net), + driver: virtio_net, common, dhcp_handle, weak_self: weak.clone(), @@ -113,7 +114,7 @@ impl Iface for IfaceVirtio { fn poll(&self) { let mut driver = self.driver.lock_irq_disabled(); - self.common.poll(&mut *driver); + self.common.poll(&mut **driver); self.process_dhcp(); } } diff --git a/services/libs/jinux-std/src/net/mod.rs b/services/libs/jinux-std/src/net/mod.rs index e5caccff8..dee7122c4 100644 --- a/services/libs/jinux-std/src/net/mod.rs +++ b/services/libs/jinux-std/src/net/mod.rs @@ -2,7 +2,6 @@ use crate::{ net::iface::{Iface, IfaceLoopback, IfaceVirtio}, prelude::*, }; -use jinux_network::register_net_device_irq_handler; use spin::Once; use self::iface::spawn_background_poll_thread; @@ -18,12 +17,14 @@ pub fn init() { let iface_loopback = IfaceLoopback::new(); vec![iface_virtio, iface_loopback] }); - register_net_device_irq_handler(|irq_num| { - debug!("irq num = {}", irq_num); - // TODO: further check that the irq num is the same as iface's irq num - let iface_virtio = &IFACES.get().unwrap()[0]; - iface_virtio.poll(); - }); + + for (name, _) in jinux_network::all_devices() { + jinux_network::register_recv_callback(&name, || { + // TODO: further check that the irq num is the same as iface's irq num + let iface_virtio = &IFACES.get().unwrap()[0]; + iface_virtio.poll(); + }) + } poll_ifaces(); } diff --git a/services/libs/jinux-util/src/safe_ptr.rs b/services/libs/jinux-util/src/safe_ptr.rs index ef7ae6a94..a9517073f 100644 --- a/services/libs/jinux-util/src/safe_ptr.rs +++ b/services/libs/jinux-util/src/safe_ptr.rs @@ -360,11 +360,12 @@ macro_rules! field_ptr { ($ptr:expr, $type:ty, $($field:tt)+) => {{ use jinux_frame::offset_of; use jinux_frame::vm::VmIo; - use jinux_rights::TRights; - use jinux_rights::TRightSet; use jinux_rights::Dup; - use jinux_util::safe_ptr::SetContain; + use jinux_rights::TRightSet; + use jinux_rights::TRights; use jinux_util::safe_ptr::Pod; + use jinux_util::safe_ptr::SetContain; + use jinux_util::safe_ptr::SafePtr; #[inline] fn new_field_ptr(