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(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 let r = match result {
87 IoResult::Ok => Ok(()),
88 IoResult::Defer(mut token) => match &mut io_type {
89 IoType::Read(bytes) => {
90 let r = poll_fn(|cx| token.poll_read(cx, bytes)).await;
91 if r.is_err() {
92 bytes.fill(!0);
93 }
94 r
95 }
96 IoType::Write(_) => poll_fn(|cx| token.poll_write(cx)).await,
97 },
98 IoResult::Err(err) => {
99 let error = match err {
100 IoError::InvalidRegister => "register not present",
101 IoError::InvalidAccessSize => "invalid access size",
102 IoError::UnalignedAccess => "unaligned access",
103 };
104 match &mut io_type {
105 IoType::Read(bytes) => {
106 bytes.fill(!0);
108 tracelimit::warn_ratelimited!(
109 CVM_CONFIDENTIAL,
110 device = &*lookup.dev_name,
111 address,
112 len,
113 ?kind,
114 error,
115 "device io read error"
116 );
117 }
118 IoType::Write(bytes) => tracelimit::warn_ratelimited!(
119 CVM_CONFIDENTIAL,
120 device = &*lookup.dev_name,
121 address,
122 len,
123 ?kind,
124 error,
125 ?bytes,
126 "device io write error"
127 ),
128 }
129 Ok(())
130 }
131 };
132
133 match r {
134 Ok(()) => {
135 if let Some(range_name) = &lookup.trace {
136 tracing::info!(
139 device = &*lookup.dev_name,
140 range_name = range_name.as_ref(),
141 ?kind,
142 address,
143 direction = if matches!(io_type, IoType::Read(_)) {
144 "read"
145 } else {
146 "write"
147 },
148 data = format_args!("{:02x?}", io_type.bytes()),
149 "device io"
150 );
151 }
152 }
153 Err(err) => {
154 tracelimit::error_ratelimited!(
155 CVM_CONFIDENTIAL,
156 device = &*lookup.dev_name,
157 ?kind,
158 address,
159 direction = if matches!(io_type, IoType::Read(_)) {
160 "read"
161 } else {
162 "write"
163 },
164 error = &err as &dyn std::error::Error,
165 "deferred io failed"
166 );
167 }
168 }
169 }
170
171 pub async fn mmio_read(&self, vp: u32, address: u64, data: &mut [u8]) {
173 let lookup = self.mmio_ranges.lookup(address, true);
174 let r = lookup
175 .dev
176 .lock()
177 .supports_mmio()
178 .expect("objects on the mmio bus support mmio")
179 .mmio_read(address, data);
180
181 self.handle_io_result(
182 lookup,
183 vp,
184 IoKind::Mmio,
185 address,
186 data.len(),
187 IoType::Read(data),
188 r,
189 )
190 .await
191 }
192
193 pub async fn mmio_write(&self, vp: u32, address: u64, data: &[u8]) {
195 let lookup = self.mmio_ranges.lookup(address, false);
196 let r = lookup
197 .dev
198 .lock()
199 .supports_mmio()
200 .expect("objects on the mmio bus support mmio")
201 .mmio_write(address, data);
202
203 self.handle_io_result(
204 lookup,
205 vp,
206 IoKind::Mmio,
207 address,
208 data.len(),
209 IoType::Write(data),
210 r,
211 )
212 .await
213 }
214
215 pub fn is_mmio(&self, addr: u64) -> bool {
217 self.mmio_ranges.is_occupied(addr)
218 }
219
220 pub async fn io_read(&self, vp: u32, port: u16, data: &mut [u8]) {
222 let lookup = self.pio_ranges.lookup(port, true);
223 let r = lookup
224 .dev
225 .lock()
226 .supports_pio()
227 .expect("objects on the pio bus support pio")
228 .io_read(port, data);
229
230 self.handle_io_result(
231 lookup,
232 vp,
233 IoKind::Pio,
234 port.into(),
235 data.len(),
236 IoType::Read(data),
237 r,
238 )
239 .await
240 }
241
242 pub async fn io_write(&self, vp: u32, port: u16, data: &[u8]) {
244 let lookup = self.pio_ranges.lookup(port, false);
245 let r = lookup
246 .dev
247 .lock()
248 .supports_pio()
249 .expect("objects on the pio bus support pio")
250 .io_write(port, data);
251
252 self.handle_io_result(
253 lookup,
254 vp,
255 IoKind::Pio,
256 port.into(),
257 data.len(),
258 IoType::Write(data),
259 r,
260 )
261 .await
262 }
263
264 pub fn acknowledge_pic_interrupt(&self) -> Option<u8> {
267 self.pic
268 .as_ref()?
269 .lock()
270 .supports_acknowledge_pic_interrupt()
271 .unwrap()
272 .acknowledge_interrupt()
273 }
274
275 pub fn handle_eoi(&self, irq: u32) {
279 if let Some(eoi_handler) = &self.eoi_handler {
280 eoi_handler
281 .lock()
282 .supports_handle_eoi()
283 .unwrap()
284 .handle_eoi(irq);
285 } else {
286 unreachable!("eoi exit received without a registered interrupt controller");
287 }
288 }
289}
290
291#[derive(Debug)]
292pub enum PciConflictReason {
293 ExistingDev(Arc<str>),
294 MissingBus,
295}
296
297#[derive(Debug)]
298pub struct PciConflict {
299 pub bdf: (u8, u8, u8),
300 pub conflict_dev: Arc<str>,
301 pub reason: PciConflictReason,
302}
303
304impl std::fmt::Display for PciConflict {
305 fn fmt(&self, fmt: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
306 match &self.reason {
307 PciConflictReason::ExistingDev(existing_dev) => {
308 let (b, d, f) = self.bdf;
309 write!(
310 fmt,
311 "cannot attach {} to {:02x}:{:02x}:{}, already occupied by {}",
312 self.conflict_dev, b, d, f, existing_dev
313 )
314 }
315 PciConflictReason::MissingBus => {
316 let (b, d, f) = self.bdf;
317 write!(
318 fmt,
319 "cannot attach {} to {:02x}:{:02x}:{}, no valid PCI bus",
320 self.conflict_dev, b, d, f
321 )
322 }
323 }
324 }
325}