1use bitfield_struct::bitfield;
5use chipset_device::ChipsetDevice;
6use chipset_device::interrupt::AcknowledgePicInterrupt;
7use chipset_device::interrupt::LineInterruptTarget;
8use chipset_device::io::IoError;
9use chipset_device::io::IoResult;
10use chipset_device::pio::ControlPortIoIntercept;
11use chipset_device::pio::PortIoIntercept;
12use chipset_device::pio::RegisterPortIoIntercept;
13use inspect::Inspect;
14use inspect::InspectMut;
15use inspect_counters::Counter;
16use std::num::Wrapping;
17use vmcore::device_state::ChangeDeviceState;
18use vmcore::line_interrupt::LineInterrupt;
19
20const PRIMARY_PIC_COMMAND_PORT: u16 = 0x20;
21const PRIMARY_PIC_DATA_PORT: u16 = 0x21;
22const SECONDARY_PIC_COMMAND_PORT: u16 = 0xa0;
23const SECONDARY_PIC_DATA_PORT: u16 = 0xa1;
24const PRIMARY_PIC_ELCR_PORT: u16 = 0x4d0;
25const SECONDARY_PIC_ELCR_PORT: u16 = 0x4d1;
26
27const PIC_CHAIN_COMMUNICATION_IRQ: u8 = 2;
29
30const IRQ_MASK: u8 = 0b111;
32
33const SPURIOUS_IRQ: u8 = 7;
35
36#[derive(InspectMut)]
37pub struct DualPic {
38 ready: LineInterrupt,
40 #[inspect(iter_by_index)]
41 port_io_regions: [Box<dyn ControlPortIoIntercept>; 3],
42
43 stats: DualPicStats,
45
46 #[inspect(flatten, with = r#"|x| inspect::iter_by_index(x).prefix("pic")"#)]
48 pics: [Pic; 2],
49}
50
51#[derive(Inspect, Default)]
52struct DualPicStats {
53 #[inspect(iter_by_index)]
54 interrupts_per_irq: [Counter; 16],
55 interrupts: Counter,
56}
57
58impl DualPic {
59 pub fn new(ready: LineInterrupt, port_io: &mut dyn RegisterPortIoIntercept) -> Self {
60 let mut primary_region = port_io.new_io_region(
61 "primary",
62 (PRIMARY_PIC_COMMAND_PORT..=PRIMARY_PIC_DATA_PORT).len() as u16,
63 );
64 let mut secondary_region = port_io.new_io_region(
65 "secondary",
66 (SECONDARY_PIC_COMMAND_PORT..=SECONDARY_PIC_DATA_PORT).len() as u16,
67 );
68 let mut elcr_region = port_io.new_io_region(
69 "edge_level_control",
70 (PRIMARY_PIC_ELCR_PORT..=SECONDARY_PIC_ELCR_PORT).len() as u16,
71 );
72
73 primary_region.map(PRIMARY_PIC_COMMAND_PORT);
74 secondary_region.map(SECONDARY_PIC_COMMAND_PORT);
75 elcr_region.map(PRIMARY_PIC_ELCR_PORT);
76
77 DualPic {
78 pics: [Pic::new(true), Pic::new(false)],
79 ready,
80 port_io_regions: [primary_region, secondary_region, elcr_region],
81 stats: Default::default(),
82 }
83 }
84
85 fn sync_outputs(&mut self) {
88 self.pics[0].set_irq(
89 PIC_CHAIN_COMMUNICATION_IRQ,
90 self.pics[1].interrupt_pending(),
91 );
92 self.ready.set_level(self.pics[0].interrupt_pending());
93 }
94
95 fn set_irq(&mut self, n: u8, high: bool) {
96 if n >= 8 {
97 self.pics[1].set_irq(n - 8, high);
98 } else {
99 self.pics[0].set_irq(n, high);
100 }
101 self.sync_outputs();
102 }
103}
104
105impl AcknowledgePicInterrupt for DualPic {
106 fn acknowledge_interrupt(&mut self) -> Option<u8> {
107 let (requested, n) = self.pics[0].acknowledge_interrupt(&mut self.stats);
108 let irq = requested.then(|| {
109 if n & IRQ_MASK == PIC_CHAIN_COMMUNICATION_IRQ {
110 let (requested, m) = self.pics[1].acknowledge_interrupt(&mut self.stats);
111 assert!(requested, "pic1 ready was set");
112 m
113 } else {
114 n
115 }
116 });
117 self.sync_outputs();
120 irq
121 }
122}
123
124impl LineInterruptTarget for DualPic {
125 fn set_irq(&mut self, n: u32, high: bool) {
126 self.set_irq(n as u8, high);
127 }
128
129 fn valid_lines(&self) -> &[std::ops::RangeInclusive<u32>] {
130 &[0..=1, 3..=15]
133 }
134}
135
136impl ChangeDeviceState for DualPic {
137 fn start(&mut self) {}
138
139 async fn stop(&mut self) {}
140
141 async fn reset(&mut self) {
142 for pic in &mut self.pics {
143 pic.reset(false);
144 }
145 self.sync_outputs();
146 }
147}
148
149impl ChipsetDevice for DualPic {
150 fn supports_pio(&mut self) -> Option<&mut dyn PortIoIntercept> {
151 Some(self)
152 }
153
154 fn supports_line_interrupt_target(&mut self) -> Option<&mut dyn LineInterruptTarget> {
155 Some(self)
156 }
157
158 fn supports_acknowledge_pic_interrupt(&mut self) -> Option<&mut dyn AcknowledgePicInterrupt> {
159 Some(self)
160 }
161}
162
163impl PortIoIntercept for DualPic {
164 fn io_read(&mut self, io_port: u16, data: &mut [u8]) -> IoResult {
165 if data.len() != 1 {
166 return IoResult::Err(IoError::InvalidAccessSize);
167 }
168 data[0] = match io_port {
169 PRIMARY_PIC_COMMAND_PORT => self.pics[0].read_command(&mut self.stats),
170 PRIMARY_PIC_DATA_PORT => self.pics[0].read_data(),
171 SECONDARY_PIC_COMMAND_PORT => self.pics[1].read_command(&mut self.stats),
172 SECONDARY_PIC_DATA_PORT => self.pics[1].read_data(),
173 PRIMARY_PIC_ELCR_PORT => self.pics[0].elcr,
174 SECONDARY_PIC_ELCR_PORT => self.pics[1].elcr,
175 _ => return IoResult::Err(IoError::InvalidRegister),
176 };
177 IoResult::Ok
178 }
179
180 fn io_write(&mut self, io_port: u16, data: &[u8]) -> IoResult {
181 if data.len() == 2
185 && (io_port == PRIMARY_PIC_COMMAND_PORT || io_port == SECONDARY_PIC_COMMAND_PORT)
186 {
187 let &[mut prim, mut sec] = data else {
188 unreachable!()
189 };
190 if io_port == SECONDARY_PIC_COMMAND_PORT {
191 (prim, sec) = (sec, prim);
192 }
193 self.pics[0].write_command(prim);
194 self.pics[1].write_command(sec);
195 self.sync_outputs();
196 return IoResult::Ok;
197 }
198
199 if data.len() != 1 {
200 return IoResult::Err(IoError::InvalidAccessSize);
201 }
202
203 match io_port {
204 PRIMARY_PIC_COMMAND_PORT => self.pics[0].write_command(data[0]),
205 PRIMARY_PIC_DATA_PORT => self.pics[0].write_data(data[0]),
206 SECONDARY_PIC_COMMAND_PORT => self.pics[1].write_command(data[0]),
207 SECONDARY_PIC_DATA_PORT => self.pics[1].write_data(data[0]),
208 PRIMARY_PIC_ELCR_PORT => self.pics[0].elcr = data[0],
209 SECONDARY_PIC_ELCR_PORT => self.pics[1].elcr = data[0],
210 _ => return IoResult::Err(IoError::InvalidRegister),
211 }
212
213 self.sync_outputs();
214 IoResult::Ok
215 }
216}
217
218#[derive(Debug, Copy, Clone, Inspect)]
222struct Pic {
223 init: InitStage,
225
226 #[inspect(skip)]
228 primary: bool,
229
230 #[inspect(hex)]
233 icw2: u8,
234
235 #[inspect(binary)]
237 imr: u8,
238
239 ocw3: Ocw3,
241
242 #[inspect(binary)]
244 isr: u8,
245
246 #[inspect(binary)]
248 elcr: u8,
249
250 #[inspect(binary)]
252 lines: u8,
253
254 #[inspect(binary)]
258 line_low_latch: u8,
259}
260
261#[derive(Copy, Clone, Debug, Inspect)]
262enum InitStage {
263 Uninitialized,
264 ExpectingIcw2,
265 ExpectingIcw3,
266 ExpectingIcw4,
267 Initialized,
268}
269
270impl Pic {
271 fn new(primary: bool) -> Self {
272 Pic {
273 imr: 0,
274 init: InitStage::Uninitialized,
275 icw2: if primary { 0 } else { 8 },
278 ocw3: Ocw3(0),
279 isr: 0,
280 elcr: 0,
281 primary,
282 lines: 0,
283 line_low_latch: !0,
284 }
285 }
286
287 fn reset(&mut self, during_icw1: bool) {
290 *self = Self {
291 lines: self.lines,
292 elcr: if during_icw1 { self.elcr } else { 0 },
293 ..Self::new(self.primary)
294 };
295 }
296
297 fn irr(&self) -> u8 {
298 self.lines & (self.elcr | self.line_low_latch)
299 }
300
301 fn set_irq(&mut self, n: u8, high: bool) {
302 let bit = 1 << n;
303 if high {
304 if !matches!(self.init, InitStage::Initialized) {
305 tracelimit::warn_ratelimited!(
306 primary = self.primary,
307 ?n,
308 "interrupt request sent to uninitialized PIC"
309 );
310 }
311 self.lines |= bit;
312 } else {
313 self.lines &= !bit;
314 self.line_low_latch |= bit;
315 }
316 }
317
318 fn ready_vec(&self) -> u8 {
319 let isr = Wrapping(self.isr);
322 let highest_isr = isr & -isr;
323 let higher_not_isr = highest_isr - Wrapping(1);
324 self.irr() & !self.imr & higher_not_isr.0
326 }
327
328 fn interrupt_pending(&self) -> bool {
329 self.ready_vec() != 0
330 }
331
332 fn pending_line(&self) -> Option<u8> {
333 let m = self.ready_vec();
334 if m != 0 {
335 Some(m.trailing_zeros() as u8)
337 } else {
338 None
339 }
340 }
341
342 fn acknowledge_interrupt(&mut self, stats: &mut DualPicStats) -> (bool, u8) {
343 if !matches!(self.init, InitStage::Initialized) {
344 tracelimit::warn_ratelimited!(
345 primary = self.primary,
346 "interrupt servicing sent to uninitialized PIC"
347 );
348 }
349
350 let (requested, irq) = if let Some(n) = self.pending_line() {
351 let bit = 1 << n;
352 self.line_low_latch &= !bit;
355 self.isr |= bit;
357 stats.interrupts.increment();
358 stats.interrupts_per_irq[if self.primary { 0 } else { 8 } + n as usize].increment();
359 (true, n)
360 } else {
361 (false, SPURIOUS_IRQ)
363 };
364
365 assert!(self.icw2 & IRQ_MASK == 0);
368 (requested, self.icw2 | irq)
369 }
370
371 fn eoi(&mut self, n: Option<u8>) {
372 tracing::trace!(primary = self.primary, n, "eoi");
373 let bit = match n {
374 Some(level) => 1 << level,
375 None => self.isr & self.isr.wrapping_neg(),
377 };
378
379 self.isr &= !bit;
380 }
381
382 fn read_command(&mut self, stats: &mut DualPicStats) -> u8 {
383 if self.ocw3.p() {
384 self.ocw3.set_p(false);
385 let (int, irq) = self.acknowledge_interrupt(stats);
386 ((int as u8) << 7) | (irq & IRQ_MASK)
387 } else if self.ocw3.rr() {
388 if self.ocw3.ris() {
389 self.isr
390 } else {
391 self.irr()
392 }
393 } else {
394 0
395 }
396 }
397
398 fn read_data(&self) -> u8 {
399 self.imr
400 }
401
402 fn write_command(&mut self, data: u8) {
403 const INIT_BIT: u8 = 0b10000;
404 const COMMAND_BIT: u8 = 0b1000;
405
406 if data & INIT_BIT != 0 {
407 if data != 0b00010001 {
411 tracelimit::error_ratelimited!(primary = self.primary, ?data, "unsupported ICW1");
412 }
413
414 self.reset(true);
415 self.init = InitStage::ExpectingIcw2;
416 } else {
417 if !matches!(self.init, InitStage::Initialized) {
418 tracelimit::warn_ratelimited!(
419 primary = self.primary,
420 ?data,
421 "OCW sent to uninitialized PIC"
422 );
423 }
424 if data & COMMAND_BIT == 0 {
425 let ocw2 = Ocw2(data);
426
427 match (ocw2.r(), ocw2.sl(), ocw2.eoi()) {
428 (true, _, _) | (false, false, false) => {
429 tracelimit::error_ratelimited!(
430 primary = self.primary,
431 ?data,
432 "unsupported OCW2"
433 )
434 }
435 (false, true, true) => self.eoi(Some(ocw2.level())),
436 (false, false, true) => self.eoi(None),
437 (false, true, false) => {} }
439 } else {
440 self.ocw3 = Ocw3(data);
441 if self.ocw3.esmm() || self.ocw3.smm() {
443 tracelimit::error_ratelimited!(
444 primary = self.primary,
445 ?data,
446 "unsupported OCW3"
447 );
448 }
449 }
450 }
451 }
452
453 fn write_data(&mut self, data: u8) {
454 match self.init {
455 InitStage::Uninitialized | InitStage::Initialized => {
456 self.imr = data; }
458 InitStage::ExpectingIcw2 => {
459 if data & IRQ_MASK != 0 {
460 tracelimit::error_ratelimited!(primary = self.primary, ?data, "invalid ICW2");
461 }
462 self.icw2 = data & !IRQ_MASK;
463 self.init = InitStage::ExpectingIcw3;
464 }
465 InitStage::ExpectingIcw3 => {
466 if self.primary {
468 if data != (1 << PIC_CHAIN_COMMUNICATION_IRQ) {
469 tracelimit::error_ratelimited!(
470 primary = self.primary,
471 ?data,
472 "invalid primary ICW3"
473 );
474 }
475 } else {
476 if data != PIC_CHAIN_COMMUNICATION_IRQ {
477 tracelimit::error_ratelimited!(
478 primary = self.primary,
479 ?data,
480 "invalid secondary ICW3"
481 );
482 }
483 }
484
485 self.init = InitStage::ExpectingIcw4;
486 }
487 InitStage::ExpectingIcw4 => {
488 if data != 1 {
491 if data == 3 {
493 tracing::debug!(
494 primary = self.primary,
495 "got ICW4 of 3, this is expected for Linux boot but not any other time"
496 );
497 } else {
498 tracelimit::error_ratelimited!(
499 primary = self.primary,
500 ?data,
501 "unsupported ICW4"
502 );
503 }
504 };
505 self.init = InitStage::Initialized;
506 }
507 }
508 }
509}
510
511#[bitfield(u8)]
512struct Ocw2 {
514 #[bits(3)]
516 level: u8,
517
518 #[bits(2)]
519 _command: u8,
520
521 eoi: bool,
523
524 sl: bool,
526
527 r: bool,
529}
530
531#[derive(Inspect)]
532#[bitfield(u8)]
533struct Ocw3 {
535 ris: bool,
537
538 rr: bool,
540
541 p: bool,
543
544 #[bits(2)]
545 _command: u8,
546
547 smm: bool,
549
550 esmm: bool,
552
553 _reserved: bool,
554}
555
556mod save_restore {
557 use super::DualPic;
558 use super::IRQ_MASK;
559 use super::InitStage;
560 use super::Ocw3;
561 use super::Pic;
562 use thiserror::Error;
563 use vmcore::save_restore::RestoreError;
564 use vmcore::save_restore::SaveError;
565 use vmcore::save_restore::SaveRestore;
566
567 mod state {
568 use mesh::payload::Protobuf;
569 use vmcore::save_restore::SavedStateRoot;
570
571 #[derive(Protobuf, SavedStateRoot)]
572 #[mesh(package = "chipset.pic")]
573 pub struct SavedState {
574 #[mesh(1)]
575 pub(super) primary: SavedPic,
576 #[mesh(2)]
577 pub(super) secondary: SavedPic,
578 }
579
580 #[derive(Protobuf)]
581 #[mesh(package = "chipset.pic")]
582 pub struct SavedPic {
583 #[mesh(1)]
584 pub init: SavedInitStage,
585 #[mesh(2)]
586 pub icw2: u8,
587 #[mesh(3)]
588 pub imr: u8,
589 #[mesh(4)]
590 pub ocw3: u8,
591 #[mesh(5)]
592 pub isr: u8,
593 #[mesh(6)]
594 pub elcr: u8,
595 #[mesh(7)]
596 pub line_low_latch: u8,
597 }
598
599 #[derive(Protobuf)]
600 #[mesh(package = "chipset.pic")]
601 pub enum SavedInitStage {
602 #[mesh(1)]
603 Uninitialized,
604 #[mesh(2)]
605 ExpectingIcw2,
606 #[mesh(3)]
607 ExpectingIcw3,
608 #[mesh(4)]
609 ExpectingIcw4,
610 #[mesh(5)]
611 Initialized,
612 }
613 }
614
615 #[derive(Debug, Error)]
616 enum Error {
617 #[error("invalid icw2 value {0:#x}")]
618 InvalidIcw2(u8),
619 }
620
621 impl state::SavedPic {
622 fn restore(self, pic: &mut Pic) -> Result<(), Error> {
623 let Self {
624 init,
625 icw2,
626 imr,
627 ocw3,
628 isr,
629 elcr,
630 line_low_latch,
631 } = self;
632 pic.init = match init {
633 state::SavedInitStage::Uninitialized => InitStage::Uninitialized,
634 state::SavedInitStage::ExpectingIcw2 => InitStage::ExpectingIcw2,
635 state::SavedInitStage::ExpectingIcw3 => InitStage::ExpectingIcw3,
636 state::SavedInitStage::ExpectingIcw4 => InitStage::ExpectingIcw4,
637 state::SavedInitStage::Initialized => InitStage::Initialized,
638 };
639 if icw2 & IRQ_MASK != 0 {
640 return Err(Error::InvalidIcw2(icw2));
641 }
642 pic.icw2 = icw2;
643 pic.imr = imr;
644 pic.ocw3 = Ocw3(ocw3);
645 pic.isr = isr;
646 pic.elcr = elcr;
647 pic.line_low_latch = line_low_latch;
648 Ok(())
649 }
650
651 fn save(pic: &Pic) -> Self {
652 let &Pic {
653 init,
654 primary: _,
655 icw2,
656 imr,
657 ocw3,
658 isr,
659 elcr,
660 lines: _,
661 line_low_latch,
662 } = pic;
663 Self {
664 init: match init {
665 InitStage::Uninitialized => state::SavedInitStage::Uninitialized,
666 InitStage::ExpectingIcw2 => state::SavedInitStage::ExpectingIcw2,
667 InitStage::ExpectingIcw3 => state::SavedInitStage::ExpectingIcw3,
668 InitStage::ExpectingIcw4 => state::SavedInitStage::ExpectingIcw4,
669 InitStage::Initialized => state::SavedInitStage::Initialized,
670 },
671 icw2,
672 imr,
673 ocw3: ocw3.0,
674 isr,
675 elcr,
676 line_low_latch,
677 }
678 }
679 }
680
681 impl SaveRestore for DualPic {
682 type SavedState = state::SavedState;
683
684 fn save(&mut self) -> Result<Self::SavedState, SaveError> {
685 let Self {
686 ready: _,
687 stats: _,
688 port_io_regions: _,
689 pics: [primary, secondary],
690 } = &self;
691
692 Ok(state::SavedState {
693 primary: state::SavedPic::save(primary),
694 secondary: state::SavedPic::save(secondary),
695 })
696 }
697
698 fn restore(&mut self, state: Self::SavedState) -> Result<(), RestoreError> {
699 let state::SavedState { primary, secondary } = state;
700 primary
701 .restore(&mut self.pics[0])
702 .map_err(|err| RestoreError::Other(err.into()))?;
703 secondary
704 .restore(&mut self.pics[1])
705 .map_err(|err| RestoreError::Other(err.into()))?;
706 self.sync_outputs();
707 Ok(())
708 }
709 }
710}
711
712#[cfg(test)]
713mod tests {
714 use super::*;
715 use chipset_device::pio::ExternallyManagedPortIoIntercepts;
716 use chipset_device::pio::PortIoIntercept;
717 use vmcore::line_interrupt::test_helpers::TestLineInterruptTarget;
718
719 const IV_BASE: u8 = 0x30;
720
721 fn create_pic() -> (impl Fn() -> bool, DualPic) {
722 let ready = TestLineInterruptTarget::new_arc();
723 let mut pic = DualPic::new(
724 LineInterrupt::new_with_target("ready", ready.clone(), 0),
725 &mut ExternallyManagedPortIoIntercepts,
726 );
727
728 pic.io_write(0x20, &[0x11]).unwrap();
730 pic.io_write(0x21, &[IV_BASE]).unwrap(); pic.io_write(0x21, &[0x04]).unwrap();
732 pic.io_write(0x21, &[0x01]).unwrap();
733 pic.io_write(0xa0, &[0x11]).unwrap();
734 pic.io_write(0xa1, &[IV_BASE + 8]).unwrap(); pic.io_write(0xa1, &[0x02]).unwrap();
736 pic.io_write(0xa1, &[0x01]).unwrap();
737
738 (move || ready.is_high(0), pic)
739 }
740
741 fn send_eoi(pic: &mut DualPic, consts: PicTestConstants) {
742 let (v, _) = consts;
743 match v {
744 0..=7 => pic
745 .io_write(
746 PRIMARY_PIC_COMMAND_PORT,
747 &[Ocw2::new().with_eoi(true).with_sl(true).with_level(v).0],
748 )
749 .unwrap(),
750 8..=15 => {
751 pic.io_write(
752 PRIMARY_PIC_COMMAND_PORT,
753 &[Ocw2::new()
754 .with_eoi(true)
755 .with_sl(true)
756 .with_level(PIC_CHAIN_COMMUNICATION_IRQ)
757 .0],
758 )
759 .unwrap();
760 pic.io_write(
761 SECONDARY_PIC_COMMAND_PORT,
762 &[Ocw2::new().with_eoi(true).with_sl(true).with_level(v - 8).0],
763 )
764 .unwrap();
765 }
766 _ => unreachable!(),
767 }
768 }
769
770 #[test]
771 fn test_create_pic() {
772 let (ready, pic) = create_pic();
773 assert!(matches!(pic.pics[0].init, InitStage::Initialized));
774 assert!(matches!(pic.pics[1].init, InitStage::Initialized));
775 assert!(!ready());
776 }
777
778 type PicTestConstants = (u8, usize);
780 const PRIMARY: PicTestConstants = (0, 0);
781 const SECONDARY: PicTestConstants = (8, 1);
782
783 #[test]
784 fn test_edge_interrupt_primary() {
785 test_edge_interrupt(PRIMARY);
786 }
787
788 #[test]
789 fn test_edge_interrupt_secondary() {
790 test_edge_interrupt(SECONDARY);
791 }
792
793 fn test_edge_interrupt(consts: PicTestConstants) {
794 let (ready, mut pic) = create_pic();
795 let (v, i) = consts;
796
797 pic.set_irq(v, true);
798 assert!(ready());
799 assert!(pic.pics[i].irr() == 0b1);
800 pic.set_irq(v, false);
801 assert!(pic.pics[i].irr() == 0b0);
802 let pending = pic.acknowledge_interrupt();
803 assert!(pending.is_none());
804
805 assert!(!ready());
806
807 pic.set_irq(v, true);
808 assert!(ready());
809 assert!(pic.pics[i].irr() == 0b1);
810
811 let pending = pic.acknowledge_interrupt();
812 assert_eq!(pending, Some(IV_BASE + v));
813 assert!(pic.pics[i].irr() == 0);
814 assert!(pic.pics[i].isr == 0b1);
815
816 send_eoi(&mut pic, consts);
817
818 assert!(pic.pics[i].irr() == 0);
819 assert!(pic.pics[i].isr == 0);
820 let pending = pic.acknowledge_interrupt();
821 assert!(pending.is_none());
822
823 assert!(!ready());
824
825 pic.set_irq(v, true);
826 assert!(pic.pics[i].irr() == 0b0);
827 pic.set_irq(v, false);
828 pic.set_irq(v, true);
829 assert!(pic.pics[i].irr() == 0b1);
830 assert!(ready());
831
832 pic.set_irq(v, false);
833 assert!(pic.pics[i].irr() == 0b0);
834 }
835
836 #[test]
837 fn test_level_interrupts_primary() {
838 test_level_interrupts(PRIMARY);
839 }
840
841 #[test]
842 fn test_level_interrupts_secondary() {
843 test_level_interrupts(SECONDARY);
844 }
845
846 fn test_level_interrupts(consts: PicTestConstants) {
847 let (ready, mut pic) = create_pic();
848 let (v, i) = consts;
849
850 pic.io_write(
851 match i {
852 0 => PRIMARY_PIC_ELCR_PORT,
853 1 => SECONDARY_PIC_ELCR_PORT,
854 _ => unreachable!(),
855 },
856 &[0b1],
857 )
858 .unwrap();
859
860 pic.set_irq(v, true);
861 assert!(ready());
862 assert!(pic.pics[i].irr() == 0b1);
863 pic.set_irq(v, false);
864 assert!(pic.pics[i].irr() == 0b0);
865 let pending = pic.acknowledge_interrupt();
866
867 assert!(pending.is_none());
869
870 assert!(!ready());
871
872 pic.set_irq(v, true);
873 assert!(ready());
874 assert!(pic.pics[i].irr() == 0b1);
875
876 let pending = pic.acknowledge_interrupt();
877 assert_eq!(pending, Some(IV_BASE + v));
878 assert!(pic.pics[i].irr() == 0b1);
879 assert!(pic.pics[i].isr == 0b1);
880
881 send_eoi(&mut pic, consts);
882 assert!(pic.pics[i].irr() == 0b1);
883 assert!(pic.pics[i].isr == 0);
884
885 let pending = pic.acknowledge_interrupt();
886 assert_eq!(pending, Some(IV_BASE + v));
887 pic.set_irq(v, false);
888 send_eoi(&mut pic, consts);
889 assert!(pic.pics[i].irr() == 0);
890 let pending = pic.acknowledge_interrupt();
891 assert!(pending.is_none());
892 }
893
894 #[test]
895 fn test_multiple_edge_interrupt_primary() {
896 test_multiple_edge_interrupt(PRIMARY);
897 }
898
899 #[test]
900 fn test_multiple_edge_interrupt_secondary() {
901 test_multiple_edge_interrupt(SECONDARY);
902 }
903
904 fn test_multiple_edge_interrupt(consts: PicTestConstants) {
905 let (ready, mut pic) = create_pic();
906 let (v, i) = consts;
907
908 pic.set_irq(v, true);
909 assert!(ready());
910 assert!(pic.pics[i].irr() == 0b1);
911 pic.set_irq(v, false);
912 assert!(pic.pics[i].irr() == 0b0);
913
914 pic.set_irq(v + 1, true);
915 assert!(pic.pics[i].irr() == 0b10);
916 pic.set_irq(v + 1, false);
917 assert!(pic.pics[i].irr() == 0b00);
918
919 assert!(!ready());
920
921 pic.set_irq(v, true);
922 assert!(ready());
923 assert!(pic.pics[i].irr() == 0b1);
924 pic.set_irq(v + 1, true);
925 assert!(pic.pics[i].irr() == 0b11);
926
927 let pending = pic.acknowledge_interrupt();
928 assert_eq!(pending, Some(IV_BASE + v));
929 assert!(pic.pics[i].irr() == 0b10);
930 assert!(pic.pics[i].isr == 0b01);
931
932 send_eoi(&mut pic, consts);
933 assert!(pic.pics[i].irr() == 0b10);
934 assert!(pic.pics[i].isr == 0b00);
935 assert!(ready());
936 let pending = pic.acknowledge_interrupt();
937 assert_eq!(pending, Some(IV_BASE + 1 + v));
938 assert!(!ready());
939 }
940
941 #[test]
942 fn test_non_specific_eois() {
943 let (_, mut pic) = create_pic();
944
945 pic.set_irq(5, true);
946 assert_eq!(pic.acknowledge_interrupt(), Some(IV_BASE + 5));
947
948 pic.set_irq(3, true);
949 assert_eq!(pic.acknowledge_interrupt(), Some(IV_BASE + 3));
950
951 pic.set_irq(1, true);
952 assert_eq!(pic.acknowledge_interrupt(), Some(IV_BASE + 1));
953
954 assert_eq!(pic.pics[0].isr, 0b101010);
955
956 pic.io_write(
957 PRIMARY_PIC_COMMAND_PORT,
958 &[Ocw2::new().with_eoi(true).with_sl(false).0],
959 )
960 .unwrap();
961
962 assert_eq!(pic.pics[0].isr, 0b101000);
963 }
964}