Skip to main content

arcbox_hypervisor/linux/vm/
virtual_machine.rs

1use std::sync::atomic::Ordering;
2
3use crate::{
4    error::HypervisorError,
5    traits::VirtualMachine,
6    types::{DeviceSnapshot, VirtioDeviceConfig},
7};
8
9use super::virtio::{VIRTIO_MMIO_SIZE, bincode_serialize_device_config};
10use super::{KvmMemory, KvmVm, VirtioDeviceInfo, VmState};
11use crate::linux::KvmVcpu;
12
13impl VirtualMachine for KvmVm {
14    type Vcpu = KvmVcpu;
15    type Memory = KvmMemory;
16
17    fn memory(&self) -> &Self::Memory {
18        &self.memory
19    }
20
21    fn create_vcpu(&mut self, id: u32) -> Result<Self::Vcpu, HypervisorError> {
22        if id >= self.config.vcpu_count {
23            return Err(HypervisorError::VcpuCreationFailed {
24                id,
25                reason: format!(
26                    "vCPU ID {} exceeds configured count {}",
27                    id, self.config.vcpu_count
28                ),
29            });
30        }
31
32        // Check if already created
33        {
34            let vcpus = self
35                .vcpus
36                .read()
37                .map_err(|_| HypervisorError::VcpuCreationFailed {
38                    id,
39                    reason: "Lock poisoned".to_string(),
40                })?;
41
42            if vcpus.contains(&id) {
43                return Err(HypervisorError::VcpuCreationFailed {
44                    id,
45                    reason: "vCPU already created".to_string(),
46                });
47            }
48        }
49
50        // Create vCPU via KVM
51        let vcpu_fd = self
52            .vm_fd
53            .create_vcpu(id, self.vcpu_mmap_size)
54            .map_err(|e| HypervisorError::VcpuCreationFailed {
55                id,
56                reason: format!("KVM error: {}", e),
57            })?;
58
59        // Create wrapper
60        let vcpu = KvmVcpu::new(id, vcpu_fd)?;
61
62        // Record creation
63        {
64            let mut vcpus =
65                self.vcpus
66                    .write()
67                    .map_err(|_| HypervisorError::VcpuCreationFailed {
68                        id,
69                        reason: "Lock poisoned".to_string(),
70                    })?;
71            vcpus.push(id);
72        }
73
74        tracing::debug!("Created vCPU {} for VM {}", id, self.id);
75
76        Ok(vcpu)
77    }
78
79    fn add_virtio_device(&mut self, device: VirtioDeviceConfig) -> Result<(), HypervisorError> {
80        // 1. Check state - devices can only be added before VM starts.
81        let state = self.state();
82        if state != VmState::Created {
83            return Err(HypervisorError::DeviceError(
84                "Cannot add device: VM not in Created state".to_string(),
85            ));
86        }
87
88        // 2. Allocate MMIO address space for the device.
89        let mmio_base = self.allocate_mmio_region()?;
90
91        // 3. Allocate an IRQ (GSI) for the device.
92        let gsi = self.allocate_irq();
93
94        // 4. Create eventfd for IRQ injection and register IRQFD.
95        let irq_fd = Self::create_eventfd()?;
96
97        if let Err(e) = self.register_irqfd(irq_fd, gsi, None) {
98            // Clean up on failure.
99            unsafe { libc::close(irq_fd) };
100            return Err(e);
101        }
102
103        // 5. Set up IOEVENTFD for queue notification.
104        let notify_fd = match self.setup_ioeventfd(mmio_base) {
105            Ok(fd) => fd,
106            Err(e) => {
107                // Clean up on failure.
108                let _ = self.unregister_irqfd(irq_fd, gsi);
109                unsafe { libc::close(irq_fd) };
110                return Err(e);
111            }
112        };
113
114        // 6. Record the device information.
115        let device_info = VirtioDeviceInfo {
116            device_type: device.device_type.clone(),
117            mmio_base,
118            mmio_size: VIRTIO_MMIO_SIZE,
119            irq: gsi,
120            irq_fd,
121            notify_fd,
122        };
123
124        {
125            let mut devices = self.virtio_devices.write().map_err(|_| {
126                // Clean up on failure.
127                let _ = self.unregister_irqfd(irq_fd, gsi);
128                unsafe {
129                    libc::close(irq_fd);
130                    libc::close(notify_fd);
131                }
132                HypervisorError::DeviceError("Lock poisoned".to_string())
133            })?;
134
135            devices.push(device_info);
136        }
137
138        tracing::info!(
139            "Added {:?} device to VM {}: MMIO={:#x}-{:#x}, IRQ={}, irq_fd={}, notify_fd={}",
140            device.device_type,
141            self.id,
142            mmio_base,
143            mmio_base + VIRTIO_MMIO_SIZE,
144            gsi,
145            irq_fd,
146            notify_fd
147        );
148
149        Ok(())
150    }
151
152    fn start(&mut self) -> Result<(), HypervisorError> {
153        let state = self.state();
154        if state != VmState::Created && state != VmState::Stopped {
155            return Err(HypervisorError::VmStateError {
156                expected: "Created or Stopped".to_string(),
157                actual: format!("{:?}", state),
158            });
159        }
160
161        self.set_state(VmState::Starting);
162
163        // Mark as running
164        self.running.store(true, Ordering::SeqCst);
165        self.set_state(VmState::Running);
166
167        tracing::info!("Started VM {}", self.id);
168
169        Ok(())
170    }
171
172    fn pause(&mut self) -> Result<(), HypervisorError> {
173        let state = self.state();
174        if state != VmState::Running {
175            return Err(HypervisorError::VmStateError {
176                expected: "Running".to_string(),
177                actual: format!("{:?}", state),
178            });
179        }
180
181        // Signal all vCPUs to pause
182        // In KVM, this is typically done by setting immediate_exit and signaling
183        // the vCPU threads
184
185        self.set_state(VmState::Paused);
186
187        tracing::info!("Paused VM {}", self.id);
188
189        Ok(())
190    }
191
192    fn resume(&mut self) -> Result<(), HypervisorError> {
193        let state = self.state();
194        if state != VmState::Paused {
195            return Err(HypervisorError::VmStateError {
196                expected: "Paused".to_string(),
197                actual: format!("{:?}", state),
198            });
199        }
200
201        self.set_state(VmState::Running);
202
203        tracing::info!("Resumed VM {}", self.id);
204
205        Ok(())
206    }
207
208    fn stop(&mut self) -> Result<(), HypervisorError> {
209        let state = self.state();
210        if state != VmState::Running && state != VmState::Paused {
211            return Err(HypervisorError::VmStateError {
212                expected: "Running or Paused".to_string(),
213                actual: format!("{:?}", state),
214            });
215        }
216
217        self.set_state(VmState::Stopping);
218
219        // Signal all vCPUs to stop
220        self.running.store(false, Ordering::SeqCst);
221
222        self.set_state(VmState::Stopped);
223
224        tracing::info!("Stopped VM {}", self.id);
225
226        Ok(())
227    }
228
229    fn as_any(&self) -> &dyn std::any::Any {
230        self
231    }
232
233    fn as_any_mut(&mut self) -> &mut dyn std::any::Any {
234        self
235    }
236
237    fn vcpu_count(&self) -> u32 {
238        self.config.vcpu_count
239    }
240
241    fn snapshot_devices(&self) -> Result<Vec<DeviceSnapshot>, HypervisorError> {
242        let devices = self
243            .virtio_devices
244            .read()
245            .map_err(|_| HypervisorError::SnapshotError("Lock poisoned".to_string()))?;
246
247        let mut snapshots = Vec::with_capacity(devices.len());
248
249        for device in devices.iter() {
250            // KVM VirtIO devices don't have internal state accessible from host.
251            // The actual device state is managed by the VMM layer (e.g., arcbox-virtio).
252            // We record the device configuration here; the VMM would need to
253            // serialize its own device state.
254            let state_bytes = bincode_serialize_device_config(device);
255
256            snapshots.push(DeviceSnapshot {
257                device_type: device.device_type.clone(),
258                name: format!("{:?}-{}", device.device_type, snapshots.len()),
259                state: state_bytes,
260            });
261        }
262
263        tracing::debug!(
264            "snapshot_devices: captured {} device configurations",
265            snapshots.len()
266        );
267
268        Ok(snapshots)
269    }
270
271    fn restore_devices(&mut self, snapshots: &[DeviceSnapshot]) -> Result<(), HypervisorError> {
272        // KVM device restoration is complex:
273        // 1. VirtIO MMIO regions must be at the same addresses
274        // 2. IRQs must be assigned to the same GSIs
275        // 3. The VMM layer must restore internal device state
276        //
277        // For now, we verify that the device configuration matches.
278        let devices = self
279            .virtio_devices
280            .read()
281            .map_err(|_| HypervisorError::SnapshotError("Lock poisoned".to_string()))?;
282
283        if snapshots.len() != devices.len() {
284            return Err(HypervisorError::SnapshotError(format!(
285                "Device count mismatch: snapshot has {}, VM has {}",
286                snapshots.len(),
287                devices.len()
288            )));
289        }
290
291        for (snapshot, device) in snapshots.iter().zip(devices.iter()) {
292            if snapshot.device_type != device.device_type {
293                return Err(HypervisorError::SnapshotError(format!(
294                    "Device type mismatch: snapshot has {:?}, VM has {:?}",
295                    snapshot.device_type, device.device_type
296                )));
297            }
298        }
299
300        tracing::debug!(
301            "restore_devices: verified {} device configurations",
302            snapshots.len()
303        );
304
305        Ok(())
306    }
307}