1pub mod backing;
7mod builder;
8mod io_ranges;
9mod line_sets;
10
11pub use self::builder::ChipsetBuilder;
12pub use self::builder::ChipsetDevices;
13
14use self::io_ranges::IoRanges;
15use self::io_ranges::LookupResult;
16use crate::DebugEventHandler;
17use chipset_device::ChipsetDevice;
18use chipset_device::io::IoError;
19use chipset_device::io::IoResult;
20use closeable_mutex::CloseableMutex;
21use inspect::Inspect;
22use std::future::poll_fn;
23use std::sync::Arc;
24
25#[derive(Inspect)]
28pub struct Chipset {
29 #[inspect(rename = "mmio")]
30 mmio_ranges: IoRanges<u64>,
31 #[inspect(rename = "pio")]
32 pio_ranges: IoRanges<u16>,
33
34 #[inspect(rename = "has_pic", with = "Option::is_some")]
35 pic: Option<Arc<CloseableMutex<dyn ChipsetDevice>>>,
36 #[inspect(rename = "has_eoi_handler", with = "Option::is_some")]
37 eoi_handler: Option<Arc<CloseableMutex<dyn ChipsetDevice>>>,
38
39 #[inspect(skip)]
40 debug_event_handler: Arc<dyn DebugEventHandler>,
41}
42
43enum IoType<'a> {
44 Read(&'a mut [u8]),
45 Write(&'a [u8]),
46}
47
48#[derive(Debug)]
49enum IoKind {
50 Pio,
51 Mmio,
52}
53
54impl IoType<'_> {
55 fn bytes(&self) -> &[u8] {
56 match self {
57 IoType::Read(b) => b,
58 IoType::Write(b) => b,
59 }
60 }
61}
62
63impl Chipset {
64 async fn handle_io_result(
65 &self,
66 lookup: LookupResult,
67 vp: u32,
68 kind: IoKind,
69 address: u64,
70 len: usize,
71 mut io_type: IoType<'_>,
72 result: IoResult,
73 ) {
74 if lookup.debug_break {
75 tracing::warn!(
76 device = &*lookup.dev_name,
77 address,
78 len,
79 ?kind,
80 "debug break due to io"
81 );
82 self.debug_event_handler.on_debug_break(Some(vp));
83 }
84 let r = match result {
85 IoResult::Ok => Ok(()),
86 IoResult::Defer(mut token) => match &mut io_type {
87 IoType::Read(bytes) => {
88 let r = poll_fn(|cx| token.poll_read(cx, bytes)).await;
89 if r.is_err() {
90 bytes.fill(!0);
91 }
92 r
93 }
94 IoType::Write(_) => poll_fn(|cx| token.poll_write(cx)).await,
95 },
96 IoResult::Err(err) => {
97 let error = match err {
98 IoError::InvalidRegister => "register not present",
99 IoError::InvalidAccessSize => "invalid access size",
100 IoError::UnalignedAccess => "unaligned access",
101 };
102 match &mut io_type {
103 IoType::Read(bytes) => {
104 bytes.fill(!0);
106 tracelimit::warn_ratelimited!(
107 device = &*lookup.dev_name,
108 address,
109 len,
110 ?kind,
111 error,
112 "device io read error"
113 );
114 }
115 IoType::Write(bytes) => tracelimit::warn_ratelimited!(
116 device = &*lookup.dev_name,
117 address,
118 len,
119 ?kind,
120 error,
121 ?bytes,
122 "device io write error"
123 ),
124 }
125 Ok(())
126 }
127 };
128
129 match r {
130 Ok(()) => {
131 if let Some(range_name) = &lookup.trace {
132 tracing::info!(
135 device = &*lookup.dev_name,
136 range_name = range_name.as_ref(),
137 ?kind,
138 address,
139 direction = if matches!(io_type, IoType::Read(_)) {
140 "read"
141 } else {
142 "write"
143 },
144 data = format_args!("{:02x?}", io_type.bytes()),
145 "device io"
146 );
147 }
148 }
149 Err(err) => {
150 tracing::error!(
151 device = &*lookup.dev_name,
152 ?kind,
153 address,
154 direction = if matches!(io_type, IoType::Read(_)) {
155 "read"
156 } else {
157 "write"
158 },
159 error = &err as &dyn std::error::Error,
160 "deferred io failed"
161 );
162 }
163 }
164 }
165
166 pub async fn mmio_read(&self, vp: u32, address: u64, data: &mut [u8]) {
168 let lookup = self.mmio_ranges.lookup(address, true);
169 let r = lookup
170 .dev
171 .lock()
172 .supports_mmio()
173 .expect("objects on the mmio bus support mmio")
174 .mmio_read(address, data);
175
176 self.handle_io_result(
177 lookup,
178 vp,
179 IoKind::Mmio,
180 address,
181 data.len(),
182 IoType::Read(data),
183 r,
184 )
185 .await
186 }
187
188 pub async fn mmio_write(&self, vp: u32, address: u64, data: &[u8]) {
190 let lookup = self.mmio_ranges.lookup(address, false);
191 let r = lookup
192 .dev
193 .lock()
194 .supports_mmio()
195 .expect("objects on the mmio bus support mmio")
196 .mmio_write(address, data);
197
198 self.handle_io_result(
199 lookup,
200 vp,
201 IoKind::Mmio,
202 address,
203 data.len(),
204 IoType::Write(data),
205 r,
206 )
207 .await
208 }
209
210 pub fn is_mmio(&self, addr: u64) -> bool {
212 self.mmio_ranges.is_occupied(addr)
213 }
214
215 pub async fn io_read(&self, vp: u32, port: u16, data: &mut [u8]) {
217 let lookup = self.pio_ranges.lookup(port, true);
218 let r = lookup
219 .dev
220 .lock()
221 .supports_pio()
222 .expect("objects on the pio bus support pio")
223 .io_read(port, data);
224
225 self.handle_io_result(
226 lookup,
227 vp,
228 IoKind::Pio,
229 port.into(),
230 data.len(),
231 IoType::Read(data),
232 r,
233 )
234 .await
235 }
236
237 pub async fn io_write(&self, vp: u32, port: u16, data: &[u8]) {
239 let lookup = self.pio_ranges.lookup(port, false);
240 let r = lookup
241 .dev
242 .lock()
243 .supports_pio()
244 .expect("objects on the pio bus support pio")
245 .io_write(port, data);
246
247 self.handle_io_result(
248 lookup,
249 vp,
250 IoKind::Pio,
251 port.into(),
252 data.len(),
253 IoType::Write(data),
254 r,
255 )
256 .await
257 }
258
259 pub fn acknowledge_pic_interrupt(&self) -> Option<u8> {
262 self.pic
263 .as_ref()?
264 .lock()
265 .supports_acknowledge_pic_interrupt()
266 .unwrap()
267 .acknowledge_interrupt()
268 }
269
270 pub fn handle_eoi(&self, irq: u32) {
274 if let Some(eoi_handler) = &self.eoi_handler {
275 eoi_handler
276 .lock()
277 .supports_handle_eoi()
278 .unwrap()
279 .handle_eoi(irq);
280 } else {
281 unreachable!("eoi exit received without a registered interrupt controller");
282 }
283 }
284}
285
286#[derive(Debug)]
287pub enum PciConflictReason {
288 ExistingDev(Arc<str>),
289 MissingBus,
290}
291
292#[derive(Debug)]
293pub struct PciConflict {
294 pub bdf: (u8, u8, u8),
295 pub conflict_dev: Arc<str>,
296 pub reason: PciConflictReason,
297}
298
299impl std::fmt::Display for PciConflict {
300 fn fmt(&self, fmt: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
301 match &self.reason {
302 PciConflictReason::ExistingDev(existing_dev) => {
303 let (b, d, f) = self.bdf;
304 write!(
305 fmt,
306 "cannot attach {} to {:02x}:{:02x}:{}, already occupied by {}",
307 self.conflict_dev, b, d, f, existing_dev
308 )
309 }
310 PciConflictReason::MissingBus => {
311 let (b, d, f) = self.bdf;
312 write!(
313 fmt,
314 "cannot attach {} to {:02x}:{:02x}:{}, no valid PCI bus",
315 self.conflict_dev, b, d, f
316 )
317 }
318 }
319 }
320}