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::DRAM1 => self.state.host_pci_dram1,
175 ConfigSpace::DRAM2 => self.state.host_pci_dram2,
176 ConfigSpace::PAGING_POLICY => 0x380A0000,
178 ConfigSpace::BIOS_SCRATCH1 => self.state.bios_scratch1,
179 ConfigSpace::BIOS_SCRATCH2 => self.state.bios_scratch2,
180 ConfigSpace::SYS_MNG => {
181 (self.state.smm_config_word as u32 & 0xC77C | 0x3802) << 16
184 }
185 ConfigSpace::MANUFACTURER_ID => 0x00000F20,
186 ConfigSpace::BUFFER_CONTROL
187 | ConfigSpace::SDRAM_CONTROL
188 | ConfigSpace::CACHE
189 | ConfigSpace::DRAM_C
190 | ConfigSpace::DRAM_RT1
191 | ConfigSpace::UNKNOWN_F4 => 0, _ => {
193 tracing::debug!(?offset, "unimplemented config space read");
194 return IoResult::Err(IoError::InvalidRegister);
195 }
196 };
197
198 IoResult::Ok
199 }
200
201 fn pci_cfg_write(&mut self, offset: u16, value: u32) -> IoResult {
202 match ConfigSpace(offset) {
203 _ if offset < 0x40 => return self.cfg_space.write_u32(offset, value),
204 ConfigSpace::DRAM1 => self.state.host_pci_dram1 = value,
205 ConfigSpace::DRAM2 => self.state.host_pci_dram2 = value,
206 ConfigSpace::PAM1 => {
207 self.adjust_bios_override_ranges(value, self.state.pam_reg2, false);
208 }
209 ConfigSpace::PAM2 => {
210 self.adjust_bios_override_ranges(self.state.pam_reg1, value, false);
211 }
212 ConfigSpace::BIOS_SCRATCH1 => self.state.bios_scratch1 = value,
213 ConfigSpace::BIOS_SCRATCH2 => self.state.bios_scratch2 = value,
214 ConfigSpace::SYS_MNG => {
215 let mut new_smm_word = (value >> 16) as u16;
219
220 if self.state.smm_config_word & 0x10 == 0 {
223 if new_smm_word & 0x8700 != 0 {
225 tracelimit::warn_ratelimited!(bits = ?new_smm_word & !0x8700, "guest set unsupported feature bits");
226 }
227
228 new_smm_word &= !0x8700;
229 new_smm_word &= 0xC77C;
231 new_smm_word |= 0x3802;
233 new_smm_word &= !0x4000;
236
237 if new_smm_word & 0x0040 != 0 {
239 tracelimit::warn_ratelimited!("guest attempted to enable SMM RAM");
240 }
241 new_smm_word &= !0x0040;
242
243 self.state.smm_config_word = new_smm_word;
244 }
245 }
246 ConfigSpace::BUFFER_CONTROL
247 | ConfigSpace::SDRAM_CONTROL
248 | ConfigSpace::CACHE
249 | ConfigSpace::DRAM_C
250 | ConfigSpace::DRAM_RT1
251 | ConfigSpace::PAGING_POLICY
252 | ConfigSpace::UNKNOWN_F4 => {} _ => {
254 tracing::debug!(?offset, ?value, "unimplemented config space write");
255 return IoResult::Err(IoError::InvalidRegister);
256 }
257 }
258
259 IoResult::Ok
260 }
261
262 fn suggested_bdf(&mut self) -> Option<(u8, u8, u8)> {
263 Some((0, 0, 0)) }
265}
266
267open_enum! {
268 enum ConfigSpace: u16 {
269 CACHE = 0x50,
270 DRAM_C = 0x54,
271 TIMING = 0x58,
272 PAM1 = 0x58,
273 PAM2 = 0x5C,
274 DRAM1 = 0x60,
275 DRAM2 = 0x64,
276 DRAM_RT1 = 0x68,
277 DRAM_RT2 = 0x6C,
278 SYS_MNG = 0x70,
279 SDRAM_CONTROL = 0x74,
280 PAGING_POLICY = 0x78,
281 SUSPEND_CBR = 0x7C, MEM_BUFF_FREQ = 0xCC,
283 BIOS_SCRATCH1 = 0xD0,
284 BIOS_SCRATCH2 = 0xD4,
285 BUFFER_CONTROL = 0xF0,
286 UNKNOWN_F4 = 0xF4,
287 MANUFACTURER_ID = 0xF8,
288 }
289}
290
291mod pam {
292 use memory_range::MemoryRange;
293
294 #[derive(Default, Copy, Clone, Debug, PartialEq, Eq)]
295 pub enum GpaState {
296 #[default]
298 Writable,
299 WriteProtected,
301 WriteOnly,
303 Mmio,
305 }
306
307 pub const PAM_RANGES: &[MemoryRange; 13] = &[
308 MemoryRange::new(0xf0000..0x100000),
309 MemoryRange::new(0xc0000..0xc4000),
310 MemoryRange::new(0xc4000..0xc8000),
311 MemoryRange::new(0xc8000..0xcc000),
312 MemoryRange::new(0xcc000..0xd0000),
313 MemoryRange::new(0xd0000..0xd4000),
314 MemoryRange::new(0xd4000..0xd8000),
315 MemoryRange::new(0xd8000..0xdc000),
316 MemoryRange::new(0xdc000..0xe0000),
317 MemoryRange::new(0xe0000..0xe4000),
318 MemoryRange::new(0xe4000..0xe8000),
319 MemoryRange::new(0xe8000..0xec000),
320 MemoryRange::new(0xec000..0xf0000),
321 ];
322
323 pub fn parse_pam_registers(
324 reg1: u32,
325 reg2: u32,
326 ) -> impl Iterator<Item = (MemoryRange, GpaState)> {
327 let reg = ((reg2 as u64) << 32) | reg1 as u64;
335 PAM_RANGES.iter().enumerate().map(move |(i, range)| {
336 let state = match (reg >> ((i + 3) * 4)) & 3 {
337 0b00 => GpaState::Mmio,
338 0b01 => GpaState::WriteProtected,
339 0b10 => GpaState::WriteOnly,
340 0b11 => GpaState::Writable,
341 _ => unreachable!(),
342 };
343 (*range, state)
344 })
345 }
346}
347
348mod save_restore {
349 use super::*;
350 use vmcore::save_restore::RestoreError;
351 use vmcore::save_restore::SaveError;
352 use vmcore::save_restore::SaveRestore;
353
354 mod state {
355 use mesh::payload::Protobuf;
356 use pci_core::cfg_space_emu::ConfigSpaceType0Emulator;
357 use vmcore::save_restore::SaveRestore;
358 use vmcore::save_restore::SavedStateRoot;
359
360 #[derive(Protobuf, SavedStateRoot)]
361 #[mesh(package = "chipset.i440bx.host_pci_bridge")]
362 pub struct SavedState {
363 #[mesh(1)]
364 pub host_pci_dram1: u32,
365 #[mesh(2)]
366 pub host_pci_dram2: u32,
367 #[mesh(3)]
368 pub pam_reg1: u32,
369 #[mesh(4)]
370 pub pam_reg2: u32,
371 #[mesh(5)]
372 pub bios_scratch1: u32,
373 #[mesh(6)]
374 pub bios_scratch2: u32,
375 #[mesh(7)]
376 pub smm_config_word: u16,
377 #[mesh(8)]
378 pub cfg_space: <ConfigSpaceType0Emulator as SaveRestore>::SavedState,
379 }
380 }
381
382 impl SaveRestore for HostPciBridge {
383 type SavedState = state::SavedState;
384
385 fn save(&mut self) -> Result<Self::SavedState, SaveError> {
386 let HostPciBridgeState {
387 host_pci_dram1,
388 host_pci_dram2,
389 pam_reg1,
390 pam_reg2,
391 bios_scratch1,
392 bios_scratch2,
393 smm_config_word,
394 } = self.state;
395
396 Ok(state::SavedState {
397 host_pci_dram1,
398 host_pci_dram2,
399 pam_reg1,
400 pam_reg2,
401 bios_scratch1,
402 bios_scratch2,
403 smm_config_word,
404 cfg_space: self.cfg_space.save()?,
405 })
406 }
407
408 fn restore(&mut self, state: Self::SavedState) -> Result<(), RestoreError> {
409 let state::SavedState {
410 host_pci_dram1,
411 host_pci_dram2,
412 pam_reg1,
413 pam_reg2,
414 bios_scratch1,
415 bios_scratch2,
416 smm_config_word,
417 cfg_space,
418 } = state;
419
420 self.state = HostPciBridgeState {
421 host_pci_dram1,
422 host_pci_dram2,
423 pam_reg1,
424 pam_reg2,
425 bios_scratch1,
426 bios_scratch2,
427 smm_config_word,
428 };
429
430 self.adjust_bios_override_ranges(pam_reg1, pam_reg2, true);
431
432 self.cfg_space.restore(cfg_space)?;
433
434 Ok(())
435 }
436 }
437}