1pub mod resolver;
7
8pub use chipset_resources::i440bx_host_pci_bridge::AdjustGpaRange;
9pub use chipset_resources::i440bx_host_pci_bridge::GpaState;
10
11use chipset_device::ChipsetDevice;
12use chipset_device::io::IoError;
13use chipset_device::io::IoResult;
14use chipset_device::pci::ByteEnabledDwordRead;
15use chipset_device::pci::ByteEnabledDwordWrite;
16use chipset_device::pci::PciConfigSpace;
17use inspect::Inspect;
18use inspect::InspectMut;
19use memory_range::MemoryRange;
20use open_enum::open_enum;
21use pci_core::cfg_space_emu::ConfigSpaceType0Emulator;
22use pci_core::cfg_space_emu::DeviceBars;
23use pci_core::spec::hwid::ClassCode;
24use pci_core::spec::hwid::HardwareIds;
25use pci_core::spec::hwid::ProgrammingInterface;
26use pci_core::spec::hwid::Subclass;
27use vmcore::device_state::ChangeDeviceState;
28
29struct HostPciBridgeRuntime {
30 adjust_gpa_range: Box<dyn AdjustGpaRange>,
31}
32
33#[derive(InspectMut)]
37pub struct HostPciBridge {
38 #[inspect(skip)]
40 rt: HostPciBridgeRuntime,
41
42 cfg_space: ConfigSpaceType0Emulator,
44
45 state: HostPciBridgeState,
47}
48
49#[derive(Debug, Inspect)]
50struct HostPciBridgeState {
51 host_pci_dram1: u32,
52 host_pci_dram2: u32,
53 pam_reg1: u32,
54 pam_reg2: u32,
55 bios_scratch1: u32,
56 bios_scratch2: u32,
57 smm_config_word: u16,
58}
59
60const INITIAL_PAM_REG1: u32 = 0x00000003;
62const INITIAL_PAM_REG2: u32 = 0;
63
64impl HostPciBridgeState {
65 fn new() -> Self {
66 Self {
67 host_pci_dram1: 0x02020202,
69 host_pci_dram2: 0x00000002,
70 pam_reg1: INITIAL_PAM_REG1,
71 pam_reg2: INITIAL_PAM_REG2,
72 bios_scratch1: 0,
73 bios_scratch2: 0,
74 smm_config_word: 0x3802,
75 }
76 }
77}
78
79impl HostPciBridge {
80 pub fn new(adjust_gpa_range: Box<dyn AdjustGpaRange>, is_restoring: bool) -> Self {
81 let cfg_space = ConfigSpaceType0Emulator::new(
82 HardwareIds {
83 vendor_id: 0x8086,
84 device_id: 0x7192,
85 revision_id: 0x03,
86 prog_if: ProgrammingInterface::NONE,
87 sub_class: Subclass::BRIDGE_HOST,
88 base_class: ClassCode::BRIDGE,
89 type0_sub_vendor_id: 0,
90 type0_sub_system_id: 0,
91 },
92 Vec::new(),
93 Vec::new(),
94 DeviceBars::new(),
95 );
96
97 let mut dev = Self {
98 rt: HostPciBridgeRuntime { adjust_gpa_range },
99
100 cfg_space,
101
102 state: HostPciBridgeState::new(),
103 };
104
105 if !is_restoring {
106 dev.rt
109 .adjust_gpa_range
110 .adjust_gpa_range(MemoryRange::new(0xa0000..0xc0000), GpaState::Mmio);
111
112 dev.adjust_bios_override_ranges(dev.state.pam_reg1, dev.state.pam_reg2, true);
113 }
114
115 dev
116 }
117}
118
119impl HostPciBridge {
120 fn adjust_bios_override_ranges(&mut self, new_reg1: u32, new_reg2: u32, force: bool) {
126 tracing::trace!(?self.state.pam_reg1, ?self.state.pam_reg2, new_reg1, new_reg2, "updating PAM registers");
127
128 let old = pam::parse_pam_registers(self.state.pam_reg1, self.state.pam_reg2);
129 let new = pam::parse_pam_registers(new_reg1, new_reg2);
130
131 for ((range, old_state), (_, new_state)) in old.zip(new) {
132 if old_state != new_state || force {
133 self.rt.adjust_gpa_range.adjust_gpa_range(range, new_state);
134 }
135 }
136
137 self.state.pam_reg1 = new_reg1;
138 self.state.pam_reg2 = new_reg2;
139 }
140}
141
142impl ChangeDeviceState for HostPciBridge {
143 fn start(&mut self) {}
144
145 async fn stop(&mut self) {}
146
147 async fn reset(&mut self) {
148 self.cfg_space.reset();
149 self.state = HostPciBridgeState::new();
150
151 self.adjust_bios_override_ranges(INITIAL_PAM_REG1, INITIAL_PAM_REG2, true);
152 }
153}
154
155impl ChipsetDevice for HostPciBridge {
156 fn supports_pci(&mut self) -> Option<&mut dyn PciConfigSpace> {
157 Some(self)
158 }
159}
160
161impl PciConfigSpace for HostPciBridge {
162 fn pci_cfg_read(&mut self, offset: u16, mut value: ByteEnabledDwordRead<'_>) -> IoResult {
163 value.set(match ConfigSpace(offset) {
164 _ if offset == pci_core::spec::cfg_space::HeaderType00::STATUS_COMMAND.0 => 0x02000006,
168 _ if offset < 0x40 => return self.cfg_space.read_byte_enabled(offset, value),
169 ConfigSpace::PAM1 => self.state.pam_reg1,
170 ConfigSpace::PAM2 => self.state.pam_reg2,
171 ConfigSpace::DRB_1 => self.state.host_pci_dram1,
172 ConfigSpace::DRB_2 => self.state.host_pci_dram2,
173 ConfigSpace::PGPOL => 0x380A0000,
176 ConfigSpace::BSPAD_1 => self.state.bios_scratch1,
177 ConfigSpace::BSPAD_2 => self.state.bios_scratch2,
178 ConfigSpace::SMRAM => {
179 ((self.state.smm_config_word & 0b01111010 | 0b00111000_00000010) as u32) << 16
182 }
183 ConfigSpace::MANUFACTURER_ID => 0x00000F20,
184 ConfigSpace::BUFFC
185 | ConfigSpace::SDRAMC
186 | ConfigSpace::NBXCFG
187 | ConfigSpace::DRAMC
188 | ConfigSpace::MBSC_1
189 | ConfigSpace::SCRR_2
190 | ConfigSpace::ERR
191 | ConfigSpace::ACAPID
192 | ConfigSpace::AGPSTAT
193 | ConfigSpace::AGPCMD
194 | ConfigSpace::AGPCTRL
195 | ConfigSpace::APSIZE
196 | ConfigSpace::ATTBASE
197 | ConfigSpace::UNKNOWN_BC
198 | ConfigSpace::UNKNOWN_F4 => 0, _ => {
200 tracing::debug!(?offset, "unimplemented config space read");
201 return IoResult::Err(IoError::InvalidRegister);
202 }
203 });
204
205 IoResult::Ok
206 }
207
208 fn pci_cfg_write(&mut self, offset: u16, value: ByteEnabledDwordWrite) -> IoResult {
209 match ConfigSpace(offset) {
210 _ if offset < 0x40 => return self.cfg_space.write_byte_enabled(offset, value),
211 ConfigSpace::DRB_1 => value.merge_into(&mut self.state.host_pci_dram1),
212 ConfigSpace::DRB_2 => value.merge_into(&mut self.state.host_pci_dram2),
213 ConfigSpace::PAM1 => {
214 let value = value.merge(self.state.pam_reg1);
215 self.adjust_bios_override_ranges(value, self.state.pam_reg2, false);
216 }
217 ConfigSpace::PAM2 => {
218 let value = value.merge(self.state.pam_reg2);
219 self.adjust_bios_override_ranges(self.state.pam_reg1, value, false);
220 }
221 ConfigSpace::BSPAD_1 => value.merge_into(&mut self.state.bios_scratch1),
222 ConfigSpace::BSPAD_2 => value.merge_into(&mut self.state.bios_scratch2),
223 ConfigSpace::SMRAM => {
224 let mut new_smm_word = value.merge_high(self.state.smm_config_word);
228
229 if self.state.smm_config_word & 0x10 == 0 {
232 const UNSUPPORTED_BITS: u16 = 0b10000111_00000000;
234 if new_smm_word & UNSUPPORTED_BITS != 0 {
235 tracelimit::warn_ratelimited!(
236 bits = new_smm_word & !UNSUPPORTED_BITS,
237 "guest set unsupported feature bits"
238 );
239 }
240
241 new_smm_word &= !UNSUPPORTED_BITS;
242 new_smm_word &= 0b01111010;
244 new_smm_word |= 0b00111000_00000010;
246 new_smm_word &= !0b01000000_00000000;
249
250 if new_smm_word & 0b01000000 != 0 {
252 tracelimit::warn_ratelimited!("guest attempted to enable SMM RAM");
253 }
254 new_smm_word &= !0b01000000;
255
256 self.state.smm_config_word = new_smm_word;
257 }
258 }
259 ConfigSpace::BUFFC
260 | ConfigSpace::SDRAMC
261 | ConfigSpace::NBXCFG
262 | ConfigSpace::DRAMC
263 | ConfigSpace::MBSC_1
264 | ConfigSpace::PGPOL
265 | ConfigSpace::SCRR_2
266 | ConfigSpace::ERR
267 | ConfigSpace::ACAPID
268 | ConfigSpace::AGPSTAT
269 | ConfigSpace::AGPCMD
270 | ConfigSpace::AGPCTRL
271 | ConfigSpace::APSIZE
272 | ConfigSpace::ATTBASE
273 | ConfigSpace::UNKNOWN_BC
274 | ConfigSpace::UNKNOWN_F4 => {} _ => {
276 tracing::debug!(?offset, ?value, "unimplemented config space write");
277 return IoResult::Err(IoError::InvalidRegister);
278 }
279 }
280
281 IoResult::Ok
282 }
283
284 fn suggested_bdf(&mut self) -> Option<(u8, u8, u8)> {
285 Some((0, 0, 0)) }
287}
288
289open_enum! {
290 enum ConfigSpace: u16 {
294 NBXCFG = 0x50,
295 DRAMC = 0x54,
297 DRAMT = 0x58,
299 PAM1 = 0x58,
301 PAM2 = 0x5C,
302 DRB_1 = 0x60,
303 DRB_2 = 0x64,
304 FDHC = 0x68,
306 MBSC_1 = 0x68,
308 MBSC_2 = 0x6C,
310 SMRAM = 0x70,
312 SDRAMC = 0x74,
313 PGPOL = 0x78,
315 SCRR_1 = 0x78,
317 SCRR_2 = 0x7C,
319 ERR = 0x90,
321 ACAPID = 0xA0,
322 AGPSTAT = 0xA4,
323 AGPCMD = 0xA8,
324 AGPCTRL = 0xB0,
325 APSIZE = 0xB4,
327 ATTBASE = 0xB8,
328 UNKNOWN_BC = 0xBC,
330 MBFS = 0xCC,
331 BSPAD_1 = 0xD0,
332 BSPAD_2 = 0xD4,
333 BUFFC = 0xF0,
335 UNKNOWN_F4 = 0xF4,
337 MANUFACTURER_ID = 0xF8,
338 }
339}
340
341mod pam {
342 use super::GpaState;
343 use memory_range::MemoryRange;
344
345 pub const PAM_RANGES: &[MemoryRange; 13] = &[
346 MemoryRange::new(0xf0000..0x100000),
347 MemoryRange::new(0xc0000..0xc4000),
348 MemoryRange::new(0xc4000..0xc8000),
349 MemoryRange::new(0xc8000..0xcc000),
350 MemoryRange::new(0xcc000..0xd0000),
351 MemoryRange::new(0xd0000..0xd4000),
352 MemoryRange::new(0xd4000..0xd8000),
353 MemoryRange::new(0xd8000..0xdc000),
354 MemoryRange::new(0xdc000..0xe0000),
355 MemoryRange::new(0xe0000..0xe4000),
356 MemoryRange::new(0xe4000..0xe8000),
357 MemoryRange::new(0xe8000..0xec000),
358 MemoryRange::new(0xec000..0xf0000),
359 ];
360
361 pub fn parse_pam_registers(
362 reg1: u32,
363 reg2: u32,
364 ) -> impl Iterator<Item = (MemoryRange, GpaState)> {
365 let reg = ((reg2 as u64) << 32) | reg1 as u64;
373 PAM_RANGES.iter().enumerate().map(move |(i, range)| {
374 let state = match (reg >> ((i + 3) * 4)) & 3 {
375 0b00 => GpaState::Mmio,
376 0b01 => GpaState::WriteProtected,
377 0b10 => GpaState::WriteOnly,
378 0b11 => GpaState::Writable,
379 _ => unreachable!(),
380 };
381 (*range, state)
382 })
383 }
384}
385
386mod save_restore {
387 use super::*;
388 use vmcore::save_restore::RestoreError;
389 use vmcore::save_restore::SaveError;
390 use vmcore::save_restore::SaveRestore;
391
392 mod state {
393 use mesh::payload::Protobuf;
394 use pci_core::cfg_space_emu::ConfigSpaceType0Emulator;
395 use vmcore::save_restore::SaveRestore;
396 use vmcore::save_restore::SavedStateRoot;
397
398 #[derive(Protobuf, SavedStateRoot)]
399 #[mesh(package = "chipset.i440bx.host_pci_bridge")]
400 pub struct SavedState {
401 #[mesh(1)]
402 pub host_pci_dram1: u32,
403 #[mesh(2)]
404 pub host_pci_dram2: u32,
405 #[mesh(3)]
406 pub pam_reg1: u32,
407 #[mesh(4)]
408 pub pam_reg2: u32,
409 #[mesh(5)]
410 pub bios_scratch1: u32,
411 #[mesh(6)]
412 pub bios_scratch2: u32,
413 #[mesh(7)]
414 pub smm_config_word: u16,
415 #[mesh(8)]
416 pub cfg_space: <ConfigSpaceType0Emulator as SaveRestore>::SavedState,
417 }
418 }
419
420 impl SaveRestore for HostPciBridge {
421 type SavedState = state::SavedState;
422
423 fn save(&mut self) -> Result<Self::SavedState, SaveError> {
424 let HostPciBridgeState {
425 host_pci_dram1,
426 host_pci_dram2,
427 pam_reg1,
428 pam_reg2,
429 bios_scratch1,
430 bios_scratch2,
431 smm_config_word,
432 } = self.state;
433
434 Ok(state::SavedState {
435 host_pci_dram1,
436 host_pci_dram2,
437 pam_reg1,
438 pam_reg2,
439 bios_scratch1,
440 bios_scratch2,
441 smm_config_word,
442 cfg_space: self.cfg_space.save()?,
443 })
444 }
445
446 fn restore(&mut self, state: Self::SavedState) -> Result<(), RestoreError> {
447 let state::SavedState {
448 host_pci_dram1,
449 host_pci_dram2,
450 pam_reg1,
451 pam_reg2,
452 bios_scratch1,
453 bios_scratch2,
454 smm_config_word,
455 cfg_space,
456 } = state;
457
458 self.state = HostPciBridgeState {
459 host_pci_dram1,
460 host_pci_dram2,
461 pam_reg1,
462 pam_reg2,
463 bios_scratch1,
464 bios_scratch2,
465 smm_config_word,
466 };
467
468 self.adjust_bios_override_ranges(pam_reg1, pam_reg2, true);
469
470 self.cfg_space.restore(cfg_space)?;
471
472 Ok(())
473 }
474 }
475}