1use alloc::{boxed::Box, string::String};
8use core::marker::PhantomData;
9
10use axdevice_base::{AccessWidth, BusAccess, BusKind, BusResponse, Device, DeviceError, Resource};
11use x86_vlapic::{
12 EmulatedIoApic, EmulatedPit, EmulatedSerialPort, IoApicEoi, IoApicInterrupt, X86AccessWidth,
13 X86GuestPhysAddr, X86GuestPhysAddrRange, X86Port, X86PortRange, X86VlapicHostOps,
14};
15
16use crate::{ServiceCardinality, ServiceKey};
17
18pub trait X86IoApicDeviceOps: Send + Sync {
20 fn vector_for_gsi(&self, gsi: usize) -> Option<u8>;
22
23 fn assert_gsi(&self, gsi: usize) -> Option<IoApicInterrupt>;
25
26 fn end_of_interrupt(&self, vector: u8) -> Option<IoApicEoi>;
28}
29
30pub trait X86PitDeviceOps: Send + Sync {
32 fn consume_irq0_if_due(&self, now_ns: u64) -> bool;
34}
35
36pub trait X86SerialDeviceOps: Send + Sync {
38 fn poll_irq(&self) -> bool;
40}
41
42pub trait X86InterruptDomainOps: Send + Sync {
48 fn vector_for_gsi(&self, gsi: usize) -> Option<u8>;
50
51 fn assert_gsi(&self, gsi: usize) -> Option<IoApicInterrupt>;
53
54 fn end_of_interrupt(&self, vector: u8) -> Option<IoApicEoi>;
56}
57
58pub struct X86IoApicServiceKey;
60
61impl ServiceKey for X86IoApicServiceKey {
62 type Service = dyn X86IoApicDeviceOps;
63
64 const NAME: &'static str = "x86-ioapic";
65 const CARDINALITY: ServiceCardinality = ServiceCardinality::Single;
66}
67
68pub struct X86InterruptDomainKey;
70
71impl ServiceKey for X86InterruptDomainKey {
72 type Service = dyn X86InterruptDomainOps;
73
74 const NAME: &'static str = "x86-interrupt-domain";
75 const CARDINALITY: ServiceCardinality = ServiceCardinality::Single;
76}
77
78pub struct X86PitServiceKey;
80
81impl ServiceKey for X86PitServiceKey {
82 type Service = dyn X86PitDeviceOps;
83
84 const NAME: &'static str = "x86-pit";
85 const CARDINALITY: ServiceCardinality = ServiceCardinality::Single;
86}
87
88pub struct X86SerialServiceKey;
90
91impl ServiceKey for X86SerialServiceKey {
92 type Service = dyn X86SerialDeviceOps;
93
94 const NAME: &'static str = "x86-serial-com1";
95 const CARDINALITY: ServiceCardinality = ServiceCardinality::Single;
96}
97
98pub struct X86IoApicDevice {
100 inner: EmulatedIoApic,
101 name: String,
102 resources: Box<[Resource]>,
103}
104
105impl X86IoApicDevice {
106 pub fn new(base: X86GuestPhysAddr, size: Option<usize>) -> Self {
108 let inner = EmulatedIoApic::new(base, size);
109 let resources = mmio_resources(inner.address_range());
110 Self {
111 inner,
112 name: String::from("x86-ioapic"),
113 resources,
114 }
115 }
116
117 pub const fn inner(&self) -> &EmulatedIoApic {
119 &self.inner
120 }
121}
122
123impl X86IoApicDeviceOps for X86IoApicDevice {
124 fn vector_for_gsi(&self, gsi: usize) -> Option<u8> {
125 self.inner.vector_for_gsi(gsi)
126 }
127
128 fn assert_gsi(&self, gsi: usize) -> Option<IoApicInterrupt> {
129 self.inner.assert_gsi(gsi)
130 }
131
132 fn end_of_interrupt(&self, vector: u8) -> Option<IoApicEoi> {
133 self.inner.end_of_interrupt(vector)
134 }
135}
136
137impl Device for X86IoApicDevice {
138 fn name(&self) -> &str {
139 &self.name
140 }
141
142 fn resources(&self) -> &[Resource] {
143 &self.resources
144 }
145
146 fn access(
147 &self,
148 access: &BusAccess,
149 _context: &mut dyn axdevice_base::DeviceAccess,
150 ) -> Result<BusResponse, DeviceError> {
151 if access.kind != BusKind::Mmio {
152 return Err(DeviceError::OutOfRange { addr: access.addr });
153 }
154 let addr = X86GuestPhysAddr::from_usize(access.addr as usize);
155 let width = x86_access_width(access.width);
156 if access.is_read {
157 self.inner
158 .handle_read(addr, width)
159 .map(|value| BusResponse::Read {
160 value: value as u64,
161 })
162 .map_err(|_| DeviceError::Internal)
163 } else {
164 self.inner
165 .handle_write(addr, width, access.data as usize)
166 .map(|_| BusResponse::Write)
167 .map_err(|_| DeviceError::Internal)
168 }
169 }
170}
171
172pub struct X86PitDevice<H: X86VlapicHostOps> {
174 inner: EmulatedPit<H>,
175 name: String,
176 resources: Box<[Resource]>,
177 _host: PhantomData<fn() -> H>,
178}
179
180impl<H: X86VlapicHostOps> X86PitDevice<H> {
181 pub fn new() -> Self {
183 let inner = EmulatedPit::<H>::new();
184 let resources = port_resources(inner.address_range());
185 Self {
186 inner,
187 name: String::from("x86-pit"),
188 resources,
189 _host: PhantomData,
190 }
191 }
192
193 pub const fn inner(&self) -> &EmulatedPit<H> {
195 &self.inner
196 }
197}
198
199impl<H: X86VlapicHostOps> Default for X86PitDevice<H> {
200 fn default() -> Self {
201 Self::new()
202 }
203}
204
205impl<H: X86VlapicHostOps> X86PitDeviceOps for X86PitDevice<H> {
206 fn consume_irq0_if_due(&self, now_ns: u64) -> bool {
207 self.inner.consume_irq0_if_due(now_ns)
208 }
209}
210
211impl<H: X86VlapicHostOps + 'static> Device for X86PitDevice<H> {
212 fn name(&self) -> &str {
213 &self.name
214 }
215
216 fn resources(&self) -> &[Resource] {
217 &self.resources
218 }
219
220 fn access(
221 &self,
222 access: &BusAccess,
223 _context: &mut dyn axdevice_base::DeviceAccess,
224 ) -> Result<BusResponse, DeviceError> {
225 if access.kind != BusKind::Port {
226 return Err(DeviceError::OutOfRange { addr: access.addr });
227 }
228 let port = X86Port::new(
229 u16::try_from(access.addr)
230 .map_err(|_| DeviceError::OutOfRange { addr: access.addr })?,
231 );
232 let width = x86_access_width(access.width);
233 if access.is_read {
234 self.inner
235 .handle_read(port, width)
236 .map(|value| BusResponse::Read {
237 value: value as u64,
238 })
239 .map_err(|_| DeviceError::Internal)
240 } else {
241 self.inner
242 .handle_write(port, width, access.data as usize)
243 .map(|_| BusResponse::Write)
244 .map_err(|_| DeviceError::Internal)
245 }
246 }
247}
248
249pub struct X86SerialPortDevice<H: X86VlapicHostOps> {
251 inner: EmulatedSerialPort<H>,
252 name: String,
253 resources: Box<[Resource]>,
254 _host: PhantomData<fn() -> H>,
255}
256
257impl<H: X86VlapicHostOps> X86SerialPortDevice<H> {
258 pub fn new() -> Self {
260 let inner = EmulatedSerialPort::<H>::new();
261 let resources = port_resources(inner.address_range());
262 Self {
263 inner,
264 name: String::from("x86-serial-com1"),
265 resources,
266 _host: PhantomData,
267 }
268 }
269
270 pub const fn inner(&self) -> &EmulatedSerialPort<H> {
272 &self.inner
273 }
274}
275
276impl<H: X86VlapicHostOps> Default for X86SerialPortDevice<H> {
277 fn default() -> Self {
278 Self::new()
279 }
280}
281
282impl<H: X86VlapicHostOps> X86SerialDeviceOps for X86SerialPortDevice<H> {
283 fn poll_irq(&self) -> bool {
284 self.inner.poll_irq()
285 }
286}
287
288impl<H: X86VlapicHostOps + 'static> Device for X86SerialPortDevice<H> {
289 fn name(&self) -> &str {
290 &self.name
291 }
292
293 fn resources(&self) -> &[Resource] {
294 &self.resources
295 }
296
297 fn access(
298 &self,
299 access: &BusAccess,
300 _context: &mut dyn axdevice_base::DeviceAccess,
301 ) -> Result<BusResponse, DeviceError> {
302 if access.kind != BusKind::Port {
303 return Err(DeviceError::OutOfRange { addr: access.addr });
304 }
305 let port = X86Port::new(
306 u16::try_from(access.addr)
307 .map_err(|_| DeviceError::OutOfRange { addr: access.addr })?,
308 );
309 let width = x86_access_width(access.width);
310 if access.is_read {
311 self.inner
312 .handle_read(port, width)
313 .map(|value| BusResponse::Read {
314 value: value as u64,
315 })
316 .map_err(|_| DeviceError::Internal)
317 } else {
318 self.inner
319 .handle_write(port, width, access.data as usize)
320 .map(|_| BusResponse::Write)
321 .map_err(|_| DeviceError::Internal)
322 }
323 }
324}
325
326fn x86_access_width(width: AccessWidth) -> X86AccessWidth {
327 match width {
328 AccessWidth::Byte => X86AccessWidth::Byte,
329 AccessWidth::Word => X86AccessWidth::Word,
330 AccessWidth::Dword => X86AccessWidth::Dword,
331 AccessWidth::Qword => X86AccessWidth::Qword,
332 }
333}
334
335fn mmio_resources(range: X86GuestPhysAddrRange) -> Box<[Resource]> {
336 let base = range.start.as_usize() as u64;
337 let size = range.end.as_usize().saturating_sub(range.start.as_usize()) as u64;
338 alloc::vec![Resource::MmioRange { base, size }].into_boxed_slice()
339}
340
341fn port_resources(range: X86PortRange) -> Box<[Resource]> {
342 let base = range.start.number();
343 let size = range
344 .end
345 .number()
346 .saturating_sub(range.start.number())
347 .saturating_add(1);
348 alloc::vec![Resource::PortRange { base, size }].into_boxed_slice()
349}