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