Files
RedBear-OS/virtio-core/src/spec/split_virtqueue.rs
T
bjorn3 380a416258 Reorganize virtio headers and add a couple of new fields
Also add a decent amount of comments from the virtio spec.
2024-01-18 17:17:39 +01:00

206 lines
6.1 KiB
Rust

//! [2.7 Split Virtqueues](https://docs.oasis-open.org/virtio/virtio/v1.2/cs01/virtio-v1.2-cs01.html#x1-350007)
//!
//! This file contains comments copied from the VirtIO specification which are
//! licensed under the following conditions:
//!
//! Copyright © OASIS Open 2022. All Rights Reserved.
//!
//! All capitalized terms in the following text have the meanings assigned to them
//! in the OASIS Intellectual Property Rights Policy (the "OASIS IPR Policy"). The
//! full Policy may be found at the OASIS website.
//!
//! This document and translations of it may be copied and furnished to others,
//! and derivative works that comment on or otherwise explain it or assist in its
//! implementation may be prepared, copied, published, and distributed, in whole
//! or in part, without restriction of any kind, provided that the above copyright
//! notice and this section are included on all such copies and derivative works.
//! However, this document itself may not be modified in any way, including by
//! removing the copyright notice or references to OASIS, except as needed for the
//! purpose of developing any document or deliverable produced by an OASIS Technical
//! Committee (in which case the rules applicable to copyrights, as set forth in the
//! OASIS IPR Policy, must be followed) or as required to translate it into languages
//! other than English.
use std::sync::atomic::{AtomicU16, AtomicU32, AtomicU64, Ordering};
use crate::utils::{IncompleteArrayField, VolatileCell};
use static_assertions::const_assert_eq;
/// [2.7.5 The Virtqueue Descriptor table](https://docs.oasis-open.org/virtio/virtio/v1.2/cs01/virtio-v1.2-cs01.html#x1-430005)
#[repr(C, align(16))]
pub struct Descriptor {
/// Address (guest-physical).
address: AtomicU64,
/// Size of the descriptor.
size: AtomicU32,
flags: AtomicU16,
/// Next field if flags & NEXT
next: AtomicU16,
}
const_assert_eq!(core::mem::size_of::<Descriptor>(), 16);
bitflags::bitflags! {
#[derive(Debug, Copy, Clone)]
#[repr(transparent)]
pub struct DescriptorFlags: u16 {
/// This marks a buffer as continuing via the next field.
const NEXT = 1 << 0;
/// This marks a buffer as device write-only (otherwise device read-only).
const WRITE_ONLY = 1 << 1;
/// This means the buffer contains a list of buffer descriptors.
const INDIRECT = 1 << 2;
}
}
impl Descriptor {
pub fn set_addr(&self, addr: u64) {
self.address.store(addr, Ordering::SeqCst)
}
pub fn set_size(&self, size: u32) {
self.size.store(size, Ordering::SeqCst)
}
pub fn set_next(&self, next: Option<u16>) {
self.next.store(next.unwrap_or_default(), Ordering::SeqCst)
}
pub fn set_flags(&self, flags: DescriptorFlags) {
self.flags.store(flags.bits(), Ordering::SeqCst)
}
pub fn next(&self) -> u16 {
self.next.load(Ordering::SeqCst)
}
pub fn flags(&self) -> DescriptorFlags {
DescriptorFlags::from_bits_truncate(self.flags.load(Ordering::SeqCst))
}
}
// ======== Available Ring ========
//
// XXX: The driver uses the available ring to offer buffers to the
// device. Each ring entry refers to the head of a descriptor
// chain.
/// [2.7.6 The Virtqueue Available Ring](https://docs.oasis-open.org/virtio/virtio/v1.2/cs01/virtio-v1.2-cs01.html#x1-490006)
#[repr(C, align(2))]
pub struct AvailableRing {
pub flags: VolatileCell<u16>,
pub head_index: AtomicU16,
pub elements: IncompleteArrayField<AvailableRingElement>,
}
const_assert_eq!(core::mem::size_of::<AvailableRing>(), 4);
#[repr(C)]
pub struct AvailableRingElement {
pub table_index: AtomicU16,
}
impl AvailableRingElement {
pub fn set_table_index(&self, index: u16) {
self.table_index.store(index, Ordering::SeqCst)
}
}
const_assert_eq!(core::mem::size_of::<AvailableRingElement>(), 2);
#[repr(C)]
pub struct AvailableRingExtra {
pub avail_event: VolatileCell<u16>, // Only if `VIRTIO_F_EVENT_IDX`
}
const_assert_eq!(core::mem::size_of::<AvailableRingExtra>(), 2);
// ======== Used Ring ========
/// [2.7.8 The Virtqueue Used Ring](https://docs.oasis-open.org/virtio/virtio/v1.2/cs01/virtio-v1.2-cs01.html#x1-540008)
#[repr(C, align(4))]
pub struct UsedRing {
pub flags: VolatileCell<u16>,
pub head_index: VolatileCell<u16>,
pub elements: IncompleteArrayField<UsedRingElement>,
}
const_assert_eq!(core::mem::size_of::<UsedRing>(), 4);
#[repr(C)]
pub struct UsedRingElement {
pub table_index: VolatileCell<u32>,
pub written: VolatileCell<u32>,
}
const_assert_eq!(core::mem::size_of::<UsedRingElement>(), 8);
#[repr(C)]
pub struct UsedRingExtra {
pub event_index: VolatileCell<u16>,
}
// ======== Utils ========
pub struct Buffer {
pub(crate) buffer: usize,
pub(crate) size: usize,
pub(crate) flags: DescriptorFlags,
}
impl Buffer {
pub fn new<T>(val: &common::dma::Dma<T>) -> Self {
Self {
buffer: val.physical(),
size: core::mem::size_of::<T>(),
flags: DescriptorFlags::empty(),
}
}
pub fn new_unsized<T>(val: &common::dma::Dma<[T]>) -> Self {
Self {
buffer: val.physical(),
size: core::mem::size_of::<T>() * val.len(),
flags: DescriptorFlags::empty(),
}
}
pub fn new_sized<T>(val: &common::dma::Dma<[T]>, size: usize) -> Self {
Self {
buffer: val.physical(),
size,
flags: DescriptorFlags::empty(),
}
}
pub fn flags(mut self, flags: DescriptorFlags) -> Self {
self.flags = flags;
self
}
}
/// XXX: The [`DescriptorFlags::NEXT`] flag is set automatically.
pub struct ChainBuilder {
buffers: Vec<Buffer>,
}
impl ChainBuilder {
pub fn new() -> Self {
Self {
buffers: Vec::new(),
}
}
pub fn chain(mut self, mut buffer: Buffer) -> Self {
buffer.flags |= DescriptorFlags::NEXT;
self.buffers.push(buffer);
self
}
pub fn build(mut self) -> Vec<Buffer> {
let last_buffer = self.buffers.last_mut().expect("virtio-core: empty chain");
last_buffer.flags.remove(DescriptorFlags::NEXT);
self.buffers
}
}