1use chipset_device::ChipsetDevice;
32use inspect::Inspect;
33use inspect::InspectMut;
34use open_enum::open_enum;
35use thiserror::Error;
36use vmcore::device_state::ChangeDeviceState;
37
38const NUM_SIO_DEVICES: usize = 9;
39
40#[derive(Debug, Error)]
41pub enum SioConfigError {
42 #[error("sio controller not in config mode: {0:?} state")]
43 NotInConfigMode(ConfigIdxState),
44}
45
46#[derive(Default, Debug, Inspect, Copy, Clone)]
47#[inspect(debug)]
48pub enum ConfigIdxState {
49 #[default]
51 Idle,
52 Handshake,
54 Ready,
56}
57
58open_enum! {
59 #[derive(Default, Inspect)]
61 #[inspect(debug)]
62 enum ConfigRegister: u8 {
63 LOGICAL_DEVICE_NUMBER = 0x07,
65 DEVICE_ID = 0x20,
66 REVISION_NUMBER = 0x21,
67 POWER_DOWN_CONTROL = 0x22,
68 PNP_CONTROL = 0x24,
69
70 ENABLE_DEVICE = 0x30,
72 IO_BASE_MSB0 = 0x60,
73 IO_BASE_LSB0 = 0x61,
74 IO_BASE_MSB1 = 0x62,
75 IO_BASE_LSB1 = 0x63,
76
77 IRQ_SELECT1 = 0x70,
78 IRQ_TYPE1 = 0x71,
79 IRQ_SELECT2 = 0x72,
80 IRQ_TYPE2 = 0x73,
81 DMA_CONFIG1 = 0x74,
82 DMA_CONFIG2 = 0x75,
83
84 ADDRESS_UNDOCUMENTED = 0xBA,
85
86 DEVICE_BIT_CONFIG0 = 0xE8,
87 DEVICE_BIT_CONFIG1 = 0xE9,
88 DEVICE_BIT_CONFIG2 = 0xEA,
89 DEVICE_BIT_CONFIG3 = 0xEB,
90 DEVICE_BIT_CONFIG4 = 0xEC,
91 DEVICE_BIT_CONFIG5 = 0xED,
92 DEVICE_BIT_CONFIG6 = 0xEE,
93 DEVICE_BIT_CONFIG7 = 0xEF,
94
95 DEVICE_CONFIG0 = 0xF0,
96 DEVICE_CONFIG1 = 0xF1,
97 DEVICE_CONFIG2 = 0xF2,
98 DEVICE_CONFIG3 = 0xF3,
99 DEVICE_CONFIG4 = 0xF4,
100 DEVICE_CONFIG5 = 0xF5,
101 }
102}
103
104impl ConfigRegister {
105 fn is_device_specific(&self) -> bool {
107 self.0 >= 0x30
108 }
109}
110
111open_enum! {
112 #[derive(Default, Inspect)]
113 #[inspect(debug)]
114 enum LogicalDeviceIndex: u8 {
115 FLOPPY_CONTROLLER = 0,
116 PARALLEL_PORT = 1,
117 COM1_PORT = 2,
118 COM2_PORT = 3,
119 RTC = 4,
120 KEYBOARD_CONTROLLER = 5,
121 INFRARED_PORT = 6,
122 AUX_IO_CONTROL1 = 7,
123 AUX_IO_CONTROL2 = 8,
124 }
125}
126
127#[derive(Debug, Default, Copy, Clone, Inspect)]
128struct LogicalDeviceData {
129 enabled: bool,
130 #[inspect(hex, iter_by_index)]
131 io_port_base: [u16; 2],
132 #[inspect(bytes)]
133 irq_vector: [u8; 2],
134 #[inspect(bytes)]
135 dma_channel: [u8; 2],
136 #[inspect(bytes)]
140 config_data: [u8; 8],
141}
142
143impl LogicalDeviceData {
144 fn default_data() -> [Self; NUM_SIO_DEVICES] {
154 let mut defaults: [Self; NUM_SIO_DEVICES] = [Self::default(); NUM_SIO_DEVICES];
155
156 defaults[LogicalDeviceIndex::FLOPPY_CONTROLLER.0 as usize] = Self {
157 enabled: true,
158 io_port_base: [0x3F0, 0x370], irq_vector: [6, 0],
160 dma_channel: [2, 0],
161 config_data: [0x0E, 0x00, 0xFF, 0x00, 0x00, 0x00, 0x00, 0x00],
162 };
163
164 defaults[LogicalDeviceIndex::PARALLEL_PORT.0 as usize] = Self {
167 enabled: false,
168 io_port_base: [0x0000; 2],
169 irq_vector: [0, 0],
170 dma_channel: [4, 0],
171 config_data: [0x3C, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00],
172 };
173
174 defaults[LogicalDeviceIndex::COM1_PORT.0 as usize] = Self {
175 enabled: true,
176 io_port_base: [0x3F8, 0],
177 irq_vector: [3, 0],
178 dma_channel: [4, 0],
179 config_data: [0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00],
180 };
181
182 defaults[LogicalDeviceIndex::COM2_PORT.0 as usize] = Self {
183 enabled: true,
184 io_port_base: [0x2F8, 0],
185 irq_vector: [4, 0],
186 dma_channel: [4, 0],
187 config_data: [0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00],
188 };
189
190 defaults[LogicalDeviceIndex::RTC.0 as usize] = Self {
191 enabled: true,
192 io_port_base: [0x70, 0],
193 irq_vector: [8, 0],
194 dma_channel: [4, 0],
195 config_data: [0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00],
196 };
197
198 defaults[LogicalDeviceIndex::KEYBOARD_CONTROLLER.0 as usize] = Self {
199 enabled: true,
200 io_port_base: [0x60, 0x64], irq_vector: [1, 12], dma_channel: [4, 0],
203 config_data: [0x40, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00],
204 };
205
206 defaults[LogicalDeviceIndex::INFRARED_PORT.0 as usize] = Self {
207 enabled: false,
208 io_port_base: [0x0000; 2],
209 irq_vector: [0, 0],
210 dma_channel: [4, 4],
211 config_data: [0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00],
212 };
213
214 defaults[LogicalDeviceIndex::AUX_IO_CONTROL1.0 as usize] = Self {
215 enabled: false,
216 io_port_base: [0x0000; 2],
217 irq_vector: [0, 0],
218 dma_channel: [4, 0],
219 config_data: [0xEF, 0x00, 0x00, 0x00, 0x00, 0x40, 0x00, 0x00],
220 };
221
222 defaults[LogicalDeviceIndex::AUX_IO_CONTROL2.0 as usize] = Self {
223 enabled: false,
224 io_port_base: [0x0000; 2],
225 irq_vector: [0, 0],
226 dma_channel: [4, 4],
227 config_data: [0xEF, 0x00, 0x00, 0x00, 0x00, 0x40, 0x00, 0x00],
228 };
229
230 defaults
231 }
232}
233
234#[derive(Debug, Inspect)]
235struct SioControllerState {
236 config_idx_state: ConfigIdxState,
237 config_idx: ConfigRegister,
238 device_idx: LogicalDeviceIndex,
239 #[inspect(iter_by_index)]
240 device_data: [LogicalDeviceData; NUM_SIO_DEVICES],
241}
242
243#[derive(Debug, InspectMut)]
244pub struct SioController {
245 state: SioControllerState,
247}
248
249impl Default for SioController {
250 fn default() -> Self {
251 Self {
252 state: SioControllerState {
253 config_idx_state: ConfigIdxState::default(),
254 config_idx: ConfigRegister::default(),
255 device_idx: LogicalDeviceIndex::default(),
256 device_data: LogicalDeviceData::default_data(),
257 },
258 }
259 }
260}
261
262impl SioController {
263 pub fn update_config_state(&mut self, value: u8) {
264 let prev_state = self.state.config_idx_state;
265
266 self.state.config_idx_state = match (self.state.config_idx_state, value) {
275 (ConfigIdxState::Ready, 0xAA) => ConfigIdxState::Idle,
277 (ConfigIdxState::Ready, _) => {
279 self.state.config_idx = ConfigRegister(value);
282 ConfigIdxState::Ready
284 }
285 (ConfigIdxState::Idle, 0x87) => ConfigIdxState::Handshake,
287 (ConfigIdxState::Handshake, 0x87) => ConfigIdxState::Ready,
288 (_, _) => ConfigIdxState::Idle,
290 };
291
292 tracing::trace!(
293 ?value,
294 ?prev_state,
295 cur_state = ?self.state.config_idx_state,
296 register = ?self.state.config_idx,
297 "update sio config state"
298 );
299 }
300
301 pub fn config_read(&mut self) -> Result<u8, SioConfigError> {
302 if !matches!(self.state.config_idx_state, ConfigIdxState::Ready) {
304 let state = self.state.config_idx_state;
305 self.state.config_idx_state = ConfigIdxState::Idle;
306 return Err(SioConfigError::NotInConfigMode(state));
307 }
308
309 if !self.state.config_idx.is_device_specific() {
311 let value = match self.state.config_idx {
312 ConfigRegister::DEVICE_ID => 0x97,
313 ConfigRegister::REVISION_NUMBER => 0x71,
314 _ => {
315 tracelimit::warn_ratelimited!(
316 ?self.state.config_idx,
317 "unexpected config register read"
318 );
319 0x00
320 }
321 };
322 return Ok(value);
323 }
324
325 let dev = match self
326 .state
327 .device_data
328 .get_mut(self.state.device_idx.0 as usize)
329 {
330 Some(dev) => dev,
331 None => {
332 tracelimit::warn_ratelimited!(
333 logical_device_number = self.state.device_idx.0,
334 "invalid logical device index"
335 );
336
337 return Ok(0);
338 }
339 };
340
341 let value = match self.state.config_idx {
343 ConfigRegister::ENABLE_DEVICE => dev.enabled as u8,
344 ConfigRegister::IO_BASE_MSB0 => (dev.io_port_base[0] >> 8) as u8,
345 ConfigRegister::IO_BASE_LSB0 => dev.io_port_base[0] as u8,
346 ConfigRegister::IO_BASE_MSB1 => (dev.io_port_base[1] >> 8) as u8,
347 ConfigRegister::IO_BASE_LSB1 => dev.io_port_base[1] as u8,
348 ConfigRegister::IRQ_SELECT1 => dev.irq_vector[0],
349 ConfigRegister::IRQ_TYPE1 => 0b10, ConfigRegister::IRQ_SELECT2 => dev.irq_vector[1],
351 ConfigRegister::IRQ_TYPE2 => 0b10, ConfigRegister::DMA_CONFIG1 => dev.dma_channel[0],
353 ConfigRegister::DMA_CONFIG2 => dev.dma_channel[1],
354 ConfigRegister::DEVICE_CONFIG0 => dev.config_data[0],
355 ConfigRegister::DEVICE_CONFIG1 => dev.config_data[1],
356 ConfigRegister::DEVICE_CONFIG2 => dev.config_data[2],
357 ConfigRegister::DEVICE_CONFIG3 => dev.config_data[3],
358 ConfigRegister::DEVICE_CONFIG4 => dev.config_data[4],
359 ConfigRegister::DEVICE_CONFIG5 => dev.config_data[5],
360 _ => {
361 tracelimit::warn_ratelimited!(
362 ?self.state.config_idx,
363 "unexpected config register read"
364 );
365 0x00
366 }
367 };
368
369 tracing::trace!(
370 config_reg = ?self.state.config_idx,
371 device_idx = ?self.state.device_idx,
372 ?value,
373 "sio config read"
374 );
375
376 Ok(value)
377 }
378
379 pub fn config_write(&mut self, value: u8) {
380 if !matches!(self.state.config_idx_state, ConfigIdxState::Ready) {
382 self.state.config_idx_state = ConfigIdxState::Idle;
383 return;
384 }
385
386 tracing::trace!(
387 config_reg = ?self.state.config_idx,
388 device_idx = ?self.state.device_idx,
389 ?value,
390 "sio config write"
391 );
392
393 if !self.state.config_idx.is_device_specific() {
395 match self.state.config_idx {
396 ConfigRegister::LOGICAL_DEVICE_NUMBER => {
397 self.state.device_idx = LogicalDeviceIndex(value);
398 }
399 ConfigRegister::POWER_DOWN_CONTROL => {
400 if value != 0xFF {
401 tracelimit::warn_ratelimited!(
402 value = value,
403 "invalid value written to POWER_DOWN_CONTROL register"
404 )
405 }
406 }
407 ConfigRegister::PNP_CONTROL => {
408 if value != 0xC4 {
409 tracelimit::warn_ratelimited!(
410 value = value,
411 "invalid value written to PNP_CONTROL register"
412 )
413 }
414 }
415 _ => {
416 tracelimit::warn_ratelimited!(
417 ?self.state.config_idx,
418 ?value,
419 "unexpected config register write"
420 )
421 }
422 }
423 return;
424 }
425
426 let dev = match self
427 .state
428 .device_data
429 .get_mut(self.state.device_idx.0 as usize)
430 {
431 Some(dev) => dev,
432 None => {
433 tracelimit::warn_ratelimited!(
434 logical_device_number = self.state.device_idx.0,
435 "invalid logical device index"
436 );
437
438 return;
439 }
440 };
441
442 match self.state.config_idx {
444 ConfigRegister::ENABLE_DEVICE => {
445 if self.state.device_idx != LogicalDeviceIndex::PARALLEL_PORT {
447 dev.enabled = value & 0x1 == 1;
448 } else {
449 tracing::debug!("attempted to enable parallel port")
450 }
451 }
452 ConfigRegister::IO_BASE_MSB0 => {
453 dev.io_port_base[0] = (dev.io_port_base[0] & !0xFF00) | (value as u16) << 8;
454 }
455 ConfigRegister::IO_BASE_LSB0 => {
456 dev.io_port_base[0] = (dev.io_port_base[0] & !0x00FF) | (value as u16);
457 }
458 ConfigRegister::IO_BASE_MSB1 => {
459 dev.io_port_base[1] = (dev.io_port_base[1] & !0xFF00) | (value as u16) << 8;
460 }
461 ConfigRegister::IO_BASE_LSB1 => {
462 dev.io_port_base[1] = (dev.io_port_base[1] & !0x00FF) | (value as u16);
463 }
464 ConfigRegister::IRQ_SELECT1 => dev.irq_vector[0] = value,
465 ConfigRegister::IRQ_SELECT2 => dev.irq_vector[1] = value,
466 ConfigRegister::DMA_CONFIG1 => dev.dma_channel[0] = value,
467 ConfigRegister::DMA_CONFIG2 => dev.dma_channel[1] = value,
468 ConfigRegister::DEVICE_CONFIG0 => dev.config_data[0] = value,
469 ConfigRegister::DEVICE_CONFIG1 => dev.config_data[1] = value,
470 ConfigRegister::DEVICE_CONFIG2 => dev.config_data[2] = value,
471 ConfigRegister::DEVICE_CONFIG3 => dev.config_data[3] = value,
472 ConfigRegister::DEVICE_CONFIG4 => dev.config_data[4] = value,
473 ConfigRegister::DEVICE_CONFIG5 => dev.config_data[5] = value,
474 _ => {
475 if self.state.device_idx == LogicalDeviceIndex::AUX_IO_CONTROL2 {
478 let expected = match self.state.config_idx {
479 ConfigRegister::DEVICE_BIT_CONFIG0 => value == 0x10 || value == 0x12, ConfigRegister::DEVICE_BIT_CONFIG5 => value == 0x08, ConfigRegister::ADDRESS_UNDOCUMENTED => value == 0xf0, _ => false,
483 };
484
485 if !expected {
486 tracelimit::warn_ratelimited!(?self.state.config_idx, ?value, "wrote an unexpected value");
487 }
488 } else {
489 tracelimit::warn_ratelimited!(
490 ?self.state.config_idx,
491 ?value,
492 "unexpected config register write"
493 )
494 }
495 }
496 }
497 }
498}
499
500impl ChangeDeviceState for SioController {
501 fn start(&mut self) {}
502
503 async fn stop(&mut self) {}
504
505 async fn reset(&mut self) {
506 self.state.config_idx_state = ConfigIdxState::default();
507 self.state.config_idx = ConfigRegister::default();
508 self.state.device_idx = LogicalDeviceIndex::default();
509 self.state.device_data = LogicalDeviceData::default_data();
510 }
511}
512
513impl ChipsetDevice for SioController {}
520
521mod save_restore {
522 use super::*;
523 use vmcore::save_restore::RestoreError;
524 use vmcore::save_restore::SaveError;
525 use vmcore::save_restore::SaveRestore;
526
527 mod state {
528 use mesh::payload::Protobuf;
529 use vmcore::save_restore::SavedStateRoot;
530
531 const SAVED_NUM_SIO_DEVICES: usize = 9;
532
533 #[derive(Protobuf)]
534 #[mesh(package = "chipset.superio")]
535 pub enum SavedConfigIdxState {
536 #[mesh(1)]
537 Idle,
538 #[mesh(2)]
539 Handshake,
540 #[mesh(3)]
541 Ready,
542 }
543
544 #[derive(Protobuf)]
545 #[mesh(package = "chipset.superio")]
546 pub struct SavedLogicalDeviceData {
547 #[mesh(1)]
548 pub enabled: bool,
549 #[mesh(2)]
550 pub io_port_base: [u16; 2],
551 #[mesh(3)]
552 pub irq_vector: [u8; 2],
553 #[mesh(4)]
554 pub dma_channel: [u8; 2],
555 #[mesh(5)]
556 pub config_data: [u8; 8],
557 }
558
559 #[derive(Protobuf, SavedStateRoot)]
560 #[mesh(package = "chipset.superio")]
561 pub struct SavedState {
562 #[mesh(1)]
563 pub config_idx_state: SavedConfigIdxState,
564 #[mesh(2)]
565 pub config_idx: u8,
566 #[mesh(3)]
567 pub device_idx: u8,
568 #[mesh(4)]
569 pub device_data: [SavedLogicalDeviceData; SAVED_NUM_SIO_DEVICES],
570 }
571 }
572
573 impl SaveRestore for SioController {
574 type SavedState = state::SavedState;
575
576 fn save(&mut self) -> Result<Self::SavedState, SaveError> {
577 let SioControllerState {
578 config_idx_state,
579 config_idx,
580 device_idx,
581 device_data,
582 } = self.state;
583
584 let saved_state = state::SavedState {
585 config_idx_state: match config_idx_state {
586 ConfigIdxState::Idle => state::SavedConfigIdxState::Idle,
587 ConfigIdxState::Handshake => state::SavedConfigIdxState::Handshake,
588 ConfigIdxState::Ready => state::SavedConfigIdxState::Ready,
589 },
590 config_idx: config_idx.0,
591 device_idx: device_idx.0,
592 device_data: device_data.map(|data| {
593 let LogicalDeviceData {
594 enabled,
595 io_port_base,
596 irq_vector,
597 dma_channel,
598 config_data,
599 } = data;
600
601 state::SavedLogicalDeviceData {
602 enabled,
603 io_port_base,
604 irq_vector,
605 dma_channel,
606 config_data,
607 }
608 }),
609 };
610
611 Ok(saved_state)
612 }
613
614 fn restore(&mut self, state: Self::SavedState) -> Result<(), RestoreError> {
615 let state::SavedState {
616 config_idx_state,
617 config_idx,
618 device_idx,
619 device_data,
620 } = state;
621
622 self.state = SioControllerState {
623 config_idx_state: match config_idx_state {
624 state::SavedConfigIdxState::Idle => ConfigIdxState::Idle,
625 state::SavedConfigIdxState::Handshake => ConfigIdxState::Handshake,
626 state::SavedConfigIdxState::Ready => ConfigIdxState::Ready,
627 },
628 config_idx: ConfigRegister(config_idx),
629 device_idx: LogicalDeviceIndex(device_idx),
630 device_data: device_data.map(|data| {
631 let state::SavedLogicalDeviceData {
632 enabled,
633 io_port_base,
634 irq_vector,
635 dma_channel,
636 config_data,
637 } = data;
638
639 LogicalDeviceData {
640 enabled,
641 io_port_base,
642 irq_vector,
643 dma_channel,
644 config_data,
645 }
646 }),
647 };
648
649 Ok(())
650 }
651 }
652}