use alloc::rc::Rc;
use alloc::string::{String, ToString};
use alloc::vec::Vec;
use pci_types::InterruptLine;
use self::constants::{FeatureSet, Features};
use crate::config::VIRTIO_MAX_QUEUE_SIZE;
#[cfg(feature = "pci")]
use crate::drivers::fs::virtio_pci::FsDevCfgRaw;
use crate::drivers::virtio::error::VirtioFsError;
#[cfg(not(feature = "pci"))]
use crate::drivers::virtio::transport::mmio::{ComCfg, IsrStatus, NotifCfg};
#[cfg(feature = "pci")]
use crate::drivers::virtio::transport::pci::{ComCfg, IsrStatus, NotifCfg};
use crate::drivers::virtio::virtqueue::{
AsSliceU8, BuffSpec, BufferToken, Bytes, Virtq, VqIndex, VqSize, VqType,
};
use crate::fs::fuse::{self, FuseInterface};
pub(crate) struct FsDevCfg {
pub raw: &'static FsDevCfgRaw,
pub dev_id: u16,
pub features: FeatureSet,
}
#[allow(dead_code)]
pub(crate) struct VirtioFsDriver {
pub(super) dev_cfg: FsDevCfg,
pub(super) com_cfg: ComCfg,
pub(super) isr_stat: IsrStatus,
pub(super) notif_cfg: NotifCfg,
pub(super) vqueues: Vec<Rc<Virtq>>,
pub(super) ready_queue: Vec<BufferToken>,
pub(super) irq: InterruptLine,
}
impl VirtioFsDriver {
#[cfg(feature = "pci")]
pub fn get_dev_id(&self) -> u16 {
self.dev_cfg.dev_id
}
#[cfg(feature = "pci")]
pub fn set_failed(&mut self) {
self.com_cfg.set_failed();
}
fn negotiate_features(&mut self, wanted_feats: &[Features]) -> Result<(), VirtioFsError> {
let mut drv_feats = FeatureSet::new(0);
for feat in wanted_feats.iter() {
drv_feats |= *feat;
}
let dev_feats = FeatureSet::new(self.com_cfg.dev_features());
match FeatureSet::check_features(wanted_feats) {
Ok(_) => {
debug!("Feature set wanted by filesystem driver are in conformance with specification.")
}
Err(fs_err) => return Err(fs_err),
}
if (dev_feats & drv_feats) == drv_feats {
self.com_cfg.set_drv_features(drv_feats.into());
Ok(())
} else {
Err(VirtioFsError::IncompFeatsSet(drv_feats, dev_feats))
}
}
pub(crate) fn init_dev(&mut self) -> Result<(), VirtioFsError> {
self.com_cfg.reset_dev();
self.com_cfg.ack_dev();
self.com_cfg.set_drv();
let feats: Vec<Features> = vec![Features::VIRTIO_F_VERSION_1];
self.negotiate_features(&feats)?;
self.com_cfg.features_ok();
if self.com_cfg.check_features() {
info!(
"Features have been negotiated between virtio filesystem device {:x} and driver.",
self.dev_cfg.dev_id
);
self.dev_cfg.features.set_features(&feats);
} else {
return Err(VirtioFsError::FailFeatureNeg(self.dev_cfg.dev_id));
}
let vqnum = self.dev_cfg.raw.get_num_queues() + 1;
if vqnum == 0 {
error!("0 request queues requested from device. Aborting!");
return Err(VirtioFsError::Unknown);
}
for i in 0..vqnum as u16 {
let vq = Virtq::new(
&mut self.com_cfg,
&self.notif_cfg,
VqSize::from(VIRTIO_MAX_QUEUE_SIZE),
VqType::Split,
VqIndex::from(i),
self.dev_cfg.features.into(),
);
self.vqueues.push(Rc::new(vq));
}
let cmd_spec = Some(BuffSpec::Single(Bytes::new(64 * 1024 + 128).unwrap()));
let rsp_spec = Some(BuffSpec::Single(Bytes::new(64 * 1024 + 128).unwrap()));
if let Ok(buff_tkn) =
self.vqueues[1].prep_buffer(Rc::clone(&self.vqueues[1]), cmd_spec, rsp_spec)
{
self.ready_queue.push(buff_tkn);
self.com_cfg.drv_ok();
}
Ok(())
}
}
impl FuseInterface for VirtioFsDriver {
fn send_command<S, T>(&mut self, cmd: &fuse::Cmd<S>, rsp: &mut fuse::Rsp<T>)
where
S: fuse::FuseIn + core::fmt::Debug,
T: fuse::FuseOut + core::fmt::Debug,
{
if let Some(mut buff_tkn) = self.ready_queue.pop() {
let cmd_len = Some(cmd.len());
let rsp_len = Some(rsp.len());
buff_tkn.restr_size(cmd_len, rsp_len).unwrap();
let transfer_tkn = buff_tkn.write(Some(cmd), Some(rsp)).unwrap();
let transfer = transfer_tkn.dispatch_blocking().unwrap();
let (_, response) = transfer.ret_cpy().unwrap();
let tkn = transfer.reuse().unwrap();
self.ready_queue.push(tkn);
if let Some(response) = response {
rsp.as_slice_u8_mut()[..response.len()].copy_from_slice(response.as_ref());
}
}
}
fn get_mount_point(&self) -> String {
self.dev_cfg.raw.get_tag().to_string()
}
}
pub mod constants {
use alloc::vec::Vec;
use core::ops::{BitAnd, BitAndAssign, BitOr, BitOrAssign};
pub use super::error::VirtioFsError;
#[allow(dead_code, non_camel_case_types)]
#[derive(Copy, Clone, Debug)]
#[repr(u64)]
pub enum Features {
VIRTIO_F_RING_INDIRECT_DESC = 1 << 28,
VIRTIO_F_RING_EVENT_IDX = 1 << 29,
VIRTIO_F_VERSION_1 = 1 << 32,
VIRTIO_F_ACCESS_PLATFORM = 1 << 33,
VIRTIO_F_RING_PACKED = 1 << 34,
VIRTIO_F_IN_ORDER = 1 << 35,
VIRTIO_F_ORDER_PLATFORM = 1 << 36,
VIRTIO_F_SR_IOV = 1 << 37,
VIRTIO_F_NOTIFICATION_DATA = 1 << 38,
}
impl From<Features> for u64 {
fn from(val: Features) -> Self {
match val {
Features::VIRTIO_F_RING_INDIRECT_DESC => 1 << 28,
Features::VIRTIO_F_RING_EVENT_IDX => 1 << 29,
Features::VIRTIO_F_VERSION_1 => 1 << 32,
Features::VIRTIO_F_ACCESS_PLATFORM => 1 << 33,
Features::VIRTIO_F_RING_PACKED => 1 << 34,
Features::VIRTIO_F_IN_ORDER => 1 << 35,
Features::VIRTIO_F_ORDER_PLATFORM => 1 << 36,
Features::VIRTIO_F_SR_IOV => 1 << 37,
Features::VIRTIO_F_NOTIFICATION_DATA => 1 << 38,
}
}
}
impl BitOr for Features {
type Output = u64;
fn bitor(self, rhs: Self) -> Self::Output {
u64::from(self) | u64::from(rhs)
}
}
impl BitOr<Features> for u64 {
type Output = u64;
fn bitor(self, rhs: Features) -> Self::Output {
self | u64::from(rhs)
}
}
impl BitOrAssign<Features> for u64 {
fn bitor_assign(&mut self, rhs: Features) {
*self |= u64::from(rhs);
}
}
impl BitAnd for Features {
type Output = u64;
fn bitand(self, rhs: Features) -> Self::Output {
u64::from(self) & u64::from(rhs)
}
}
impl BitAnd<Features> for u64 {
type Output = u64;
fn bitand(self, rhs: Features) -> Self::Output {
self & u64::from(rhs)
}
}
impl BitAndAssign<Features> for u64 {
fn bitand_assign(&mut self, rhs: Features) {
*self &= u64::from(rhs);
}
}
impl core::fmt::Display for Features {
fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
match *self {
Features::VIRTIO_F_RING_INDIRECT_DESC => write!(f, "VIRTIO_F_RING_INDIRECT_DESC"),
Features::VIRTIO_F_RING_EVENT_IDX => write!(f, "VIRTIO_F_RING_EVENT_IDX"),
Features::VIRTIO_F_VERSION_1 => write!(f, "VIRTIO_F_VERSION_1"),
Features::VIRTIO_F_ACCESS_PLATFORM => write!(f, "VIRTIO_F_ACCESS_PLATFORM"),
Features::VIRTIO_F_RING_PACKED => write!(f, "VIRTIO_F_RING_PACKED"),
Features::VIRTIO_F_IN_ORDER => write!(f, "VIRTIO_F_IN_ORDER"),
Features::VIRTIO_F_ORDER_PLATFORM => write!(f, "VIRTIO_F_ORDER_PLATFORM"),
Features::VIRTIO_F_SR_IOV => write!(f, "VIRTIO_F_SR_IOV"),
Features::VIRTIO_F_NOTIFICATION_DATA => write!(f, "VIRTIO_F_NOTIFICATION_DATA"),
}
}
}
impl Features {
pub fn from_set(feat_set: FeatureSet) -> Option<Vec<Features>> {
let mut vec_of_feats: Vec<Features> = Vec::new();
let feats = feat_set.0;
if feats & (1 << 28) != 0 {
vec_of_feats.push(Features::VIRTIO_F_RING_INDIRECT_DESC)
}
if feats & (1 << 29) != 0 {
vec_of_feats.push(Features::VIRTIO_F_RING_EVENT_IDX)
}
if feats & (1 << 32) != 0 {
vec_of_feats.push(Features::VIRTIO_F_VERSION_1)
}
if feats & (1 << 33) != 0 {
vec_of_feats.push(Features::VIRTIO_F_ACCESS_PLATFORM)
}
if feats & (1 << 34) != 0 {
vec_of_feats.push(Features::VIRTIO_F_RING_PACKED)
}
if feats & (1 << 35) != 0 {
vec_of_feats.push(Features::VIRTIO_F_IN_ORDER)
}
if feats & (1 << 36) != 0 {
vec_of_feats.push(Features::VIRTIO_F_ORDER_PLATFORM)
}
if feats & (1 << 37) != 0 {
vec_of_feats.push(Features::VIRTIO_F_SR_IOV)
}
if feats & (1 << 38) != 0 {
vec_of_feats.push(Features::VIRTIO_F_NOTIFICATION_DATA)
}
if vec_of_feats.is_empty() {
None
} else {
Some(vec_of_feats)
}
}
}
#[derive(Debug, Copy, Clone, PartialOrd, PartialEq, Eq)]
pub struct FeatureSet(u64);
impl BitOr for FeatureSet {
type Output = FeatureSet;
fn bitor(self, rhs: Self) -> Self::Output {
FeatureSet(self.0 | rhs.0)
}
}
impl BitOr<FeatureSet> for u64 {
type Output = u64;
fn bitor(self, rhs: FeatureSet) -> Self::Output {
self | u64::from(rhs)
}
}
impl BitOrAssign<FeatureSet> for u64 {
fn bitor_assign(&mut self, rhs: FeatureSet) {
*self |= u64::from(rhs);
}
}
impl BitOrAssign<Features> for FeatureSet {
fn bitor_assign(&mut self, rhs: Features) {
self.0 = self.0 | u64::from(rhs);
}
}
impl BitAnd for FeatureSet {
type Output = FeatureSet;
fn bitand(self, rhs: FeatureSet) -> Self::Output {
FeatureSet(self.0 & rhs.0)
}
}
impl BitAnd<FeatureSet> for u64 {
type Output = u64;
fn bitand(self, rhs: FeatureSet) -> Self::Output {
self & u64::from(rhs)
}
}
impl BitAndAssign<FeatureSet> for u64 {
fn bitand_assign(&mut self, rhs: FeatureSet) {
*self &= u64::from(rhs);
}
}
impl From<FeatureSet> for u64 {
fn from(feature_set: FeatureSet) -> Self {
feature_set.0
}
}
impl FeatureSet {
pub fn check_features(feats: &[Features]) -> Result<(), VirtioFsError> {
let mut feat_bits = 0u64;
for feat in feats.iter() {
feat_bits |= *feat;
}
for feat in feats {
match feat {
Features::VIRTIO_F_RING_INDIRECT_DESC => continue,
Features::VIRTIO_F_RING_EVENT_IDX => continue,
Features::VIRTIO_F_VERSION_1 => continue,
Features::VIRTIO_F_ACCESS_PLATFORM => continue,
Features::VIRTIO_F_RING_PACKED => continue,
Features::VIRTIO_F_IN_ORDER => continue,
Features::VIRTIO_F_ORDER_PLATFORM => continue,
Features::VIRTIO_F_SR_IOV => continue,
Features::VIRTIO_F_NOTIFICATION_DATA => continue,
}
}
Ok(())
}
pub fn is_feature(self, feat: Features) -> bool {
self.0 & feat != 0
}
pub fn set_features(&mut self, feats: &[Features]) {
for feat in feats {
self.0 |= *feat;
}
}
pub fn new(val: u64) -> Self {
FeatureSet(val)
}
}
}
pub mod error {
use super::constants::FeatureSet;
#[derive(Debug, Copy, Clone)]
pub enum VirtioFsError {
NoDevCfg(u16),
NoComCfg(u16),
NoIsrCfg(u16),
NoNotifCfg(u16),
FailFeatureNeg(u16),
IncompFeatsSet(FeatureSet, FeatureSet),
Unknown,
}
}