Skip to content
Open
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 README.org
Original file line number Diff line number Diff line change
Expand Up @@ -174,7 +174,7 @@ direction...
to control user capabilities
- [[./doc/journal/26-servers.org][26. Servers]], generalising the RAMdisk server code
- [[./doc/journal/27-text-editor.org][27. Text editor]]. Writing a text editor to run on EuraliOS.

- [[./doc/journal/27-virtio.org][28. Virtio devices]]

** Notes

Expand Down
32 changes: 32 additions & 0 deletions doc/journal/27-virtio.org
Original file line number Diff line number Diff line change
@@ -0,0 +1,32 @@
* Virtio devices

http://www.dumais.io/index.php?article=aca38a9a2b065b24dfa1dee728062a12


[[https://docs.oasis-open.org/virtio/virtio/v1.1/csprd01/virtio-v1.1-csprd01.html][Virtio 1.1 draft specificiation]]

In July 2022 a new version of Virtio was released, [[https://docs.oasis-open.org/virtio/virtio/v1.2/cs01/virtio-v1.2-cs01.pdf][Virtio 1.2]] that
includes filesystem devices.


There is a description of [[https://wiki.osdev.org/Virtio][Virtio on OSdev.org]], but it seems to
describe the pre-1.0 "legacy" interface.

** Configuration

PCI devices provide a 256-byte configuration space that should be read
in 32-bit chunks by reading and writing ports.


** Network adapter

The QEMU run arguments can be set in =kernel/Cargo.toml=, specifying
that the Virtio network adapter should be used:

#+begin_src ini
[package.metadata.bootimage]
run-args = ["-nic", "user,model=virtio-net-pci"]
#+end_src

A lot of the initial setup for the Virtio network card is
very similar to the [[./14-network.org][RTL8139 network card]]
8 changes: 4 additions & 4 deletions init/src/main.rs
Original file line number Diff line number Diff line change
Expand Up @@ -201,13 +201,13 @@ fn main() {
syscalls::EXEC_PERM_IO, // I/O permissions
writer_sys.clone());

mount("/dev/nic", include_bytes!("../../user/rtl8139"),
mount("/dev/nic", include_bytes!("../../user/virtio_net"),
syscalls::EXEC_PERM_IO,
writer_sys.clone());

mount("/tcp", include_bytes!("../../user/tcp"),
0, // No I/O permissions
writer_sys.clone());
// mount("/tcp", include_bytes!("../../user/tcp"),
// 0, // No I/O permissions
// writer_sys.clone());

// Start a user shell on new Console
consoles[1] = {
Expand Down
3 changes: 2 additions & 1 deletion kernel/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -22,8 +22,9 @@ version = "1.0"
features = ["spin_no_std"]

[package.metadata.bootimage]
run-args = ["-cpu", "Skylake-Client-v3", "-nic", "user,model=rtl8139,hostfwd=tcp::5555-:23"]
#run-args = ["-cpu", "Skylake-Client-v3", "-nic", "user,model=rtl8139,hostfwd=tcp::5555-:23"]
#run-args = ["-netdev", "user,id=u1", "-device", "rtl8139,netdev=u1", "-object", "filter-dump,id=f1,netdev=u1,file=dump.dat"]
run-args = ["-cpu", "Skylake-Client-v3", "-nic", "user,model=virtio-net-pci"]

test-args = ["-device", "isa-debug-exit,iobase=0xf4,iosize=0x04", "-serial", "stdio",
"-display", "none"]
Expand Down
98 changes: 94 additions & 4 deletions virtio_net/src/main.rs
Original file line number Diff line number Diff line change
@@ -1,13 +1,41 @@
// Parts adapted from
// https://github.com/torokernel/torokernel/blob/7d6df4c40fa4cc85febd5fd5799404592ffdff53/rtl/drivers/VirtIONet.pas

#![no_std]
#![no_main]

use euralios_std::{println,
syscalls,
message::{self, rcall, pci, MessageData}};
net::MacAddress,
message::{self, rcall, pci, MessageData},
ports::{outportb, outportw, outportd,
inportb, inportw, inportd}};

// Device status field, in the order the bits are typically set
const VIRTIO_RESET: u8 = 0;
const VIRTIO_ACKNOWLEDGE: u8 = 1;
const VIRTIO_DRIVER_LOADED: u8 = 2;
const VIRTIO_DRIVER_FAILED: u8 = 128;
const VIRTIO_DRIVER_FEATURES_OK: u8 = 8;
const VIRTIO_DRIVER_OK: u8 = 4;
const VIRTIO_DEVICE_NEEDS_RESET: u8 = 64;

// Feature bits
// 0 to 23, and 50 to 127 Feature bits for the specific device type
// 24 to 40 Feature bits reserved for extensions to the queue and feature negotiation mechanisms
// 41 to 49, and 128 and above Feature bits reserved for future extensions.

//const VIRTIO_F_VERSION_1

const REG_DEVICE_FEATURES: u16 = 0;
const REG_GUEST_FEATURES: u16 = 4;
const REG_STATUS: u16 = 0x12;
const REG_QUEUE_SIZE: u16 = 0x0C;
const REG_QUEUE_SELECT: u16 = 0x0E;

#[no_mangle]
fn main() {
println!("[virtio-net] Starting driver");
println!("[virtio_net] Starting driver");

let handle = syscalls::open("/pci", message::O_READ).expect("Couldn't open pci");

Expand All @@ -17,9 +45,71 @@ fn main() {
None).unwrap();
let address = md_address.value();
if msg_type != pci::ADDRESS {
println!("[virtio-net] Device not found. Exiting.");
println!("[virtio_net] Device not found. Exiting.");
return;
}
println!("[virtio-net] Found at address: {:08X}", address);
println!("[virtio_net] Found at address: {:08X}", address);

// Read BAR0 to get the I/O address
let (_, md_bar0, _) = rcall(&handle, pci::READ_BAR,
address.into(), 0.into(),
Some(pci::BAR)).unwrap();
let bar0 = md_bar0.value();
let ioaddr = (bar0 & 0xFFFC) as u16;
println!("[virtio_net] BAR0: {:08X}. I/O addr: {:04X}", bar0, ioaddr);

let mut device = Device{ioaddr};
device.reset();
println!("[virtio_net] MAC address {}", device.mac_address());


}

struct Device {
ioaddr: u16
}

impl Device {
/// Perform a software reset
fn reset(&mut self) -> Result<(), &'static str> {
// reset device
outportb(self.ioaddr + REG_STATUS, VIRTIO_RESET);

// Wait for the device to present 0 device status
while inportb(self.ioaddr + REG_STATUS) != VIRTIO_RESET {
syscalls::thread_yield(); // Wait for a while
}

// Tell the device that we found it
outportb(self.ioaddr + REG_STATUS, VIRTIO_ACKNOWLEDGE);
outportb(self.ioaddr + REG_STATUS, VIRTIO_ACKNOWLEDGE | VIRTIO_DRIVER_LOADED);

// Negotiation phase

let features = inportd(self.ioaddr + REG_DEVICE_FEATURES);
println!("[virtio_net] features: {:0x}", features);

// Setup virtual queues
for i in 0..16 {
outportw(self.ioaddr + REG_QUEUE_SELECT, i);
// Read the size of the queue needed
let queue_size = inportw(self.ioaddr + REG_QUEUE_SIZE);
if queue_size == 0 {
continue;
}

println!("Queue {}: {}", i, queue_size);
}
Err("Incomplete")
}

/// Read the Media Access Control (MAC) address
/// from the network card.
fn mac_address(&self) -> MacAddress {
let mut octet: [u8; 6] = [0; 6];
for ind in 0..octet.len() {
octet[ind] = inportb(self.ioaddr + 0x14 + ind as u16);
}
MacAddress::new(octet)
}
}