veridian_kernel/virt/hypervisor/
smp.rs1#[cfg(feature = "alloc")]
6use alloc::{collections::BTreeMap, vec::Vec};
7
8use super::{GuestRegisters, MAX_VCPUS};
9use crate::virt::VmError;
10
11#[derive(Debug, Clone, Copy, PartialEq, Eq, Default)]
17pub enum VcpuState {
18 #[default]
20 Created,
21 Running,
23 Halted,
25 WaitingForSipi,
27 Paused,
29 Stopped,
31}
32
33#[derive(Debug, Clone, Copy, PartialEq, Eq)]
35pub enum IpiDeliveryMode {
36 Fixed,
38 LowestPriority,
40 Nmi,
42 Init,
44 Sipi,
46 ExtInt,
48}
49
50#[derive(Debug, Clone, Copy, PartialEq, Eq)]
52pub struct IpiMessage {
53 pub source: u8,
55 pub destination: u8,
57 pub delivery_mode: IpiDeliveryMode,
59 pub vector: u8,
61 pub level: bool,
63 pub trigger_level: bool,
65}
66
67#[cfg(feature = "alloc")]
69pub struct VirtualCpu {
70 pub id: u8,
72 pub state: VcpuState,
74 pub registers: GuestRegisters,
76 pub apic_id: u8,
78 pub pending_ipis: Vec<IpiMessage>,
80 pub is_bsp: bool,
82 pub host_affinity: Option<u32>,
84 pub sipi_vector: u8,
86 pub vmcs_fields: BTreeMap<u32, u64>,
88}
89
90#[cfg(feature = "alloc")]
91impl VirtualCpu {
92 pub fn new(id: u8, is_bsp: bool) -> Self {
93 let initial_state = if is_bsp {
94 VcpuState::Created
95 } else {
96 VcpuState::WaitingForSipi
97 };
98
99 Self {
100 id,
101 state: initial_state,
102 registers: GuestRegisters::default(),
103 apic_id: id,
104 pending_ipis: Vec::new(),
105 is_bsp,
106 host_affinity: None,
107 sipi_vector: 0,
108 vmcs_fields: BTreeMap::new(),
109 }
110 }
111
112 pub fn deliver_ipi(&mut self, ipi: IpiMessage) {
114 match ipi.delivery_mode {
115 IpiDeliveryMode::Init => {
116 self.state = VcpuState::WaitingForSipi;
118 self.registers = GuestRegisters::default();
119 }
120 IpiDeliveryMode::Sipi => {
121 if self.state == VcpuState::WaitingForSipi {
122 self.sipi_vector = ipi.vector;
124 self.registers.rip = (ipi.vector as u64) << 12;
125 self.state = VcpuState::Running;
126 }
127 }
129 IpiDeliveryMode::Nmi => {
130 if self.state == VcpuState::Halted {
132 self.state = VcpuState::Running;
133 }
134 self.pending_ipis.push(ipi);
135 }
136 _ => {
137 if self.state == VcpuState::Halted {
138 self.state = VcpuState::Running;
139 }
140 self.pending_ipis.push(ipi);
141 }
142 }
143 }
144
145 pub fn pop_ipi(&mut self) -> Option<IpiMessage> {
147 if self.pending_ipis.is_empty() {
148 None
149 } else {
150 Some(self.pending_ipis.remove(0))
151 }
152 }
153
154 pub fn set_affinity(&mut self, host_cpu: u32) {
156 self.host_affinity = Some(host_cpu);
157 }
158
159 pub fn pending_ipi_count(&self) -> usize {
160 self.pending_ipis.len()
161 }
162
163 pub fn halt(&mut self) {
165 self.state = VcpuState::Halted;
166 }
167
168 pub fn pause(&mut self) {
170 if self.state == VcpuState::Running {
171 self.state = VcpuState::Paused;
172 }
173 }
174
175 pub fn resume(&mut self) {
177 if self.state == VcpuState::Paused {
178 self.state = VcpuState::Running;
179 }
180 }
181
182 pub fn stop(&mut self) {
184 self.state = VcpuState::Stopped;
185 }
186}
187
188#[cfg(feature = "alloc")]
190pub struct SmpVm {
191 pub vm_id: u64,
193 pub vcpus: Vec<VirtualCpu>,
195 pub max_vcpus: usize,
197}
198
199#[cfg(feature = "alloc")]
200impl SmpVm {
201 pub fn new(vm_id: u64, vcpu_count: usize) -> Result<Self, VmError> {
202 if vcpu_count == 0 || vcpu_count > MAX_VCPUS {
203 return Err(VmError::InvalidVmState);
204 }
205
206 let mut vcpus = Vec::with_capacity(vcpu_count);
207 for i in 0..vcpu_count {
208 vcpus.push(VirtualCpu::new(i as u8, i == 0));
209 }
210
211 Ok(Self {
212 vm_id,
213 vcpus,
214 max_vcpus: vcpu_count,
215 })
216 }
217
218 pub fn send_ipi(
220 &mut self,
221 source: u8,
222 dest: u8,
223 mode: IpiDeliveryMode,
224 vector: u8,
225 ) -> Result<(), VmError> {
226 if source as usize >= self.vcpus.len() {
227 return Err(VmError::InvalidVmState);
228 }
229
230 let ipi = IpiMessage {
231 source,
232 destination: dest,
233 delivery_mode: mode,
234 vector,
235 level: true,
236 trigger_level: false,
237 };
238
239 if dest == 0xFF {
240 for vcpu in &mut self.vcpus {
242 if vcpu.id != source {
243 vcpu.deliver_ipi(ipi);
244 }
245 }
246 } else {
247 let target = self.vcpus.iter_mut().find(|v| v.apic_id == dest);
248 if let Some(vcpu) = target {
249 vcpu.deliver_ipi(ipi);
250 } else {
251 return Err(VmError::InvalidVmState);
252 }
253 }
254
255 Ok(())
256 }
257
258 pub fn startup_ap(&mut self, ap_id: u8, sipi_vector: u8) -> Result<(), VmError> {
260 self.send_ipi(0, ap_id, IpiDeliveryMode::Init, 0)?;
262 self.send_ipi(0, ap_id, IpiDeliveryMode::Sipi, sipi_vector)?;
264 Ok(())
265 }
266
267 pub fn vcpu_count(&self) -> usize {
268 self.vcpus.len()
269 }
270
271 pub fn running_vcpu_count(&self) -> usize {
272 self.vcpus
273 .iter()
274 .filter(|v| v.state == VcpuState::Running)
275 .count()
276 }
277
278 pub fn vcpu(&self, id: u8) -> Option<&VirtualCpu> {
280 self.vcpus.iter().find(|v| v.id == id)
281 }
282
283 pub fn vcpu_mut(&mut self, id: u8) -> Option<&mut VirtualCpu> {
285 self.vcpus.iter_mut().find(|v| v.id == id)
286 }
287
288 pub fn pause_all(&mut self) {
290 for vcpu in &mut self.vcpus {
291 vcpu.pause();
292 }
293 }
294
295 pub fn resume_all(&mut self) {
297 for vcpu in &mut self.vcpus {
298 vcpu.resume();
299 }
300 }
301}