qemu_plugin/memory/
mod.rs

1//! Memory-related functionality for QEMU plugins
2
3#[cfg(not(any(
4    feature = "plugin-api-v0",
5    feature = "plugin-api-v1",
6    feature = "plugin-api-v2",
7    feature = "plugin-api-v3",
8)))]
9use crate::Error;
10#[cfg(not(feature = "plugin-api-v0"))]
11use crate::Result;
12#[cfg(not(any(
13    feature = "plugin-api-v0",
14    feature = "plugin-api-v1",
15    feature = "plugin-api-v2",
16    feature = "plugin-api-v3",
17    feature = "plugin-api-v4"
18)))]
19use crate::sys::qemu_plugin_hwaddr_operation_result;
20#[cfg(not(any(
21    feature = "plugin-api-v0",
22    feature = "plugin-api-v1",
23    feature = "plugin-api-v2",
24    feature = "plugin-api-v3"
25)))]
26use crate::sys::{GByteArray, qemu_plugin_mem_value, qemu_plugin_mem_value_type};
27use crate::sys::{qemu_plugin_hwaddr, qemu_plugin_meminfo_t};
28use std::marker::PhantomData;
29
30/// Wrapper structure for a `qemu_plugin_meminfo_t`
31///
32/// # Safety
33///
34/// This structure is safe to use during the invocation of the callback which receives it as an
35/// argument. The structure is always opaque, and therefore may not be accessed directly.
36pub struct MemoryInfo<'a> {
37    memory_info: qemu_plugin_meminfo_t,
38    marker: PhantomData<&'a ()>,
39}
40
41impl<'a> From<qemu_plugin_meminfo_t> for MemoryInfo<'a> {
42    fn from(info: qemu_plugin_meminfo_t) -> Self {
43        Self {
44            memory_info: info,
45            marker: PhantomData,
46        }
47    }
48}
49
50impl<'a> MemoryInfo<'a> {
51    /// Returns the size of the access in base-2, e.g. 0 for byte, 1 for 16-bit, 2 for
52    /// 32-bit, etc.
53    pub fn size_shift(&self) -> usize {
54        (unsafe { crate::sys::qemu_plugin_mem_size_shift(self.memory_info) }) as usize
55    }
56
57    /// Returns whether the access was sign extended
58    pub fn sign_extended(&self) -> bool {
59        unsafe { crate::sys::qemu_plugin_mem_is_sign_extended(self.memory_info) }
60    }
61
62    /// Returns whether the access was big-endian
63    pub fn big_endian(&self) -> bool {
64        unsafe { crate::sys::qemu_plugin_mem_is_big_endian(self.memory_info) }
65    }
66
67    /// Returns whether the access was a store
68    pub fn is_store(&self) -> bool {
69        unsafe { crate::sys::qemu_plugin_mem_is_store(self.memory_info) }
70    }
71
72    /// Return a handle to query details about the physical address backing the virtual address
73    /// in system emulation. In user-mode, this method always returns `None`.
74    pub fn hwaddr(&'a self, vaddr: u64) -> Option<HwAddr<'a>> {
75        let hwaddr = unsafe { crate::sys::qemu_plugin_get_hwaddr(self.memory_info, vaddr) };
76        if hwaddr.is_null() {
77            None
78        } else {
79            Some(HwAddr::from(hwaddr))
80        }
81    }
82
83    /// Return last value loaded/stored
84    #[cfg(not(any(
85        feature = "plugin-api-v0",
86        feature = "plugin-api-v1",
87        feature = "plugin-api-v2",
88        feature = "plugin-api-v3"
89    )))]
90    pub fn value(&self) -> MemValue {
91        let qemu_mem_value = unsafe { crate::sys::qemu_plugin_mem_get_value(self.memory_info) };
92        MemValue::from(qemu_mem_value)
93    }
94}
95
96#[cfg(not(any(
97    feature = "plugin-api-v0",
98    feature = "plugin-api-v1",
99    feature = "plugin-api-v2",
100    feature = "plugin-api-v3"
101)))]
102#[derive(Clone)]
103/// Memory value loaded/stored (in memory callback)
104///
105/// Wrapper structure for a `qemu_plugin_mem_value`
106pub enum MemValue {
107    /// 8-bit value
108    U8(u8),
109    /// 16-bit value
110    U16(u16),
111    /// 32-bit value
112    U32(u32),
113    /// 64-bit value
114    U64(u64),
115    /// 128-bit value
116    U128(u128),
117}
118
119#[cfg(not(any(
120    feature = "plugin-api-v0",
121    feature = "plugin-api-v1",
122    feature = "plugin-api-v2",
123    feature = "plugin-api-v3"
124)))]
125impl From<qemu_plugin_mem_value> for MemValue {
126    fn from(value: qemu_plugin_mem_value) -> Self {
127        unsafe {
128            match value.type_ {
129                qemu_plugin_mem_value_type::QEMU_PLUGIN_MEM_VALUE_U8 => Self::U8(value.data.u8_),
130                qemu_plugin_mem_value_type::QEMU_PLUGIN_MEM_VALUE_U16 => Self::U16(value.data.u16_),
131                qemu_plugin_mem_value_type::QEMU_PLUGIN_MEM_VALUE_U32 => Self::U32(value.data.u32_),
132                qemu_plugin_mem_value_type::QEMU_PLUGIN_MEM_VALUE_U64 => Self::U64(value.data.u64_),
133                qemu_plugin_mem_value_type::QEMU_PLUGIN_MEM_VALUE_U128 => {
134                    let high = value.data.u128_.high as u128;
135                    let low = value.data.u128_.low as u128;
136                    Self::U128(high << 64 | low)
137                }
138            }
139        }
140    }
141}
142
143/// Wrapper structure for a `qemu_plugin_hwaddr *`
144///
145/// # Safety
146///
147/// This structure is safe to use as long as the pointer is valid. The pointer is
148/// always opaque, and therefore may not be dereferenced.
149pub struct HwAddr<'a> {
150    hwaddr: usize,
151    marker: PhantomData<&'a ()>,
152}
153
154impl<'a> From<*mut qemu_plugin_hwaddr> for HwAddr<'a> {
155    fn from(hwaddr: *mut qemu_plugin_hwaddr) -> Self {
156        Self {
157            hwaddr: hwaddr as usize,
158            marker: PhantomData,
159        }
160    }
161}
162
163impl<'a> HwAddr<'a> {
164    /// Returns whether the memory operation is to MMIO. Returns false if the operation is to
165    /// RAM.
166    pub fn is_io(&self) -> bool {
167        unsafe { crate::sys::qemu_plugin_hwaddr_is_io(self.hwaddr as *mut qemu_plugin_hwaddr) }
168    }
169
170    #[cfg(not(feature = "plugin-api-v0"))]
171    /// Returns the physical address for the memory operation
172    pub fn hwaddr(&self) -> u64 {
173        unsafe { crate::sys::qemu_plugin_hwaddr_phys_addr(self.hwaddr as *mut qemu_plugin_hwaddr) }
174    }
175
176    #[cfg(not(feature = "plugin-api-v0"))]
177    /// Returns a string representing the device
178    pub fn device_name(&self) -> Result<Option<String>> {
179        let device_name = unsafe {
180            crate::sys::qemu_plugin_hwaddr_device_name(self.hwaddr as *mut qemu_plugin_hwaddr)
181        };
182
183        if device_name.is_null() {
184            Ok(None)
185        } else {
186            let device_name_string = unsafe {
187                use std::ffi::CStr;
188                CStr::from_ptr(device_name)
189            }
190            .to_str()?
191            .to_string();
192            // NOTE: The string is static, so we do not free it
193            Ok(Some(device_name_string))
194        }
195    }
196}
197
198#[cfg(not(any(
199    feature = "plugin-api-v0",
200    feature = "plugin-api-v1",
201    feature = "plugin-api-v2",
202    feature = "plugin-api-v3"
203)))]
204/// Read memory from a virtual address. The address must be valid and mapped.
205pub fn qemu_plugin_read_memory_vaddr(addr: u64, buf: &mut [u8]) -> Result<()> {
206    let mut buf = GByteArray {
207        data: buf.as_mut_ptr(),
208        len: buf.len() as u32,
209    };
210
211    if unsafe {
212        crate::sys::qemu_plugin_read_memory_vaddr(
213            addr,
214            &mut buf as *mut GByteArray,
215            buf.len as usize,
216        )
217    } {
218        Ok(())
219    } else {
220        Err(Error::VaddrReadError { addr, len: buf.len })
221    }
222}
223
224#[cfg(not(any(
225    feature = "plugin-api-v0",
226    feature = "plugin-api-v1",
227    feature = "plugin-api-v2",
228    feature = "plugin-api-v3",
229    feature = "plugin-api-v4"
230)))]
231/// Write memory to a virtual address. The address must be valid and mapped.
232pub fn qemu_plugin_write_memory_vaddr(addr: u64, buf: &mut [u8]) -> Result<()> {
233    let mut buf = GByteArray {
234        data: buf.as_mut_ptr(),
235        len: buf.len() as u32,
236    };
237
238    if unsafe { crate::sys::qemu_plugin_write_memory_vaddr(addr, &mut buf as *mut GByteArray) } {
239        Ok(())
240    } else {
241        Err(Error::VaddrWriteError { addr, len: buf.len })
242    }
243}
244
245#[cfg(not(any(
246    feature = "plugin-api-v0",
247    feature = "plugin-api-v1",
248    feature = "plugin-api-v2",
249    feature = "plugin-api-v3",
250    feature = "plugin-api-v4"
251)))]
252#[derive(thiserror::Error, Debug, Clone, Copy, PartialEq, Eq)]
253/// The result of a hardware operation
254pub enum HwaddrOperationResult {
255    #[error("Operation completed successfully")]
256    /// Operation completed successfully
257    Ok = 0,
258    #[error("Unspecified error")]
259    /// Unspecified error
260    Error = 1,
261    #[error("Device error")]
262    /// Device error
263    DeviceError = 2,
264    #[error("Access denied")]
265    /// Access denied
266    AccessDenied = 3,
267    /// Invalid address
268    #[error("Invalid address")]
269    InvalidAddress = 4,
270    /// Invalid address space
271    #[error("Invalid address space")]
272    InvalidAddressSpace = 5,
273}
274
275#[cfg(not(any(
276    feature = "plugin-api-v0",
277    feature = "plugin-api-v1",
278    feature = "plugin-api-v2",
279    feature = "plugin-api-v3",
280    feature = "plugin-api-v4"
281)))]
282impl From<qemu_plugin_hwaddr_operation_result> for HwaddrOperationResult {
283    fn from(value: qemu_plugin_hwaddr_operation_result) -> Self {
284        match value {
285            qemu_plugin_hwaddr_operation_result::QEMU_PLUGIN_HWADDR_OPERATION_OK => Self::Ok,
286            qemu_plugin_hwaddr_operation_result::QEMU_PLUGIN_HWADDR_OPERATION_ERROR => Self::Error,
287            qemu_plugin_hwaddr_operation_result::QEMU_PLUGIN_HWADDR_OPERATION_DEVICE_ERROR => Self::DeviceError,
288            qemu_plugin_hwaddr_operation_result::QEMU_PLUGIN_HWADDR_OPERATION_ACCESS_DENIED => Self::AccessDenied,
289            qemu_plugin_hwaddr_operation_result::QEMU_PLUGIN_HWADDR_OPERATION_INVALID_ADDRESS => Self::InvalidAddress,
290            qemu_plugin_hwaddr_operation_result::QEMU_PLUGIN_HWADDR_OPERATION_INVALID_ADDRESS_SPACE => Self::InvalidAddressSpace,
291        }
292    }
293}
294
295#[cfg(not(any(
296    feature = "plugin-api-v0",
297    feature = "plugin-api-v1",
298    feature = "plugin-api-v2",
299    feature = "plugin-api-v3",
300    feature = "plugin-api-v4"
301)))]
302/// Read memory from a hardware address. The address must be valid and mapped.
303pub fn qemu_plugin_read_memory_hwaddr(addr: u64, buf: &mut [u8]) -> Result<()> {
304    let mut buf = GByteArray {
305        data: buf.as_mut_ptr(),
306        len: buf.len() as u32,
307    };
308
309    match unsafe {
310        crate::sys::qemu_plugin_read_memory_hwaddr(
311            addr,
312            &mut buf as *mut GByteArray,
313            buf.len as usize,
314        )
315    }
316    .into()
317    {
318        HwaddrOperationResult::Ok => Ok(()),
319        error => Err(Error::HwaddrReadError {
320            addr,
321            len: buf.len,
322            result: error,
323        }),
324    }
325}
326
327#[cfg(not(any(
328    feature = "plugin-api-v0",
329    feature = "plugin-api-v1",
330    feature = "plugin-api-v2",
331    feature = "plugin-api-v3",
332    feature = "plugin-api-v4"
333)))]
334/// Read memory from a virtual address. The address must be valid and mapped.
335pub fn qemu_plugin_write_memory_hwaddr(addr: u64, buf: &mut [u8]) -> Result<()> {
336    let mut buf = GByteArray {
337        data: buf.as_mut_ptr(),
338        len: buf.len() as u32,
339    };
340
341    match unsafe { crate::sys::qemu_plugin_write_memory_hwaddr(addr, &mut buf as *mut GByteArray) }
342        .into()
343    {
344        HwaddrOperationResult::Ok => Ok(()),
345        error => Err(Error::HwaddrWriteError {
346            addr,
347            len: buf.len,
348            result: error,
349        }),
350    }
351}
352
353#[cfg(not(any(
354    feature = "plugin-api-v0",
355    feature = "plugin-api-v1",
356    feature = "plugin-api-v2",
357    feature = "plugin-api-v3",
358    feature = "plugin-api-v4"
359)))]
360/// Translate a virtual address to a hardware address. If the address is not
361/// mapped, an error is returned.
362pub fn qemu_plugin_translate_vaddr(vaddr: u64) -> Result<u64> {
363    let mut hwaddr: u64 = 0;
364    if unsafe { crate::sys::qemu_plugin_translate_vaddr(vaddr, &mut hwaddr as *mut _) } {
365        Ok(hwaddr)
366    } else {
367        Err(Error::VaddrTranslateError { vaddr })
368    }
369}