chipset_legacy/
piix4_pci_bus.rs1use chipset_device::ChipsetDevice;
7use chipset_device::io::IoResult;
8use chipset_device::pio::PortIoIntercept;
9use chipset_device::pio::RegisterPortIoIntercept;
10use chipset_device::poll_device::PollDevice;
11use inspect::InspectMut;
12use pci_bus::GenericPciBus;
13use vmcore::device_state::ChangeDeviceState;
14
15mod io_ports {
17 pub const PCI_ADDR_START: u16 = 0xCF8;
18 pub const RESET_CF9: u16 = 0xCF9; pub const PCI_DATA_START: u16 = 0xCFC;
20}
21
22#[derive(InspectMut)]
29pub struct Piix4PciBus {
30 #[inspect(skip)]
32 reset_evt: Box<dyn Fn() + Send + Sync>,
33
34 #[inspect(mut)]
36 bus: GenericPciBus,
37}
38
39impl Piix4PciBus {
40 pub fn new(
42 register_pio: &mut dyn RegisterPortIoIntercept,
43 reset_evt: Box<dyn Fn() + Send + Sync>,
44 ) -> Self {
45 Piix4PciBus {
46 reset_evt,
47 bus: GenericPciBus::new(
48 register_pio,
49 io_ports::PCI_ADDR_START,
50 io_ports::PCI_DATA_START,
51 ),
52 }
53 }
54
55 pub fn as_pci_bus(&mut self) -> &mut GenericPciBus {
58 &mut self.bus
59 }
60
61 fn handle_reset_cf9_read(&mut self, data: &mut [u8]) {
62 if data.len() != 1 {
63 tracelimit::warn_ratelimited!(len = ?data.len(), "unexpected RESET_CF9 read len");
64 return;
65 }
66
67 tracelimit::warn_ratelimited!("read from the RESET_CF9 io port");
68 data[0] = 0;
69 }
70
71 fn handle_reset_cf9_write(&mut self, data: &[u8]) {
72 if data.len() != 1 {
73 tracelimit::warn_ratelimited!(len = ?data.len(), "unexpected RESET_CF9 write len");
74 return;
75 }
76
77 if (data[0] & 0x6) != 0 {
78 tracing::info!("initiating guest reset via RESET_CF9");
79 (self.reset_evt)();
80 }
81 }
82}
83
84impl ChangeDeviceState for Piix4PciBus {
85 fn start(&mut self) {}
86
87 async fn stop(&mut self) {}
88
89 async fn reset(&mut self) {
90 self.bus.reset().await;
91 }
92}
93
94impl ChipsetDevice for Piix4PciBus {
95 fn supports_pio(&mut self) -> Option<&mut dyn PortIoIntercept> {
96 Some(self)
97 }
98
99 fn supports_poll_device(&mut self) -> Option<&mut dyn PollDevice> {
100 Some(self.as_pci_bus())
101 }
102}
103
104impl PortIoIntercept for Piix4PciBus {
105 fn io_read(&mut self, io_port: u16, data: &mut [u8]) -> IoResult {
106 if data.len() == 1 && io_port == io_ports::RESET_CF9 {
107 self.handle_reset_cf9_read(data);
108 return IoResult::Ok;
109 }
110
111 self.bus.io_read(io_port, data)
112 }
113
114 fn io_write(&mut self, io_port: u16, data: &[u8]) -> IoResult {
115 if data.len() == 1 && io_port == io_ports::RESET_CF9 {
116 self.handle_reset_cf9_write(data);
117 return IoResult::Ok;
118 }
119
120 self.bus.io_write(io_port, data)
121 }
122}
123
124mod save_restore {
125 use super::*;
126 use vmcore::save_restore::RestoreError;
127 use vmcore::save_restore::SaveError;
128 use vmcore::save_restore::SaveRestore;
129
130 mod state {
131 use mesh::payload::Protobuf;
132 use pci_bus::GenericPciBus;
133 use vmcore::save_restore::SaveRestore;
134 use vmcore::save_restore::SavedStateRoot;
135
136 #[derive(Protobuf, SavedStateRoot)]
137 #[mesh(package = "chipset.piix4.pci_bus")]
138 pub struct SavedState {
139 #[mesh(1)]
140 pub bus: <GenericPciBus as SaveRestore>::SavedState,
141 }
142 }
143
144 impl SaveRestore for Piix4PciBus {
145 type SavedState = state::SavedState;
146
147 fn save(&mut self) -> Result<Self::SavedState, SaveError> {
148 let Piix4PciBus { reset_evt: _, bus } = self;
149
150 let saved_state = state::SavedState { bus: bus.save()? };
151
152 Ok(saved_state)
153 }
154
155 fn restore(&mut self, state: Self::SavedState) -> Result<(), RestoreError> {
156 let state::SavedState { bus } = state;
157
158 self.bus.restore(bus)?;
159
160 Ok(())
161 }
162 }
163}