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