use serde::Serialize;
use super::DeviceManager;
#[derive(Debug, Clone, Serialize)]
pub struct QueueDebug {
pub index: u16,
pub size: u16,
pub ready: bool,
pub kicks: u64,
pub avail_idx: Option<u16>,
pub used_idx: Option<u16>,
pub avail_flags: Option<u16>,
pub used_flags: Option<u16>,
pub used_event: Option<u16>,
pub avail_event: Option<u16>,
}
#[derive(Debug, Clone, Serialize)]
pub struct DeviceDebug {
pub id: u32,
pub device_type: String,
pub name: String,
pub status: u8,
pub interrupt_status: u32,
pub event_idx: bool,
pub interrupts: u64,
pub queues: Vec<QueueDebug>,
}
impl DeviceManager {
#[must_use]
pub fn virtio_debug(&self) -> Vec<DeviceDebug> {
let mut devices: Vec<&super::RegisteredDevice> = self.devices.values().collect();
devices.sort_by_key(|device| device.info.id.raw());
devices
.into_iter()
.filter_map(|device| {
let state = device.mmio_state.as_ref()?;
let state = match state.read() {
Ok(guard) => guard,
Err(poisoned) => poisoned.into_inner(),
};
let event_idx =
(state.driver_features & arcbox_virtio::queue::VIRTIO_F_EVENT_IDX) != 0;
let queues = (0..super::MAX_VIRTQUEUES)
.filter(|&qi| {
state.queue_num[qi] != 0 || state.queue_ready[qi] || state.kicks[qi] != 0
})
.map(|qi| {
let size = u64::from(state.queue_num[qi]);
let avail = state.queue_driver[qi];
let used = state.queue_device[qi];
QueueDebug {
index: qi as u16,
size: state.queue_num[qi],
ready: state.queue_ready[qi],
kicks: state.kicks[qi],
avail_flags: self.read_guest_u16(avail, 0),
avail_idx: self.read_guest_u16(avail, 2),
used_flags: self.read_guest_u16(used, 0),
used_idx: self.read_guest_u16(used, 2),
used_event: event_idx
.then(|| self.read_guest_u16(avail, 4 + 2 * size))
.flatten(),
avail_event: event_idx
.then(|| self.read_guest_u16(used, 4 + 8 * size))
.flatten(),
}
})
.collect();
Some(DeviceDebug {
id: device.info.id.raw(),
device_type: format!("{:?}", device.info.device_type),
name: device.info.name.clone(),
status: state.status,
interrupt_status: state.interrupt_status,
event_idx,
interrupts: state.interrupts,
queues,
})
})
.collect()
}
#[allow(
clippy::cast_ptr_alignment,
reason = "pointer alignment is checked at runtime just above the cast"
)]
fn read_guest_u16(&self, base_gpa: u64, field_offset: u64) -> Option<u16> {
let base = self.guest_ram_base?;
if base_gpa == 0 {
return None;
}
let gpa = base_gpa.checked_add(field_offset)?;
let offset = gpa.checked_sub(self.guest_ram_gpa)?;
let end = offset.checked_add(2)?;
if end > self.guest_ram_size as u64 {
return None;
}
let ptr = unsafe { base.add(offset as usize) };
if ptr as usize % 2 != 0 {
return None;
}
Some(unsafe { ptr.cast::<u16>().read_volatile() })
}
}