1#[cfg(feature = "alloc")]
6use alloc::{string::String, vec::Vec};
7
8use super::{
9 lapic::{LvtEntry, VirtualLapic},
10 migration::SerializedVmcs,
11 GuestRegisters, PAGE_SIZE, SNAPSHOT_MAGIC, SNAPSHOT_VERSION,
12};
13
14#[derive(Debug, Clone, Copy, PartialEq, Eq)]
20#[repr(u32)]
21pub enum SnapshotRegionType {
22 VmcsState = 1,
24 GeneralRegisters = 2,
26 MsrValues = 3,
28 MemoryPage = 4,
30 DeviceState = 5,
32 LapicState = 6,
34 VcpuState = 7,
36}
37
38#[derive(Debug, Clone, Copy, PartialEq, Eq)]
40#[repr(C)]
41pub struct SnapshotHeader {
42 pub magic: u32,
44 pub version: u32,
46 pub vm_id: u64,
48 pub vcpu_count: u32,
50 pub memory_pages: u64,
52 pub region_count: u32,
54 pub total_size: u64,
56 pub timestamp: u64,
58 pub checksum: u64,
60}
61
62impl Default for SnapshotHeader {
63 fn default() -> Self {
64 Self {
65 magic: SNAPSHOT_MAGIC,
66 version: SNAPSHOT_VERSION,
67 vm_id: 0,
68 vcpu_count: 0,
69 memory_pages: 0,
70 region_count: 0,
71 total_size: 0,
72 timestamp: 0,
73 checksum: 0,
74 }
75 }
76}
77
78impl SnapshotHeader {
79 pub fn is_valid(&self) -> bool {
80 self.magic == SNAPSHOT_MAGIC && self.version == SNAPSHOT_VERSION
81 }
82
83 pub fn compute_checksum(&self) -> u64 {
86 let mut ck: u64 = 0;
87 ck ^= self.magic as u64;
88 ck ^= self.version as u64;
89 ck ^= self.vm_id;
90 ck ^= self.vcpu_count as u64;
91 ck ^= self.memory_pages;
92 ck ^= self.region_count as u64;
93 ck ^= self.total_size;
94 ck ^= self.timestamp;
95 ck
96 }
97}
98
99#[derive(Debug, Clone, Copy, PartialEq, Eq)]
101#[repr(C)]
102pub struct SnapshotRegionDescriptor {
103 pub region_type: u32,
105 pub vcpu_index: u32,
107 pub offset: u64,
109 pub size: u64,
111}
112
113#[derive(Debug, Clone, Copy, PartialEq, Eq)]
115#[repr(C)]
116pub struct MsrEntry {
117 pub index: u32,
118 pub _reserved: u32,
119 pub value: u64,
120}
121
122#[allow(unused)]
124pub struct CommonMsrs;
125
126#[allow(unused)]
127impl CommonMsrs {
128 pub const IA32_EFER: u32 = 0xC000_0080;
129 pub const IA32_STAR: u32 = 0xC000_0081;
130 pub const IA32_LSTAR: u32 = 0xC000_0082;
131 pub const IA32_CSTAR: u32 = 0xC000_0083;
132 pub const IA32_FMASK: u32 = 0xC000_0084;
133 pub const IA32_FS_BASE: u32 = 0xC000_0100;
134 pub const IA32_GS_BASE: u32 = 0xC000_0101;
135 pub const IA32_KERNEL_GS_BASE: u32 = 0xC000_0102;
136 pub const IA32_TSC_AUX: u32 = 0xC000_0103;
137 pub const IA32_SYSENTER_CS: u32 = 0x174;
138 pub const IA32_SYSENTER_ESP: u32 = 0x175;
139 pub const IA32_SYSENTER_EIP: u32 = 0x176;
140 pub const IA32_PAT: u32 = 0x277;
141 pub const IA32_DEBUGCTL: u32 = 0x1D9;
142 pub const IA32_APIC_BASE: u32 = 0x1B;
143}
144
145#[cfg(feature = "alloc")]
147pub struct VmSnapshot {
148 pub header: SnapshotHeader,
150 pub regions: Vec<SnapshotRegionDescriptor>,
152 pub vmcs_states: Vec<SerializedVmcs>,
154 pub register_states: Vec<GuestRegisters>,
156 pub msr_states: Vec<Vec<MsrEntry>>,
158 pub lapic_states: Vec<LapicSnapshot>,
160 pub memory_page_indices: Vec<u64>,
162 pub device_states: Vec<DeviceStateBlob>,
164}
165
166#[derive(Debug, Clone)]
168pub struct LapicSnapshot {
169 pub id: u32,
170 pub tpr: u32,
171 pub svr: u32,
172 pub isr: [u32; 8],
173 pub irr: [u32; 8],
174 pub tmr: [u32; 8],
175 pub lvt_timer_raw: u32,
176 pub lvt_thermal_raw: u32,
177 pub lvt_perfmon_raw: u32,
178 pub lvt_lint0_raw: u32,
179 pub lvt_lint1_raw: u32,
180 pub lvt_error_raw: u32,
181 pub timer_initial_count: u32,
182 pub timer_current_count: u32,
183 pub timer_divide_config: u32,
184 pub tsc_deadline: u64,
185 pub icr_low: u32,
186 pub icr_high: u32,
187 pub ldr: u32,
188 pub dfr: u32,
189 pub enabled: bool,
190}
191
192impl LapicSnapshot {
193 pub fn from_lapic(lapic: &VirtualLapic) -> Self {
194 Self {
195 id: lapic.id,
196 tpr: lapic.tpr,
197 svr: lapic.svr,
198 isr: lapic.isr,
199 irr: lapic.irr,
200 tmr: lapic.tmr,
201 lvt_timer_raw: lapic.lvt_timer.raw,
202 lvt_thermal_raw: lapic.lvt_thermal.raw,
203 lvt_perfmon_raw: lapic.lvt_perfmon.raw,
204 lvt_lint0_raw: lapic.lvt_lint0.raw,
205 lvt_lint1_raw: lapic.lvt_lint1.raw,
206 lvt_error_raw: lapic.lvt_error.raw,
207 timer_initial_count: lapic.timer_initial_count,
208 timer_current_count: lapic.timer_current_count,
209 timer_divide_config: lapic.timer_divide_config,
210 tsc_deadline: lapic.tsc_deadline,
211 icr_low: lapic.icr_low,
212 icr_high: lapic.icr_high,
213 ldr: lapic.ldr,
214 dfr: lapic.dfr,
215 enabled: lapic.enabled,
216 }
217 }
218
219 pub fn restore_to_lapic(&self, lapic: &mut VirtualLapic) {
221 lapic.id = self.id;
222 lapic.tpr = self.tpr;
223 lapic.svr = self.svr;
224 lapic.isr = self.isr;
225 lapic.irr = self.irr;
226 lapic.tmr = self.tmr;
227 lapic.lvt_timer = LvtEntry {
228 raw: self.lvt_timer_raw,
229 };
230 lapic.lvt_thermal = LvtEntry {
231 raw: self.lvt_thermal_raw,
232 };
233 lapic.lvt_perfmon = LvtEntry {
234 raw: self.lvt_perfmon_raw,
235 };
236 lapic.lvt_lint0 = LvtEntry {
237 raw: self.lvt_lint0_raw,
238 };
239 lapic.lvt_lint1 = LvtEntry {
240 raw: self.lvt_lint1_raw,
241 };
242 lapic.lvt_error = LvtEntry {
243 raw: self.lvt_error_raw,
244 };
245 lapic.timer_initial_count = self.timer_initial_count;
246 lapic.timer_current_count = self.timer_current_count;
247 lapic.timer_divide_config = self.timer_divide_config;
248 lapic.tsc_deadline = self.tsc_deadline;
249 lapic.icr_low = self.icr_low;
250 lapic.icr_high = self.icr_high;
251 lapic.ldr = self.ldr;
252 lapic.dfr = self.dfr;
253 lapic.enabled = self.enabled;
254 }
255}
256
257#[cfg(feature = "alloc")]
259#[derive(Debug, Clone)]
260pub struct DeviceStateBlob {
261 pub name: String,
263 pub data: Vec<u8>,
265}
266
267#[cfg(feature = "alloc")]
268impl VmSnapshot {
269 pub fn new(vm_id: u64, vcpu_count: u32, memory_pages: u64, timestamp: u64) -> Self {
271 let header = SnapshotHeader {
272 vm_id,
273 vcpu_count,
274 memory_pages,
275 timestamp,
276 ..Default::default()
277 };
278
279 Self {
280 header,
281 regions: Vec::new(),
282 vmcs_states: Vec::new(),
283 register_states: Vec::new(),
284 msr_states: Vec::new(),
285 lapic_states: Vec::new(),
286 memory_page_indices: Vec::new(),
287 device_states: Vec::new(),
288 }
289 }
290
291 pub fn add_register_state(&mut self, vcpu_idx: u32, regs: GuestRegisters) {
293 self.register_states.push(regs);
294 self.regions.push(SnapshotRegionDescriptor {
295 region_type: SnapshotRegionType::GeneralRegisters as u32,
296 vcpu_index: vcpu_idx,
297 offset: 0, size: core::mem::size_of::<GuestRegisters>() as u64,
299 });
300 self.header.region_count += 1;
301 }
302
303 pub fn add_vmcs_state(&mut self, vcpu_idx: u32, vmcs: SerializedVmcs) {
305 let field_size = vmcs.field_count() as u64 * 12; self.vmcs_states.push(vmcs);
307 self.regions.push(SnapshotRegionDescriptor {
308 region_type: SnapshotRegionType::VmcsState as u32,
309 vcpu_index: vcpu_idx,
310 offset: 0,
311 size: field_size,
312 });
313 self.header.region_count += 1;
314 }
315
316 pub fn add_msr_state(&mut self, vcpu_idx: u32, msrs: Vec<MsrEntry>) {
318 let size = msrs.len() as u64 * 16; self.msr_states.push(msrs);
320 self.regions.push(SnapshotRegionDescriptor {
321 region_type: SnapshotRegionType::MsrValues as u32,
322 vcpu_index: vcpu_idx,
323 offset: 0,
324 size,
325 });
326 self.header.region_count += 1;
327 }
328
329 pub fn add_lapic_state(&mut self, vcpu_idx: u32, lapic: &VirtualLapic) {
331 self.lapic_states.push(LapicSnapshot::from_lapic(lapic));
332 self.regions.push(SnapshotRegionDescriptor {
333 region_type: SnapshotRegionType::LapicState as u32,
334 vcpu_index: vcpu_idx,
335 offset: 0,
336 size: core::mem::size_of::<LapicSnapshot>() as u64,
337 });
338 self.header.region_count += 1;
339 }
340
341 pub fn add_memory_page(&mut self, page_index: u64) {
343 self.memory_page_indices.push(page_index);
344 self.regions.push(SnapshotRegionDescriptor {
345 region_type: SnapshotRegionType::MemoryPage as u32,
346 vcpu_index: 0,
347 offset: 0,
348 size: PAGE_SIZE,
349 });
350 self.header.region_count += 1;
351 }
352
353 pub fn add_device_state(&mut self, name: String, data: Vec<u8>) {
355 let size = data.len() as u64;
356 self.device_states.push(DeviceStateBlob { name, data });
357 self.regions.push(SnapshotRegionDescriptor {
358 region_type: SnapshotRegionType::DeviceState as u32,
359 vcpu_index: 0,
360 offset: 0,
361 size,
362 });
363 self.header.region_count += 1;
364 }
365
366 pub fn finalize(&mut self) {
368 let mut total: u64 = core::mem::size_of::<SnapshotHeader>() as u64;
369
370 total = total.saturating_add(
372 (self.regions.len() as u64)
373 .checked_mul(core::mem::size_of::<SnapshotRegionDescriptor>() as u64)
374 .unwrap_or(0),
375 );
376
377 for region in &self.regions {
379 total = total.saturating_add(region.size);
380 }
381
382 self.header.total_size = total;
383 self.header.checksum = self.header.compute_checksum();
384 }
385
386 pub fn validate(&self) -> bool {
388 self.header.is_valid() && self.header.checksum == self.header.compute_checksum()
389 }
390
391 pub fn region_count(&self) -> usize {
392 self.regions.len()
393 }
394
395 pub fn memory_page_count(&self) -> usize {
396 self.memory_page_indices.len()
397 }
398
399 pub fn vcpu_state_count(&self) -> usize {
400 self.register_states.len()
401 }
402
403 pub fn device_state_count(&self) -> usize {
404 self.device_states.len()
405 }
406}
407
408#[cfg(feature = "alloc")]
410pub fn restore_lapic_from_snapshot(snapshot: &VmSnapshot, vcpu_idx: usize) -> Option<VirtualLapic> {
411 let lapic_snap = snapshot.lapic_states.get(vcpu_idx)?;
412 let mut lapic = VirtualLapic::new(lapic_snap.id);
413 lapic_snap.restore_to_lapic(&mut lapic);
414 Some(lapic)
415}