panda/
guest_ptr.rs

1use crate::prelude::*;
2use once_cell::sync::OnceCell;
3
4use std::alloc::Layout;
5use std::ops::Deref;
6
7mod guest_align;
8mod impls;
9
10pub(crate) use guest_align::GuestAlign;
11
12#[derive(Copy, Clone, Debug)]
13pub struct GuestReadFail;
14
15#[derive(Copy, Clone, Debug)]
16pub struct GuestWriteFail;
17
18/// A type which can be converted to and from a guest memory representation, allowing
19/// it to be used with [`GuestPtr`].
20pub trait GuestType: Sized {
21    fn guest_layout() -> Option<Layout>;
22
23    /// The size of the type in the guest, `None` if the type is dynamically sized
24    fn guest_size() -> Option<usize> {
25        Self::guest_layout().map(|layout| layout.size())
26    }
27
28    /// The required minimum alignment of the type in the guest
29    fn guest_align() -> usize {
30        Self::guest_layout()
31            .map(|layout| layout.align())
32            .unwrap_or(1)
33    }
34
35    fn read_from_guest(cpu: &mut CPUState, ptr: target_ptr_t) -> Result<Self, GuestReadFail>;
36    fn write_to_guest(&self, cpu: &mut CPUState, ptr: target_ptr_t) -> Result<(), GuestWriteFail>;
37
38    fn read_from_guest_phys(ptr: target_ptr_t) -> Result<Self, GuestReadFail>;
39    fn write_to_guest_phys(&self, ptr: target_ptr_t) -> Result<(), GuestWriteFail>;
40}
41
42pub struct GuestPtr<T: GuestType> {
43    pointer: target_ptr_t,
44    guest_type: OnceCell<Box<T>>,
45}
46
47impl<T: GuestType> From<target_ptr_t> for GuestPtr<T> {
48    fn from(pointer: target_ptr_t) -> Self {
49        GuestPtr {
50            pointer,
51            guest_type: OnceCell::new(),
52        }
53    }
54}
55
56impl<T: GuestType> Clone for GuestPtr<T> {
57    fn clone(&self) -> Self {
58        Self::from(self.pointer)
59    }
60}
61
62impl<T: GuestType> GuestPtr<T> {
63    /// Reads the value from the guest to be accessed later. This is a no-op if a value
64    /// has already been cached. This is only needed if you need to read at a different
65    /// time than you intend to.
66    ///
67    /// If you want read a value and replace the cache if it exists, use
68    /// [`GuestPtr::update`] instead. If you wish to read at time of first access,
69    /// the `GuestPtr` only needs to be dereferenced without calling `read` ahead of
70    /// time.
71    pub fn read(&self) -> Result<&T, GuestReadFail> {
72        let cpu = unsafe { &mut *crate::sys::get_cpu() };
73
74        self.guest_type
75            .get_or_try_init(|| T::read_from_guest(cpu, self.pointer).map(Box::new))
76            .map(|x| &**x) // &Box<T> -> &T
77    }
78
79    /// Reads the value from the guest, replacing it if any exists.
80    pub fn update(&mut self) {
81        self.clear_cache();
82        self.read().unwrap();
83    }
84
85    /// Clear the cached value, if any exists.
86    pub fn clear_cache(&mut self) {
87        self.guest_type = OnceCell::new();
88    }
89
90    /// Returns a reference to the cached value if one exists.
91    pub fn get_cached(&self) -> Option<&T> {
92        self.guest_type.get().map(Box::as_ref)
93    }
94
95    /// Creates a copy of the pointer offset by N items.
96    ///
97    /// **Note:** Similar to normal pointer arithmatic the actual value of the offset
98    /// will be multiplied by the size of the object.
99    pub fn offset(&self, off: usize) -> Self {
100        let size =
101            T::guest_size().expect("Attempted to offset an unsized GuestType") as target_ptr_t;
102        GuestPtr {
103            pointer: self.pointer + (size * (off as target_ptr_t)),
104            guest_type: OnceCell::new(),
105        }
106    }
107
108    /// Creates a copy of the pointer offset by N bytes.
109    pub fn offset_bytes(&self, bytes: usize) -> Self {
110        GuestPtr {
111            pointer: self.pointer + (bytes as target_ptr_t),
112            guest_type: OnceCell::new(),
113        }
114    }
115
116    /// Casts the GuestPtr to another type of GuestPtr
117    pub fn cast<U: GuestType>(&self) -> GuestPtr<U> {
118        GuestPtr {
119            pointer: self.pointer,
120            guest_type: OnceCell::new(),
121        }
122    }
123
124    /// Write to the GuestPtr, with all modifications flushed at the end of the scope of
125    /// the function provided to `write`.
126    pub fn write(&mut self, func: impl FnOnce(&mut T)) -> Result<(), GuestWriteFail> {
127        if self.guest_type.get().is_none() {
128            self.read().unwrap();
129        }
130
131        let mut inner = self.guest_type.get_mut();
132        let inner = inner.as_mut().unwrap();
133
134        func(inner);
135
136        let cpu = unsafe { &mut *crate::sys::get_cpu() };
137        inner.write_to_guest(cpu, self.pointer)
138    }
139}
140
141impl<T: GuestType> Deref for GuestPtr<T> {
142    type Target = T;
143
144    fn deref(&self) -> &Self::Target {
145        self.read().unwrap();
146        self.get_cached()
147            .expect("Failed to read cached value from GuestPtr")
148    }
149}