1use chipset::pm::PmTimerAssist;
7use chipset::pm::PowerAction;
8use chipset::pm::PowerActionFn;
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::PciConfigSpace;
15use chipset_device::pio::ControlPortIoIntercept;
16use chipset_device::pio::PortIoIntercept;
17use chipset_device::pio::RegisterPortIoIntercept;
18use inspect::Inspect;
19use inspect::InspectMut;
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;
28use vmcore::line_interrupt::LineInterrupt;
29use vmcore::vmtime::VmTimeAccess;
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(
121 power_action: PowerActionFn,
122 interrupt: LineInterrupt,
123 register_pio: &mut dyn RegisterPortIoIntercept,
124 vmtime: VmTimeAccess,
125 pm_timer_assist: Option<Box<dyn PmTimerAssist>>,
126 ) -> Self {
127 let cfg_space = ConfigSpaceType0Emulator::new(
128 HardwareIds {
129 vendor_id: 0x8086,
130 device_id: 0x7113,
131 revision_id: 0x02,
132 prog_if: ProgrammingInterface::NONE,
133 sub_class: Subclass::BRIDGE_OTHER,
134 base_class: ClassCode::BRIDGE,
135 type0_sub_vendor_id: 0,
136 type0_sub_system_id: 0,
137 },
138 Vec::new(),
139 DeviceBars::new(),
140 );
141
142 let mut pio_static_control = register_pio.new_io_region("control", 1);
143 let mut pio_static_status = register_pio.new_io_region("status", 1);
144
145 pio_static_control.map(io_ports::CONTROL_PORT);
146 pio_static_status.map(io_ports::STATUS_PORT);
147
148 Self {
149 inner: PowerManagementDevice::new(
150 power_action,
151 interrupt,
152 register_pio,
153 vmtime,
154 None, pm_timer_assist,
156 ),
157 cfg_space,
158 rt: Piix4PmRt {
159 pio_static_control,
160 pio_static_status,
161 },
162 state: Piix4PmState::new(),
163 }
164 }
165
166 fn update_io_mappings(&mut self) {
167 if self.state.base_io_enable && self.state.base_io_addr != 0 {
168 self.inner
169 .update_dynamic_pio_mappings(Some(self.state.base_io_addr))
170 } else {
171 self.inner.update_dynamic_pio_mappings(None)
172 }
173 }
174
175 fn read_static(&mut self, reg: StaticReg, data: &mut [u8]) {
176 if data.len() != 1 {
177 tracelimit::warn_ratelimited!(?reg, ?data, "unexpected read");
178 return;
179 }
180
181 data[0] = match reg {
182 StaticReg::Control => self.state.power_control,
183 StaticReg::Status => self.state.power_status,
184 }
185 }
186
187 fn write_static(&mut self, reg: StaticReg, data: &[u8]) {
188 if data.len() != 1 {
189 tracelimit::warn_ratelimited!(?reg, ?data, "unexpected write");
190 return;
191 }
192
193 let data = data[0];
194 match reg {
195 StaticReg::Control => {
196 if self.state.device_activity_flags[1] & 1 << 25 != 0 {
199 if data == 0xE1 {
211 self.inner.pcat_facp_acpi_enable(true);
212 } else if data == 0x1E {
213 self.inner.pcat_facp_acpi_enable(false);
214 }
215 }
216
217 let old_control = self.state.power_control;
218 self.state.power_control = data;
219
220 if old_control == b'E' && self.state.power_control == b'T' {
225 (self.inner.power_action())(PowerAction::PowerOff)
233 }
234 }
235 StaticReg::Status => self.state.power_status = data,
236 }
237 }
238}
239
240impl ChangeDeviceState for Piix4Pm {
241 fn start(&mut self) {}
242
243 async fn stop(&mut self) {}
244
245 async fn reset(&mut self) {
246 self.inner.reset().await;
247 self.cfg_space.reset();
248 self.state = Piix4PmState::new();
249
250 self.update_io_mappings()
251 }
252}
253
254impl ChipsetDevice for Piix4Pm {
255 fn supports_pio(&mut self) -> Option<&mut dyn PortIoIntercept> {
256 Some(self)
257 }
258
259 fn supports_pci(&mut self) -> Option<&mut dyn PciConfigSpace> {
260 Some(self)
261 }
262
263 fn supports_line_interrupt_target(&mut self) -> Option<&mut dyn LineInterruptTarget> {
264 Some(self)
265 }
266}
267
268impl PortIoIntercept for Piix4Pm {
269 fn io_read(&mut self, io_port: u16, data: &mut [u8]) -> IoResult {
270 if let Some(0) = self.rt.pio_static_control.offset_of(io_port) {
272 self.read_static(StaticReg::Control, data);
273 return IoResult::Ok;
274 }
275
276 if let Some(0) = self.rt.pio_static_status.offset_of(io_port) {
278 self.read_static(StaticReg::Status, data);
279 return IoResult::Ok;
280 }
281
282 self.inner.io_read(io_port, data)
283 }
284
285 fn io_write(&mut self, io_port: u16, data: &[u8]) -> IoResult {
286 if let Some(0) = self.rt.pio_static_control.offset_of(io_port) {
288 self.write_static(StaticReg::Control, data);
289
290 self.inner.check_interrupt_assertion();
291 return IoResult::Ok;
292 }
293
294 if let Some(0) = self.rt.pio_static_status.offset_of(io_port) {
296 self.write_static(StaticReg::Status, data);
297
298 self.inner.check_interrupt_assertion();
299 return IoResult::Ok;
300 }
301
302 self.inner.io_write(io_port, data)
303 }
304}
305
306impl LineInterruptTarget for Piix4Pm {
312 fn set_irq(&mut self, vector: u32, high: bool) {
313 LineInterruptTarget::set_irq(&mut self.inner, vector, high)
314 }
315
316 fn valid_lines(&self) -> &[std::ops::RangeInclusive<u32>] {
317 &[0..=0, 8..=11]
319 }
320}
321
322impl PciConfigSpace for Piix4Pm {
324 fn pci_cfg_read(&mut self, offset: u16, value: &mut u32) -> IoResult {
325 *value = match ConfigSpace(offset) {
326 _ if offset == pci_core::spec::cfg_space::HeaderType00::STATUS_COMMAND.0 => {
330 let mut v = 0x02800000;
331 if self.state.smbus_io_enabled {
332 v |= 1;
333 }
334 v
335 }
336 _ if offset == pci_core::spec::cfg_space::HeaderType00::LATENCY_INTERRUPT.0 => {
338 let res = self.cfg_space.read_u32(offset, value);
341 *value = *value & 0xff | (1 << 8);
342 return res;
343 }
344 _ if offset < 0x40 => return self.cfg_space.read_u32(offset, value),
345 ConfigSpace::IO_BASE => self.state.base_io_addr as u32 | 1,
347 ConfigSpace::COUNT_A => self.state.counter_info_a,
348 ConfigSpace::COUNT_B => self.state.counter_info_b,
349 ConfigSpace::GENERAL_PURPOSE => self.state.general_purpose_config_info,
350 ConfigSpace::ACTIVITY_A => self.state.device_activity_flags[0],
351 ConfigSpace::ACTIVITY_B => self.state.device_activity_flags[1],
352 ConfigSpace::RESOURCE_A => self.state.device_resource_flags[0],
353 ConfigSpace::RESOURCE_B => self.state.device_resource_flags[1],
354 ConfigSpace::RESOURCE_C => self.state.device_resource_flags[2],
355 ConfigSpace::RESOURCE_D => self.state.device_resource_flags[3],
356 ConfigSpace::RESOURCE_E => self.state.device_resource_flags[4],
357 ConfigSpace::RESOURCE_F => self.state.device_resource_flags[5],
358 ConfigSpace::RESOURCE_G => self.state.device_resource_flags[6],
359 ConfigSpace::RESOURCE_H => self.state.device_resource_flags[7],
360 ConfigSpace::RESOURCE_I => self.state.device_resource_flags[8],
361 ConfigSpace::RESOURCE_J => self.state.device_resource_flags[9],
362 ConfigSpace::IO_ENABLE => self.state.base_io_enable as u32,
363 ConfigSpace::SM_BASE | ConfigSpace::SM_HOST => 0, _ => {
365 tracing::debug!(?offset, "unimplemented config space read");
366 return IoResult::Err(IoError::InvalidRegister);
367 }
368 };
369
370 IoResult::Ok
371 }
372
373 fn pci_cfg_write(&mut self, offset: u16, value: u32) -> IoResult {
374 match ConfigSpace(offset) {
375 _ if offset == pci_core::spec::cfg_space::HeaderType00::STATUS_COMMAND.0 => {
378 self.state.smbus_io_enabled = value & 1 != 0;
379 return self.cfg_space.write_u32(offset, value);
380 }
381 _ if offset < 0x40 => return self.cfg_space.write_u32(offset, value),
382 ConfigSpace::IO_BASE => {
383 self.state.base_io_addr = (value & 0xFFC0) as u16;
388 self.update_io_mappings()
389 }
390 ConfigSpace::COUNT_A => self.state.counter_info_a = value,
391 ConfigSpace::COUNT_B => self.state.counter_info_b = value,
392 ConfigSpace::GENERAL_PURPOSE => self.state.general_purpose_config_info = value,
393 ConfigSpace::ACTIVITY_A => self.state.device_activity_flags[0] = value,
394 ConfigSpace::ACTIVITY_B => self.state.device_activity_flags[1] = value,
395 ConfigSpace::RESOURCE_A => self.state.device_resource_flags[0] = value,
396 ConfigSpace::RESOURCE_B => self.state.device_resource_flags[1] = value,
397 ConfigSpace::RESOURCE_C => self.state.device_resource_flags[2] = value,
398 ConfigSpace::RESOURCE_D => self.state.device_resource_flags[3] = value,
399 ConfigSpace::RESOURCE_E => self.state.device_resource_flags[4] = value,
400 ConfigSpace::RESOURCE_F => self.state.device_resource_flags[5] = value,
401 ConfigSpace::RESOURCE_G => self.state.device_resource_flags[6] = value,
402 ConfigSpace::RESOURCE_H => self.state.device_resource_flags[7] = value,
403 ConfigSpace::RESOURCE_I => self.state.device_resource_flags[8] = value,
404 ConfigSpace::RESOURCE_J => self.state.device_resource_flags[9] = value,
405 ConfigSpace::IO_ENABLE => {
406 self.state.base_io_enable = value & 1 != 0;
407 self.update_io_mappings()
408 }
409 ConfigSpace::SM_BASE | ConfigSpace::SM_HOST => {} _ => {
411 tracelimit::warn_ratelimited!(?offset, ?value, "unimplemented config space write");
412 return IoResult::Err(IoError::InvalidRegister);
413 }
414 }
415
416 IoResult::Ok
417 }
418
419 fn suggested_bdf(&mut self) -> Option<(u8, u8, u8)> {
420 Some((0, 7, 3)) }
422}
423
424open_enum! {
425 enum ConfigSpace: u16 {
426 IO_BASE = 0x40,
427 COUNT_A = 0x44,
428 COUNT_B = 0x48,
429 GENERAL_PURPOSE = 0x4C,
430 RESOURCE_D = 0x50,
431 ACTIVITY_A = 0x54,
432 ACTIVITY_B = 0x58,
433 RESOURCE_A = 0x5C,
434 RESOURCE_B = 0x60,
435 RESOURCE_C = 0x64,
436 RESOURCE_E = 0x68,
437 RESOURCE_F = 0x6C,
438 RESOURCE_G = 0x70,
439 RESOURCE_H = 0x74,
440 RESOURCE_I = 0x78,
441 RESOURCE_J = 0x7C,
442 IO_ENABLE = 0x80,
443 SM_BASE = 0x90,
444 SM_HOST = 0xD0,
445 }
446}
447
448mod save_restore {
449 use super::*;
450 use vmcore::save_restore::RestoreError;
451 use vmcore::save_restore::SaveError;
452 use vmcore::save_restore::SaveRestore;
453
454 mod state {
455 use chipset::pm::PowerManagementDevice;
456 use mesh::payload::Protobuf;
457 use pci_core::cfg_space_emu::ConfigSpaceType0Emulator;
458 use vmcore::save_restore::SaveRestore;
459 use vmcore::save_restore::SavedStateRoot;
460
461 #[derive(Protobuf, SavedStateRoot)]
462 #[mesh(package = "chipset.piix4.pm")]
463 pub struct SavedState {
464 #[mesh(1)]
465 pub power_status: u8,
466 #[mesh(2)]
467 pub power_control: u8,
468 #[mesh(3)]
469 pub smbus_io_enabled: bool,
470 #[mesh(4)]
471 pub base_io_addr: u16,
472 #[mesh(5)]
473 pub base_io_enable: bool,
474 #[mesh(6)]
475 pub counter_info_a: u32,
476 #[mesh(7)]
477 pub counter_info_b: u32,
478 #[mesh(8)]
479 pub general_purpose_config_info: u32,
480 #[mesh(9)]
481 pub device_resource_flags: [u32; 10],
482 #[mesh(10)]
483 pub device_activity_flags: [u32; 2],
484 #[mesh(11)]
485 pub cfg_space: <ConfigSpaceType0Emulator as SaveRestore>::SavedState,
486 #[mesh(12)]
487 pub inner: <PowerManagementDevice as SaveRestore>::SavedState,
488 }
489 }
490
491 impl SaveRestore for Piix4Pm {
492 type SavedState = state::SavedState;
493
494 fn save(&mut self) -> Result<Self::SavedState, SaveError> {
495 let Piix4PmState {
496 power_status,
497 power_control,
498 smbus_io_enabled,
499 base_io_addr,
500 base_io_enable,
501 counter_info_a,
502 counter_info_b,
503 general_purpose_config_info,
504 device_resource_flags,
505 device_activity_flags,
506 } = self.state;
507
508 let saved_state = state::SavedState {
509 power_status,
510 power_control,
511 smbus_io_enabled,
512 base_io_addr,
513 base_io_enable,
514 counter_info_a,
515 counter_info_b,
516 general_purpose_config_info,
517 device_resource_flags,
518 device_activity_flags,
519 cfg_space: self.cfg_space.save()?,
520 inner: self.inner.save()?,
521 };
522
523 Ok(saved_state)
524 }
525
526 fn restore(&mut self, state: Self::SavedState) -> Result<(), RestoreError> {
527 let state::SavedState {
528 power_status,
529 power_control,
530 smbus_io_enabled,
531 base_io_addr,
532 base_io_enable,
533 counter_info_a,
534 counter_info_b,
535 general_purpose_config_info,
536 device_resource_flags,
537 device_activity_flags,
538 cfg_space,
539 inner,
540 } = state;
541
542 let state = Piix4PmState {
543 power_status,
544 power_control,
545 smbus_io_enabled,
546 base_io_addr,
547 base_io_enable,
548 counter_info_a,
549 counter_info_b,
550 general_purpose_config_info,
551 device_resource_flags,
552 device_activity_flags,
553 };
554
555 self.state = state;
556
557 self.update_io_mappings();
558 self.cfg_space.restore(cfg_space)?;
559 self.inner.restore(inner)?;
560 Ok(())
561 }
562 }
563}