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