1pub use x86defs::apic::DeliveryMode;
7
8use hvdef::HvInterruptType;
9use inspect::Inspect;
10use parking_lot::Mutex;
11use std::fmt::Debug;
12use x86defs::msi::MsiAddress;
13use x86defs::msi::MsiData;
14
15pub trait IoApicRouting: Send + Sync {
25 fn set_irq_route(&self, irq: u8, request: Option<MsiRequest>);
27
28 fn assert_irq(&self, irq: u8);
30}
31
32pub trait ControlGic: Send + Sync {
34 fn set_spi_irq(&self, irq_id: u32, high: bool);
36}
37
38pub const IRQ_LINES: usize = 24;
40
41#[derive(Debug, Copy, Clone, PartialEq, Eq, Inspect)]
43pub struct MsiRequest {
44 #[inspect(hex)]
46 pub address: u64,
47 #[inspect(hex)]
49 pub data: u32,
50}
51
52impl MsiRequest {
53 pub fn new_x86(
55 mode: DeliveryMode,
56 destination: u32,
57 is_logical_destination: bool,
58 vector: u8,
59 is_level_triggered: bool,
60 ) -> Self {
61 let address = MsiAddress::new()
62 .with_address(x86defs::msi::MSI_ADDRESS)
63 .with_redirection_hint(mode == DeliveryMode::LOWEST_PRIORITY)
64 .with_virt_destination(destination as u16)
65 .with_destination_mode_logical(is_logical_destination);
66
67 let data = MsiData::new()
68 .with_vector(vector)
69 .with_delivery_mode(mode.0 & 0x7)
70 .with_assert(is_level_triggered)
71 .with_trigger_mode_level(is_level_triggered);
72
73 Self {
74 address: u32::from(address).into(),
75 data: data.into(),
76 }
77 }
78
79 pub fn as_x86(&self) -> (MsiAddress, MsiData) {
81 (
82 MsiAddress::from(self.address as u32),
83 MsiData::from(self.data),
84 )
85 }
86
87 pub fn hv_x86_interrupt_control(&self) -> hvdef::HvInterruptControl {
93 let (address, data) = self.as_x86();
94 let ty = match DeliveryMode(data.delivery_mode()) {
95 DeliveryMode::FIXED => HvInterruptType::HvX64InterruptTypeFixed,
96 DeliveryMode::LOWEST_PRIORITY => HvInterruptType::HvX64InterruptTypeLowestPriority,
97 DeliveryMode::SMI => HvInterruptType::HvX64InterruptTypeSmi,
98 DeliveryMode::REMOTE_READ => HvInterruptType::HvX64InterruptTypeRemoteRead,
99 DeliveryMode::NMI => HvInterruptType::HvX64InterruptTypeNmi,
100 DeliveryMode::INIT => HvInterruptType::HvX64InterruptTypeInit,
101 DeliveryMode::SIPI => HvInterruptType::HvX64InterruptTypeSipi,
102 _ => HvInterruptType(!0),
108 };
109 hvdef::HvInterruptControl::new()
110 .with_interrupt_type(ty)
111 .with_x86_level_triggered(data.trigger_mode_level())
112 .with_x86_logical_destination_mode(address.destination_mode_logical())
113 }
114}
115
116#[derive(Debug, Inspect)]
121pub struct IrqRoutes {
122 #[inspect(
123 with = "|x| inspect::adhoc(|req| inspect::iter_by_index(x.lock().iter()).inspect(req))"
124 )]
125 routes: Mutex<Vec<Option<MsiRequest>>>,
126}
127
128impl Default for IrqRoutes {
129 fn default() -> Self {
130 Self::new()
131 }
132}
133
134impl IrqRoutes {
135 pub fn new() -> Self {
136 let routes = vec![None; IRQ_LINES];
137 Self {
138 routes: Mutex::new(routes),
139 }
140 }
141
142 pub fn set_irq_route(&self, irq: u8, request: Option<MsiRequest>) {
144 self.routes.lock()[irq as usize] = request;
145 }
146
147 pub fn assert_irq(&self, irq: u8, assert: impl FnOnce(MsiRequest)) {
151 let request = self.routes.lock()[irq as usize];
152 match request {
153 Some(request) => {
154 assert(request);
155 }
156 None => {
157 tracelimit::warn_ratelimited!(irq, "irq for masked interrupt");
158 }
159 }
160 }
161}