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