1pub mod resolver;
7
8use chipset::pm::PowerAction;
9use chipset::pm::PowerManagementDevice;
10use chipset_device::ChipsetDevice;
11use chipset_device::interrupt::LineInterruptTarget;
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 chipset_device::pio::ControlPortIoIntercept;
18use chipset_device::pio::PortIoIntercept;
19use chipset_device::pio::RegisterPortIoIntercept;
20use inspect::Inspect;
21use inspect::InspectMut;
22use open_enum::open_enum;
23use pci_core::cfg_space_emu::ConfigSpaceType0Emulator;
24use pci_core::cfg_space_emu::DeviceBars;
25use pci_core::spec::hwid::ClassCode;
26use pci_core::spec::hwid::HardwareIds;
27use pci_core::spec::hwid::ProgrammingInterface;
28use pci_core::spec::hwid::Subclass;
29use vmcore::device_state::ChangeDeviceState;
30
31pub mod io_ports {
33 pub const DEFAULT_DYN_BASE: u16 = 0x400;
43
44 pub const CONTROL_PORT: u16 = 0xB2;
45 pub const STATUS_PORT: u16 = 0xB3;
46}
47
48#[derive(Debug)]
49enum StaticReg {
50 Control,
51 Status,
52}
53
54struct Piix4PmRt {
55 pio_static_control: Box<dyn ControlPortIoIntercept>,
56 pio_static_status: Box<dyn ControlPortIoIntercept>,
57}
58
59impl std::fmt::Debug for Piix4PmRt {
60 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
61 f.debug_struct("Piix4PmRt").finish()
62 }
63}
64
65#[derive(Debug, Inspect)]
66struct Piix4PmState {
67 power_status: u8,
68 power_control: u8,
69
70 smbus_io_enabled: bool,
71 #[inspect(hex)]
72 base_io_addr: u16,
73 base_io_enable: bool,
74 counter_info_a: u32,
75 counter_info_b: u32,
76 general_purpose_config_info: u32,
77 #[inspect(hex, iter_by_index)]
78 device_resource_flags: [u32; 10],
79 #[inspect(hex, iter_by_index)]
80 device_activity_flags: [u32; 2],
81}
82
83impl Piix4PmState {
84 fn new() -> Self {
85 Self {
86 power_status: 0,
87 power_control: 0,
88
89 smbus_io_enabled: false,
90 base_io_addr: 0,
91 base_io_enable: false,
92 counter_info_a: 0,
93 counter_info_b: 0,
94 general_purpose_config_info: 0,
95 device_resource_flags: [0; 10],
96 device_activity_flags: [0; 2],
97 }
98 }
99}
100
101#[derive(InspectMut)]
105pub struct Piix4Pm {
106 #[inspect(skip)]
108 rt: Piix4PmRt,
109
110 #[inspect(mut)]
112 inner: PowerManagementDevice,
113 cfg_space: ConfigSpaceType0Emulator,
114
115 state: Piix4PmState,
117}
118
119impl Piix4Pm {
120 pub fn new(
128 inner: PowerManagementDevice,
129 register_pio: &mut dyn RegisterPortIoIntercept,
130 ) -> Self {
131 let cfg_space = ConfigSpaceType0Emulator::new(
132 HardwareIds {
133 vendor_id: 0x8086,
134 device_id: 0x7113,
135 revision_id: 0x02,
136 prog_if: ProgrammingInterface::NONE,
137 sub_class: Subclass::BRIDGE_OTHER,
138 base_class: ClassCode::BRIDGE,
139 type0_sub_vendor_id: 0,
140 type0_sub_system_id: 0,
141 },
142 Vec::new(),
143 Vec::new(),
144 DeviceBars::new(),
145 );
146
147 let mut pio_static_control = register_pio.new_io_region("control", 1);
148 let mut pio_static_status = register_pio.new_io_region("status", 1);
149
150 pio_static_control.map(io_ports::CONTROL_PORT);
151 pio_static_status.map(io_ports::STATUS_PORT);
152
153 Self {
154 inner,
155 cfg_space,
156 rt: Piix4PmRt {
157 pio_static_control,
158 pio_static_status,
159 },
160 state: Piix4PmState::new(),
161 }
162 }
163
164 fn update_io_mappings(&mut self) {
165 if self.state.base_io_enable && self.state.base_io_addr != 0 {
166 self.inner
167 .update_dynamic_pio_mappings(Some(self.state.base_io_addr))
168 } else {
169 self.inner.update_dynamic_pio_mappings(None)
170 }
171 }
172
173 fn read_static(&mut self, reg: StaticReg, data: &mut [u8]) {
174 if data.len() != 1 {
175 tracelimit::warn_ratelimited!(?reg, ?data, "unexpected read");
176 return;
177 }
178
179 data[0] = match reg {
180 StaticReg::Control => self.state.power_control,
181 StaticReg::Status => self.state.power_status,
182 }
183 }
184
185 fn write_static(&mut self, reg: StaticReg, data: &[u8]) {
186 if data.len() != 1 {
187 tracelimit::warn_ratelimited!(?reg, ?data, "unexpected write");
188 return;
189 }
190
191 let data = data[0];
192 match reg {
193 StaticReg::Control => {
194 if self.state.device_activity_flags[1] & 1 << 25 != 0 {
197 if data == 0xE1 {
209 self.inner.pcat_facp_acpi_enable(true);
210 } else if data == 0x1E {
211 self.inner.pcat_facp_acpi_enable(false);
212 }
213 }
214
215 let old_control = self.state.power_control;
216 self.state.power_control = data;
217
218 if old_control == b'E' && self.state.power_control == b'T' {
223 (self.inner.power_action())(PowerAction::PowerOff)
231 }
232 }
233 StaticReg::Status => self.state.power_status = data,
234 }
235 }
236}
237
238impl ChangeDeviceState for Piix4Pm {
239 fn start(&mut self) {}
240
241 async fn stop(&mut self) {}
242
243 async fn reset(&mut self) {
244 self.inner.reset().await;
245 self.cfg_space.reset();
246 self.state = Piix4PmState::new();
247
248 self.update_io_mappings()
249 }
250}
251
252impl ChipsetDevice for Piix4Pm {
253 fn supports_pio(&mut self) -> Option<&mut dyn PortIoIntercept> {
254 Some(self)
255 }
256
257 fn supports_pci(&mut self) -> Option<&mut dyn PciConfigSpace> {
258 Some(self)
259 }
260
261 fn supports_line_interrupt_target(&mut self) -> Option<&mut dyn LineInterruptTarget> {
262 Some(self)
263 }
264}
265
266impl PortIoIntercept for Piix4Pm {
267 fn io_read(&mut self, io_port: u16, data: &mut [u8]) -> IoResult {
268 if let Some(0) = self.rt.pio_static_control.offset_of(io_port) {
270 self.read_static(StaticReg::Control, data);
271 return IoResult::Ok;
272 }
273
274 if let Some(0) = self.rt.pio_static_status.offset_of(io_port) {
276 self.read_static(StaticReg::Status, data);
277 return IoResult::Ok;
278 }
279
280 self.inner.io_read(io_port, data)
281 }
282
283 fn io_write(&mut self, io_port: u16, data: &[u8]) -> IoResult {
284 if let Some(0) = self.rt.pio_static_control.offset_of(io_port) {
286 self.write_static(StaticReg::Control, data);
287
288 self.inner.check_interrupt_assertion();
289 return IoResult::Ok;
290 }
291
292 if let Some(0) = self.rt.pio_static_status.offset_of(io_port) {
294 self.write_static(StaticReg::Status, data);
295
296 self.inner.check_interrupt_assertion();
297 return IoResult::Ok;
298 }
299
300 self.inner.io_write(io_port, data)
301 }
302}
303
304impl LineInterruptTarget for Piix4Pm {
310 fn set_irq(&mut self, vector: u32, high: bool) {
311 LineInterruptTarget::set_irq(&mut self.inner, vector, high)
312 }
313
314 fn valid_lines(&self) -> &[std::ops::RangeInclusive<u32>] {
315 &[0..=0, 8..=11]
317 }
318}
319
320impl PciConfigSpace for Piix4Pm {
322 fn pci_cfg_read(&mut self, offset: u16, mut value: ByteEnabledDwordRead<'_>) -> IoResult {
323 value.set(match ConfigSpace(offset) {
324 _ if offset == pci_core::spec::cfg_space::HeaderType00::STATUS_COMMAND.0 => {
328 let mut v = 0x02800000;
329 if self.state.smbus_io_enabled {
330 v |= 1;
331 }
332 v
333 }
334 _ if offset == pci_core::spec::cfg_space::HeaderType00::LATENCY_INTERRUPT.0 => {
336 let res = self.cfg_space.read_byte_enabled(offset, value.reborrow());
339 value.set(value.extract() & 0xff | (1 << 8));
340 return res;
341 }
342 _ if offset < 0x40 => return self.cfg_space.read_byte_enabled(offset, value),
343 ConfigSpace::IO_BASE => self.state.base_io_addr as u32 | 1,
345 ConfigSpace::COUNT_A => self.state.counter_info_a,
346 ConfigSpace::COUNT_B => self.state.counter_info_b,
347 ConfigSpace::GENERAL_PURPOSE => self.state.general_purpose_config_info,
348 ConfigSpace::ACTIVITY_A => self.state.device_activity_flags[0],
349 ConfigSpace::ACTIVITY_B => self.state.device_activity_flags[1],
350 ConfigSpace::RESOURCE_A => self.state.device_resource_flags[0],
351 ConfigSpace::RESOURCE_B => self.state.device_resource_flags[1],
352 ConfigSpace::RESOURCE_C => self.state.device_resource_flags[2],
353 ConfigSpace::RESOURCE_D => self.state.device_resource_flags[3],
354 ConfigSpace::RESOURCE_E => self.state.device_resource_flags[4],
355 ConfigSpace::RESOURCE_F => self.state.device_resource_flags[5],
356 ConfigSpace::RESOURCE_G => self.state.device_resource_flags[6],
357 ConfigSpace::RESOURCE_H => self.state.device_resource_flags[7],
358 ConfigSpace::RESOURCE_I => self.state.device_resource_flags[8],
359 ConfigSpace::RESOURCE_J => self.state.device_resource_flags[9],
360 ConfigSpace::IO_ENABLE => self.state.base_io_enable as u32,
361 ConfigSpace::SM_BASE | ConfigSpace::SM_HOST => 0, _ => {
363 tracing::debug!(?offset, "unimplemented config space read");
364 return IoResult::Err(IoError::InvalidRegister);
365 }
366 });
367
368 IoResult::Ok
369 }
370
371 fn pci_cfg_write(&mut self, offset: u16, value: ByteEnabledDwordWrite) -> IoResult {
372 match ConfigSpace(offset) {
373 _ if offset == pci_core::spec::cfg_space::HeaderType00::STATUS_COMMAND.0 => {
376 if value.valid_mask() & 1 != 0 {
377 self.state.smbus_io_enabled = value.extract() & 1 != 0;
378 }
379 return self.cfg_space.write_byte_enabled(offset, value);
380 }
381 _ if offset < 0x40 => return self.cfg_space.write_byte_enabled(offset, value),
382 ConfigSpace::IO_BASE => {
383 let value = value.merge_low(self.state.base_io_addr);
388 self.state.base_io_addr = value & 0xFFC0;
389 self.update_io_mappings()
390 }
391 ConfigSpace::COUNT_A => value.merge_into(&mut self.state.counter_info_a),
392 ConfigSpace::COUNT_B => value.merge_into(&mut self.state.counter_info_b),
393 ConfigSpace::GENERAL_PURPOSE => {
394 value.merge_into(&mut self.state.general_purpose_config_info)
395 }
396 ConfigSpace::ACTIVITY_A => value.merge_into(&mut self.state.device_activity_flags[0]),
397 ConfigSpace::ACTIVITY_B => value.merge_into(&mut self.state.device_activity_flags[1]),
398 ConfigSpace::RESOURCE_A => value.merge_into(&mut self.state.device_resource_flags[0]),
399 ConfigSpace::RESOURCE_B => value.merge_into(&mut self.state.device_resource_flags[1]),
400 ConfigSpace::RESOURCE_C => value.merge_into(&mut self.state.device_resource_flags[2]),
401 ConfigSpace::RESOURCE_D => value.merge_into(&mut self.state.device_resource_flags[3]),
402 ConfigSpace::RESOURCE_E => value.merge_into(&mut self.state.device_resource_flags[4]),
403 ConfigSpace::RESOURCE_F => value.merge_into(&mut self.state.device_resource_flags[5]),
404 ConfigSpace::RESOURCE_G => value.merge_into(&mut self.state.device_resource_flags[6]),
405 ConfigSpace::RESOURCE_H => value.merge_into(&mut self.state.device_resource_flags[7]),
406 ConfigSpace::RESOURCE_I => value.merge_into(&mut self.state.device_resource_flags[8]),
407 ConfigSpace::RESOURCE_J => value.merge_into(&mut self.state.device_resource_flags[9]),
408 ConfigSpace::IO_ENABLE => {
409 if value.valid_mask() & 1 != 0 {
410 self.state.base_io_enable = value.extract() & 1 != 0;
411 }
412 self.update_io_mappings()
413 }
414 ConfigSpace::SM_BASE | ConfigSpace::SM_HOST => {} _ => {
416 tracelimit::warn_ratelimited!(?offset, ?value, "unimplemented config space write");
417 return IoResult::Err(IoError::InvalidRegister);
418 }
419 }
420
421 IoResult::Ok
422 }
423
424 fn suggested_bdf(&mut self) -> Option<(u8, u8, u8)> {
425 Some((0, 7, 3)) }
427}
428
429open_enum! {
430 enum ConfigSpace: u16 {
431 IO_BASE = 0x40,
432 COUNT_A = 0x44,
433 COUNT_B = 0x48,
434 GENERAL_PURPOSE = 0x4C,
435 RESOURCE_D = 0x50,
436 ACTIVITY_A = 0x54,
437 ACTIVITY_B = 0x58,
438 RESOURCE_A = 0x5C,
439 RESOURCE_B = 0x60,
440 RESOURCE_C = 0x64,
441 RESOURCE_E = 0x68,
442 RESOURCE_F = 0x6C,
443 RESOURCE_G = 0x70,
444 RESOURCE_H = 0x74,
445 RESOURCE_I = 0x78,
446 RESOURCE_J = 0x7C,
447 IO_ENABLE = 0x80,
448 SM_BASE = 0x90,
449 SM_HOST = 0xD0,
450 }
451}
452
453mod save_restore {
454 use super::*;
455 use vmcore::save_restore::RestoreError;
456 use vmcore::save_restore::SaveError;
457 use vmcore::save_restore::SaveRestore;
458
459 mod state {
460 use chipset::pm::PowerManagementDevice;
461 use mesh::payload::Protobuf;
462 use pci_core::cfg_space_emu::ConfigSpaceType0Emulator;
463 use vmcore::save_restore::SaveRestore;
464 use vmcore::save_restore::SavedStateRoot;
465
466 #[derive(Protobuf, SavedStateRoot)]
467 #[mesh(package = "chipset.piix4.pm")]
468 pub struct SavedState {
469 #[mesh(1)]
470 pub power_status: u8,
471 #[mesh(2)]
472 pub power_control: u8,
473 #[mesh(3)]
474 pub smbus_io_enabled: bool,
475 #[mesh(4)]
476 pub base_io_addr: u16,
477 #[mesh(5)]
478 pub base_io_enable: bool,
479 #[mesh(6)]
480 pub counter_info_a: u32,
481 #[mesh(7)]
482 pub counter_info_b: u32,
483 #[mesh(8)]
484 pub general_purpose_config_info: u32,
485 #[mesh(9)]
486 pub device_resource_flags: [u32; 10],
487 #[mesh(10)]
488 pub device_activity_flags: [u32; 2],
489 #[mesh(11)]
490 pub cfg_space: <ConfigSpaceType0Emulator as SaveRestore>::SavedState,
491 #[mesh(12)]
492 pub inner: <PowerManagementDevice as SaveRestore>::SavedState,
493 }
494 }
495
496 impl SaveRestore for Piix4Pm {
497 type SavedState = state::SavedState;
498
499 fn save(&mut self) -> Result<Self::SavedState, SaveError> {
500 let Piix4PmState {
501 power_status,
502 power_control,
503 smbus_io_enabled,
504 base_io_addr,
505 base_io_enable,
506 counter_info_a,
507 counter_info_b,
508 general_purpose_config_info,
509 device_resource_flags,
510 device_activity_flags,
511 } = self.state;
512
513 let saved_state = state::SavedState {
514 power_status,
515 power_control,
516 smbus_io_enabled,
517 base_io_addr,
518 base_io_enable,
519 counter_info_a,
520 counter_info_b,
521 general_purpose_config_info,
522 device_resource_flags,
523 device_activity_flags,
524 cfg_space: self.cfg_space.save()?,
525 inner: self.inner.save()?,
526 };
527
528 Ok(saved_state)
529 }
530
531 fn restore(&mut self, state: Self::SavedState) -> Result<(), RestoreError> {
532 let state::SavedState {
533 power_status,
534 power_control,
535 smbus_io_enabled,
536 base_io_addr,
537 base_io_enable,
538 counter_info_a,
539 counter_info_b,
540 general_purpose_config_info,
541 device_resource_flags,
542 device_activity_flags,
543 cfg_space,
544 inner,
545 } = state;
546
547 let state = Piix4PmState {
548 power_status,
549 power_control,
550 smbus_io_enabled,
551 base_io_addr,
552 base_io_enable,
553 counter_info_a,
554 counter_info_b,
555 general_purpose_config_info,
556 device_resource_flags,
557 device_activity_flags,
558 };
559
560 self.state = state;
561
562 self.update_io_mappings();
563 self.cfg_space.restore(cfg_space)?;
564 self.inner.restore(inner)?;
565 Ok(())
566 }
567 }
568}