1use chipset_device::ChipsetDevice;
7use chipset_device::io::IoError;
8use chipset_device::io::IoResult;
9use chipset_device::pci::PciConfigSpace;
10use chipset_device::pio::PortIoIntercept;
11use inspect::Inspect;
12use inspect::InspectMut;
13use open_enum::open_enum;
14use pci_core::cfg_space_emu::ConfigSpaceType0Emulator;
15use pci_core::cfg_space_emu::DeviceBars;
16use pci_core::spec::hwid::ClassCode;
17use pci_core::spec::hwid::HardwareIds;
18use pci_core::spec::hwid::ProgrammingInterface;
19use pci_core::spec::hwid::Subclass;
20use vmcore::device_state::ChangeDeviceState;
21
22mod io_ports {
24 pub const FAST_A20_GATE: u16 = 0x92;
25 pub const MATH_COPROC0: u16 = 0xF0;
26 pub const MATH_COPROC1: u16 = 0xF1;
27}
28
29struct PciIsaBridgeRuntime {
30 reset_evt: Box<dyn Fn() + Send + Sync>,
31 set_a20_signal: Box<dyn FnMut(bool) + Send + Sync>,
32}
33
34#[derive(InspectMut)]
38pub struct PciIsaBridge {
39 #[inspect(skip)]
41 rt: PciIsaBridgeRuntime,
42
43 cfg_space: ConfigSpaceType0Emulator,
45
46 state: PciIsaBridgeState,
48}
49
50#[derive(Inspect)]
51struct PciIsaBridgeState {
52 pci_irq_routing: u32,
53 smi_control: u32,
54 smi_request: u32,
55 system_event: u32,
56 clock_scale: u32,
57 apic_base: u32,
58 a20_gate_enabled: bool,
59}
60
61impl PciIsaBridgeState {
62 fn new() -> Self {
63 Self {
64 pci_irq_routing: 0x80808000 | 11,
66 smi_control: 0x00000008,
67 smi_request: 0x0000000F,
68 system_event: 0,
69 clock_scale: 0,
70 apic_base: 0,
71 a20_gate_enabled: true,
72 }
73 }
74}
75
76impl PciIsaBridge {
77 pub fn new(
78 reset_evt: Box<dyn Fn() + Send + Sync>,
79 set_a20_signal: Box<dyn FnMut(bool) + Send + Sync>,
80 ) -> Self {
81 let cfg_space = ConfigSpaceType0Emulator::new(
82 HardwareIds {
83 vendor_id: 0x8086,
84 device_id: 0x7110,
85 revision_id: 0x03,
86 prog_if: ProgrammingInterface::NONE,
87 sub_class: Subclass::BRIDGE_ISA,
88 base_class: ClassCode::BRIDGE,
89 type0_sub_vendor_id: 0x1414,
90 type0_sub_system_id: 0,
91 },
92 Vec::new(),
93 DeviceBars::new(),
94 )
95 .with_multi_function_bit(true);
96
97 Self {
98 rt: PciIsaBridgeRuntime {
99 reset_evt,
100 set_a20_signal,
101 },
102
103 cfg_space,
104 state: PciIsaBridgeState::new(),
105 }
106 }
107
108 fn handle_math_coproc_read(&mut self, max_access_size: usize, data: &mut [u8]) {
109 if data.len() > max_access_size {
110 tracelimit::warn_ratelimited!(?max_access_size, len = ?data.len(), "unexpected MATH_COPROC read len");
111 data.fill(0xff);
112 return;
113 }
114
115 data.fill(0xff)
117 }
118
119 fn handle_math_coproc_write(&mut self, max_access_size: usize, data: &[u8]) {
120 if data.len() > max_access_size {
121 tracelimit::warn_ratelimited!(?max_access_size, len = ?data.len(), "unexpected MATH_COPROC write len");
122 return;
123 }
124
125 let _ = data;
128 }
129
130 fn handle_fast_a20_read(&mut self, data: &mut [u8]) {
131 if data.len() != 1 {
132 tracelimit::warn_ratelimited!(len = ?data.len(), "unexpected FAST_A20_GATE read len");
133 return;
134 }
135
136 data[0] = if self.state.a20_gate_enabled {
138 0x00
139 } else {
140 0x02
141 };
142 }
143
144 fn handle_fast_a20_write(&mut self, data: &[u8]) {
145 if data.len() != 1 {
146 tracelimit::warn_ratelimited!(len = ?data.len(), "unexpected FAST_A20_GATE write len");
147 return;
148 }
149
150 let v = data[0];
151
152 if v & 0x01 != 0 {
153 tracing::info!("initiating guest reset via FAST_A20_GATE");
154 (self.rt.reset_evt)();
155 return;
156 }
157
158 self.state.a20_gate_enabled = v & 0x02 == 0;
159 (self.rt.set_a20_signal)(self.state.a20_gate_enabled);
160 }
161}
162
163impl ChangeDeviceState for PciIsaBridge {
164 fn start(&mut self) {}
165
166 async fn stop(&mut self) {}
167
168 async fn reset(&mut self) {
169 self.state = PciIsaBridgeState::new();
171 self.cfg_space.reset();
172 }
173}
174
175impl ChipsetDevice for PciIsaBridge {
176 fn supports_pio(&mut self) -> Option<&mut dyn PortIoIntercept> {
177 Some(self)
178 }
179
180 fn supports_pci(&mut self) -> Option<&mut dyn PciConfigSpace> {
181 Some(self)
182 }
183}
184
185impl PortIoIntercept for PciIsaBridge {
186 fn io_read(&mut self, io_port: u16, data: &mut [u8]) -> IoResult {
187 use self::io_ports::*;
188 match io_port {
189 FAST_A20_GATE => self.handle_fast_a20_read(data),
190 MATH_COPROC0 => self.handle_math_coproc_read(2, data),
191 MATH_COPROC1 => self.handle_math_coproc_read(1, data),
192 _ => return IoResult::Err(IoError::InvalidRegister),
193 }
194 IoResult::Ok
195 }
196
197 fn io_write(&mut self, io_port: u16, data: &[u8]) -> IoResult {
198 use self::io_ports::*;
199 match io_port {
200 FAST_A20_GATE => self.handle_fast_a20_write(data),
201 MATH_COPROC0 => self.handle_math_coproc_write(2, data),
202 MATH_COPROC1 => self.handle_math_coproc_write(1, data),
203 _ => return IoResult::Err(IoError::InvalidRegister),
204 }
205 IoResult::Ok
206 }
207
208 fn get_static_regions(&mut self) -> &[(&str, std::ops::RangeInclusive<u16>)] {
209 use self::io_ports::*;
210
211 &[
212 ("fast_a20_gate", FAST_A20_GATE..=FAST_A20_GATE),
213 ("math_coproc", MATH_COPROC0..=MATH_COPROC1),
214 ]
218 }
219}
220
221impl PciConfigSpace for PciIsaBridge {
222 fn pci_cfg_read(&mut self, offset: u16, value: &mut u32) -> IoResult {
223 *value = match ConfigSpace(offset) {
224 _ if offset == pci_core::spec::cfg_space::HeaderType00::STATUS_COMMAND.0 => 0x02000007,
228 _ if offset < 0x40 => return self.cfg_space.read_u32(offset, value),
229 ConfigSpace::TOP => 0x00000200,
232 ConfigSpace::IO_REC => 0x0003004D,
233 ConfigSpace::PIRQ => self.state.pci_irq_routing,
234 ConfigSpace::SER_IRQ => 0x0000000D0,
235 ConfigSpace::SMI => self.state.smi_control,
236 ConfigSpace::SEE => self.state.system_event,
237 ConfigSpace::FTM => self.state.smi_request,
238 ConfigSpace::CTL_TMR => self.state.clock_scale,
239 ConfigSpace::RTC_CONFIG => 0x25000000,
240 ConfigSpace::MANUF_ID => 0x00000F30,
241 ConfigSpace::APIC_BASE => self.state.apic_base,
242 ConfigSpace::DMA_CFG1
243 | ConfigSpace::DMA_CFG2
244 | ConfigSpace::IRQ_RT
245 | ConfigSpace::DMA
246 | ConfigSpace::PCSC
247 | ConfigSpace::GEN_CONFIG => 0,
248 _ => {
249 tracing::debug!(?offset, "unimplemented config space read");
250 return IoResult::Err(IoError::InvalidRegister);
251 }
252 };
253
254 IoResult::Ok
255 }
256
257 fn pci_cfg_write(&mut self, offset: u16, value: u32) -> IoResult {
258 match ConfigSpace(offset) {
259 _ if offset < 0x40 => return self.cfg_space.write_u32(offset, value),
260 ConfigSpace::PIRQ => {
261 if self.state.pci_irq_routing != value {
262 tracelimit::info_ratelimited!(new_pci_irq_routing = ?value, "custom PCI IRQ routing is not implemented!");
263 }
264
265 self.state.pci_irq_routing = value;
266 }
267 ConfigSpace::SER_IRQ => {
268 if !(value == 0x0000000D0 || value == 0x000000010) {
269 tracelimit::warn_ratelimited!(
270 ?value,
271 "set invalid serial IRQ control register value"
272 );
273 }
274 }
275 ConfigSpace::TOP => {
276 if (value & 0x00000200) == 0 {
280 tracing::debug!("ISA/DMA 512-640K Region Forwarding Enable was cleared!");
281 }
282 }
283 ConfigSpace::SMI => self.state.smi_control = value & 0x00FF001F,
284 ConfigSpace::SEE => self.state.system_event = value,
285 ConfigSpace::FTM => self.state.smi_request = value,
286 ConfigSpace::CTL_TMR => self.state.clock_scale = value,
287 ConfigSpace::RTC_CONFIG => {
288 if (value & 0x04000000) == 0 {
291 tracing::debug!("Trying to disable extended CMOS - not supported")
292 }
293
294 if value != 0x25000000 {
295 tracelimit::warn_ratelimited!(?value, "unexpected value for RTC_CONFIG write")
296 }
297 }
298 ConfigSpace::APIC_BASE => {
299 if (value & 0x3F) != (self.state.apic_base & 0x3F) {
302 tracelimit::error_ratelimited!(
308 ?value,
309 "changing the IOAPIC base is not implemented!"
310 );
311 }
312
313 self.state.apic_base = value;
314 }
315 ConfigSpace::GEN_CONFIG
316 | ConfigSpace::DMA_CFG1
317 | ConfigSpace::DMA_CFG2
318 | ConfigSpace::IO_REC
319 | ConfigSpace::IRQ_RT
320 | ConfigSpace::DMA
321 | ConfigSpace::PCSC => {
322 }
324 _ => {
325 tracelimit::warn_ratelimited!(?offset, ?value, "unimplemented config space write");
326 return IoResult::Err(IoError::InvalidRegister);
327 }
328 }
329
330 IoResult::Ok
331 }
332
333 fn suggested_bdf(&mut self) -> Option<(u8, u8, u8)> {
334 Some((0, 7, 0)) }
336}
337
338open_enum! {
339 enum ConfigSpace: u16 {
340 IO_REC = 0x4C,
341 PIRQ = 0x60,
342 SER_IRQ = 0x64,
343 TOP = 0x68,
344 IRQ_RT = 0x70,
345 DMA = 0x74,
346 PCSC = 0x78,
347 APIC_BASE = 0x80,
348 DMA_CFG1 = 0x90,
349 DMA_CFG2 = 0x94,
350 SMI = 0xA0,
351 SEE = 0xA4,
352 FTM = 0xA8,
353 CTL_TMR = 0xAC,
354 GEN_CONFIG = 0xB0,
355 RTC_CONFIG = 0xC8,
356 MANUF_ID = 0xF8,
357 }
358}
359
360mod save_restore {
361 use super::*;
362 use vmcore::save_restore::RestoreError;
363 use vmcore::save_restore::SaveError;
364 use vmcore::save_restore::SaveRestore;
365
366 mod state {
367 use mesh::payload::Protobuf;
368 use pci_core::cfg_space_emu::ConfigSpaceType0Emulator;
369 use vmcore::save_restore::SaveRestore;
370 use vmcore::save_restore::SavedStateRoot;
371
372 #[derive(Protobuf, SavedStateRoot)]
373 #[mesh(package = "chipset.piix4.pci_isa_bridge")]
374 pub struct SavedState {
375 #[mesh(1)]
376 pub pci_irq_routing: u32,
377 #[mesh(2)]
378 pub smi_control: u32,
379 #[mesh(3)]
380 pub smi_request: u32,
381 #[mesh(4)]
382 pub system_event: u32,
383 #[mesh(5)]
384 pub clock_scale: u32,
385 #[mesh(6)]
386 pub apic_base: u32,
387 #[mesh(7)]
388 pub a20_gate_enabled: bool,
389 #[mesh(8)]
390 pub cfg_space: <ConfigSpaceType0Emulator as SaveRestore>::SavedState,
391 }
392 }
393
394 impl SaveRestore for PciIsaBridge {
395 type SavedState = state::SavedState;
396
397 fn save(&mut self) -> Result<Self::SavedState, SaveError> {
398 let PciIsaBridgeState {
399 pci_irq_routing,
400 smi_control,
401 smi_request,
402 system_event,
403 clock_scale,
404 apic_base,
405 a20_gate_enabled,
406 } = self.state;
407
408 let saved_state = state::SavedState {
409 pci_irq_routing,
410 smi_control,
411 smi_request,
412 system_event,
413 clock_scale,
414 apic_base,
415 a20_gate_enabled,
416 cfg_space: self.cfg_space.save()?,
417 };
418
419 Ok(saved_state)
420 }
421
422 fn restore(&mut self, state: Self::SavedState) -> Result<(), RestoreError> {
423 let state::SavedState {
424 pci_irq_routing,
425 smi_control,
426 smi_request,
427 system_event,
428 clock_scale,
429 apic_base,
430 a20_gate_enabled,
431 cfg_space,
432 } = state;
433
434 let state = PciIsaBridgeState {
435 pci_irq_routing,
436 smi_control,
437 smi_request,
438 system_event,
439 clock_scale,
440 apic_base,
441 a20_gate_enabled,
442 };
443
444 self.state = state;
445
446 (self.rt.set_a20_signal)(self.state.a20_gate_enabled);
448
449 self.cfg_space.restore(cfg_space)?;
450
451 Ok(())
452 }
453 }
454}