Skip to content

Add virtio-net driver #606

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Draft
wants to merge 2 commits into
base: trunk
Choose a base branch
from
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion Makefile
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down
12 changes: 11 additions & 1 deletion src/sys/net/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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 {
Expand All @@ -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(),
}
}

Expand All @@ -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(),
}
}

Expand All @@ -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(),
}
}

Expand All @@ -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),
}
}

Expand All @@ -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),
}
}
}
Expand Down Expand Up @@ -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();
Expand Down
1 change: 1 addition & 0 deletions src/sys/net/nic/mod.rs
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
pub mod e1000;
pub mod pcnet;
pub mod rtl8139;
pub mod virtio;
162 changes: 162 additions & 0 deletions src/sys/net/nic/virtio.rs
Original file line number Diff line number Diff line change
@@ -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<u32>, // r
pub driver_features: Port<u32>, // r+w
pub queue_addr: Port<u32>, // r+w
pub queue_size: Port<u16>, // r
pub queue_select: Port<u16>, // r+w
pub queue_notify: Port<u16>, // r+w
pub device_status: Port<u8>, // r+w
pub isr_status: Port<u8>, // r
pub mac: [Port<u8>; 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<Config>,
stats: Arc<Stats>,
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<Config> {
self.config.clone()
}

fn stats(&self) -> Arc<Stats> {
self.stats.clone()
}

fn receive_packet(&mut self) -> Option<Vec<u8>> {
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
}
}
Loading