1use std::convert::Infallible;
5use std::mem::offset_of;
6use std::sync::atomic::{Ordering, fence};
7
8use acpi_tables::{Aml, aml};
9use log::error;
10use serde::{Deserialize, Serialize};
11use vm_allocator::AllocPolicy;
12use vm_memory::{Address, ByteValued, Bytes, GuestAddress, GuestMemoryError};
13
14use crate::devices::acpi::generated::vmclock_abi::{
15 VMCLOCK_COUNTER_INVALID, VMCLOCK_MAGIC, VMCLOCK_STATUS_UNKNOWN, vmclock_abi,
16};
17use crate::snapshot::Persist;
18use crate::vstate::memory::GuestMemoryMmap;
19use crate::vstate::resources::ResourceAllocator;
20
21unsafe impl ByteValued for vmclock_abi {}
23
24const VMCLOCK_SIZE: u32 = 0x1000;
26
27macro_rules! write_vmclock_field {
30 ($vmclock:expr, $mem:expr, $field:ident, $value:expr) => {
31 $vmclock.inner.$field = $value;
32 $mem.write_obj(
33 $vmclock.inner.$field,
34 $vmclock
35 .guest_address
36 .unchecked_add(offset_of!(vmclock_abi, $field) as u64),
37 );
38 };
39}
40
41#[derive(Debug)]
47pub struct VmClock {
48 pub guest_address: GuestAddress,
50 inner: vmclock_abi,
52}
53
54impl VmClock {
55 pub fn new(resource_allocator: &mut ResourceAllocator) -> VmClock {
57 let addr = resource_allocator
58 .allocate_system_memory(
59 VMCLOCK_SIZE as u64,
60 VMCLOCK_SIZE as u64,
61 AllocPolicy::LastMatch,
62 )
63 .expect("vmclock: could not allocate guest memory for device");
64
65 let mut inner = vmclock_abi {
66 magic: VMCLOCK_MAGIC,
67 size: VMCLOCK_SIZE,
68 version: 1,
69 clock_status: VMCLOCK_STATUS_UNKNOWN,
70 counter_id: VMCLOCK_COUNTER_INVALID,
71 ..Default::default()
72 };
73
74 VmClock {
75 guest_address: GuestAddress(addr),
76 inner,
77 }
78 }
79
80 pub fn activate(&self, mem: &GuestMemoryMmap) -> Result<(), GuestMemoryError> {
82 mem.write_slice(self.inner.as_slice(), self.guest_address)?;
83 Ok(())
84 }
85
86 pub fn post_load_update(&mut self, mem: &GuestMemoryMmap) {
88 write_vmclock_field!(self, mem, seq_count, self.inner.seq_count | 1);
89
90 fence(Ordering::Release);
93
94 write_vmclock_field!(
95 self,
96 mem,
97 disruption_marker,
98 self.inner.disruption_marker.wrapping_add(1)
99 );
100
101 fence(Ordering::Release);
104
105 write_vmclock_field!(self, mem, seq_count, self.inner.seq_count.wrapping_add(1));
106 }
107}
108
109#[derive(Default, Debug, Clone, Serialize, Deserialize)]
113pub struct VmClockState {
114 pub guest_address: u64,
116 pub inner: vmclock_abi,
118}
119
120impl<'a> Persist<'a> for VmClock {
121 type State = VmClockState;
122 type ConstructorArgs = &'a GuestMemoryMmap;
123 type Error = Infallible;
124
125 fn save(&self) -> Self::State {
126 VmClockState {
127 guest_address: self.guest_address.0,
128 inner: self.inner,
129 }
130 }
131
132 fn restore(
133 constructor_args: Self::ConstructorArgs,
134 state: &Self::State,
135 ) -> Result<Self, Self::Error> {
136 let mut vmclock = VmClock {
137 guest_address: GuestAddress(state.guest_address),
138 inner: state.inner,
139 };
140 vmclock.post_load_update(constructor_args);
141 Ok(vmclock)
142 }
143}
144
145impl Aml for VmClock {
146 fn append_aml_bytes(&self, v: &mut Vec<u8>) -> Result<(), aml::AmlError> {
147 aml::Device::new(
148 "_SB_.VCLK".try_into()?,
149 vec![
150 &aml::Name::new("_HID".try_into()?, &"AMZNC10C")?,
151 &aml::Name::new("_CID".try_into()?, &"VMCLOCK")?,
152 &aml::Name::new("_DDN".try_into()?, &"VMCLOCK")?,
153 &aml::Method::new(
154 "_STA".try_into()?,
155 0,
156 false,
157 vec![&aml::Return::new(&0x0fu8)],
158 ),
159 &aml::Name::new(
160 "_CRS".try_into()?,
161 &aml::ResourceTemplate::new(vec![&aml::AddressSpace::new_memory(
162 aml::AddressSpaceCacheable::Cacheable,
163 false,
164 self.guest_address.0,
165 self.guest_address.0 + VMCLOCK_SIZE as u64 - 1,
166 )?]),
167 )?,
168 ],
169 )
170 .append_aml_bytes(v)
171 }
172}
173
174#[cfg(test)]
175mod tests {
176 use vm_memory::{Bytes, GuestAddress};
177
178 use crate::arch;
179 use crate::devices::acpi::generated::vmclock_abi::vmclock_abi;
180 use crate::devices::acpi::vmclock::{VMCLOCK_SIZE, VmClock};
181 use crate::snapshot::Persist;
182 use crate::test_utils::single_region_mem;
183 use crate::utils::u64_to_usize;
184 use crate::vstate::resources::ResourceAllocator;
185
186 const VMCLOCK_TEST_GUEST_ADDR: GuestAddress =
188 GuestAddress(arch::SYSTEM_MEM_START + arch::SYSTEM_MEM_SIZE - VMCLOCK_SIZE as u64);
189
190 fn default_vmclock() -> VmClock {
191 let mut resource_allocator = ResourceAllocator::new();
192 VmClock::new(&mut resource_allocator)
193 }
194
195 #[test]
196 fn test_new_device() {
197 let vmclock = default_vmclock();
198 let mem = single_region_mem(
199 u64_to_usize(arch::SYSTEM_MEM_START) + u64_to_usize(arch::SYSTEM_MEM_SIZE),
200 );
201
202 let guest_data: vmclock_abi = mem.read_obj(VMCLOCK_TEST_GUEST_ADDR).unwrap();
203 assert_ne!(guest_data, vmclock.inner);
204
205 vmclock.activate(&mem);
206
207 let guest_data: vmclock_abi = mem.read_obj(VMCLOCK_TEST_GUEST_ADDR).unwrap();
208 assert_eq!(guest_data, vmclock.inner);
209 }
210
211 #[test]
212 fn test_device_save_restore() {
213 let vmclock = default_vmclock();
214 let mem = single_region_mem(
215 u64_to_usize(arch::SYSTEM_MEM_START) + u64_to_usize(arch::SYSTEM_MEM_SIZE),
216 );
217
218 vmclock.activate(&mem).unwrap();
219 let guest_data: vmclock_abi = mem.read_obj(VMCLOCK_TEST_GUEST_ADDR).unwrap();
220
221 let state = vmclock.save();
222 let vmclock_new = VmClock::restore(&mem, &state).unwrap();
223
224 let guest_data_new: vmclock_abi = mem.read_obj(VMCLOCK_TEST_GUEST_ADDR).unwrap();
225 assert_ne!(guest_data_new, vmclock.inner);
226 assert_eq!(guest_data_new, vmclock_new.inner);
227 assert_eq!(
228 vmclock.inner.disruption_marker + 1,
229 vmclock_new.inner.disruption_marker
230 );
231 }
232}