milestone: desktop path Phases 1-5
Phase 1 (Runtime Substrate): 4 check binaries, --probe, POSIX tests Phase 2 (Wayland Compositor): bounded scaffold, zero warnings Phase 3 (KWin Session): preflight checker (KWin stub, gated on Qt6Quick) Phase 4 (KDE Plasma): 18 KF6 enabled, preflight checker Phase 5 (Hardware GPU): DRM/firmware/Mesa preflight checker Build: zero warnings, all scripts syntax-clean. Oracle-verified.
This commit is contained in:
@@ -0,0 +1,22 @@
|
||||
[package]
|
||||
name = "virtio-netd"
|
||||
description = "VirtIO network driver"
|
||||
version = "0.1.0"
|
||||
edition = "2021"
|
||||
|
||||
[dependencies]
|
||||
log.workspace = true
|
||||
static_assertions.workspace = true
|
||||
futures = { version = "0.3.28", features = ["executor"] }
|
||||
|
||||
virtio-core = { path = "../../virtio-core" }
|
||||
pcid = { path = "../../pcid" }
|
||||
common = { path = "../../common" }
|
||||
daemon = { path = "../../../daemon" }
|
||||
driver-network = { path = "../driver-network" }
|
||||
|
||||
redox_syscall.workspace = true
|
||||
libredox.workspace = true
|
||||
|
||||
[lints]
|
||||
workspace = true
|
||||
@@ -0,0 +1,6 @@
|
||||
[[drivers]]
|
||||
name = "virtio-net"
|
||||
class = 0x02
|
||||
vendor = 0x1AF4
|
||||
device = 0x1000
|
||||
command = ["virtio-netd"]
|
||||
@@ -0,0 +1,137 @@
|
||||
mod scheme;
|
||||
|
||||
use std::fs::File;
|
||||
use std::io::{Read, Write};
|
||||
use std::mem;
|
||||
|
||||
use driver_network::NetworkScheme;
|
||||
use pcid_interface::PciFunctionHandle;
|
||||
|
||||
use scheme::VirtioNet;
|
||||
|
||||
pub const VIRTIO_NET_F_MAC: u32 = 5;
|
||||
|
||||
#[derive(Debug)]
|
||||
#[repr(C)]
|
||||
pub struct VirtHeader {
|
||||
pub flags: u8,
|
||||
pub gso_type: u8,
|
||||
pub hdr_len: u16,
|
||||
pub gso_size: u16,
|
||||
pub csum_start: u16,
|
||||
pub csum_offset: u16,
|
||||
pub num_buffers: u16,
|
||||
}
|
||||
|
||||
static_assertions::const_assert_eq!(core::mem::size_of::<VirtHeader>(), 12);
|
||||
|
||||
const MAX_BUFFER_LEN: usize = 65535;
|
||||
fn main() {
|
||||
pcid_interface::pci_daemon(daemon_runner);
|
||||
}
|
||||
|
||||
fn daemon_runner(redox_daemon: daemon::Daemon, pcid_handle: PciFunctionHandle) -> ! {
|
||||
daemon(redox_daemon, pcid_handle).unwrap();
|
||||
unreachable!();
|
||||
}
|
||||
|
||||
fn daemon(
|
||||
daemon: daemon::Daemon,
|
||||
mut pcid_handle: PciFunctionHandle,
|
||||
) -> Result<(), Box<dyn std::error::Error>> {
|
||||
common::setup_logging(
|
||||
"net",
|
||||
"pci",
|
||||
"virtio-netd",
|
||||
common::output_level(),
|
||||
common::file_level(),
|
||||
);
|
||||
|
||||
// Double check that we have the right device.
|
||||
//
|
||||
// 0x1000 - virtio-net
|
||||
let pci_config = pcid_handle.config();
|
||||
|
||||
assert_eq!(pci_config.func.full_device_id.device_id, 0x1000);
|
||||
log::info!("virtio-net: initiating startup sequence :^)");
|
||||
|
||||
let device = virtio_core::probe_device(&mut pcid_handle)?;
|
||||
let device_space = device.device_space;
|
||||
|
||||
// Negotiate device features:
|
||||
let mac_address = if device.transport.check_device_feature(VIRTIO_NET_F_MAC) {
|
||||
let mac = unsafe {
|
||||
[
|
||||
core::ptr::read_volatile(device_space.add(0)),
|
||||
core::ptr::read_volatile(device_space.add(1)),
|
||||
core::ptr::read_volatile(device_space.add(2)),
|
||||
core::ptr::read_volatile(device_space.add(3)),
|
||||
core::ptr::read_volatile(device_space.add(4)),
|
||||
core::ptr::read_volatile(device_space.add(5)),
|
||||
]
|
||||
};
|
||||
|
||||
log::info!(
|
||||
"virtio-net: device MAC is {:>02X}:{:>02X}:{:>02X}:{:>02X}:{:>02X}:{:>02X}",
|
||||
mac[0],
|
||||
mac[1],
|
||||
mac[2],
|
||||
mac[3],
|
||||
mac[4],
|
||||
mac[5]
|
||||
);
|
||||
|
||||
device.transport.ack_driver_feature(VIRTIO_NET_F_MAC);
|
||||
mac
|
||||
} else {
|
||||
unimplemented!()
|
||||
};
|
||||
|
||||
device.transport.finalize_features();
|
||||
|
||||
// Allocate the recieve and transmit queues:
|
||||
//
|
||||
// > Empty buffers are placed in one virtqueue for receiving
|
||||
// > packets, and outgoing packets are enqueued into another
|
||||
// > for transmission in that order.
|
||||
//
|
||||
// TODO(andypython): Should we use the same IRQ vector for both?
|
||||
let rx_queue = device
|
||||
.transport
|
||||
.setup_queue(virtio_core::MSIX_PRIMARY_VECTOR, &device.irq_handle)?;
|
||||
|
||||
let tx_queue = device
|
||||
.transport
|
||||
.setup_queue(virtio_core::MSIX_PRIMARY_VECTOR, &device.irq_handle)?;
|
||||
|
||||
device.transport.run_device();
|
||||
|
||||
let mut name = pci_config.func.name();
|
||||
name.push_str("_virtio_net");
|
||||
|
||||
let device = VirtioNet::new(mac_address, rx_queue, tx_queue);
|
||||
let mut scheme = NetworkScheme::new(
|
||||
move || {
|
||||
//TODO: do device init in this function to prevent hangs
|
||||
device
|
||||
},
|
||||
daemon,
|
||||
format!("network.{name}"),
|
||||
);
|
||||
|
||||
let mut event_queue = File::open("/scheme/event")?;
|
||||
event_queue.write(&syscall::Event {
|
||||
id: scheme.event_handle().raw(),
|
||||
flags: syscall::EVENT_READ,
|
||||
data: 0,
|
||||
})?;
|
||||
|
||||
libredox::call::setrens(0, 0).expect("virtio-netd: failed to enter null namespace");
|
||||
|
||||
scheme.tick()?;
|
||||
|
||||
loop {
|
||||
event_queue.read(&mut [0; mem::size_of::<syscall::Event>()])?; // Wait for event
|
||||
scheme.tick()?;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,118 @@
|
||||
use std::sync::Arc;
|
||||
|
||||
use driver_network::NetworkAdapter;
|
||||
|
||||
use common::dma::Dma;
|
||||
|
||||
use virtio_core::spec::{Buffer, ChainBuilder, DescriptorFlags};
|
||||
use virtio_core::transport::Queue;
|
||||
|
||||
use crate::{VirtHeader, MAX_BUFFER_LEN};
|
||||
|
||||
pub struct VirtioNet<'a> {
|
||||
mac_address: [u8; 6],
|
||||
|
||||
/// Reciever Queue.
|
||||
rx: Arc<Queue<'a>>,
|
||||
rx_buffers: Vec<Dma<[u8]>>,
|
||||
|
||||
/// Transmiter Queue.
|
||||
tx: Arc<Queue<'a>>,
|
||||
|
||||
recv_head: u16,
|
||||
}
|
||||
|
||||
impl<'a> VirtioNet<'a> {
|
||||
pub fn new(mac_address: [u8; 6], rx: Arc<Queue<'a>>, tx: Arc<Queue<'a>>) -> Self {
|
||||
// Populate all of the `rx_queue` with buffers to maximize performence.
|
||||
let mut rx_buffers = vec![];
|
||||
for i in 0..(rx.descriptor_len() as usize) {
|
||||
rx_buffers.push(unsafe {
|
||||
Dma::<[u8]>::zeroed_slice(MAX_BUFFER_LEN)
|
||||
.unwrap()
|
||||
.assume_init()
|
||||
});
|
||||
|
||||
let chain = ChainBuilder::new()
|
||||
.chain(Buffer::new_unsized(&rx_buffers[i]).flags(DescriptorFlags::WRITE_ONLY))
|
||||
.build();
|
||||
|
||||
let _ = rx.send(chain);
|
||||
}
|
||||
|
||||
Self {
|
||||
mac_address,
|
||||
|
||||
rx,
|
||||
rx_buffers,
|
||||
tx,
|
||||
|
||||
recv_head: 0,
|
||||
}
|
||||
}
|
||||
|
||||
/// Returns the number of bytes read. Returns `0` if the operation would block.
|
||||
fn try_recv(&mut self, target: &mut [u8]) -> usize {
|
||||
let header_size = core::mem::size_of::<VirtHeader>();
|
||||
|
||||
if self.recv_head == self.rx.used.head_index() {
|
||||
// The read would block.
|
||||
return 0;
|
||||
}
|
||||
|
||||
let idx = self.rx.used.head_index() as usize;
|
||||
let element = self.rx.used.get_element_at(idx - 1);
|
||||
|
||||
let descriptor_idx = element.table_index.get();
|
||||
let payload_size = element.written.get() as usize - header_size;
|
||||
|
||||
// XXX: The header and packet are added as one output descriptor to the transmit queue,
|
||||
// and the device is notified of the new entry (see 5.1.5 Device Initialization).
|
||||
let buffer = &self.rx_buffers[descriptor_idx as usize];
|
||||
// TODO: Check the header.
|
||||
let _header = unsafe { &*(buffer.as_ptr() as *const VirtHeader) };
|
||||
let packet = &buffer[header_size..(header_size + payload_size)];
|
||||
|
||||
// Copy the packet into the buffer.
|
||||
target[..payload_size].copy_from_slice(&packet);
|
||||
|
||||
self.recv_head = self.rx.used.head_index();
|
||||
payload_size
|
||||
}
|
||||
}
|
||||
|
||||
impl<'a> NetworkAdapter for VirtioNet<'a> {
|
||||
fn mac_address(&mut self) -> [u8; 6] {
|
||||
self.mac_address
|
||||
}
|
||||
|
||||
fn available_for_read(&mut self) -> usize {
|
||||
(self.rx.used.head_index() - self.recv_head).into()
|
||||
}
|
||||
|
||||
fn read_packet(&mut self, buf: &mut [u8]) -> syscall::Result<Option<usize>> {
|
||||
let bytes = self.try_recv(buf);
|
||||
|
||||
if bytes != 0 {
|
||||
// We read some bytes.
|
||||
Ok(Some(bytes))
|
||||
} else {
|
||||
Ok(None)
|
||||
}
|
||||
}
|
||||
|
||||
fn write_packet(&mut self, buffer: &[u8]) -> syscall::Result<usize> {
|
||||
let header = unsafe { Dma::<VirtHeader>::zeroed()?.assume_init() };
|
||||
|
||||
let mut payload = unsafe { Dma::<[u8]>::zeroed_slice(buffer.len())?.assume_init() };
|
||||
payload.copy_from_slice(buffer);
|
||||
|
||||
let chain = ChainBuilder::new()
|
||||
.chain(Buffer::new(&header))
|
||||
.chain(Buffer::new_unsized(&payload))
|
||||
.build();
|
||||
|
||||
futures::executor::block_on(self.tx.send(chain));
|
||||
Ok(buffer.len())
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user