diff --git a/README.org b/README.org index dba1368..d6ab04f 100644 --- a/README.org +++ b/README.org @@ -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 diff --git a/doc/journal/27-virtio.org b/doc/journal/27-virtio.org new file mode 100644 index 0000000..1dfe9fd --- /dev/null +++ b/doc/journal/27-virtio.org @@ -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]] diff --git a/init/src/main.rs b/init/src/main.rs index da093b8..f8850eb 100644 --- a/init/src/main.rs +++ b/init/src/main.rs @@ -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] = { diff --git a/kernel/Cargo.toml b/kernel/Cargo.toml index a9eb177..8993b74 100644 --- a/kernel/Cargo.toml +++ b/kernel/Cargo.toml @@ -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"] diff --git a/virtio_net/src/main.rs b/virtio_net/src/main.rs index 86220bb..d5ec779 100644 --- a/virtio_net/src/main.rs +++ b/virtio_net/src/main.rs @@ -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"); @@ -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) + } }