1pub use x86defs::apic::DeliveryMode;
7
8use hvdef::HvInterruptType;
9use hvdef::Vtl;
10use inspect::Inspect;
11use parking_lot::Mutex;
12use std::fmt::Debug;
13use std::sync::Arc;
14use vm_topology::processor::VpIndex;
15use vmcore::line_interrupt::LineSetTarget;
16use x86defs::msi::MsiAddress;
17use x86defs::msi::MsiData;
18
19pub trait IoApicRouting: Send + Sync {
26 fn set_irq_route(&self, irq: u8, request: Option<MsiRequest>);
28
29 fn assert_irq(&self, irq: u8);
31}
32
33pub trait ControlGic: Send + Sync {
35 fn set_spi_irq(&self, irq_id: u32, high: bool);
37}
38
39pub const IRQ_LINES: usize = 24;
41
42#[derive(Debug, Copy, Clone, Inspect)]
44pub struct MsiRequest {
45 #[inspect(hex)]
47 pub address: u64,
48 #[inspect(hex)]
50 pub data: u32,
51}
52
53impl MsiRequest {
54 pub fn new_x86(
56 mode: DeliveryMode,
57 destination: u32,
58 is_logical_destination: bool,
59 vector: u8,
60 is_level_triggered: bool,
61 ) -> Self {
62 let address = MsiAddress::new()
63 .with_address(x86defs::msi::MSI_ADDRESS)
64 .with_redirection_hint(mode == DeliveryMode::LOWEST_PRIORITY)
65 .with_virt_destination(destination as u16)
66 .with_destination_mode_logical(is_logical_destination);
67
68 let data = MsiData::new()
69 .with_vector(vector)
70 .with_delivery_mode(mode.0 & 0x7)
71 .with_assert(is_level_triggered)
72 .with_trigger_mode_level(is_level_triggered);
73
74 Self {
75 address: u32::from(address).into(),
76 data: data.into(),
77 }
78 }
79
80 pub fn as_x86(&self) -> (MsiAddress, MsiData) {
82 (
83 MsiAddress::from(self.address as u32),
84 MsiData::from(self.data),
85 )
86 }
87
88 pub fn hv_x86_interrupt_control(&self) -> hvdef::HvInterruptControl {
94 let (address, data) = self.as_x86();
95 let ty = match DeliveryMode(data.delivery_mode()) {
96 DeliveryMode::FIXED => HvInterruptType::HvX64InterruptTypeFixed,
97 DeliveryMode::LOWEST_PRIORITY => HvInterruptType::HvX64InterruptTypeLowestPriority,
98 DeliveryMode::SMI => HvInterruptType::HvX64InterruptTypeSmi,
99 DeliveryMode::REMOTE_READ => HvInterruptType::HvX64InterruptTypeRemoteRead,
100 DeliveryMode::NMI => HvInterruptType::HvX64InterruptTypeNmi,
101 DeliveryMode::INIT => HvInterruptType::HvX64InterruptTypeInit,
102 DeliveryMode::SIPI => HvInterruptType::HvX64InterruptTypeSipi,
103 _ => HvInterruptType(!0),
109 };
110 hvdef::HvInterruptControl::new()
111 .with_interrupt_type(ty)
112 .with_x86_level_triggered(data.trigger_mode_level())
113 .with_x86_logical_destination_mode(address.destination_mode_logical())
114 }
115}
116
117#[derive(Debug, Inspect)]
122pub struct IrqRoutes {
123 #[inspect(
124 with = "|x| inspect::adhoc(|req| inspect::iter_by_index(x.lock().iter()).inspect(req))"
125 )]
126 routes: Mutex<Vec<Option<MsiRequest>>>,
127}
128
129impl Default for IrqRoutes {
130 fn default() -> Self {
131 Self::new()
132 }
133}
134
135impl IrqRoutes {
136 pub fn new() -> Self {
137 let routes = vec![None; IRQ_LINES];
138 Self {
139 routes: Mutex::new(routes),
140 }
141 }
142
143 pub fn set_irq_route(&self, irq: u8, request: Option<MsiRequest>) {
145 self.routes.lock()[irq as usize] = request;
146 }
147
148 pub fn assert_irq(&self, irq: u8, assert: impl FnOnce(MsiRequest)) {
152 let request = self.routes.lock()[irq as usize];
153 match request {
154 Some(request) => {
155 assert(request);
156 }
157 None => {
158 tracelimit::warn_ratelimited!(irq, "irq for masked interrupt");
159 }
160 }
161 }
162}
163
164pub struct ApicLintLineTarget<T> {
166 partition: Arc<T>,
167 vtl: Vtl,
168}
169
170impl<T: crate::X86Partition> ApicLintLineTarget<T> {
171 pub fn new(partition: Arc<T>, vtl: Vtl) -> Self {
173 Self { partition, vtl }
174 }
175}
176
177impl<T: crate::X86Partition> LineSetTarget for ApicLintLineTarget<T> {
178 fn set_irq(&self, vector: u32, high: bool) {
179 if !high {
180 return;
181 }
182 let vp_index = VpIndex::new(vector / 2);
183 let lint = vector % 2;
184 self.partition.pulse_lint(vp_index, self.vtl, lint as u8);
185 }
186}