xref: /aosp_15_r20/external/crosvm/devices/src/irqchip/whpx.rs (revision bb4ee6a4ae7042d18b07a98463b9c8b875e44b39)
1 // Copyright 2021 The ChromiumOS Authors
2 // Use of this source code is governed by a BSD-style license that can be
3 // found in the LICENSE file.
4 
5 use std::convert::TryFrom;
6 use std::sync::Arc;
7 
8 use anyhow::Context;
9 use serde::Deserialize;
10 use serde::Serialize;
11 use sync::Mutex;
12 
13 cfg_if::cfg_if! {
14     if #[cfg(test)] {
15         use base::FakeClock as Clock;
16     } else {
17         use base::Clock;
18     }
19 }
20 use base::error;
21 use base::Error;
22 use base::Event;
23 use base::Result;
24 use base::Tube;
25 use hypervisor::whpx::WhpxVcpu;
26 use hypervisor::whpx::WhpxVm;
27 use hypervisor::IoapicState;
28 use hypervisor::IrqRoute;
29 use hypervisor::IrqSource;
30 use hypervisor::IrqSourceChip;
31 use hypervisor::LapicState;
32 use hypervisor::MPState;
33 use hypervisor::MsiAddressMessage;
34 use hypervisor::MsiDataMessage;
35 use hypervisor::PicSelect;
36 use hypervisor::PicState;
37 use hypervisor::PitState;
38 use hypervisor::Vcpu;
39 use hypervisor::VcpuX86_64;
40 use hypervisor::Vm;
41 use resources::SystemAllocator;
42 
43 use crate::irqchip::DelayedIoApicIrqEvents;
44 use crate::irqchip::InterruptData;
45 use crate::irqchip::InterruptDestination;
46 use crate::irqchip::Ioapic;
47 use crate::irqchip::IrqEvent;
48 use crate::irqchip::IrqEventIndex;
49 use crate::irqchip::Pic;
50 use crate::irqchip::Routes;
51 use crate::irqchip::VcpuRunState;
52 use crate::irqchip::IOAPIC_BASE_ADDRESS;
53 use crate::irqchip::IOAPIC_MEM_LENGTH_BYTES;
54 use crate::Bus;
55 use crate::IrqChip;
56 use crate::IrqChipCap;
57 use crate::IrqChipX86_64;
58 use crate::IrqEdgeEvent;
59 use crate::IrqEventSource;
60 use crate::IrqLevelEvent;
61 use crate::Pit;
62 use crate::PitError;
63 
64 /// PIT channel 0 timer is connected to IRQ 0
65 const PIT_CHANNEL0_IRQ: u32 = 0;
66 /// TODO(b/187464379): we don't know what the right frequency is here, but 100MHz gives
67 /// us better results than the frequency that WSL seems to use, which is 500MHz.
68 const WHPX_LOCAL_APIC_EMULATION_APIC_FREQUENCY: u32 = 100_000_000;
69 
70 /// The WhpxSplitIrqChip supports
71 pub struct WhpxSplitIrqChip {
72     vm: WhpxVm,
73     routes: Arc<Mutex<Routes>>,
74     pit: Arc<Mutex<Pit>>,
75     pic: Arc<Mutex<Pic>>,
76     ioapic: Arc<Mutex<Ioapic>>,
77     ioapic_pins: usize,
78     /// Delayed ioapic irq object, that contains the delayed events because the ioapic was locked
79     /// when service_irq was called on the irqchip. This prevents deadlocks when a Vcpu thread has
80     /// locked the ioapic and the ioapic sends a AddMsiRoute signal to the main thread (which
81     /// itself may be busy trying to call service_irq).
82     ///
83     /// ## Note:
84     /// This lock may be locked by itself to access the `DelayedIoApicIrqEvents`. If accessed in
85     /// conjunction with the `irq_events` field, that lock should be taken first to prevent
86     /// deadlocks stemming from lock-ordering issues.
87     delayed_ioapic_irq_events: Arc<Mutex<DelayedIoApicIrqEvents>>,
88     /// Array of Events that devices will use to assert ioapic pins.
89     irq_events: Arc<Mutex<Vec<Option<IrqEvent>>>>,
90 }
91 
92 impl WhpxSplitIrqChip {
93     /// Construct a new WhpxSplitIrqChip.
new(vm: WhpxVm, irq_tube: Tube, ioapic_pins: Option<usize>) -> Result<Self>94     pub fn new(vm: WhpxVm, irq_tube: Tube, ioapic_pins: Option<usize>) -> Result<Self> {
95         let pit_evt = IrqEdgeEvent::new()?;
96         let pit = Pit::new(pit_evt.try_clone()?, Arc::new(Mutex::new(Clock::new()))).map_err(
97             |e| match e {
98                 PitError::CloneEvent(err) => err,
99                 PitError::CreateEvent(err) => err,
100                 PitError::CreateWaitContext(err) => err,
101                 PitError::WaitError(err) => err,
102                 PitError::TimerCreateError(err) => err,
103                 PitError::SpawnThread(_) => Error::new(libc::EIO),
104             },
105         )?;
106 
107         let pit_event_source = IrqEventSource::from_device(&pit);
108 
109         let ioapic_pins = ioapic_pins.unwrap_or(hypervisor::NUM_IOAPIC_PINS);
110         let ioapic = Ioapic::new(irq_tube, ioapic_pins)?;
111 
112         let mut chip = WhpxSplitIrqChip {
113             vm,
114             routes: Arc::new(Mutex::new(Routes::new())),
115             pit: Arc::new(Mutex::new(pit)),
116             pic: Arc::new(Mutex::new(Pic::new())),
117             ioapic: Arc::new(Mutex::new(ioapic)),
118             ioapic_pins,
119             delayed_ioapic_irq_events: Arc::new(Mutex::new(DelayedIoApicIrqEvents::new()?)),
120             irq_events: Arc::new(Mutex::new(Vec::new())),
121         };
122 
123         // This is equivalent to setting this in the blank Routes object above because
124         // WhpxSplitIrqChip doesn't interact with the WHPX API when setting routes, but in case
125         // that changes we do it this way.
126         chip.set_irq_routes(&Routes::default_pic_ioapic_routes(ioapic_pins))?;
127 
128         // Add the pit
129         chip.register_edge_irq_event(PIT_CHANNEL0_IRQ, &pit_evt, pit_event_source)?;
130         Ok(chip)
131     }
132 
133     /// Sends a Message Signaled Interrupt to one or more APICs. The WHPX API does not accept the
134     /// MSI address and data directly, so we must parse them and supply WHPX with the vector,
135     /// destination id, destination mode, trigger mode, and delivery mode (aka interrupt type).
send_msi(&self, addr: u32, data: u32) -> Result<()>136     fn send_msi(&self, addr: u32, data: u32) -> Result<()> {
137         let mut msi_addr = MsiAddressMessage::new();
138         msi_addr.set(0, 32, addr as u64);
139         let dest = InterruptDestination::try_from(&msi_addr).or(Err(Error::new(libc::EINVAL)))?;
140 
141         let mut msi_data = MsiDataMessage::new();
142         msi_data.set(0, 32, data as u64);
143         let data = InterruptData::from(&msi_data);
144 
145         self.vm.request_interrupt(
146             data.vector,
147             dest.dest_id,
148             dest.mode,
149             data.trigger,
150             data.delivery,
151         )
152     }
153 
154     /// Return true if there is a pending interrupt for the specified vcpu. For WhpxSplitIrqChip
155     /// this calls interrupt_requested on the pic.
interrupt_requested(&self, vcpu_id: usize) -> bool156     pub fn interrupt_requested(&self, vcpu_id: usize) -> bool {
157         // Pic interrupts for the split irqchip only go to vcpu 0
158         if vcpu_id != 0 {
159             return false;
160         }
161         self.pic.lock().interrupt_requested()
162     }
163 
164     /// Check if the specified vcpu has any pending interrupts. Returns [`None`] for no interrupts,
165     /// otherwise [`Some::<u8>`] should be the injected interrupt vector. For [`WhpxSplitIrqChip`]
166     /// this calls `get_external_interrupt` on the pic.
get_external_interrupt(&self, vcpu_id: usize) -> Result<Option<u8>>167     pub fn get_external_interrupt(&self, vcpu_id: usize) -> Result<Option<u8>> {
168         // Pic interrupts for the split irqchip only go to vcpu 0
169         if vcpu_id != 0 {
170             return Ok(None);
171         }
172         if let Some(vector) = self.pic.lock().get_external_interrupt() {
173             Ok(Some(vector))
174         } else {
175             Ok(None)
176         }
177     }
178 }
179 
180 impl WhpxSplitIrqChip {
register_irq_event( &mut self, irq: u32, irq_event: &Event, resample_event: Option<&Event>, source: IrqEventSource, ) -> Result<Option<usize>>181     fn register_irq_event(
182         &mut self,
183         irq: u32,
184         irq_event: &Event,
185         resample_event: Option<&Event>,
186         source: IrqEventSource,
187     ) -> Result<Option<usize>> {
188         let mut evt = IrqEvent {
189             gsi: irq,
190             event: irq_event.try_clone()?,
191             resample_event: None,
192             source,
193         };
194 
195         if let Some(resample_event) = resample_event {
196             evt.resample_event = Some(resample_event.try_clone()?);
197         }
198 
199         let mut irq_events = self.irq_events.lock();
200         let index = irq_events.len();
201         irq_events.push(Some(evt));
202         Ok(Some(index))
203     }
204 
unregister_irq_event(&mut self, irq: u32, irq_event: &Event) -> Result<()>205     fn unregister_irq_event(&mut self, irq: u32, irq_event: &Event) -> Result<()> {
206         let mut irq_events = self.irq_events.lock();
207         for (index, evt) in irq_events.iter().enumerate() {
208             if let Some(evt) = evt {
209                 if evt.gsi == irq && irq_event.eq(&evt.event) {
210                     irq_events[index] = None;
211                     break;
212                 }
213             }
214         }
215         Ok(())
216     }
217 }
218 
219 /// This IrqChip only works with Whpx so we only implement it for WhpxVcpu.
220 impl IrqChip for WhpxSplitIrqChip {
add_vcpu(&mut self, _vcpu_id: usize, _vcpu: &dyn Vcpu) -> Result<()>221     fn add_vcpu(&mut self, _vcpu_id: usize, _vcpu: &dyn Vcpu) -> Result<()> {
222         // The WHPX API acts entirely on the VM partition, so we don't need to keep references to
223         // the vcpus.
224         Ok(())
225     }
226 
register_edge_irq_event( &mut self, irq: u32, irq_event: &IrqEdgeEvent, source: IrqEventSource, ) -> Result<Option<IrqEventIndex>>227     fn register_edge_irq_event(
228         &mut self,
229         irq: u32,
230         irq_event: &IrqEdgeEvent,
231         source: IrqEventSource,
232     ) -> Result<Option<IrqEventIndex>> {
233         self.register_irq_event(irq, irq_event.get_trigger(), None, source)
234     }
235 
unregister_edge_irq_event(&mut self, irq: u32, irq_event: &IrqEdgeEvent) -> Result<()>236     fn unregister_edge_irq_event(&mut self, irq: u32, irq_event: &IrqEdgeEvent) -> Result<()> {
237         self.unregister_irq_event(irq, irq_event.get_trigger())
238     }
239 
register_level_irq_event( &mut self, irq: u32, irq_event: &IrqLevelEvent, source: IrqEventSource, ) -> Result<Option<IrqEventIndex>>240     fn register_level_irq_event(
241         &mut self,
242         irq: u32,
243         irq_event: &IrqLevelEvent,
244         source: IrqEventSource,
245     ) -> Result<Option<IrqEventIndex>> {
246         self.register_irq_event(
247             irq,
248             irq_event.get_trigger(),
249             Some(irq_event.get_resample()),
250             source,
251         )
252     }
253 
unregister_level_irq_event(&mut self, irq: u32, irq_event: &IrqLevelEvent) -> Result<()>254     fn unregister_level_irq_event(&mut self, irq: u32, irq_event: &IrqLevelEvent) -> Result<()> {
255         self.unregister_irq_event(irq, irq_event.get_trigger())
256     }
257 
route_irq(&mut self, route: IrqRoute) -> Result<()>258     fn route_irq(&mut self, route: IrqRoute) -> Result<()> {
259         self.routes.lock().add(route)
260     }
261 
set_irq_routes(&mut self, routes: &[IrqRoute]) -> Result<()>262     fn set_irq_routes(&mut self, routes: &[IrqRoute]) -> Result<()> {
263         self.routes.lock().replace_all(routes)
264     }
265 
irq_event_tokens(&self) -> Result<Vec<(IrqEventIndex, IrqEventSource, Event)>>266     fn irq_event_tokens(&self) -> Result<Vec<(IrqEventIndex, IrqEventSource, Event)>> {
267         let mut tokens: Vec<(IrqEventIndex, IrqEventSource, Event)> = Vec::new();
268         for (index, evt) in self.irq_events.lock().iter().enumerate() {
269             if let Some(evt) = evt {
270                 tokens.push((index, evt.source.clone(), evt.event.try_clone()?));
271             }
272         }
273         Ok(tokens)
274     }
275 
service_irq(&mut self, irq: u32, level: bool) -> Result<()>276     fn service_irq(&mut self, irq: u32, level: bool) -> Result<()> {
277         for route in self.routes.lock()[irq as usize].iter() {
278             match *route {
279                 IrqSource::Irqchip {
280                     chip: IrqSourceChip::PicPrimary,
281                     pin,
282                 }
283                 | IrqSource::Irqchip {
284                     chip: IrqSourceChip::PicSecondary,
285                     pin,
286                 } => {
287                     self.pic.lock().service_irq(pin as u8, level);
288                 }
289                 IrqSource::Irqchip {
290                     chip: IrqSourceChip::Ioapic,
291                     pin,
292                 } => {
293                     self.ioapic.lock().service_irq(pin as usize, level);
294                 }
295                 // service_irq's level parameter is ignored for MSIs.  MSI data specifies the level.
296                 IrqSource::Msi { address, data } => self.send_msi(address as u32, data)?,
297                 _ => {
298                     error!("Unexpected route source {:?}", route);
299                     return Err(Error::new(libc::EINVAL));
300                 }
301             }
302         }
303         Ok(())
304     }
305 
306     /// Services an IRQ event by asserting then deasserting an IRQ line.  The associated Event
307     /// that triggered the irq event will be read from.  If the irq is associated with a resample
308     /// Event, then the deassert will only happen after an EOI is broadcast for a vector
309     /// associated with the irq line.
310     /// For WhpxSplitIrqChip, this function identifies the destination(s) of the irq: PIC, IOAPIC,
311     /// or APIC (MSI).  If it's a PIC or IOAPIC route, we attempt to call service_irq on those
312     /// chips.  If the IOAPIC is unable to be immediately locked, we add the irq to the
313     /// delayed_ioapic_irq_events (though we still read from the Event that triggered the irq
314     /// event).  If it's an MSI route, we call send_msi to decode the MSI and send the interrupt
315     /// to WHPX.
service_irq_event(&mut self, event_index: IrqEventIndex) -> Result<()>316     fn service_irq_event(&mut self, event_index: IrqEventIndex) -> Result<()> {
317         let irq_events = self.irq_events.lock();
318         let evt = if let Some(evt) = &irq_events[event_index] {
319             evt
320         } else {
321             return Ok(());
322         };
323         evt.event.wait()?;
324 
325         for route in self.routes.lock()[evt.gsi as usize].iter() {
326             match *route {
327                 IrqSource::Irqchip {
328                     chip: IrqSourceChip::PicPrimary,
329                     pin,
330                 }
331                 | IrqSource::Irqchip {
332                     chip: IrqSourceChip::PicSecondary,
333                     pin,
334                 } => {
335                     let mut pic = self.pic.lock();
336                     if evt.resample_event.is_some() {
337                         pic.service_irq(pin as u8, true);
338                     } else {
339                         pic.service_irq(pin as u8, true);
340                         pic.service_irq(pin as u8, false);
341                     }
342                 }
343                 IrqSource::Irqchip {
344                     chip: IrqSourceChip::Ioapic,
345                     pin,
346                 } => {
347                     if let Ok(mut ioapic) = self.ioapic.try_lock() {
348                         if evt.resample_event.is_some() {
349                             ioapic.service_irq(pin as usize, true);
350                         } else {
351                             ioapic.service_irq(pin as usize, true);
352                             ioapic.service_irq(pin as usize, false);
353                         }
354                     } else {
355                         let mut delayed_events = self.delayed_ioapic_irq_events.lock();
356                         delayed_events.events.push(event_index);
357                         delayed_events.trigger.signal()?;
358                     }
359                 }
360                 IrqSource::Msi { address, data } => self.send_msi(address as u32, data)?,
361                 _ => {
362                     error!("Unexpected route source {:?}", route);
363                     return Err(Error::new(libc::EINVAL));
364                 }
365             }
366         }
367 
368         Ok(())
369     }
370 
371     /// Broadcasts an end of interrupt. For WhpxSplitIrqChip this sends the EOI to the Ioapic.
broadcast_eoi(&self, vector: u8) -> Result<()>372     fn broadcast_eoi(&self, vector: u8) -> Result<()> {
373         self.ioapic.lock().end_of_interrupt(vector);
374         Ok(())
375     }
376 
377     /// Injects any pending interrupts for `vcpu`.
378     /// For WhpxSplitIrqChip this injects any PIC interrupts on vcpu_id 0.
inject_interrupts(&self, vcpu: &dyn Vcpu) -> Result<()>379     fn inject_interrupts(&self, vcpu: &dyn Vcpu) -> Result<()> {
380         let vcpu: &WhpxVcpu = vcpu
381             .downcast_ref()
382             .expect("WhpxSplitIrqChip::add_vcpu called with non-WhpxVcpu");
383         let vcpu_id = vcpu.id();
384         if !self.interrupt_requested(vcpu_id) || !vcpu.ready_for_interrupt() {
385             return Ok(());
386         }
387 
388         if let Some(vector) = self.get_external_interrupt(vcpu_id)? {
389             vcpu.interrupt(vector)?;
390         }
391 
392         // The second interrupt request should be handled immediately, so ask vCPU to exit as soon
393         // as possible.
394         if self.interrupt_requested(vcpu_id) {
395             vcpu.set_interrupt_window_requested(true);
396         }
397         Ok(())
398     }
399 
400     /// Notifies the irq chip that the specified VCPU has executed a halt instruction.
401     /// For WhpxSplitIrqChip this is a no-op because Whpx handles VCPU blocking.
halted(&self, _vcpu_id: usize)402     fn halted(&self, _vcpu_id: usize) {}
403 
404     /// Blocks until `vcpu` is in a runnable state or until interrupted by
405     /// `IrqChip::kick_halted_vcpus`.  Returns `VcpuRunState::Runnable if vcpu is runnable, or
406     /// `VcpuRunState::Interrupted` if the wait was interrupted.
407     /// For WhpxSplitIrqChip this is a no-op and always returns Runnable because Whpx handles VCPU
408     /// blocking.
wait_until_runnable(&self, _vcpu: &dyn Vcpu) -> Result<VcpuRunState>409     fn wait_until_runnable(&self, _vcpu: &dyn Vcpu) -> Result<VcpuRunState> {
410         Ok(VcpuRunState::Runnable)
411     }
412 
413     /// Makes unrunnable VCPUs return immediately from `wait_until_runnable`.
414     /// For WhpxSplitIrqChip this is a no-op because Whpx handles VCPU blocking.
kick_halted_vcpus(&self)415     fn kick_halted_vcpus(&self) {}
416 
get_mp_state(&self, _vcpu_id: usize) -> Result<MPState>417     fn get_mp_state(&self, _vcpu_id: usize) -> Result<MPState> {
418         // WHPX does not seem to have an API for this, but luckily this API isn't used anywhere
419         // except the plugin.
420         Err(Error::new(libc::ENXIO))
421     }
422 
set_mp_state(&mut self, _vcpu_id: usize, _state: &MPState) -> Result<()>423     fn set_mp_state(&mut self, _vcpu_id: usize, _state: &MPState) -> Result<()> {
424         // WHPX does not seem to have an API for this, but luckily this API isn't used anywhere
425         // except the plugin.
426         Err(Error::new(libc::ENXIO))
427     }
428 
try_clone(&self) -> Result<Self> where Self: Sized,429     fn try_clone(&self) -> Result<Self>
430     where
431         Self: Sized,
432     {
433         Ok(WhpxSplitIrqChip {
434             vm: self.vm.try_clone()?,
435             routes: self.routes.clone(),
436             pit: self.pit.clone(),
437             pic: self.pic.clone(),
438             ioapic: self.ioapic.clone(),
439             ioapic_pins: self.ioapic_pins,
440             delayed_ioapic_irq_events: self.delayed_ioapic_irq_events.clone(),
441             irq_events: self.irq_events.clone(),
442         })
443     }
444 
finalize_devices( &mut self, resources: &mut SystemAllocator, io_bus: &Bus, mmio_bus: &Bus, ) -> Result<()>445     fn finalize_devices(
446         &mut self,
447         resources: &mut SystemAllocator,
448         io_bus: &Bus,
449         mmio_bus: &Bus,
450     ) -> Result<()> {
451         // Insert pit into io_bus
452         io_bus.insert(self.pit.clone(), 0x040, 0x8).unwrap();
453         io_bus.insert(self.pit.clone(), 0x061, 0x1).unwrap();
454 
455         // Insert pic into io_bus
456         io_bus.insert(self.pic.clone(), 0x20, 0x2).unwrap();
457         io_bus.insert(self.pic.clone(), 0xa0, 0x2).unwrap();
458         io_bus.insert(self.pic.clone(), 0x4d0, 0x2).unwrap();
459 
460         // Insert ioapic into mmio_bus
461         mmio_bus
462             .insert(
463                 self.ioapic.clone(),
464                 IOAPIC_BASE_ADDRESS,
465                 IOAPIC_MEM_LENGTH_BYTES,
466             )
467             .unwrap();
468 
469         // At this point, all of our devices have been created and they have registered their
470         // irq events, so we can clone our resample events
471         let mut ioapic_resample_events: Vec<Vec<Event>> =
472             (0..self.ioapic_pins).map(|_| Vec::new()).collect();
473         let mut pic_resample_events: Vec<Vec<Event>> =
474             (0..self.ioapic_pins).map(|_| Vec::new()).collect();
475 
476         for evt in self.irq_events.lock().iter().flatten() {
477             if (evt.gsi as usize) >= self.ioapic_pins {
478                 continue;
479             }
480             if let Some(resample_evt) = &evt.resample_event {
481                 ioapic_resample_events[evt.gsi as usize].push(resample_evt.try_clone()?);
482                 pic_resample_events[evt.gsi as usize].push(resample_evt.try_clone()?);
483             }
484         }
485 
486         // Register resample events with the ioapic
487         self.ioapic
488             .lock()
489             .register_resample_events(ioapic_resample_events);
490         // Register resample events with the pic
491         self.pic
492             .lock()
493             .register_resample_events(pic_resample_events);
494 
495         // Make sure all future irq numbers are >= self.ioapic_pins
496         let mut irq_num = resources.allocate_irq().unwrap();
497         while irq_num < self.ioapic_pins as u32 {
498             irq_num = resources.allocate_irq().unwrap();
499         }
500 
501         Ok(())
502     }
503 
process_delayed_irq_events(&mut self) -> Result<()>504     fn process_delayed_irq_events(&mut self) -> Result<()> {
505         let irq_events = self.irq_events.lock();
506         let mut delayed_events = self.delayed_ioapic_irq_events.lock();
507         delayed_events.events.retain(|&event_index| {
508             if let Some(evt) = &irq_events[event_index] {
509                 if let Ok(mut ioapic) = self.ioapic.try_lock() {
510                     if evt.resample_event.is_some() {
511                         ioapic.service_irq(evt.gsi as usize, true);
512                     } else {
513                         ioapic.service_irq(evt.gsi as usize, true);
514                         ioapic.service_irq(evt.gsi as usize, false);
515                     }
516 
517                     false
518                 } else {
519                     true
520                 }
521             } else {
522                 true
523             }
524         });
525 
526         if delayed_events.events.is_empty() {
527             delayed_events.trigger.wait()?;
528         }
529         Ok(())
530     }
531 
irq_delayed_event_token(&self) -> Result<Option<Event>>532     fn irq_delayed_event_token(&self) -> Result<Option<Event>> {
533         Ok(Some(
534             self.delayed_ioapic_irq_events.lock().trigger.try_clone()?,
535         ))
536     }
537 
check_capability(&self, c: IrqChipCap) -> bool538     fn check_capability(&self, c: IrqChipCap) -> bool {
539         match c {
540             // It appears as though WHPX does not have tsc deadline support because we get guest
541             // MSR write failures if we enable it.
542             IrqChipCap::TscDeadlineTimer => false,
543             // TODO(b/180966070): Figure out how to query x2apic support.
544             IrqChipCap::X2Apic => false,
545             IrqChipCap::MpStateGetSet => false,
546         }
547     }
548 }
549 
550 #[derive(Serialize, Deserialize)]
551 struct WhpxSplitIrqChipSnapshot {
552     routes: Vec<IrqRoute>,
553 }
554 
555 impl IrqChipX86_64 for WhpxSplitIrqChip {
try_box_clone(&self) -> Result<Box<dyn IrqChipX86_64>>556     fn try_box_clone(&self) -> Result<Box<dyn IrqChipX86_64>> {
557         Ok(Box::new(self.try_clone()?))
558     }
559 
as_irq_chip(&self) -> &dyn IrqChip560     fn as_irq_chip(&self) -> &dyn IrqChip {
561         self
562     }
563 
as_irq_chip_mut(&mut self) -> &mut dyn IrqChip564     fn as_irq_chip_mut(&mut self) -> &mut dyn IrqChip {
565         self
566     }
567 
568     /// Get the current state of the PIC
get_pic_state(&self, select: PicSelect) -> Result<PicState>569     fn get_pic_state(&self, select: PicSelect) -> Result<PicState> {
570         Ok(self.pic.lock().get_pic_state(select))
571     }
572 
573     /// Set the current state of the PIC
set_pic_state(&mut self, select: PicSelect, state: &PicState) -> Result<()>574     fn set_pic_state(&mut self, select: PicSelect, state: &PicState) -> Result<()> {
575         self.pic.lock().set_pic_state(select, state);
576         Ok(())
577     }
578 
579     /// Get the current state of the IOAPIC
get_ioapic_state(&self) -> Result<IoapicState>580     fn get_ioapic_state(&self) -> Result<IoapicState> {
581         Ok(self.ioapic.lock().get_ioapic_state())
582     }
583 
584     /// Set the current state of the IOAPIC
set_ioapic_state(&mut self, state: &IoapicState) -> Result<()>585     fn set_ioapic_state(&mut self, state: &IoapicState) -> Result<()> {
586         self.ioapic.lock().set_ioapic_state(state);
587         Ok(())
588     }
589 
590     /// Get the current state of the specified VCPU's local APIC
get_lapic_state(&self, vcpu_id: usize) -> Result<LapicState>591     fn get_lapic_state(&self, vcpu_id: usize) -> Result<LapicState> {
592         self.vm.get_vcpu_lapic_state(vcpu_id)
593     }
594 
595     /// Set the current state of the specified VCPU's local APIC
set_lapic_state(&mut self, vcpu_id: usize, state: &LapicState) -> Result<()>596     fn set_lapic_state(&mut self, vcpu_id: usize, state: &LapicState) -> Result<()> {
597         self.vm.set_vcpu_lapic_state(vcpu_id, state)
598     }
599 
lapic_frequency(&self) -> u32600     fn lapic_frequency(&self) -> u32 {
601         WHPX_LOCAL_APIC_EMULATION_APIC_FREQUENCY
602     }
603 
604     /// Retrieves the state of the PIT.
get_pit(&self) -> Result<PitState>605     fn get_pit(&self) -> Result<PitState> {
606         Ok(self.pit.lock().get_pit_state())
607     }
608 
609     /// Sets the state of the PIT.
set_pit(&mut self, state: &PitState) -> Result<()>610     fn set_pit(&mut self, state: &PitState) -> Result<()> {
611         self.pit.lock().set_pit_state(state);
612         Ok(())
613     }
614 
615     /// Returns true if the PIT uses port 0x61 for the PC speaker, false if 0x61 is unused.
616     /// devices::Pit uses 0x61.
pit_uses_speaker_port(&self) -> bool617     fn pit_uses_speaker_port(&self) -> bool {
618         true
619     }
620 
snapshot_chip_specific(&self) -> anyhow::Result<serde_json::Value>621     fn snapshot_chip_specific(&self) -> anyhow::Result<serde_json::Value> {
622         serde_json::to_value(&WhpxSplitIrqChipSnapshot {
623             routes: self.routes.lock().get_routes(),
624         })
625         .context("failed to snapshot WhpxSplitIrqChip")
626     }
627 
restore_chip_specific(&mut self, data: serde_json::Value) -> anyhow::Result<()>628     fn restore_chip_specific(&mut self, data: serde_json::Value) -> anyhow::Result<()> {
629         let mut deser: WhpxSplitIrqChipSnapshot =
630             serde_json::from_value(data).context("failed to deserialize WhpxSplitIrqChip")?;
631         self.set_irq_routes(deser.routes.as_slice())?;
632         Ok(())
633     }
634 }
635