1pub mod backing;
7mod builder;
8mod io_ranges;
9mod line_sets;
10
11pub use self::builder::ChipsetBuilder;
12pub use self::builder::ChipsetDevices;
13pub use self::builder::DynamicDeviceUnit;
14
15use self::io_ranges::IoRanges;
16use self::io_ranges::LookupResult;
17use crate::DebugEventHandler;
18use chipset_device::ChipsetDevice;
19use chipset_device::io::IoError;
20use chipset_device::io::IoResult;
21use closeable_mutex::CloseableMutex;
22use cvm_tracing::CVM_CONFIDENTIAL;
23use inspect::Inspect;
24use std::future::poll_fn;
25use std::sync::Arc;
26
27#[derive(Inspect)]
30pub struct Chipset {
31 #[inspect(rename = "mmio")]
32 mmio_ranges: IoRanges<u64>,
33 #[inspect(rename = "pio")]
34 pio_ranges: IoRanges<u16>,
35
36 #[inspect(rename = "has_pic", with = "Option::is_some")]
37 pic: Option<Arc<CloseableMutex<dyn ChipsetDevice>>>,
38 #[inspect(rename = "has_eoi_handler", with = "Option::is_some")]
39 eoi_handler: Option<Arc<CloseableMutex<dyn ChipsetDevice>>>,
40
41 #[inspect(skip)]
42 debug_event_handler: Arc<dyn DebugEventHandler>,
43}
44
45enum IoType<'a> {
46 Read(&'a mut [u8]),
47 Write(&'a [u8]),
48}
49
50#[derive(Copy, Clone, Debug)]
51enum IoKind {
52 Pio,
53 Mmio,
54}
55
56impl IoType<'_> {
57 fn bytes(&self) -> &[u8] {
58 match self {
59 IoType::Read(b) => b,
60 IoType::Write(b) => b,
61 }
62 }
63}
64
65impl Chipset {
66 async fn handle_io_result(
67 &self,
68 lookup: LookupResult,
69 vp: u32,
70 kind: IoKind,
71 address: u64,
72 len: usize,
73 mut io_type: IoType<'_>,
74 result: IoResult,
75 ) {
76 if lookup.debug_break {
77 tracing::warn!(
78 device = &*lookup.dev_name,
79 address,
80 len,
81 ?kind,
82 "debug break due to io"
83 );
84 self.debug_event_handler.on_debug_break(Some(vp));
85 }
86 match result {
87 IoResult::Ok => {}
88 IoResult::Defer(mut token) => match &mut io_type {
89 IoType::Read(bytes) => {
90 if let Err(err) = poll_fn(|cx| token.poll_read(cx, bytes)).await {
91 self.handle_io_error(err, &lookup, kind, address, len, &mut io_type);
92 }
93 }
94 IoType::Write(_) => {
95 if let Err(err) = poll_fn(|cx| token.poll_write(cx)).await {
96 self.handle_io_error(err, &lookup, kind, address, len, &mut io_type);
97 }
98 }
99 },
100 IoResult::Err(err) => {
101 self.handle_io_error(err, &lookup, kind, address, len, &mut io_type);
102 }
103 };
104
105 if let Some(range_name) = &lookup.trace {
106 tracing::info!(
109 device = &*lookup.dev_name,
110 range_name = range_name.as_ref(),
111 ?kind,
112 address,
113 direction = if matches!(io_type, IoType::Read(_)) {
114 "read"
115 } else {
116 "write"
117 },
118 data = format_args!("{:02x?}", io_type.bytes()),
119 "device io"
120 );
121 }
122 }
123
124 fn handle_io_error(
125 &self,
126 err: IoError,
127 lookup: &LookupResult,
128 kind: IoKind,
129 address: u64,
130 len: usize,
131 io_type: &mut IoType<'_>,
132 ) {
133 let error = match err {
134 IoError::InvalidRegister => "register not present",
135 IoError::InvalidAccessSize => "invalid access size",
136 IoError::UnalignedAccess => "unaligned access",
137 IoError::NoResponse => "device didn't respond",
138 };
139 match io_type {
140 IoType::Read(bytes) => {
141 bytes.fill(!0);
143 tracelimit::warn_ratelimited!(
144 CVM_CONFIDENTIAL,
145 device = &*lookup.dev_name,
146 address,
147 len,
148 ?kind,
149 error,
150 "device io read error"
151 );
152 }
153 IoType::Write(bytes) => tracelimit::warn_ratelimited!(
154 CVM_CONFIDENTIAL,
155 device = &*lookup.dev_name,
156 address,
157 len,
158 ?kind,
159 error,
160 ?bytes,
161 "device io write error"
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}
321
322#[derive(Debug)]
323pub enum PcieConflictReason {
324 ExistingDev(Arc<str>),
325 MissingDownstreamPort,
326 MissingEnumerator,
327}
328
329#[derive(Debug)]
330pub struct PcieConflict {
331 pub conflict_dev: Arc<str>,
332 pub reason: PcieConflictReason,
333}
334
335impl std::fmt::Display for PcieConflict {
336 fn fmt(&self, fmt: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
337 match &self.reason {
338 PcieConflictReason::ExistingDev(existing_dev) => {
339 write!(
340 fmt,
341 "cannot attach {}, port already occupied by {}",
342 self.conflict_dev, existing_dev
343 )
344 }
345 PcieConflictReason::MissingDownstreamPort => {
346 write!(
347 fmt,
348 "cannot attach {}, no valid pcie downstream port",
349 self.conflict_dev
350 )
351 }
352 PcieConflictReason::MissingEnumerator => {
353 write!(
354 fmt,
355 "cannot attach {}, no valid pcie enumerator",
356 self.conflict_dev
357 )
358 }
359 }
360 }
361}