use isr_macros::Field;
use super::VmiOs;
use crate::{AccessContext, Registers, Va, VmiCore, VmiError, VmiRead, VmiState};
pub struct StructReader(Vec<u8>);
impl StructReader {
pub fn new<Os>(vmi: &VmiState<Os>, va: Va, len: usize) -> Result<Self, VmiError>
where
Os: VmiOs,
Os::Driver: VmiRead,
{
Self::new_in(vmi, vmi.registers().address_context(va), len)
}
pub fn new_in<Driver>(
vmi: &VmiCore<Driver>,
ctx: impl Into<AccessContext>,
len: usize,
) -> Result<Self, VmiError>
where
Driver: VmiRead,
{
let mut buffer = vec![0u8; len];
vmi.read(ctx.into(), &mut buffer)?;
Ok(Self(buffer))
}
pub fn read(&self, field: Field) -> Result<u64, VmiError> {
let offset = field.offset() as usize;
let size = field.size() as usize;
let offset_end = match offset.checked_add(size) {
Some(offset_end) => offset_end,
None => return Err(VmiError::OutOfBounds),
};
if offset_end > self.0.len() {
return Err(VmiError::OutOfBounds);
}
let data = &self.0[offset..offset_end];
match size {
1 => Ok(data[0] as u64),
2 => Ok(u16::from_le_bytes([data[0], data[1]]) as u64),
4 => Ok(u32::from_le_bytes([data[0], data[1], data[2], data[3]]) as u64),
8 => Ok(u64::from_le_bytes([
data[0], data[1], data[2], data[3], data[4], data[5], data[6], data[7],
])),
_ => Err(VmiError::OutOfBounds),
}
}
}