diff --git a/Makefile b/Makefile index 8972ba52d..633218e1a 100644 --- a/Makefile +++ b/Makefile @@ -13,7 +13,7 @@ keyboard = qwerty# qwerty, azerty, dvorak mode = release # Emulation options -nic = rtl8139# rtl8139, pcnet, e1000 +nic = rtl8139# rtl8139, pcnet, e1000, virtio-net audio = sdl# sdl, coreaudio signal = off# on kvm = false diff --git a/src/sys/net/mod.rs b/src/sys/net/mod.rs index d8dda5c96..d3739047d 100644 --- a/src/sys/net/mod.rs +++ b/src/sys/net/mod.rs @@ -36,7 +36,7 @@ pub enum EthernetDevice { RTL8139(nic::rtl8139::Device), PCNET(nic::pcnet::Device), E1000(nic::e1000::Device), - //VirtIO, + VirtIO(nic::virtio::Device), } pub trait EthernetDeviceIO { @@ -53,6 +53,7 @@ impl EthernetDeviceIO for EthernetDevice { EthernetDevice::RTL8139(dev) => dev.config(), EthernetDevice::PCNET(dev) => dev.config(), EthernetDevice::E1000(dev) => dev.config(), + EthernetDevice::VirtIO(dev) => dev.config(), } } @@ -61,6 +62,7 @@ impl EthernetDeviceIO for EthernetDevice { EthernetDevice::RTL8139(dev) => dev.stats(), EthernetDevice::PCNET(dev) => dev.stats(), EthernetDevice::E1000(dev) => dev.stats(), + EthernetDevice::VirtIO(dev) => dev.stats(), } } @@ -69,6 +71,7 @@ impl EthernetDeviceIO for EthernetDevice { EthernetDevice::RTL8139(dev) => dev.receive_packet(), EthernetDevice::PCNET(dev) => dev.receive_packet(), EthernetDevice::E1000(dev) => dev.receive_packet(), + EthernetDevice::VirtIO(dev) => dev.receive_packet(), } } @@ -77,6 +80,7 @@ impl EthernetDeviceIO for EthernetDevice { EthernetDevice::RTL8139(dev) => dev.transmit_packet(len), EthernetDevice::PCNET(dev) => dev.transmit_packet(len), EthernetDevice::E1000(dev) => dev.transmit_packet(len), + EthernetDevice::VirtIO(dev) => dev.transmit_packet(len), } } @@ -85,6 +89,7 @@ impl EthernetDeviceIO for EthernetDevice { EthernetDevice::RTL8139(dev) => dev.next_tx_buffer(len), EthernetDevice::PCNET(dev) => dev.next_tx_buffer(len), EthernetDevice::E1000(dev) => dev.next_tx_buffer(len), + EthernetDevice::VirtIO(dev) => dev.next_tx_buffer(len), } } } @@ -287,6 +292,11 @@ pub fn init() { let nic = nic::pcnet::Device::new(io); add(EthernetDevice::PCNET(nic), "PCNET"); } + if let Some(dev) = find_device(0x1AF4, 0x1000) { + let io = dev.io_base(); + let nic = nic::virtio::Device::new(io); + add(EthernetDevice::VirtIO(nic), "VirtIO"); + } for id in E1000_DEVICES { if let Some(dev) = find_device(0x8086, id) { let io = dev.io_base(); diff --git a/src/sys/net/nic/mod.rs b/src/sys/net/nic/mod.rs index 8b9ddcb01..911ed756e 100644 --- a/src/sys/net/nic/mod.rs +++ b/src/sys/net/nic/mod.rs @@ -1,3 +1,4 @@ pub mod e1000; pub mod pcnet; pub mod rtl8139; +pub mod virtio; diff --git a/src/sys/net/nic/virtio.rs b/src/sys/net/nic/virtio.rs new file mode 100644 index 000000000..219bd9fb2 --- /dev/null +++ b/src/sys/net/nic/virtio.rs @@ -0,0 +1,162 @@ +use crate::sys::allocator::PhysBuf; +use crate::sys::net::{EthernetDeviceIO, Config, Stats}; + +use alloc::sync::Arc; +use alloc::vec::Vec; +use smoltcp::wire::EthernetAddress; +use x86_64::instructions::port::Port; + +// https://ozlabs.org/~rusty/virtio-spec/virtio-0.9.5.pdf +// https://docs.oasis-open.org/virtio/virtio/v1.0/virtio-v1.0.html + +const ACKNOWLEDGE: u8 = 1; +const DRIVER: u8 = 2; +const DRIVER_OK: u8 = 4; +const FAILED: u8 = 128; + +#[derive(Clone)] +pub struct Ports { + pub device_features: Port, // r + pub driver_features: Port, // r+w + pub queue_addr: Port, // r+w + pub queue_size: Port, // r + pub queue_select: Port, // r+w + pub queue_notify: Port, // r+w + pub device_status: Port, // r+w + pub isr_status: Port, // r + pub mac: [Port; 6], +} + +impl Ports { + pub fn new(io_base: u16) -> Self { + Self { + device_features: Port::new(io_base + 0x00), + driver_features: Port::new(io_base + 0x04), + queue_addr: Port::new(io_base + 0x08), + queue_size: Port::new(io_base + 0x0C), + queue_select: Port::new(io_base + 0x0E), + queue_notify: Port::new(io_base + 0x10), + device_status: Port::new(io_base + 0x12), + isr_status: Port::new(io_base + 0x13), + mac: [ + Port::new(io_base + 0x14), + Port::new(io_base + 0x15), + Port::new(io_base + 0x16), + Port::new(io_base + 0x17), + Port::new(io_base + 0x18), + Port::new(io_base + 0x19), + ], + } + } + + fn mac(&mut self) -> [u8; 6] { + unsafe { + [ + self.mac[0].read(), + self.mac[1].read(), + self.mac[2].read(), + self.mac[3].read(), + self.mac[4].read(), + self.mac[5].read(), + ] + } + } +} + +#[derive(Clone)] +pub struct Device { + config: Arc, + stats: Arc, + ports: Ports, + + rx_buffer: PhysBuf, + tx_buffer: PhysBuf, +} + +impl Device { + pub fn new(io_base: u16) -> Self { + let mut device = Self { + ports: Ports::new(io_base), + config: Arc::new(Config::new()), + stats: Arc::new(Stats::new()), + rx_buffer: PhysBuf::new((4096 * 5) / 8), + tx_buffer: PhysBuf::new((4096 * 5) / 8), + }; + device.init(); + device + } + + fn init(&mut self) { + unsafe { + self.ports.device_status.write(0); // Reset + + let device_status = self.ports.device_status.read(); + self.ports.device_status.write(device_status | ACKNOWLEDGE); + + let device_status = self.ports.device_status.read(); + self.ports.device_status.write(device_status | DRIVER); + } + + let device_features = unsafe { self.ports.device_features.read() }; + debug!("VirtIO Net: device features: {:#X}", device_features); + + + let device_status = unsafe { self.ports.device_status.read() }; + debug!("VirtIO Net: device status: {:#X}", device_status); + + // RX + unsafe { + let queue_index = 0; + self.ports.queue_select.write(queue_index); + let queue_size = self.ports.queue_size.read() as usize; + debug!("VirtIO Net: queue({}) size: {}", queue_index, queue_size); + } + + // TX + unsafe { + let queue_index = 1; + self.ports.queue_select.write(queue_index); + let queue_size = self.ports.queue_size.read() as usize; + debug!("VirtIO Net: queue({}) size: {}", queue_index, queue_size); + }; + + self.config.update_mac(EthernetAddress::from_bytes(&self.ports.mac())); + + unsafe { + let device_status = self.ports.device_status.read(); + self.ports.device_status.write(device_status | DRIVER_OK); + } + } + + fn read(&self, addr: u16) -> u32 { + debug!("READ: {:#X}", addr); + 0 + } + + fn write(&self, addr: u16, data: u32) { + debug!("WRITE: {:#X}", addr); + } +} + +impl EthernetDeviceIO for Device { + fn config(&self) -> Arc { + self.config.clone() + } + + fn stats(&self) -> Arc { + self.stats.clone() + } + + fn receive_packet(&mut self) -> Option> { + debug!("RECV"); + None + } + + fn transmit_packet(&mut self, len: usize) { + debug!("SEND"); + } + + fn next_tx_buffer(&mut self, len: usize) -> &mut [u8] { + &mut self.tx_buffer[0..len] // FIXME + } +}