1use alloc::{boxed::Box, string::String};
4use core::{any::Any, marker::PhantomData};
5
6use axdevice_base::{AccessWidth, BusAccess, BusKind, BusResponse, Device, DeviceError, Resource};
7use x86_vlapic::{
8 EmulatedIoApic, EmulatedPit, EmulatedSerialPort, IoApicEoi, IoApicInterrupt, X86AccessWidth,
9 X86GuestPhysAddr, X86GuestPhysAddrRange, X86Port, X86PortRange, X86VlapicHostOps,
10};
11
12pub trait X86IoApicDeviceOps: Send + Sync {
14 fn vector_for_gsi(&self, gsi: usize) -> Option<u8>;
16
17 fn assert_gsi(&self, gsi: usize) -> Option<IoApicInterrupt>;
19
20 fn end_of_interrupt(&self, vector: u8) -> Option<IoApicEoi>;
22}
23
24pub trait X86PitDeviceOps: Send + Sync {
26 fn consume_irq0_if_due(&self, now_ns: u64) -> bool;
28}
29
30pub trait X86SerialDeviceOps: Send + Sync {
32 fn poll_irq(&self) -> bool;
34}
35
36pub struct X86IoApicDevice {
38 inner: EmulatedIoApic,
39 name: String,
40 resources: Box<[Resource]>,
41}
42
43impl X86IoApicDevice {
44 pub fn new(base: X86GuestPhysAddr, size: Option<usize>) -> Self {
46 let inner = EmulatedIoApic::new(base, size);
47 let resources = mmio_resources(inner.address_range());
48 Self {
49 inner,
50 name: String::from("x86-ioapic"),
51 resources,
52 }
53 }
54
55 pub const fn inner(&self) -> &EmulatedIoApic {
57 &self.inner
58 }
59}
60
61impl X86IoApicDeviceOps for X86IoApicDevice {
62 fn vector_for_gsi(&self, gsi: usize) -> Option<u8> {
63 self.inner.vector_for_gsi(gsi)
64 }
65
66 fn assert_gsi(&self, gsi: usize) -> Option<IoApicInterrupt> {
67 self.inner.assert_gsi(gsi)
68 }
69
70 fn end_of_interrupt(&self, vector: u8) -> Option<IoApicEoi> {
71 self.inner.end_of_interrupt(vector)
72 }
73}
74
75impl Device for X86IoApicDevice {
76 fn name(&self) -> &str {
77 &self.name
78 }
79
80 fn resources(&self) -> &[Resource] {
81 &self.resources
82 }
83
84 fn handle(&self, access: &BusAccess) -> Result<BusResponse, DeviceError> {
85 if access.kind != BusKind::Mmio {
86 return Err(DeviceError::OutOfRange { addr: access.addr });
87 }
88 let addr = X86GuestPhysAddr::from_usize(access.addr as usize);
89 let width = x86_access_width(access.width);
90 if access.is_read {
91 self.inner
92 .handle_read(addr, width)
93 .map(|value| BusResponse::Read {
94 value: value as u64,
95 })
96 .map_err(|_| DeviceError::Internal)
97 } else {
98 self.inner
99 .handle_write(addr, width, access.data as usize)
100 .map(|_| BusResponse::Write)
101 .map_err(|_| DeviceError::Internal)
102 }
103 }
104
105 fn as_any(&self) -> &dyn Any {
106 self
107 }
108}
109
110pub struct X86PitDevice<H: X86VlapicHostOps> {
112 inner: EmulatedPit<H>,
113 name: String,
114 resources: Box<[Resource]>,
115 _host: PhantomData<fn() -> H>,
116}
117
118impl<H: X86VlapicHostOps> X86PitDevice<H> {
119 pub fn new() -> Self {
121 let inner = EmulatedPit::<H>::new();
122 let resources = port_resources(inner.address_range());
123 Self {
124 inner,
125 name: String::from("x86-pit"),
126 resources,
127 _host: PhantomData,
128 }
129 }
130
131 pub const fn inner(&self) -> &EmulatedPit<H> {
133 &self.inner
134 }
135}
136
137impl<H: X86VlapicHostOps> Default for X86PitDevice<H> {
138 fn default() -> Self {
139 Self::new()
140 }
141}
142
143impl<H: X86VlapicHostOps> X86PitDeviceOps for X86PitDevice<H> {
144 fn consume_irq0_if_due(&self, now_ns: u64) -> bool {
145 self.inner.consume_irq0_if_due(now_ns)
146 }
147}
148
149impl<H: X86VlapicHostOps + 'static> Device for X86PitDevice<H> {
150 fn name(&self) -> &str {
151 &self.name
152 }
153
154 fn resources(&self) -> &[Resource] {
155 &self.resources
156 }
157
158 fn handle(&self, access: &BusAccess) -> Result<BusResponse, DeviceError> {
159 if access.kind != BusKind::Port {
160 return Err(DeviceError::OutOfRange { addr: access.addr });
161 }
162 let port = X86Port::new(
163 u16::try_from(access.addr)
164 .map_err(|_| DeviceError::OutOfRange { addr: access.addr })?,
165 );
166 let width = x86_access_width(access.width);
167 if access.is_read {
168 self.inner
169 .handle_read(port, width)
170 .map(|value| BusResponse::Read {
171 value: value as u64,
172 })
173 .map_err(|_| DeviceError::Internal)
174 } else {
175 self.inner
176 .handle_write(port, width, access.data as usize)
177 .map(|_| BusResponse::Write)
178 .map_err(|_| DeviceError::Internal)
179 }
180 }
181
182 fn as_any(&self) -> &dyn Any {
183 self
184 }
185}
186
187pub struct X86SerialPortDevice<H: X86VlapicHostOps> {
189 inner: EmulatedSerialPort<H>,
190 name: String,
191 resources: Box<[Resource]>,
192 _host: PhantomData<fn() -> H>,
193}
194
195impl<H: X86VlapicHostOps> X86SerialPortDevice<H> {
196 pub fn new() -> Self {
198 let inner = EmulatedSerialPort::<H>::new();
199 let resources = port_resources(inner.address_range());
200 Self {
201 inner,
202 name: String::from("x86-serial-com1"),
203 resources,
204 _host: PhantomData,
205 }
206 }
207
208 pub const fn inner(&self) -> &EmulatedSerialPort<H> {
210 &self.inner
211 }
212}
213
214impl<H: X86VlapicHostOps> Default for X86SerialPortDevice<H> {
215 fn default() -> Self {
216 Self::new()
217 }
218}
219
220impl<H: X86VlapicHostOps> X86SerialDeviceOps for X86SerialPortDevice<H> {
221 fn poll_irq(&self) -> bool {
222 self.inner.poll_irq()
223 }
224}
225
226impl<H: X86VlapicHostOps + 'static> Device for X86SerialPortDevice<H> {
227 fn name(&self) -> &str {
228 &self.name
229 }
230
231 fn resources(&self) -> &[Resource] {
232 &self.resources
233 }
234
235 fn handle(&self, access: &BusAccess) -> Result<BusResponse, DeviceError> {
236 if access.kind != BusKind::Port {
237 return Err(DeviceError::OutOfRange { addr: access.addr });
238 }
239 let port = X86Port::new(
240 u16::try_from(access.addr)
241 .map_err(|_| DeviceError::OutOfRange { addr: access.addr })?,
242 );
243 let width = x86_access_width(access.width);
244 if access.is_read {
245 self.inner
246 .handle_read(port, width)
247 .map(|value| BusResponse::Read {
248 value: value as u64,
249 })
250 .map_err(|_| DeviceError::Internal)
251 } else {
252 self.inner
253 .handle_write(port, width, access.data as usize)
254 .map(|_| BusResponse::Write)
255 .map_err(|_| DeviceError::Internal)
256 }
257 }
258
259 fn as_any(&self) -> &dyn Any {
260 self
261 }
262}
263
264fn x86_access_width(width: AccessWidth) -> X86AccessWidth {
265 match width {
266 AccessWidth::Byte => X86AccessWidth::Byte,
267 AccessWidth::Word => X86AccessWidth::Word,
268 AccessWidth::Dword => X86AccessWidth::Dword,
269 AccessWidth::Qword => X86AccessWidth::Qword,
270 }
271}
272
273fn mmio_resources(range: X86GuestPhysAddrRange) -> Box<[Resource]> {
274 let base = range.start.as_usize() as u64;
275 let size = range.end.as_usize().saturating_sub(range.start.as_usize()) as u64;
276 alloc::vec![Resource::MmioRange { base, size }].into_boxed_slice()
277}
278
279fn port_resources(range: X86PortRange) -> Box<[Resource]> {
280 let base = range.start.number();
281 let size = range
282 .end
283 .number()
284 .saturating_sub(range.start.number())
285 .saturating_add(1);
286 alloc::vec![Resource::PortRange { base, size }].into_boxed_slice()
287}