diff --git a/Makefile b/Makefile index d6f77a17..dc6b3f1b 100644 --- a/Makefile +++ b/Makefile @@ -7,7 +7,7 @@ DEBUG ?= false LARGE_MEM ?= true IXGBE ?= true -VIRTIO_NET ?= false +VIRTIO_NET ?= true VIRTIO_BLOCK ?= false ifndef NO_DEFAULT_FLAGS @@ -72,6 +72,8 @@ domain_list := $(addprefix domains/build/, \ ixgbe \ virtio_net \ virtio_block \ + virtio_backend \ + virtio_net_mmio \ nvme \ tpm \ bdev_shadow \ @@ -204,7 +206,7 @@ idl_generation: tools/redIDL then echo "redIDL not found. Maybe you want to do 'git submodule init && git submodule update' then try again?"; \ exit -1; \ fi - make -C interface + # make -C interface .PHONY: domains domains: idl_generation $(xv6fs_img) memops diff --git a/domains/Cargo.lock b/domains/Cargo.lock index ad641d87..fbfaed4d 100644 --- a/domains/Cargo.lock +++ b/domains/Cargo.lock @@ -755,6 +755,7 @@ dependencies = [ "pc-keyboard", "spin 0.5.3", "syscalls", + "virtio_backend_trusted", ] [[package]] @@ -997,6 +998,42 @@ version = "0.9.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "b5a972e5669d67ba988ce3dc826706fb0a8b01471c088cb0b6110b805cc36aed" +[[package]] +name = "virtio_backend" +version = "0.1.0" +dependencies = [ + "console", + "hashbrown 0.7.2", + "interface", + "libsyscalls", + "libtime", + "malloc", + "pci_driver", + "platform", + "protocol", + "spin 0.5.3", + "syscalls", + "virtio_backend_trusted", + "virtio_device", + "virtio_net_mmio", + "virtio_net_mmio_device", +] + +[[package]] +name = "virtio_backend_trusted" +version = "0.1.0" +dependencies = [ + "console", + "hashbrown 0.7.2", + "interface", + "libsyscalls", + "libtime", + "malloc", + "spin 0.5.3", + "virtio_device", + "volatile_accessor", +] + [[package]] name = "virtio_block" version = "0.1.0" @@ -1064,6 +1101,46 @@ dependencies = [ "virtio_network_device", ] +[[package]] +name = "virtio_net_mmio" +version = "0.1.0" +dependencies = [ + "console", + "hashbrown 0.7.2", + "interface", + "libbenchnet", + "libsyscalls", + "libtime", + "malloc", + "pci_driver", + "platform", + "protocol", + "redhttpd", + "smolnet", + "smoltcp", + "spin 0.5.3", + "syscalls", + "virtio_device", + "virtio_net_mmio_device", +] + +[[package]] +name = "virtio_net_mmio_device" +version = "0.1.0" +dependencies = [ + "console", + "hashbrown 0.7.2", + "interface", + "libsyscalls", + "libtime", + "malloc", + "pci_driver", + "spin 0.5.3", + "virtio_backend_trusted", + "virtio_device", + "volatile_accessor", +] + [[package]] name = "virtio_network_device" version = "0.1.0" diff --git a/domains/Cargo.toml b/domains/Cargo.toml index a197b8fc..29390a8f 100644 --- a/domains/Cargo.toml +++ b/domains/Cargo.toml @@ -10,6 +10,8 @@ members = [ "sys/driver/tpm", "sys/driver/virtio_net", "sys/driver/virtio_block", + "sys/driver/virtio_backend", + "sys/driver/virtio_net_mmio", "sys/init", "usr/proxy", "usr/shadow/bdev", diff --git a/domains/sys/driver/virtio_backend/Cargo.toml b/domains/sys/driver/virtio_backend/Cargo.toml new file mode 100644 index 00000000..57d476e9 --- /dev/null +++ b/domains/sys/driver/virtio_backend/Cargo.toml @@ -0,0 +1,30 @@ +[package] +name = "virtio_backend" +version = "0.1.0" +authors = ["RedLeaf Team"] +edition = "2018" + +[dependencies] +libsyscalls = { path = "../../../../lib/core/libsyscalls" } +libtime = { path = "../../../../lib/core/libtime" } +console = { path = "../../../../lib/core/console" } +malloc = { path = "../../../../lib/core/malloc" } +spin = { path = "../../../../lib/core/spin-rs" } + +# Interfaces +syscalls = { path = "../../../../lib/core/interfaces/syscalls" } +pci_driver = { path = "../../../../lib/core/interfaces/dev/pci/pci_driver" } +protocol = { path = "../../../../lib/core/interfaces/protocol" } +platform = { path = "../../../../lib/core/interfaces/platform" } +interface = { path = "../../../../interface/generated" } + +virtio_net_mmio_device = { path = "../../../../lib/devices/virtio_net_mmio" } +virtio_net_mmio = { path = "../virtio_net_mmio" } +virtio_backend_trusted = { path = "../../../../lib/devices/virtio_backend" } + +virtio_device = { path = "../../../../lib/devices/virtio" } + +hashbrown = "0.7.2" + +[features] +default = [] \ No newline at end of file diff --git a/domains/sys/driver/virtio_backend/src/main.rs b/domains/sys/driver/virtio_backend/src/main.rs new file mode 100644 index 00000000..97a48627 --- /dev/null +++ b/domains/sys/driver/virtio_backend/src/main.rs @@ -0,0 +1,101 @@ +#![no_std] +#![no_main] +// #![forbid(unsafe_code)] +#![feature( + box_syntax, + const_fn, + const_raw_ptr_to_usize_cast, + const_in_array_repeat_expressions, + untagged_unions, + maybe_uninit_extra, + core_intrinsics +)] + +extern crate alloc; +extern crate malloc; + +mod virtio_backend; + +use alloc::{boxed::Box, sync::Arc, vec, vec::Vec}; +use console::{print, println}; +use core::{ + borrow::BorrowMut, + intrinsics::size_of, + panic::PanicInfo, + ptr::{read_volatile, write_volatile}, +}; +use interface::{ + net::Net, + rref::{RRef, RRefDeque}, +}; +use libsyscalls::syscalls::{sys_backtrace, sys_create_thread, sys_yield}; +use libtime::sys_ns_sleep; +use spin::{Mutex, MutexGuard, Once}; +use syscalls::{Heap, Syscall}; +use virtio_backend::VirtioBackend; +use virtio_backend_trusted::{ + defs::{ + Buffer, DeviceNotificationType, BATCH_SIZE, DEVICE_NOTIFY, MAX_SUPPORTED_QUEUES, + MMIO_ADDRESS, + }, + get_device_notifications, get_thread_arguments, VirtioBackendThreadArguments, THREAD_ARGUMENTS, +}; +use virtio_backend_trusted::{initialize_device_config_space, virtual_queue::VirtualQueue}; +use virtio_device::{defs::VirtQueue, VirtioPciCommonConfig}; +use virtio_net_mmio_device::VirtioNetworkDeviceConfig; + +pub extern "C" fn virtio_backend() { + // Retrieve Thread Arguments + let args = get_thread_arguments(); + + initialize_device_config_space(); + process_notifications(args.net); +} + +fn process_notifications(net: Box) -> ! { + let mut backend = VirtioBackend::new(net); + + loop { + // Check device for processed buffers and move to queues + backend.update_queues(); + + let notification = get_device_notifications(); + + match notification { + DeviceNotificationType::DeviceConfigurationUpdated => { + backend.handle_device_config_update(); + } + DeviceNotificationType::QueueUpdated => { + backend.handle_queue_notify(); + } + DeviceNotificationType::None => {} + } + + sys_yield(); + } +} + +#[no_mangle] +pub fn trusted_entry( + s: Box, + heap: Box, + net: Box, +) { + libsyscalls::syscalls::init(s); + interface::rref::init(heap, libsyscalls::syscalls::sys_get_current_domain_id()); + + // Prepare thread arguments + unsafe { + THREAD_ARGUMENTS = Some(VirtioBackendThreadArguments { net }); + } + + sys_create_thread("virtio_backend", virtio_backend); +} + +// This function is called on panic. +#[panic_handler] +fn panic(info: &PanicInfo) -> ! { + println!("{:?}", info); + sys_backtrace(); + loop {} +} diff --git a/domains/sys/driver/virtio_backend/src/virtio_backend.rs b/domains/sys/driver/virtio_backend/src/virtio_backend.rs new file mode 100644 index 00000000..8117bc92 --- /dev/null +++ b/domains/sys/driver/virtio_backend/src/virtio_backend.rs @@ -0,0 +1,226 @@ +use alloc::collections::VecDeque; +use alloc::vec; +use alloc::{boxed::Box, vec::Vec}; +use console::println; +use core::ptr::read_volatile; +use hashbrown::HashMap; +use interface::{ + net::Net, + rref::{RRef, RRefDeque}, +}; +use virtio_backend_trusted::defs::{ + Buffer, BufferPtr, VirtioQueueConfig, BATCH_SIZE, MAX_SUPPORTED_QUEUES, MMIO_ADDRESS, + SHARED_MEMORY_REGION_PTR, +}; +use virtio_backend_trusted::virtual_queue::VirtualQueue; +use virtio_backend_trusted::{copy_buffer_into_rref, copy_rref_into_buffer}; +use virtio_device::defs::VirtqUsedElement; + +pub struct VirtioBackend { + /// A copy of the queue_config's device status + device_status: u8, + + queue_information: Vec>, + virtual_queues: Vec>, + net: Box, + + // *** The below variables are used to simplify communicating with RRef Net Interface *** + /// We have to copy the arrays in order to satisfy the interface so this keeps track of which RRef + /// corresponds to which buffer + buffer_rref_map: HashMap, + + /// Used so that we don't have to create RRefDeques when calling submit_and_poll_rref() + /// 0 is packets and 1 is collect + rref_queues: (Option>, Option>), +} + +impl VirtioBackend { + pub fn new(net: Box) -> Self { + VirtioBackend { + device_status: 0, + queue_information: vec![None, None], + virtual_queues: vec![None, None], + net, + + rref_queues: ( + Some(RRefDeque::new([None; 32])), + Some(RRefDeque::new([None; 32])), + ), + buffer_rref_map: HashMap::new(), + } + } + + /// According to the VirtIO Net Spec, Even Queues are used for RX and Odd Queues are used for TX + const fn is_rx_queue(queue_idx: usize) -> bool { + queue_idx % 2 == 0 + } + + pub fn device_enabled(&self) -> bool { + self.device_status == 15 + } + + /// Call this function anytime the frontend modifies device config and backend needs to update + pub fn handle_device_config_update(&mut self) { + let device_config = unsafe { read_volatile(MMIO_ADDRESS) }; + + self.device_status = device_config.device_status; + + // Update the backend's info on the queues + if device_config.queue_enable == 1 { + if device_config.queue_select >= MAX_SUPPORTED_QUEUES { + panic!("Virtio Backend Supports at most {} queues but the device has a queue at index {}", + MAX_SUPPORTED_QUEUES, + { device_config.queue_select}); + } + + // Update the queue information + let queue = VirtioQueueConfig { + queue_index: device_config.queue_select, + queue_enable: true, + queue_size: device_config.queue_size, + queue_descriptor: device_config.queue_desc, + queue_device: device_config.queue_device, + queue_driver: device_config.queue_driver, + + device_idx: 0, + driver_idx: 0, + }; + + let index = queue.queue_index as usize; + self.queue_information[index] = Some(queue); + self.virtual_queues[index] = Some(VirtualQueue::new( + device_config.queue_desc, + device_config.queue_device, + device_config.queue_driver, + device_config.queue_size, + Self::is_rx_queue(index), + )) + } + + println!( + "virtio_device_config_modified {:#?}", + &self.queue_information + ); + } + + /// Handles Queue Notifications submitted by the frontend + pub fn handle_queue_notify(&mut self) { + if !self.device_enabled() { + return; + } + + // Since there's currently no way of knowing which queue was updated check them all + for i in 0..self.virtual_queues.len() { + self.notify_virtual_queue(i); + } + } + + fn notify_virtual_queue(&mut self, queue_idx: usize) { + self.move_buffers_from_frontend_to_rref_deque(queue_idx); + + self.call_submit_and_poll_rref(!Self::is_rx_queue(queue_idx)); + + self.move_buffers_from_rref_deque_to_frontend(queue_idx); + } + + fn move_buffers_from_frontend_to_rref_deque(&mut self, queue_idx: usize) { + // Create new RRefs and add them to the queue + let queue = self.virtual_queues[queue_idx].as_mut().unwrap(); + let buffers = queue.fetch_new_buffers(); + let packets = self.rref_queues.0.as_mut().unwrap(); + + let rx = Self::is_rx_queue(queue_idx); + + while buffers.len() > 0 && packets.len() < BATCH_SIZE { + let buffer = buffers.pop_front().unwrap(); + + let rref = RRef::new([0; 1514]); + + if !rx { + // If it's tx we need to copy the buffer's contents into the RRef + copy_buffer_into_rref(&buffer, &rref); + } + + self.buffer_rref_map + .insert(rref.as_ptr() as u64, buffer as u64); + packets.push_back(rref); + } + } + + fn move_buffers_from_rref_deque_to_frontend(&mut self, queue_idx: usize) { + let rx = Self::is_rx_queue(queue_idx); + self.call_submit_and_poll_rref(!rx); + + let queue = self.virtual_queues[queue_idx].as_mut().unwrap(); + let collect = self.rref_queues.1.as_mut().unwrap(); + assert!( + self.rref_queues.0.as_ref().unwrap().len() == 0, + "Packets queue should be flushed completely!" + ); + + // Move buffers from collect queue + while let Some(rref) = collect.pop_front() { + if let Some(buffer) = self.buffer_rref_map.remove(&(rref.as_ptr() as u64)) { + if rx { + copy_rref_into_buffer(&rref, buffer as BufferPtr); + } + + queue.mark_buffers_as_complete(&[buffer as BufferPtr]); + } else { + panic!( + "RRef address must have changed! FAILED RREF ADDR: {:#?}, EXPECTED: {:#?}", + rref.as_ptr() as u64, + self.buffer_rref_map + ); + } + } + } + + pub fn update_queues(&mut self) { + if !self.device_enabled() { + return; + } + + for queue_idx in 0..self.virtual_queues.len() { + self.move_buffers_from_rref_deque_to_frontend(queue_idx); + } + } + + /// Returns the number of packets added to the collect queue by the device + fn call_submit_and_poll_rref(&mut self, tx: bool) -> usize { + if let Ok(Ok((pkt_count, packets, collect))) = self.net.submit_and_poll_rref( + self.rref_queues.0.take().unwrap(), + self.rref_queues.1.take().unwrap(), + tx, + 1514, + ) { + self.rref_queues.0.replace(packets); + self.rref_queues.1.replace(collect); + return pkt_count; + } else { + panic!("Communication with backend device failed!"); + } + } + + fn print_descriptor_chain(queue: &VirtualQueue, chain_header_idx: u16) { + let mut current_idx: usize = chain_header_idx.into(); + let descriptors = queue.descriptor_queue.get_descriptors(); + + println!("---CHAIN {} START---", chain_header_idx); + + loop { + // Get and print the descriptor + let descriptor = descriptors[current_idx]; + println!("{:#?}", &descriptor); + + if (descriptor.flags & 0b1) == 0b1 { + // Goto Next + current_idx = descriptor.next.into(); + } else { + break; + } + } + + println!("---CHAIN {} END---", chain_header_idx); + } +} diff --git a/domains/sys/driver/virtio_net/src/main.rs b/domains/sys/driver/virtio_net/src/main.rs index 393909ce..f791fb02 100644 --- a/domains/sys/driver/virtio_net/src/main.rs +++ b/domains/sys/driver/virtio_net/src/main.rs @@ -125,6 +125,8 @@ pub fn trusted_entry( libsyscalls::syscalls::init(s); interface::rref::init(heap, libsyscalls::syscalls::sys_get_current_domain_id()); + println!("virtio_net::trusted_entry()"); + #[cfg(feature = "virtio_net")] let net = { let net = { diff --git a/domains/sys/driver/virtio_net_mmio/Cargo.toml b/domains/sys/driver/virtio_net_mmio/Cargo.toml new file mode 100644 index 00000000..80d9e08f --- /dev/null +++ b/domains/sys/driver/virtio_net_mmio/Cargo.toml @@ -0,0 +1,39 @@ +[package] +name = "virtio_net_mmio" +version = "0.1.0" +authors = ["RedLeaf Team"] +edition = "2018" + +[dependencies] +libsyscalls = { path = "../../../../lib/core/libsyscalls" } +libtime = { path = "../../../../lib/core/libtime" } +console = { path = "../../../../lib/core/console" } +malloc = { path = "../../../../lib/core/malloc" } +libbenchnet = { path = "../../../lib/libbenchnet" } +spin = { path = "../../../../lib/core/spin-rs" } + +smolnet = { path = "../../../../domains/lib/smolnet" } +hashbrown = "0.7.2" +redhttpd = { path = "../../../lib/redhttpd" } + +virtio_device = { path = "../../../../lib/devices/virtio" } +virtio_net_mmio_device = { path = "../../../../lib/devices/virtio_net_mmio" } + +# Interfaces +syscalls = { path = "../../../../lib/core/interfaces/syscalls" } +pci_driver = { path = "../../../../lib/core/interfaces/dev/pci/pci_driver" } +protocol = { path = "../../../../lib/core/interfaces/protocol" } +platform = { path = "../../../../lib/core/interfaces/platform" } +interface = { path = "../../../../interface/generated" } + + +[dependencies.smoltcp] +path = "../../../../lib/external/smoltcp" +default-features = false +features = ["alloc", "proto-ipv4", "socket-tcp", "socket-icmp", "ethernet"] + +[features] +default = [ +] + +virtio_net = [] diff --git a/domains/sys/driver/virtio_net_mmio/src/main.rs b/domains/sys/driver/virtio_net_mmio/src/main.rs new file mode 100644 index 00000000..5f3a55d7 --- /dev/null +++ b/domains/sys/driver/virtio_net_mmio/src/main.rs @@ -0,0 +1,152 @@ +#![no_std] +#![no_main] +#![feature( + box_syntax, + const_fn, + const_raw_ptr_to_usize_cast, + const_in_array_repeat_expressions, + untagged_unions, + maybe_uninit_extra, + core_intrinsics +)] + +extern crate alloc; +extern crate malloc; + +mod nullnet; + +use alloc::collections::VecDeque; +use alloc::sync::Arc; +use alloc::vec::Vec; +use alloc::{boxed::Box, collections::BTreeMap}; +use core::intrinsics::size_of; +use core::ptr::{read_volatile, write_volatile}; +use core::{borrow::BorrowMut, panic::PanicInfo, pin::Pin, usize}; +use syscalls::{Heap, Syscall}; + +use console::{print, println}; +use interface::{net::Net, rpc::RpcResult}; +use libsyscalls::syscalls::{sys_backtrace, sys_yield}; +pub use platform::PciBarAddr; +use spin::Mutex; + +pub use interface::error::{ErrorKind, Result}; +use virtio_net_mmio_device::VirtioNetInner; + +use interface::rref::{RRef, RRefDeque}; + +use smolnet::{self, SmolPhy}; + +pub use interface::net::NetworkStats; + +const MMIO_CONFIG_ADDRESS: usize = 0x100000; + +pub struct VirtioNet(Arc>); + +impl interface::net::Net for VirtioNet { + fn clone_net(&self) -> RpcResult> { + Ok(box Self(self.0.clone())) + } + + fn submit_and_poll( + &self, + mut packets: &mut VecDeque>, + mut collect: &mut VecDeque>, + tx: bool, + ) -> RpcResult> { + unimplemented!() + } + + /// If `tx` is true, packets in packets are for transmitting, else they are receive buffers + fn submit_and_poll_rref( + &self, + mut packets: RRefDeque<[u8; 1514], 32>, + mut collect: RRefDeque<[u8; 1514], 32>, + tx: bool, + pkt_len: usize, + ) -> RpcResult, RRefDeque<[u8; 1514], 32>)>> { + let mut device = self.0.lock(); + + let mut new_packet_count = 0; + + if tx { + new_packet_count = device.free_processed_tx_packets(&mut collect); + device.add_tx_buffers(&mut packets); + } else { + new_packet_count = device.get_received_packets(&mut collect); + device.add_rx_buffers(&mut packets, &mut collect); + } + + Ok(Ok((new_packet_count, packets, collect))) + } + + fn poll(&self, mut collect: &mut VecDeque>, tx: bool) -> RpcResult> { + unimplemented!() + } + + fn poll_rref( + &self, + mut collect: RRefDeque<[u8; 1514], 512>, + tx: bool, + ) -> RpcResult)>> { + // let mut new_packet_count = 0; + // let device = self.0.lock(); + + // if tx { + // new_packet_count = device.free_processed_tx_packets(&mut collect); + // } else { + // new_packet_count = device.get_received_packets(&mut collect); + // } + + // Ok(Ok((new_packet_count, collect))) + Ok(Ok((0, collect))) + } + + fn get_stats(&self) -> RpcResult> { + // unimplemented!() + Ok(Ok(NetworkStats { + tx_count: 0, + rx_count: 0, + tx_dma_ok: 0, + rx_dma_ok: 0, + rx_missed: 0, + rx_crc_err: 0, + })) + } + + fn test_domain_crossing(&self) -> RpcResult<()> { + unimplemented!() + } +} + +#[no_mangle] +pub fn trusted_entry( + s: Box, + heap: Box, + pci: Box, +) -> Box { + libsyscalls::syscalls::init(s); + interface::rref::init(heap, libsyscalls::syscalls::sys_get_current_domain_id()); + + let inner = unsafe { + let mut inner = VirtioNetInner::new(MMIO_CONFIG_ADDRESS); + inner.init(); + inner + }; + + let net = VirtioNet(Arc::new(Mutex::new(inner))); + + libbenchnet::run_fwd_udptest_rref(&net, 1514); + + loop {} + + Box::new(nullnet::NullNet::new()) +} + +// This function is called on panic. +#[panic_handler] +fn panic(info: &PanicInfo) -> ! { + println!("{:?}", info); + sys_backtrace(); + loop {} +} diff --git a/domains/sys/driver/virtio_net_mmio/src/nullnet.rs b/domains/sys/driver/virtio_net_mmio/src/nullnet.rs new file mode 100644 index 00000000..9b19a8ee --- /dev/null +++ b/domains/sys/driver/virtio_net_mmio/src/nullnet.rs @@ -0,0 +1,68 @@ +use alloc::boxed::Box; +use alloc::collections::VecDeque; +use alloc::vec::Vec; +use interface::error::Result; +use interface::net::NetworkStats; +use interface::rpc::RpcResult; +use interface::rref::RRefDeque; + +pub struct NullNet {} + +impl NullNet { + pub fn new() -> Self { + Self {} + } +} + +impl interface::net::Net for NullNet { + fn clone_net(&self) -> RpcResult> { + Ok(box Self::new()) + } + + fn submit_and_poll( + &self, + packets: &mut VecDeque>, + collect: &mut VecDeque>, + _tx: bool, + ) -> RpcResult> { + let ret = packets.len(); + while let Some(pkt) = packets.pop_front() { + collect.push_back(pkt); + } + Ok(Ok(ret)) + } + + fn submit_and_poll_rref( + &self, + mut packets: RRefDeque<[u8; 1514], 32>, + mut collect: RRefDeque<[u8; 1514], 32>, + _tx: bool, + _pkt_len: usize, + ) -> RpcResult, RRefDeque<[u8; 1514], 32>)>> { + while let Some(pkt) = packets.pop_front() { + collect.push_back(pkt); + } + + Ok(Ok((collect.len(), packets, collect))) + } + + fn poll(&self, _collect: &mut VecDeque>, _tx: bool) -> RpcResult> { + Ok(Ok(0)) + } + + fn poll_rref( + &self, + collect: RRefDeque<[u8; 1514], 512>, + _tx: bool, + ) -> RpcResult)>> { + Ok(Ok((0, collect))) + } + + fn get_stats(&self) -> RpcResult> { + Ok(Ok(NetworkStats::new())) + } + + fn test_domain_crossing(&self) -> RpcResult<()> { + Ok(()) + } +} diff --git a/domains/sys/init/Cargo.toml b/domains/sys/init/Cargo.toml index 5f83bf37..27737c16 100644 --- a/domains/sys/init/Cargo.toml +++ b/domains/sys/init/Cargo.toml @@ -15,6 +15,8 @@ spin = { path = "../../../lib/core/spin-rs" } libtime = { path = "../../../lib/core/libtime" } pc-keyboard = "0.3.1" +virtio_backend_trusted = { path = "../../../lib/devices/virtio_backend" } + [dependencies.lazy_static] version = "1.3.0" features = ["spin_no_std"] diff --git a/domains/sys/init/src/main.rs b/domains/sys/init/src/main.rs index 09db916a..0f0baa3c 100644 --- a/domains/sys/init/src/main.rs +++ b/domains/sys/init/src/main.rs @@ -109,6 +109,8 @@ pub fn trusted_entry( create_ixgbe: Arc, create_virtio_net: Arc, create_virtio_block: Arc, + create_virtio_backend: Arc, + create_virtio_net_mmio: Arc, create_net_shadow: Arc, create_nvme_shadow: Arc, create_nvme: Arc, @@ -193,6 +195,8 @@ pub fn trusted_entry( create_ixgbe, create_virtio_net, create_virtio_block, + create_virtio_backend, + create_virtio_net_mmio, create_nvme, create_net_shadow, create_nvme_shadow, @@ -238,8 +242,23 @@ pub fn trusted_entry( let (_, net) = proxy .as_domain_create_CreateVirtioNet() .create_domain_virtio_net(pci.pci_clone().unwrap()); + + #[cfg(feature = "virtio_block")] + let (_, virtio_block) = proxy + .as_domain_create_CreateVirtioBlock() + .create_domain_virtio_block(pci.pci_clone().unwrap()); + + let (_) = proxy + .as_domain_create_CreateVirtioBackend() + .create_domain_virtio_backend(net); + + let (_, net) = proxy + .as_domain_create_CreateVirtioNetMMIO() + .create_domain_virtio_net_mmio(); + #[cfg(all(not(feature = "shadow"), not(feature = "virtnet")))] let (_, net) = proxy.as_create_ixgbe().create_domain_ixgbe(pci.pci_clone()); + #[cfg(all(feature = "shadow", not(feature = "virtio_net")))] let (_, net) = proxy .as_create_net_shadow() @@ -289,11 +308,6 @@ pub fn trusted_entry( #[cfg(feature = "benchnet")] let _ = proxy.as_create_benchnet().create_domain_benchnet(net); - #[cfg(feature = "virtio_block")] - let (_, virtio_block) = proxy - .as_domain_create_CreateVirtioBlock() - .create_domain_virtio_block(pci.pci_clone().unwrap()); - #[cfg(feature = "benchnvme")] let _ = proxy .as_domain_create_CreateBenchnvme() diff --git a/domains/usr/proxy/src/main.rs b/domains/usr/proxy/src/main.rs index 1fa96d33..59d96237 100644 --- a/domains/usr/proxy/src/main.rs +++ b/domains/usr/proxy/src/main.rs @@ -25,6 +25,8 @@ pub fn trusted_entry( create_ixgbe: alloc::sync::Arc, create_virtio_net: alloc::sync::Arc, create_virtio_block: Arc, + create_virtio_backend: Arc, + create_virtio_net_mmio: Arc, create_nvme: alloc::sync::Arc, create_net_shadow: alloc::sync::Arc, create_nvme_shadow: alloc::sync::Arc, @@ -49,6 +51,8 @@ pub fn trusted_entry( create_ixgbe, create_virtio_net, create_virtio_block, + create_virtio_backend, + create_virtio_net_mmio, create_net_shadow, create_nvme_shadow, create_nvme, diff --git a/interface/Cargo.toml b/interface/Cargo.toml index 84c394d5..b2d7fc4f 100644 --- a/interface/Cargo.toml +++ b/interface/Cargo.toml @@ -18,4 +18,3 @@ spin = { path = "../lib/core/spin-rs" } hashbrown = "0.7.2" - diff --git a/interface/src/domain_create.rs b/interface/src/domain_create.rs index 333bfa5a..b38dfb58 100644 --- a/interface/src/domain_create.rs +++ b/interface/src/domain_create.rs @@ -24,6 +24,8 @@ pub trait CreateProxy { create_ixgbe: Arc, create_virtio_net: Arc, create_virtio_block: Arc, + create_virtio_backend: Arc, + create_virtio_net_mmio: Arc, create_nvme: Arc, create_net_shadow: Arc, create_nvme_shadow: Arc, @@ -86,6 +88,16 @@ pub trait CreateVirtioBlock: Send + Sync { -> (Box, Box); } +#[domain_create(path = "virtio_backend")] +pub trait CreateVirtioBackend: Send + Sync { + fn create_domain_virtio_backend(&self, net: Box) -> (Box, ()); +} + +#[domain_create(path = "virtio_net_mmio")] +pub trait CreateVirtioNetMMIO: Send + Sync { + fn create_domain_virtio_net_mmio(&self) -> (Box, Box); +} + #[domain_create(path = "net_shadow")] pub trait CreateNetShadow: Send + Sync { fn create_domain_net_shadow( diff --git a/interface/src/proxy.rs b/interface/src/proxy.rs index 8913e16f..0a5b92ca 100644 --- a/interface/src/proxy.rs +++ b/interface/src/proxy.rs @@ -3,7 +3,7 @@ use crate::domain_create::{ CreateAHCI, CreateBDevShadow, CreateBenchnet, CreateBenchnvme, CreateDomC, CreateDomD, CreateIxgbe, CreateMemBDev, CreateNetShadow, CreateNvme, CreateNvmeShadow, CreatePCI, CreateRv6, CreateRv6FS, CreateRv6Net, CreateRv6NetShadow, CreateRv6Usr, CreateShadow, - CreateVirtioBlock, CreateVirtioNet, + CreateVirtioBackend, CreateVirtioBlock, CreateVirtioNet, CreateVirtioNetMMIO, }; use alloc::boxed::Box; use alloc::sync::Arc; @@ -22,12 +22,22 @@ pub trait Proxy: + CreateDomC + CreateDomD + CreateShadow + + CreateVirtioNet + + CreateVirtioBlock + + CreateVirtioBackend + + CreateVirtioNetMMIO { // necessary because rust doesn't support trait object upcasting fn as_domain_create_CreateVirtioNet(&self) -> Arc; fn as_domain_create_CreateVirtioBlock( &self, ) -> Arc; + fn as_domain_create_CreateVirtioBackend( + &self, + ) -> Arc; + fn as_domain_create_CreateVirtioNetMMIO( + &self, + ) -> Arc; fn as_domain_create_CreateIxgbe(&self) -> Arc; fn as_domain_create_CreateDomD(&self) -> Arc; fn as_domain_create_CreateMemBDev(&self) -> Arc; diff --git a/kernel/Cargo.lock b/kernel/Cargo.lock index da9db596..4aeb5176 100644 --- a/kernel/Cargo.lock +++ b/kernel/Cargo.lock @@ -340,6 +340,14 @@ dependencies = [ "syscalls", ] +[[package]] +name = "libtime" +version = "0.1.0" +dependencies = [ + "console", + "libsyscalls", +] + [[package]] name = "log" version = "0.4.11" @@ -349,6 +357,15 @@ dependencies = [ "cfg-if", ] +[[package]] +name = "malloc" +version = "0.1.0" +dependencies = [ + "libsyscalls", + "slabmalloc 0.7.0", + "spin 0.5.3", +] + [[package]] name = "maybe-uninit" version = "2.0.0" @@ -538,10 +555,11 @@ dependencies = [ "platform", "rand", "signature", - "slabmalloc", + "slabmalloc 0.7.0 (git+https://github.com/gz/rust-slabmalloc.git)", "spin 0.5.3", "syscalls", "unwind", + "virtio_backend_trusted", "volatile", "x86 0.33.0", "x86_64", @@ -612,6 +630,13 @@ version = "1.2.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "29f060a7d147e33490ec10da418795238fd7545bba241504d6b31a409f2e6210" +[[package]] +name = "slabmalloc" +version = "0.7.0" +dependencies = [ + "log", +] + [[package]] name = "slabmalloc" version = "0.7.0" @@ -710,12 +735,48 @@ dependencies = [ "syscalls", ] +[[package]] +name = "virtio_backend_trusted" +version = "0.1.0" +dependencies = [ + "console", + "hashbrown 0.7.2", + "interface", + "libsyscalls", + "libtime", + "malloc", + "spin 0.5.3", + "virtio_device", + "volatile_accessor", +] + +[[package]] +name = "virtio_device" +version = "0.1.0" +dependencies = [ + "console", + "libsyscalls", + "libtime", + "malloc", + "volatile_accessor", +] + [[package]] name = "volatile" version = "0.2.7" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "f6b06ad3ed06fef1713569d547cdbdb439eafed76341820fb0e0344f29a41945" +[[package]] +name = "volatile_accessor" +version = "0.1.0" +dependencies = [ + "lazy_static", + "proc-macro2", + "quote", + "syn", +] + [[package]] name = "wasi" version = "0.9.0+wasi-snapshot-preview1" diff --git a/kernel/Cargo.toml b/kernel/Cargo.toml index 6da421e4..e484dc45 100644 --- a/kernel/Cargo.toml +++ b/kernel/Cargo.toml @@ -47,6 +47,7 @@ interface = { path = "../interface/generated" } ahci = { path = "../lib/core/interfaces/dev/ahci" } platform = { path = "../lib/core/interfaces/platform" } # pci_driver = { path = "../lib/core/interfaces/dev/pci/pci_driver" } +virtio_backend_trusted = { path = "../lib/devices/virtio_backend" } [dependencies.lazy_static] version = "1.3.0" diff --git a/lib/devices/virtio/src/defs.rs b/lib/devices/virtio/src/defs.rs index 2aabee18..a7d1db30 100644 --- a/lib/devices/virtio/src/defs.rs +++ b/lib/devices/virtio/src/defs.rs @@ -4,7 +4,7 @@ use alloc::{ vec::Vec, }; use console::println; -use core::{alloc::Layout, mem::size_of, usize}; +use core::{alloc::Layout, fmt::Debug, mem::size_of, usize}; // 2.6.12 Virtqueue Operation // There are two parts to virtqueue operation: supplying new available buffers to the device, and processing used buffers from the device. @@ -12,6 +12,7 @@ use core::{alloc::Layout, mem::size_of, usize}; // The driver adds outgoing (device-readable) packets to the transmit virtqueue, and then frees them after they are used. // Similarly, incoming (device-writable) buffers are added to the receive virtqueue, and processed after they are used. +#[derive(Debug)] #[repr(C, align(16))] pub struct VirtQueue { pub descriptors: Vec, @@ -54,6 +55,13 @@ pub struct VirtqUsedPacked { ring: [VirtqUsedElement; 0], // Will have size queue_size } +impl VirtqUsedPacked { + /// The struct does not know its queue size so *YOU* must check that the index is correct! + pub unsafe fn ring(&mut self, idx: u16) -> &mut VirtqUsedElement { + self.ring.get_unchecked_mut(idx as usize) + } +} + #[derive(Debug, Copy, Clone)] #[repr(C, packed(2))] pub struct VirtqAvailablePacked { @@ -66,6 +74,13 @@ pub struct VirtqAvailablePacked { ring: [u16; 0], // Will have size queue_size } +impl VirtqAvailablePacked { + /// The struct does not know its queue size so *YOU* must check that the index is correct! + pub unsafe fn ring(&mut self, idx: u16) -> &mut u16 { + self.ring.get_unchecked_mut(idx as usize) + } +} + pub struct VirtqAvailable { pub data: Box, queue_size: u16, @@ -114,6 +129,12 @@ impl Drop for VirtqAvailable { } } +impl Debug for VirtqAvailable { + fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result { + return self.data.fmt(f); + } +} + pub struct VirtqUsed { pub data: Box, queue_size: u16, @@ -159,3 +180,9 @@ impl Drop for VirtqUsed { } } } + +impl Debug for VirtqUsed { + fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result { + return self.data.fmt(f); + } +} diff --git a/lib/devices/virtio_backend/Cargo.lock b/lib/devices/virtio_backend/Cargo.lock new file mode 100644 index 00000000..08a4fee9 --- /dev/null +++ b/lib/devices/virtio_backend/Cargo.lock @@ -0,0 +1,264 @@ +# This file is automatically @generated by Cargo. +# It is not intended for manual editing. +[[package]] +name = "ahash" +version = "0.3.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e8fd72866655d1904d6b0997d0b07ba561047d070fbe29de039031c641b61217" + +[[package]] +name = "ahci" +version = "0.1.0" + +[[package]] +name = "autocfg" +version = "1.0.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "cdb031dd78e28731d87d56cc8ffef4a8f36ca26c38fe2de700543e627f8a464a" + +[[package]] +name = "bitfield" +version = "0.13.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "46afbd2983a5d5a7bd740ccb198caf5b82f45c40c09c0eed36052d91cb92e719" + +[[package]] +name = "bitflags" +version = "1.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "cf1de2fe8c75bc145a2f577add951f8134889b4795d47466a54a5c846d691693" + +[[package]] +name = "byteorder" +version = "1.4.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "14c189c53d098945499cdfa7ecc63567cf3886b3332b312a5b4585d8d3a6a610" + +[[package]] +name = "cfg-if" +version = "1.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "baf1de4339761588bc0619e3cbc0120ee582ebb74b53b4efbf79117bd2da40fd" + +[[package]] +name = "console" +version = "0.1.0" +dependencies = [ + "libsyscalls", + "spin", +] + +[[package]] +name = "hashbrown" +version = "0.7.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "96282e96bfcd3da0d3aa9938bedf1e50df3269b6db08b4876d2da0bb1a0841cf" +dependencies = [ + "ahash", + "autocfg", +] + +[[package]] +name = "interface" +version = "0.1.0" +dependencies = [ + "bitflags", + "byteorder", + "console", + "hashbrown", + "libsyscalls", + "num-derive", + "num-traits", + "pci_driver", + "spin", + "syscalls", + "unwind", +] + +[[package]] +name = "lazy_static" +version = "1.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e2abad23fbc42b3700f2f279844dc832adb2b2eb069b2df918f455c4e18cc646" + +[[package]] +name = "libsyscalls" +version = "0.1.0" +dependencies = [ + "pc-keyboard", + "platform", + "spin", + "syscalls", +] + +[[package]] +name = "libtime" +version = "0.1.0" +dependencies = [ + "console", + "libsyscalls", +] + +[[package]] +name = "log" +version = "0.4.14" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "51b9bbe6c47d51fc3e1a9b945965946b4c44142ab8792c50835a980d362c2710" +dependencies = [ + "cfg-if", +] + +[[package]] +name = "malloc" +version = "0.1.0" +dependencies = [ + "libsyscalls", + "slabmalloc", + "spin", +] + +[[package]] +name = "num-derive" +version = "0.3.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "876a53fff98e03a936a674b29568b0e605f06b29372c2489ff4de23f1949743d" +dependencies = [ + "proc-macro2", + "quote", + "syn", +] + +[[package]] +name = "num-traits" +version = "0.2.14" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9a64b1ec5cda2586e284722486d802acf1f7dbdc623e2bfc57e65ca1cd099290" +dependencies = [ + "autocfg", +] + +[[package]] +name = "pc-keyboard" +version = "0.3.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "fff50ab09ba31bcebc0669f4e64c0952fae1acdca9e6e0587e68e4e8443808ac" + +[[package]] +name = "pci_driver" +version = "0.1.0" +dependencies = [ + "ahci", + "platform", +] + +[[package]] +name = "platform" +version = "0.1.0" + +[[package]] +name = "proc-macro2" +version = "1.0.27" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f0d8caf72986c1a598726adc988bb5984792ef84f5ee5aa50209145ee8077038" +dependencies = [ + "unicode-xid", +] + +[[package]] +name = "protocol" +version = "0.1.0" +dependencies = [ + "bitfield", +] + +[[package]] +name = "quote" +version = "1.0.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c3d0b9745dc2debf507c8422de05d7226cc1f0644216dfdfead988f9b1ab32a7" +dependencies = [ + "proc-macro2", +] + +[[package]] +name = "slabmalloc" +version = "0.7.0" +dependencies = [ + "log", +] + +[[package]] +name = "spin" +version = "0.5.3" + +[[package]] +name = "syn" +version = "1.0.73" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f71489ff30030d2ae598524f61326b902466f72a0fb1a8564c001cc63425bcc7" +dependencies = [ + "proc-macro2", + "quote", + "unicode-xid", +] + +[[package]] +name = "syscalls" +version = "0.1.0" +dependencies = [ + "pc-keyboard", + "platform", + "protocol", + "spin", +] + +[[package]] +name = "unicode-xid" +version = "0.2.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8ccb82d61f80a663efe1f787a51b16b5a51e3314d6ac365b08639f52387b33f3" + +[[package]] +name = "unwind" +version = "0.1.0" +dependencies = [ + "libsyscalls", + "syscalls", +] + +[[package]] +name = "virtio_device" +version = "0.1.0" +dependencies = [ + "console", + "libsyscalls", + "libtime", + "malloc", + "volatile_accessor", +] + +[[package]] +name = "virtio_network_device" +version = "0.1.0" +dependencies = [ + "console", + "hashbrown", + "interface", + "libsyscalls", + "libtime", + "malloc", + "pci_driver", + "spin", + "virtio_device", + "volatile_accessor", +] + +[[package]] +name = "volatile_accessor" +version = "0.1.0" +dependencies = [ + "lazy_static", + "proc-macro2", + "quote", + "syn", +] diff --git a/lib/devices/virtio_backend/Cargo.toml b/lib/devices/virtio_backend/Cargo.toml new file mode 100644 index 00000000..0a01c338 --- /dev/null +++ b/lib/devices/virtio_backend/Cargo.toml @@ -0,0 +1,20 @@ +[package] +name = "virtio_backend_trusted" +version = "0.1.0" +authors = ["Redleaf team "] +edition = "2018" + +# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html + +[dependencies] +libsyscalls = { path = "../../../lib/core/libsyscalls" } +libtime = { path = "../../../lib/core/libtime" } +console = { path = "../../../lib/core/console" } +malloc = { path = "../../../lib/core/malloc" } +spin = { path = "../../../lib/core/spin-rs" } +volatile_accessor = { path = "../../../lib/external/volatile_accessor" } +interface = { path = "../../../interface/generated" } + +virtio_device = { path = "../virtio" } + +hashbrown = "0.7.2" \ No newline at end of file diff --git a/lib/devices/virtio_backend/src/defs.rs b/lib/devices/virtio_backend/src/defs.rs new file mode 100644 index 00000000..1d3be1e7 --- /dev/null +++ b/lib/devices/virtio_backend/src/defs.rs @@ -0,0 +1,72 @@ +use core::mem::size_of; +use virtio_device::{ + defs::{VirtqAvailablePacked, VirtqUsedPacked}, + VirtioPciCommonConfig, +}; + +pub const MAX_SUPPORTED_QUEUES: u16 = 2; +pub const BATCH_SIZE: usize = 32; + +pub const MMIO_ADDRESS: *mut VirtioPciCommonConfig = 0x100000 as *mut VirtioPciCommonConfig; +pub const DEVICE_NOTIFY: *mut usize = (0x100000 - size_of::()) as *mut usize; +pub const SHARED_MEMORY_REGION_PTR: *mut *mut Buffer = + (0x100000 + size_of::() + 0x1000) as *mut *mut Buffer; + +pub type Buffer = [u8; 1514]; +pub type BufferPtr = *const Buffer; + +#[derive(Debug, PartialEq)] +pub enum DeviceNotificationType { + None, + DeviceConfigurationUpdated, + QueueUpdated, +} + +impl DeviceNotificationType { + pub const fn value(&self) -> usize { + match self { + Self::None => 0, + Self::DeviceConfigurationUpdated => 1, + Self::QueueUpdated => 2, + } + } + + pub const fn from_value(value: usize) -> Self { + match value { + 0 => Self::None, + 1 => Self::DeviceConfigurationUpdated, + 2 => Self::QueueUpdated, + _ => Self::None, + } + } +} + +#[derive(Debug)] +pub struct VirtioQueueConfig { + pub queue_index: u16, + pub queue_size: u16, + pub queue_enable: bool, + pub queue_descriptor: u64, + pub queue_driver: u64, + pub queue_device: u64, + + /// The next idx to process for the driver queue + pub driver_idx: u16, + + /// The next idx to process for the device queue + pub device_idx: u16, +} + +impl VirtioQueueConfig { + pub fn get_driver_queue(&mut self) -> &mut VirtqAvailablePacked { + unsafe { + return &mut *(self.queue_driver as *mut VirtqAvailablePacked); + } + } + + pub fn get_device_queue(&mut self) -> &mut VirtqUsedPacked { + unsafe { + return &mut *(self.queue_driver as *mut VirtqUsedPacked); + } + } +} diff --git a/lib/devices/virtio_backend/src/lib.rs b/lib/devices/virtio_backend/src/lib.rs new file mode 100644 index 00000000..98b18c54 --- /dev/null +++ b/lib/devices/virtio_backend/src/lib.rs @@ -0,0 +1,100 @@ +#![no_std] +#![feature( + box_syntax, + const_fn, + const_raw_ptr_to_usize_cast, + const_in_array_repeat_expressions, + untagged_unions, + maybe_uninit_extra +)] + +pub mod defs; +pub mod virtual_queue; +extern crate alloc; + +use alloc::boxed::Box; +use core::ptr::{read_volatile, write_volatile}; +use defs::{ + Buffer, BufferPtr, DeviceNotificationType, DEVICE_NOTIFY, MAX_SUPPORTED_QUEUES, MMIO_ADDRESS, +}; +use interface::{net::Net, rref::RRef}; +use libsyscalls::syscalls::sys_yield; +use virtio_device::VirtioPciCommonConfig; + +pub struct VirtioBackendThreadArguments { + pub net: Box, +} + +pub static mut THREAD_ARGUMENTS: Option = None; + +pub fn get_thread_arguments() -> VirtioBackendThreadArguments { + unsafe { THREAD_ARGUMENTS.take().unwrap() } +} + +pub fn device_notify(notification_type: DeviceNotificationType) { + let value = notification_type.value(); + + unsafe { + write_volatile(DEVICE_NOTIFY, value); + + const WAIT_VALUE: usize = DeviceNotificationType::None.value(); + + // Wait for acknowledgement + while read_volatile(DEVICE_NOTIFY) != WAIT_VALUE { + sys_yield(); + } + } +} + +pub fn initialize_device_config_space() { + unsafe { + write_volatile(DEVICE_NOTIFY, 0); + + write_volatile( + MMIO_ADDRESS, + VirtioPciCommonConfig { + device_feature_select: 0, + device_feature: 0, + driver_feature_select: 0, + driver_feature: 0, + msix_config: 0, + num_queues: MAX_SUPPORTED_QUEUES, + device_status: 0, + config_generation: 0, + queue_select: 0, + queue_size: 256, + queue_msix_vector: 0, + queue_enable: 0, + queue_notify_off: 0, + queue_desc: 0, + queue_driver: 0, + queue_device: 0, + }, + ); + } +} + +pub fn get_device_notifications() -> DeviceNotificationType { + let dn = unsafe { read_volatile(DEVICE_NOTIFY) }; + + if dn != DeviceNotificationType::None.value() { + // Clear the notification + unsafe { + write_volatile(DEVICE_NOTIFY, 0); + } + } + + DeviceNotificationType::from_value(dn) +} + +pub fn copy_buffer_into_rref(buffer: &BufferPtr, rref: &RRef) { + unsafe { + core::ptr::copy(*buffer, rref.as_ptr() as *mut Buffer, 1); + } +} + +pub fn copy_rref_into_buffer(rref: &RRef, buffer: BufferPtr) { + unsafe { + core::ptr::copy(rref.as_ptr() as *mut Buffer, buffer as *mut Buffer, 1); + } +} diff --git a/lib/devices/virtio_backend/src/pci.rs b/lib/devices/virtio_backend/src/pci.rs new file mode 100644 index 00000000..2d35c0a6 --- /dev/null +++ b/lib/devices/virtio_backend/src/pci.rs @@ -0,0 +1,56 @@ +use console::println; +use pci_driver::DeviceBarRegions; + +use crate::VirtioNetInner; + +use alloc::boxed::Box; +use interface::net::Net; + +const VIRTIO_PCI_VID: u16 = 0x1af4; +const VIRTIO_PCI_DID: u16 = 0x1000; + +pub struct PciFactory { + mmio_base: Option, +} + +impl pci_driver::PciDriver for PciFactory { + fn probe(&mut self, bar_region: DeviceBarRegions) { + println!("VirtioNet PCI probe called"); + match bar_region { + DeviceBarRegions::Virtio(bar) => unsafe { + self.mmio_base = Some(bar.get_base() as usize); + }, + ty => { + println!("VirtioNet PCI probed with unsupported device {:?}", ty); + } + } + } + + /// Returns the Vendor ID for a VIRTIO Network Device + fn get_vid(&self) -> u16 { + VIRTIO_PCI_VID + } + + /// Returns the Device ID for a VIRTIO Network Device + fn get_did(&self) -> u16 { + // FIXME: Another possibility is the Transitional Device ID 0x1000 + VIRTIO_PCI_DID + } + + fn get_driver_type(&self) -> pci_driver::PciDrivers { + pci_driver::PciDrivers::VirtioDriver + } +} + +impl PciFactory { + pub fn new() -> Self { + Self { mmio_base: None } + } + + pub fn to_device(self) -> Option { + self.mmio_base.map(|base| { + let dev = unsafe { VirtioNetInner::new(base) }; + dev + }) + } +} diff --git a/lib/devices/virtio_backend/src/virtual_queue.rs b/lib/devices/virtio_backend/src/virtual_queue.rs new file mode 100644 index 00000000..0d7c21b5 --- /dev/null +++ b/lib/devices/virtio_backend/src/virtual_queue.rs @@ -0,0 +1,211 @@ +use crate::defs::{BufferPtr, BATCH_SIZE, SHARED_MEMORY_REGION_PTR}; +use alloc::{collections::VecDeque, slice}; +use hashbrown::HashMap; +use virtio_device::defs::{ + VirtqAvailablePacked, VirtqDescriptor, VirtqUsedElement, VirtqUsedPacked, +}; + +#[derive(Debug)] +pub struct DescriptorQueue { + address: *mut VirtqDescriptor, + queue_size: u16, +} + +impl DescriptorQueue { + pub fn new(address: *mut VirtqDescriptor, queue_size: u16) -> Self { + Self { + address, + queue_size, + } + } + + pub fn get_descriptors(&self) -> &mut [VirtqDescriptor] { + unsafe { slice::from_raw_parts_mut(self.address, self.queue_size as usize) } + } + + pub fn queue_size(&self) -> u16 { + self.queue_size + } +} +#[derive(Debug)] +pub struct DeviceQueue { + address: *mut VirtqUsedPacked, + queue_size: u16, +} + +impl DeviceQueue { + pub fn new(address: *mut VirtqUsedPacked, queue_size: u16) -> Self { + Self { + address, + queue_size, + } + } + + pub fn idx(&mut self) -> &mut u16 { + unsafe { &mut (*self.address).idx } + } + + pub fn ring(&mut self, idx: u16) -> &mut VirtqUsedElement { + assert!(idx < self.queue_size); + + unsafe { (*self.address).ring(idx) } + } +} +#[derive(Debug)] +pub struct DriverQueue { + address: *mut VirtqAvailablePacked, + queue_size: u16, + + pub previous_idx: u16, +} + +impl DriverQueue { + pub fn new(address: *mut VirtqAvailablePacked, queue_size: u16) -> Self { + Self { + address, + queue_size, + + previous_idx: 0, + } + } + + pub fn idx(&self) -> &u16 { + unsafe { &(*self.address).idx } + } + + pub fn ring(&mut self, idx: u16) -> &u16 { + assert!(idx < self.queue_size); + + unsafe { (*self.address).ring(idx) } + } +} +#[derive(Debug)] +pub struct VirtualQueue { + pub descriptor_queue: DescriptorQueue, + pub driver_queue: DriverQueue, + pub device_queue: DeviceQueue, + + queue_size: u16, + rx_queue: bool, + + // Variables used for efficiency + /// Holds the pointers to buffers that either need to be given to the device (rx queue) or given to the frontend (tx queue) + buffer_deque: VecDeque, + + /// Maps the buffer's ptr to the chain_header_idx + buffer_map: HashMap, +} + +impl VirtualQueue { + /// rx_queue should be true if the queue has an even index, false otherwise + pub fn new( + descriptor_queue_address: u64, + device_queue_address: u64, + driver_queue_address: u64, + queue_size: u16, + rx_queue: bool, + ) -> Self { + Self { + descriptor_queue: DescriptorQueue::new( + descriptor_queue_address as *mut VirtqDescriptor, + queue_size, + ), + device_queue: DeviceQueue::new( + device_queue_address as *mut VirtqUsedPacked, + queue_size, + ), + driver_queue: DriverQueue::new( + driver_queue_address as *mut VirtqAvailablePacked, + queue_size, + ), + + queue_size, + rx_queue, + + buffer_deque: VecDeque::with_capacity(BATCH_SIZE), + buffer_map: HashMap::new(), + } + } + + pub fn is_rx_queue(&self) -> bool { + self.rx_queue + } + + fn assert_is_rx(&self) { + assert!( + self.rx_queue, + "This function should only be called if the queue is an rx_queue (even index)!" + ); + } + + fn assert_is_tx(&self) { + assert!( + !self.rx_queue, + "This function should only be called if the queue is an tx_queue (odd index)!" + ); + } + + pub fn fetch_new_buffers(&mut self) -> &mut VecDeque { + while self.driver_queue.previous_idx != *self.driver_queue.idx() + && self.buffer_deque.len() < BATCH_SIZE + { + let idx = self.driver_queue.previous_idx % self.queue_size; + let chain_header_idx = *self.driver_queue.ring(idx); + + let descriptors = self.descriptor_queue.get_descriptors(); + let mut current_idx: usize = chain_header_idx.into(); + + // Find the descriptor with the correct length + loop { + let descriptor = descriptors[current_idx]; + + if descriptor.len == 1514 { + // Add it to the device and break + + // descriptor.addr is an offset, convert it to an address + let addr = (unsafe { *SHARED_MEMORY_REGION_PTR } as usize + + descriptor.addr as usize) as u64; + + self.buffer_deque.push_back(addr as BufferPtr); + self.buffer_map.insert(addr, chain_header_idx); + break; + } else { + // Try the next descriptor + if (descriptor.flags & 0b1) == 0b1 { + current_idx = descriptor.next.into(); + } else { + break; + } + } + } + + // Move to the next chain + self.driver_queue.previous_idx = self.driver_queue.previous_idx.wrapping_add(1); + } + + return &mut self.buffer_deque; + } + + pub fn mark_buffers_as_complete(&mut self, buffers: &[BufferPtr]) { + for buffer in buffers { + // Look up the chain header idx + let buffer_key = (*buffer) as u64; + if let Some(chain_header_idx) = self.buffer_map.remove(&buffer_key) { + // Mark that chain as complete + let idx = *self.device_queue.idx(); + *self.device_queue.ring(idx % self.queue_size) = VirtqUsedElement { + id: chain_header_idx.into(), + len: 1514, + }; + + // Update the idx + *self.device_queue.idx() = idx.wrapping_add(1); + } else { + panic!( + "Buffer Address Changed during processing! FAILED ADDR: {:#?}, MAP: {:#?}", + buffer, self.buffer_map + ); + } + } + } +} diff --git a/lib/devices/virtio_net/src/lib.rs b/lib/devices/virtio_net/src/lib.rs index 5cd9c14a..d930596b 100644 --- a/lib/devices/virtio_net/src/lib.rs +++ b/lib/devices/virtio_net/src/lib.rs @@ -89,7 +89,7 @@ pub struct VirtioNetInner { impl VirtioNetInner { /// Returns an initialized VirtioNet from a base address. - unsafe fn new(mmio_base: usize) -> Self { + pub unsafe fn new(mmio_base: usize) -> Self { Self { mmio: Mmio::new(mmio_base), @@ -172,6 +172,11 @@ impl VirtioNetInner { // Setup Virtual Queues self.initialize_virtual_queue(0, &(self.virtual_queues.as_ref().unwrap().receive_queue)); + + // println!("Virtio Net Should Yield here!"); + // self.print_device_config(); + // libsyscalls::syscalls::sys_yield(); + self.initialize_virtual_queue(1, &(self.virtual_queues.as_ref().unwrap().transmit_queue)); // Tell the Device we're all done, even though we aren't diff --git a/lib/devices/virtio_net_mmio/Cargo.lock b/lib/devices/virtio_net_mmio/Cargo.lock new file mode 100644 index 00000000..08a4fee9 --- /dev/null +++ b/lib/devices/virtio_net_mmio/Cargo.lock @@ -0,0 +1,264 @@ +# This file is automatically @generated by Cargo. +# It is not intended for manual editing. +[[package]] +name = "ahash" +version = "0.3.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e8fd72866655d1904d6b0997d0b07ba561047d070fbe29de039031c641b61217" + +[[package]] +name = "ahci" +version = "0.1.0" + +[[package]] +name = "autocfg" +version = "1.0.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "cdb031dd78e28731d87d56cc8ffef4a8f36ca26c38fe2de700543e627f8a464a" + +[[package]] +name = "bitfield" +version = "0.13.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "46afbd2983a5d5a7bd740ccb198caf5b82f45c40c09c0eed36052d91cb92e719" + +[[package]] +name = "bitflags" +version = "1.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "cf1de2fe8c75bc145a2f577add951f8134889b4795d47466a54a5c846d691693" + +[[package]] +name = "byteorder" +version = "1.4.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "14c189c53d098945499cdfa7ecc63567cf3886b3332b312a5b4585d8d3a6a610" + +[[package]] +name = "cfg-if" +version = "1.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "baf1de4339761588bc0619e3cbc0120ee582ebb74b53b4efbf79117bd2da40fd" + +[[package]] +name = "console" +version = "0.1.0" +dependencies = [ + "libsyscalls", + "spin", +] + +[[package]] +name = "hashbrown" +version = "0.7.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "96282e96bfcd3da0d3aa9938bedf1e50df3269b6db08b4876d2da0bb1a0841cf" +dependencies = [ + "ahash", + "autocfg", +] + +[[package]] +name = "interface" +version = "0.1.0" +dependencies = [ + "bitflags", + "byteorder", + "console", + "hashbrown", + "libsyscalls", + "num-derive", + "num-traits", + "pci_driver", + "spin", + "syscalls", + "unwind", +] + +[[package]] +name = "lazy_static" +version = "1.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e2abad23fbc42b3700f2f279844dc832adb2b2eb069b2df918f455c4e18cc646" + +[[package]] +name = "libsyscalls" +version = "0.1.0" +dependencies = [ + "pc-keyboard", + "platform", + "spin", + "syscalls", +] + +[[package]] +name = "libtime" +version = "0.1.0" +dependencies = [ + "console", + "libsyscalls", +] + +[[package]] +name = "log" +version = "0.4.14" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "51b9bbe6c47d51fc3e1a9b945965946b4c44142ab8792c50835a980d362c2710" +dependencies = [ + "cfg-if", +] + +[[package]] +name = "malloc" +version = "0.1.0" +dependencies = [ + "libsyscalls", + "slabmalloc", + "spin", +] + +[[package]] +name = "num-derive" +version = "0.3.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "876a53fff98e03a936a674b29568b0e605f06b29372c2489ff4de23f1949743d" +dependencies = [ + "proc-macro2", + "quote", + "syn", +] + +[[package]] +name = "num-traits" +version = "0.2.14" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9a64b1ec5cda2586e284722486d802acf1f7dbdc623e2bfc57e65ca1cd099290" +dependencies = [ + "autocfg", +] + +[[package]] +name = "pc-keyboard" +version = "0.3.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "fff50ab09ba31bcebc0669f4e64c0952fae1acdca9e6e0587e68e4e8443808ac" + +[[package]] +name = "pci_driver" +version = "0.1.0" +dependencies = [ + "ahci", + "platform", +] + +[[package]] +name = "platform" +version = "0.1.0" + +[[package]] +name = "proc-macro2" +version = "1.0.27" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f0d8caf72986c1a598726adc988bb5984792ef84f5ee5aa50209145ee8077038" +dependencies = [ + "unicode-xid", +] + +[[package]] +name = "protocol" +version = "0.1.0" +dependencies = [ + "bitfield", +] + +[[package]] +name = "quote" +version = "1.0.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c3d0b9745dc2debf507c8422de05d7226cc1f0644216dfdfead988f9b1ab32a7" +dependencies = [ + "proc-macro2", +] + +[[package]] +name = "slabmalloc" +version = "0.7.0" +dependencies = [ + "log", +] + +[[package]] +name = "spin" +version = "0.5.3" + +[[package]] +name = "syn" +version = "1.0.73" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f71489ff30030d2ae598524f61326b902466f72a0fb1a8564c001cc63425bcc7" +dependencies = [ + "proc-macro2", + "quote", + "unicode-xid", +] + +[[package]] +name = "syscalls" +version = "0.1.0" +dependencies = [ + "pc-keyboard", + "platform", + "protocol", + "spin", +] + +[[package]] +name = "unicode-xid" +version = "0.2.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8ccb82d61f80a663efe1f787a51b16b5a51e3314d6ac365b08639f52387b33f3" + +[[package]] +name = "unwind" +version = "0.1.0" +dependencies = [ + "libsyscalls", + "syscalls", +] + +[[package]] +name = "virtio_device" +version = "0.1.0" +dependencies = [ + "console", + "libsyscalls", + "libtime", + "malloc", + "volatile_accessor", +] + +[[package]] +name = "virtio_network_device" +version = "0.1.0" +dependencies = [ + "console", + "hashbrown", + "interface", + "libsyscalls", + "libtime", + "malloc", + "pci_driver", + "spin", + "virtio_device", + "volatile_accessor", +] + +[[package]] +name = "volatile_accessor" +version = "0.1.0" +dependencies = [ + "lazy_static", + "proc-macro2", + "quote", + "syn", +] diff --git a/lib/devices/virtio_net_mmio/Cargo.toml b/lib/devices/virtio_net_mmio/Cargo.toml new file mode 100644 index 00000000..8d9b6a99 --- /dev/null +++ b/lib/devices/virtio_net_mmio/Cargo.toml @@ -0,0 +1,23 @@ +[package] +name = "virtio_net_mmio_device" +version = "0.1.0" +authors = ["Redleaf team "] +edition = "2018" + +# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html + +[dependencies] +libsyscalls = { path = "../../../lib/core/libsyscalls" } +libtime = { path = "../../../lib/core/libtime" } +console = { path = "../../../lib/core/console" } +malloc = { path = "../../../lib/core/malloc" } +spin = { path = "../../../lib/core/spin-rs" } +volatile_accessor = { path = "../../../lib/external/volatile_accessor" } + +pci_driver = { path = "../../../lib/core/interfaces/dev/pci/pci_driver" } +interface = { path = "../../../interface/generated" } + +virtio_backend_trusted = { path = "../virtio_backend" } +virtio_device = { path = "../virtio" } + +hashbrown = "0.7.2" \ No newline at end of file diff --git a/lib/devices/virtio_net_mmio/src/lib.rs b/lib/devices/virtio_net_mmio/src/lib.rs new file mode 100644 index 00000000..e5fbeb7e --- /dev/null +++ b/lib/devices/virtio_net_mmio/src/lib.rs @@ -0,0 +1,509 @@ +#![no_std] +#![no_main] +#![feature( + box_syntax, + const_fn, + const_raw_ptr_to_usize_cast, + const_in_array_repeat_expressions, + untagged_unions, + maybe_uninit_extra, + array_methods +)] + +pub mod pci; +extern crate alloc; + +use alloc::alloc::{alloc, Layout}; +use alloc::sync::Arc; +use alloc::vec; +use alloc::vec::Vec; +use console::println; +use core::mem::size_of; +use core::ptr::{read_volatile, write_volatile}; +use core::slice::{self}; +use core::usize; +use hashbrown::HashMap; +use interface::rref::{RRef, RRefDeque}; +use libsyscalls::syscalls::sys_yield; +use spin::Mutex; +use virtio_backend_trusted::defs::{DeviceNotificationType, SHARED_MEMORY_REGION_PTR}; +use virtio_backend_trusted::device_notify; +use virtio_device::defs::{ + VirtQueue, VirtqAvailable, VirtqAvailablePacked, VirtqDescriptor, VirtqUsed, VirtqUsedElement, + VirtqUsedPacked, +}; +use virtio_device::{Mmio, VirtioDeviceStatus}; + +#[repr(C, packed)] +pub struct VirtioNetworkDeviceConfig { + mac: [u8; 6], + status: u16, + // Not available without negotiating features VIRTIO_NET_F_MQ and VIRTIO_NET_F_MTU + // max_virtqueue_pairs: u16, + // mtu: u16, +} + +#[derive(Debug, Clone, Copy)] +#[repr(C, packed)] +pub struct VirtioNetworkHeader { + pub flags: u8, + pub gso_type: u8, + pub header_length: u16, + pub gso_size: u16, + pub csum_start: u16, + pub csum_offset: u16, + // pub num_buffers: u16, +} + +struct VirtualQueues { + receive_queue: VirtQueue, + transmit_queue: VirtQueue, +} + +type NetworkPacketBuffer = [u8; 1514]; + +pub struct VirtioNetInner { + mmio: Mmio, + virtual_queues: Option, // None until init() is called + + /// This is the size of the queues used by the device, it is read during init(). + /// It would be less annoying to use if it were usize but it truly is a u16 value. + queue_size: u16, + + /// This tracks the maximum number of buffers or descriptor chains we can simultaneiously have. + /// For the network driver, each network packet requires two descriptors so this will be + /// queue_size / 2. + buffer_count: usize, + + /// Dummy VirtioNetHeaders. + /// The driver doesn't actually use these but they are required by the spec + virtio_network_headers: Vec, + + /// The shared memory region for buffers + /// Place tx at header_idx and rx at buffer_idx + virtio_network_buffers: Vec, + + /// Tracks which descriptors on the queue are free + rx_free_descriptors: Vec, + /// Tracks which descriptors on the queue are free + tx_free_descriptors: Vec, + + /// The last index (of the used ring) that was checked by the driver + rx_last_idx: u16, + /// The last index (of the used ring) that was checked by the driver + tx_last_idx: u16, + + rx_buffers: Vec>>, + tx_buffers: Vec>>, +} + +impl VirtioNetInner { + /// Returns an initialized VirtioNet from a base address. + pub unsafe fn new(mmio_base: usize) -> Self { + Self { + mmio: Mmio::new(mmio_base), + + queue_size: 0, // We will update this (and the vecs) in init() + buffer_count: 0, + + virtual_queues: None, + + virtio_network_headers: vec![], + virtio_network_buffers: vec![], + + rx_free_descriptors: vec![], + tx_free_descriptors: vec![], + + rx_buffers: vec![], + tx_buffers: vec![], + + rx_last_idx: 0, + tx_last_idx: 0, + } + } + + pub fn init(&mut self) { + println!("Initializing Virtio Network Device"); + + // VIRTIO DEVICE INIT + // https://docs.oasis-open.org/virtio/virtio/v1.1/cs01/virtio-v1.1-cs01.html#x1-920001 + // + // Reset the device. + // Set the ACKNOWLEDGE status bit: the guest OS has noticed the device. + // Set the DRIVER status bit: the guest OS knows how to drive the device. + // Read device feature bits, and write the subset of feature bits understood by the OS and driver to the device. During this step the driver MAY read (but MUST NOT write) the device-specific configuration fields to check that it can support the device before accepting it. + // Set the FEATURES_OK status bit. The driver MUST NOT accept new feature bits after this step. + // Re-read device status to ensure the FEATURES_OK bit is still set: otherwise, the device does not support our subset of features and the device is unusable. + // Perform device-specific setup, including discovery of virtqueues for the device, optional per-bus setup, reading and possibly writing the device’s virtio configuration space, and population of virtqueues. + // Set the DRIVER_OK status bit. At this point the device is “live”. + + // Reset the device + // Failing to do this DOES cause errors, don't ask how I know *sigh* + unsafe { + self.mmio.accessor.write_device_status(0); + } + Mmio::memory_fence(); + + // Acknowledge Device + unsafe { + self.mmio + .update_device_status(VirtioDeviceStatus::Acknowledge); + self.mmio.update_device_status(VirtioDeviceStatus::Driver); // But do we really know how to drive the device? + } + + self.negotiate_features(); + + // Tell the Device that feature Negotiation is complete + unsafe { + self.mmio + .update_device_status(VirtioDeviceStatus::FeaturesOk); + } + + // Check that Features OK Bit is still set! + // self.print_device_status(); + if (self.mmio.accessor.read_device_status() & VirtioDeviceStatus::FeaturesOk.value()) == 0 { + panic!("Failed to negotiate Virtio Net features!"); + } + + // Read the queue size + // This value is that largest possible queue size so we will use it to initialize all of our vectors + let queue_size = self.mmio.accessor.read_queue_size(); + if queue_size == 0 { + panic!("ERROR: VIRTIO NET: BAD QUEUE SIZE!"); + } + + self.queue_size = queue_size; + self.buffer_count = (self.queue_size / 2) as usize; // Each buffer requires two descriptors + + unsafe { + self.setup_virtual_queues(); + } + + self.initialize_vectors(); + + // Setup Virtual Queues + self.initialize_virtual_queue(0, &(self.virtual_queues.as_ref().unwrap().receive_queue)); + + println!("Should call device_notify"); + device_notify(DeviceNotificationType::DeviceConfigurationUpdated); + + self.initialize_virtual_queue(1, &(self.virtual_queues.as_ref().unwrap().transmit_queue)); + + device_notify(DeviceNotificationType::DeviceConfigurationUpdated); + + // Tell the Device we're all done, even though we aren't + unsafe { self.mmio.update_device_status(VirtioDeviceStatus::DriverOk) }; + device_notify(DeviceNotificationType::DeviceConfigurationUpdated); + + // self.print_device_status(); + + // self.mmio.accessor.write_queue_select(0); + // self.print_device_config(); + // self.mmio.accessor.write_queue_select(1); + // self.print_device_config(); + + println!("VIRTIO NET READY!"); + } + + /// Negotiates Virtio Driver Features + fn negotiate_features(&mut self) { + let mut driver_features: u32 = 0; + driver_features |= 1 << 5; // Enable Device MAC Address + driver_features |= 1 << 16; // Enable Device Status + + self.mmio.accessor.write_driver_feature(driver_features); // Should be &'d with device_features + } + + pub fn print_device_config(&mut self) { + let mut cfg = unsafe { self.mmio.read_common_config() }; + println!("{:#?}", cfg); + } + + pub fn print_device_status(&mut self) { + let device_status = self.mmio.accessor.read_device_status(); + println!("Device Status Bits: {:b}", device_status); + } + + /// Initializes all the vectors using the set buffer_count + fn initialize_vectors(&mut self) { + self.virtio_network_headers = vec![ + VirtioNetworkHeader { + csum_offset: 0, + csum_start: 0, + flags: 0, + gso_size: 0, + gso_type: 0, + header_length: 0, + }; + self.buffer_count + ]; + + self.virtio_network_buffers = vec![[0; 1514]; self.queue_size.into()]; + // Update the ptr + unsafe { + *SHARED_MEMORY_REGION_PTR = self.virtio_network_buffers.as_mut_ptr(); + } + + self.rx_free_descriptors = vec![true; self.buffer_count]; + self.tx_free_descriptors = vec![true; self.buffer_count]; + + self.rx_buffers = Vec::with_capacity(self.buffer_count); + self.rx_buffers.resize_with(self.buffer_count, || None); + self.tx_buffers = Vec::with_capacity(self.buffer_count); + self.tx_buffers.resize_with(self.buffer_count, || None); + } + + unsafe fn setup_virtual_queues(&mut self) { + self.virtual_queues = Some(VirtualQueues { + receive_queue: VirtQueue { + descriptors: vec![VirtqDescriptor::default(); self.queue_size as usize], + available: VirtqAvailable::new(self.queue_size), + used: VirtqUsed::new(self.queue_size), + }, + transmit_queue: VirtQueue { + descriptors: vec![VirtqDescriptor::default(); self.queue_size as usize], + available: VirtqAvailable::new(self.queue_size), + used: VirtqUsed::new(self.queue_size), + }, + }); + } + + /// Receive Queues must be 2*N and Transmit Queues must be 2*N + 1 + /// For example, Receive Queue must be 0 and Transmit Queue must be 1 + fn initialize_virtual_queue(&self, queue_index: u16, virt_queue: &VirtQueue) { + self.mmio.accessor.write_queue_select(queue_index); + + self.mmio + .accessor + .write_queue_desc(virt_queue.descriptors.as_ptr() as u64); + self.mmio.accessor.write_queue_driver( + (virt_queue.available.data.as_ref() as *const VirtqAvailablePacked) as u64, + ); + self.mmio + .accessor + .write_queue_device((virt_queue.used.data.as_ref() as *const VirtqUsedPacked) as u64); + self.mmio.accessor.write_queue_enable(1); + } + + /// Returns a free descriptor chain index + /// For Virtio Net, the VirtioNetworkHeader is placed at i and the Packet Buffer will be placed at i + self.buffer_count + fn get_free_idx(free_buffers: &mut Vec) -> Result { + for i in 0..free_buffers.len() { + if free_buffers[i] { + free_buffers[i] = false; + return Ok(i); + } + } + + return Err(()); + } + + #[inline] + fn get_addr(obj: &T) -> u64 { + (obj as *const T) as u64 + } + + /// If the buffer can't be added, it is returned in the Err() + fn add_rx_buffer( + &mut self, + buffer: RRef, + ) -> Result<(), RRef> { + let rx_q = &mut self.virtual_queues.as_mut().unwrap().receive_queue; + + if let Ok(header_idx) = Self::get_free_idx(&mut self.rx_free_descriptors) { + let buffer_idx = header_idx + self.buffer_count; + // let buffer_addr = buffer.as_ptr() as u64; + + // Use the shared memory region + let buffer_addr = self.virtio_network_buffers[buffer_idx].as_ptr() as u64; + let buffer_offset = buffer_addr - self.virtio_network_buffers.as_ptr() as u64; + + // Store it so it isn't dropped + self.rx_buffers[header_idx] = Some(buffer); + + // One descriptor points at the network header, chain this with a descriptor to the buffer + // Header + rx_q.descriptors[header_idx] = VirtqDescriptor { + addr: Self::get_addr(&self.virtio_network_headers[header_idx]), + len: core::mem::size_of::() as u32, // 10 bytes + // 1 is NEXT FLAG + // 2 is WRITABLE FLAG + flags: 1 | 2, + next: buffer_idx as u16, + }; + // Actual Buffer + rx_q.descriptors[buffer_idx] = VirtqDescriptor { + addr: buffer_offset, + len: 1514, + flags: 2, + next: 0, + }; + + // Mark the buffer as usable + *rx_q + .available + .ring(rx_q.available.data.idx % self.queue_size) = header_idx as u16; + rx_q.available.data.idx = rx_q.available.data.idx.wrapping_add(1); // We only added one "chain head" + + // unsafe { + // self.mmio.queue_notify(0, 0); + // } + + device_notify(DeviceNotificationType::QueueUpdated); + + return Ok(()); + } else { + return Err(buffer); + } + } + + pub fn add_rx_buffers( + &mut self, + packets: &mut RRefDeque, + collect: &mut RRefDeque, + ) { + if packets.len() == 0 { + return; + } + + while let Some(buffer) = packets.pop_front() { + let res = self.add_rx_buffer(buffer); + + if res.is_err() { + packets.push_back(res.unwrap_err()); + break; + } + } + } + + /// Returns an error if there's no free space in the TX queue, Ok otherwise + fn add_tx_packet( + &mut self, + buffer: RRef, + ) -> Result<(), RRef> { + let tx_q = &mut self.virtual_queues.as_mut().unwrap().transmit_queue; + + if let Ok(header_idx) = Self::get_free_idx(&mut self.tx_free_descriptors) { + let buffer_idx = header_idx + self.buffer_count; + + // Copy the packet into the shared memory region + let a = buffer.as_slice(); + self.virtio_network_buffers[header_idx].copy_from_slice(a); + + let buffer_addr = self.virtio_network_buffers[header_idx].as_ptr() as u64; + let buffer_offset = buffer_addr - self.virtio_network_buffers.as_ptr() as u64; + + // Store it so it isn't dropped + self.tx_buffers[header_idx] = Some(buffer); + + tx_q.descriptors[header_idx] = VirtqDescriptor { + addr: Self::get_addr(&self.virtio_network_headers[header_idx]), + len: core::mem::size_of::() as u32, // 10 bytes + flags: 1, // 1 is next flag + next: buffer_idx as u16, + }; + tx_q.descriptors[buffer_idx] = VirtqDescriptor { + addr: buffer_offset, + len: 1514, + flags: 0, + next: 0, + }; + + *tx_q + .available + .ring(tx_q.available.data.idx % self.queue_size) = header_idx as u16; + tx_q.available.data.idx = tx_q.available.data.idx.wrapping_add(1); + + // unsafe { + // self.mmio.queue_notify(1, 1); + // } + device_notify(DeviceNotificationType::QueueUpdated); + + return Ok(()); + } else { + return Err(buffer); + } + } + + pub fn add_tx_buffers(&mut self, packets: &mut RRefDeque) { + if packets.len() == 0 { + return; + } + + while let Some(packet) = packets.pop_front() { + let res = self.add_tx_packet(packet); + + if res.is_err() { + println!("ERROR: VIRTIO NET: COULD NOT ADD TX PACKET. NO FREE SPACE!"); + packets.push_back(res.unwrap_err()); + break; + } + } + } + + /// Adds new packets to `packets`. Returns the number of received packets + /// Returns the number of new packets found + pub fn get_received_packets( + &mut self, + collect: &mut RRefDeque, + ) -> usize { + /// We have to return the number of packets received + let mut new_packets_count = 0; + let rx_q = &mut self.virtual_queues.as_mut().unwrap().receive_queue; + + while self.rx_last_idx != rx_q.used.data.idx { + let used_element = rx_q.used.ring(self.rx_last_idx % self.queue_size); + let header_descriptor = &rx_q.descriptors[used_element.id as usize]; + let buffer_descriptor = &rx_q.descriptors[header_descriptor.next as usize]; + + if let Some(mut buffer) = self.rx_buffers[used_element.id as usize].take() { + // Copy rx packet from shared memory region to buffer + buffer.copy_from_slice( + &self.virtio_network_buffers[header_descriptor.next as usize].as_slice(), + ); + + // Processed packets are "collected" + collect.push_back(buffer); + new_packets_count += 1; + + // Free the descriptor + self.rx_free_descriptors[used_element.id as usize] = true; + } else { + println!("ERROR: VIRTIO NET: RX BUFFER MISSING"); + } + + self.rx_last_idx = self.rx_last_idx.wrapping_add(1); + } + + new_packets_count + } + + /// Returns the number of tx packets that have been sent + pub fn free_processed_tx_packets( + &mut self, + packets: &mut RRefDeque, + ) -> usize { + let mut freed_count = 0; + let tx_q = &mut self.virtual_queues.as_mut().unwrap().transmit_queue; + + while self.tx_last_idx != tx_q.used.data.idx { + let used_element = tx_q.used.ring(self.tx_last_idx % self.queue_size); + let header_descriptor = &tx_q.descriptors[used_element.id as usize]; + let buffer_descriptor = &tx_q.descriptors[header_descriptor.next as usize]; + + if let Some(buffer) = self.tx_buffers[used_element.id as usize].take() { + packets.push_back(buffer); + freed_count += 1; + + // Free the descriptor + self.tx_free_descriptors[used_element.id as usize] = true; + } else { + println!("ERROR: VIRTIO NET: TX BUFFER MISSING"); + } + + self.tx_last_idx = self.tx_last_idx.wrapping_add(1); + } + + freed_count + } +} diff --git a/lib/devices/virtio_net_mmio/src/pci.rs b/lib/devices/virtio_net_mmio/src/pci.rs new file mode 100644 index 00000000..2d35c0a6 --- /dev/null +++ b/lib/devices/virtio_net_mmio/src/pci.rs @@ -0,0 +1,56 @@ +use console::println; +use pci_driver::DeviceBarRegions; + +use crate::VirtioNetInner; + +use alloc::boxed::Box; +use interface::net::Net; + +const VIRTIO_PCI_VID: u16 = 0x1af4; +const VIRTIO_PCI_DID: u16 = 0x1000; + +pub struct PciFactory { + mmio_base: Option, +} + +impl pci_driver::PciDriver for PciFactory { + fn probe(&mut self, bar_region: DeviceBarRegions) { + println!("VirtioNet PCI probe called"); + match bar_region { + DeviceBarRegions::Virtio(bar) => unsafe { + self.mmio_base = Some(bar.get_base() as usize); + }, + ty => { + println!("VirtioNet PCI probed with unsupported device {:?}", ty); + } + } + } + + /// Returns the Vendor ID for a VIRTIO Network Device + fn get_vid(&self) -> u16 { + VIRTIO_PCI_VID + } + + /// Returns the Device ID for a VIRTIO Network Device + fn get_did(&self) -> u16 { + // FIXME: Another possibility is the Transitional Device ID 0x1000 + VIRTIO_PCI_DID + } + + fn get_driver_type(&self) -> pci_driver::PciDrivers { + pci_driver::PciDrivers::VirtioDriver + } +} + +impl PciFactory { + pub fn new() -> Self { + Self { mmio_base: None } + } + + pub fn to_device(self) -> Option { + self.mmio_base.map(|base| { + let dev = unsafe { VirtioNetInner::new(base) }; + dev + }) + } +} diff --git a/lib/external/volatile_accessor/src/lib.rs b/lib/external/volatile_accessor/src/lib.rs index c286b9c5..e1d10cb8 100644 --- a/lib/external/volatile_accessor/src/lib.rs +++ b/lib/external/volatile_accessor/src/lib.rs @@ -1,14 +1,14 @@ #![feature(log_syntax, proc_macro_def_site)] use core::panic; -use std::collections::HashMap; use quote::{format_ident, quote}; +use std::collections::HashMap; use lazy_static::lazy_static; use proc_macro::{Ident, TokenStream}; -use syn::{ImplItem, ImplItemMethod, ItemStruct, parse_quote}; +use syn::{parse_quote, ImplItem, ImplItemMethod, ItemStruct}; -lazy_static!( +lazy_static! { static ref SIZE_MAP: HashMap<&'static str, usize> = vec![ ("u8", 1), ("u16", 2), @@ -18,18 +18,25 @@ lazy_static!( ("i16", 2), ("i32", 4), ("i64", 8), + ] + .into_iter() + .collect(); +} - ].into_iter().collect(); -); - -/// Generate a +/// Generate a #[proc_macro_attribute] pub fn volatile_accessor(attr: TokenStream, item: TokenStream) -> TokenStream { // Parse the input tokens into a syntax tree assert!(attr.is_empty(), &attr.to_string()); - let st: ItemStruct = syn::parse(item).expect("interface definition must be a valid struct definition"); + let st: ItemStruct = + syn::parse(item).expect("interface definition must be a valid struct definition"); - assert_eq!(st.generics.params.len(), 0, "Generic is not supported: {:?}", st); + assert_eq!( + st.generics.params.len(), + 0, + "Generic is not supported: {:?}", + st + ); let mut offset: usize = 0; let mut accessor_impls: Vec = vec![]; @@ -49,18 +56,28 @@ pub fn volatile_accessor(attr: TokenStream, item: TokenStream) -> TokenStream { syn::Type::TraitObject(x) => unimplemented!("{:?}", x), syn::Type::Tuple(x) => unimplemented!("{:?}", x), syn::Type::Verbatim(x) => unimplemented!("{:?}", x), - syn::Type::__Nonexhaustive => unimplemented!(), syn::Type::Path(path) => { - let field_ident = field.ident.as_ref().expect(&format!("All field must be named: {:?}", st)); + let field_ident = field + .ident + .as_ref() + .expect(&format!("All field must be named: {:?}", st)); let field_type = &field.ty; - let path = path.path.segments.iter().map(|seg| seg.ident.to_string()).collect::>().join("::"); - let size = SIZE_MAP.get(path.as_str()).expect(&format!("Type {} not supported. Supported types are {:?}", path, *SIZE_MAP)); + let path = path + .path + .segments + .iter() + .map(|seg| seg.ident.to_string()) + .collect::>() + .join("::"); + let size = SIZE_MAP.get(path.as_str()).expect(&format!( + "Type {} not supported. Supported types are {:?}", + path, *SIZE_MAP + )); let read_accessor_ident = format_ident!("read_{}", &field_ident); let write_accessor_ident = format_ident!("write_{}", &field_ident); let offset_accessor_ident = format_ident!("{}_offset", &field_ident); let address_accessor_ident = format_ident!("{}_address", &field_ident); - // // Generate accessors. accessor_impls.push(parse_quote! { pub fn #offset_accessor_ident(&self) -> usize { @@ -84,11 +101,11 @@ pub fn volatile_accessor(attr: TokenStream, item: TokenStream) -> TokenStream { }); offset += size; - }, + } + _ => unimplemented!(), } } - let accessor_ident = format_ident!("{}VolatileAccessor", st.ident); let vis = &st.vis; TokenStream::from(quote! { @@ -96,7 +113,7 @@ pub fn volatile_accessor(attr: TokenStream, item: TokenStream) -> TokenStream { #[allow(dead_code)] #vis struct #accessor_ident { - base: usize, + base: usize, } #[allow(dead_code)]