#![allow(dead_code)]
use std::sync::{Arc, Mutex};
use super::queue::{GuestMem, Queue};
use super::VirtioDevice;
use crate::devices::mmio_bus::MmioDevice;
const MAGIC: u32 = 0x74726976; const VERSION: u32 = 2;
#[derive(Clone, Debug)]
pub struct QueueSnapshot {
pub size: u16,
pub ready: bool,
pub desc_table: u64,
pub avail_ring: u64,
pub used_ring: u64,
pub last_avail_idx: u16,
pub next_used_idx: u16,
}
#[derive(Clone, Debug)]
pub struct MmioSnapshot {
pub driver_features: [u32; 2],
pub status: u32,
pub interrupt_status: u32,
pub queues: Vec<QueueSnapshot>,
}
struct State {
device_features_sel: u32,
driver_features: [u32; 2],
driver_features_sel: u32,
queue_sel: u32,
status: u32,
interrupt_status: u32,
queues: Vec<Queue>,
activated: bool,
irq_raise: Arc<dyn Fn() + Send + Sync>,
}
pub struct MmioVirtio {
dev: Arc<dyn VirtioDevice>,
state: Mutex<State>,
}
impl MmioVirtio {
pub fn new(
dev: Arc<dyn VirtioDevice>,
mem: GuestMem,
irq_raise: Arc<dyn Fn() + Send + Sync>,
) -> Self {
let queues = (0..dev.num_queues())
.map(|_| Queue::new(mem.clone()))
.collect();
Self {
dev,
state: Mutex::new(State {
device_features_sel: 0,
driver_features: [0; 2],
driver_features_sel: 0,
queue_sel: 0,
status: 0,
interrupt_status: 0,
queues,
activated: false,
irq_raise,
}),
}
}
pub fn capture_state(&self) -> MmioSnapshot {
let st = self.state.lock().unwrap();
let live = self.dev.snapshot_queues();
let queues: Vec<QueueSnapshot> = (0..st.queues.len())
.map(|i| {
let q = live.get(i).unwrap_or(&st.queues[i]);
QueueSnapshot {
size: q.size,
ready: q.ready,
desc_table: q.desc_table,
avail_ring: q.avail_ring,
used_ring: q.used_ring,
last_avail_idx: q.last_avail_idx,
next_used_idx: q.next_used_idx,
}
})
.collect();
MmioSnapshot {
driver_features: st.driver_features,
status: st.status,
interrupt_status: st.interrupt_status,
queues,
}
}
pub fn restore_state(&self, snap: &MmioSnapshot) {
let mut st = self.state.lock().unwrap();
st.driver_features = snap.driver_features;
st.status = snap.status;
st.interrupt_status = snap.interrupt_status;
for (i, qs) in snap.queues.iter().enumerate() {
if let Some(q) = st.queues.get_mut(i) {
q.size = qs.size;
q.ready = qs.ready;
q.desc_table = qs.desc_table;
q.avail_ring = qs.avail_ring;
q.used_ring = qs.used_ring;
q.last_avail_idx = qs.last_avail_idx;
q.next_used_idx = qs.next_used_idx;
}
}
if snap.status & super::STATUS_DRIVER_OK != 0 {
st.activated = true;
let queues = st.queues.clone();
drop(st);
self.dev.activate(queues);
}
}
pub fn make_config_change_irq(self: &Arc<Self>) -> Arc<dyn Fn() + Send + Sync> {
let me = self.clone();
Arc::new(move || {
let mut st = me.state.lock().unwrap();
st.interrupt_status |= 0x2;
let f = st.irq_raise.clone();
drop(st);
f();
})
}
pub fn make_used_buffer_irq(self: &Arc<Self>) -> Arc<dyn Fn() + Send + Sync> {
let me = self.clone();
Arc::new(move || {
let mut st = me.state.lock().unwrap();
st.interrupt_status |= 0x1;
let f = st.irq_raise.clone();
drop(st);
f();
})
}
}
impl MmioDevice for MmioVirtio {
fn read(&self, offset: u64, _size: u8) -> u64 {
let st = self.state.lock().unwrap();
let v: u32 = match offset {
0x000 => MAGIC,
0x004 => VERSION,
0x008 => self.dev.device_id(),
0x00c => self.dev.vendor_id(),
0x010 => {
let f = self.dev.features();
if st.device_features_sel == 0 {
f as u32
} else {
(f >> 32) as u32
}
}
0x034 => self.dev.queue_max_size() as u32,
0x038 => st
.queues
.get(st.queue_sel as usize)
.map(|q| q.size as u32)
.unwrap_or(0),
0x044 => st
.queues
.get(st.queue_sel as usize)
.map(|q| if q.ready { 1 } else { 0 })
.unwrap_or(0),
0x060 => st.interrupt_status,
0x070 => st.status,
0x100.. => {
let cfg = self.dev.config();
let off = (offset - 0x100) as usize;
off.checked_add(4)
.and_then(|end| cfg.get(off..end))
.map(|bytes| u32::from_le_bytes([bytes[0], bytes[1], bytes[2], bytes[3]]))
.unwrap_or(0)
}
_ => 0,
};
v as u64
}
fn write(&self, offset: u64, value: u64, _size: u8) {
let mut st = self.state.lock().unwrap();
let v32 = value as u32;
match offset {
0x014 => st.device_features_sel = v32,
0x020 => {
let i = (st.driver_features_sel & 1) as usize;
st.driver_features[i] = v32;
}
0x024 => st.driver_features_sel = v32,
0x030 => st.queue_sel = v32,
0x038 => {
let sel = st.queue_sel as usize;
if let Some(q) = st.queues.get_mut(sel) {
q.size = v32 as u16;
}
}
0x044 => {
let sel = st.queue_sel as usize;
if let Some(q) = st.queues.get_mut(sel) {
q.ready = v32 != 0;
}
}
0x050 => {
drop(st);
self.dev.notify(v32 as u16);
return;
}
0x064 => st.interrupt_status &= !v32,
0x070 => {
st.status = v32;
if v32 & super::STATUS_DRIVER_OK != 0 && !st.activated {
st.activated = true;
let queues = st.queues.clone();
drop(st);
self.dev.activate(queues);
return;
}
}
0x080 => set_low(&mut st, |q| &mut q.desc_table, v32),
0x084 => set_high(&mut st, |q| &mut q.desc_table, v32),
0x090 => set_low(&mut st, |q| &mut q.avail_ring, v32),
0x094 => set_high(&mut st, |q| &mut q.avail_ring, v32),
0x0a0 => set_low(&mut st, |q| &mut q.used_ring, v32),
0x0a4 => set_high(&mut st, |q| &mut q.used_ring, v32),
0x100.. => {
drop(st);
self.dev.config_write((offset - 0x100) as usize, v32);
return;
}
_ => {}
}
}
fn len(&self) -> u64 {
0x200
}
}
fn set_low(st: &mut State, accessor: impl FnOnce(&mut Queue) -> &mut u64, v: u32) {
let sel = st.queue_sel as usize;
if let Some(q) = st.queues.get_mut(sel) {
let r = accessor(q);
*r = (*r & !0xffff_ffff) | (v as u64);
}
}
fn set_high(st: &mut State, accessor: impl FnOnce(&mut Queue) -> &mut u64, v: u32) {
let sel = st.queue_sel as usize;
if let Some(q) = st.queues.get_mut(sel) {
let r = accessor(q);
*r = (*r & 0xffff_ffff) | ((v as u64) << 32);
}
}
pub fn raise_used_buffer_irq(mmio: &MmioVirtio) {
let mut st = mmio.state.lock().unwrap();
st.interrupt_status |= 0x1; let f = st.irq_raise.clone();
drop(st);
f();
}