devices/irqchip/
pic.rs

1// Copyright 2019 The ChromiumOS Authors
2// Use of this source code is governed by a BSD-style license that can be
3// found in the LICENSE file.
4
5// Software implementation of an Intel 8259A Programmable Interrupt Controller
6// This is a legacy device used by older OSs and briefly during start-up by
7// modern OSs that use a legacy BIOS.
8// The PIC is connected to the Local APIC on CPU0.
9
10// Terminology note: The 8259A spec refers to "master" and "slave" PICs; the "slave"s are daisy
11// chained to the "master"s.
12// For the purposes of both using more descriptive terms and avoiding terms with lots of charged
13// emotional context, this file refers to them instead as "primary" and "secondary" PICs.
14
15use base::debug;
16use base::warn;
17use base::Event;
18use hypervisor::PicInitState;
19use hypervisor::PicSelect;
20use hypervisor::PicState;
21use snapshot::AnySnapshot;
22
23use crate::bus::BusAccessInfo;
24use crate::pci::CrosvmDeviceId;
25use crate::BusDevice;
26use crate::DeviceId;
27use crate::Suspendable;
28
29pub struct Pic {
30    // Indicates a pending INTR signal to LINT0 of vCPU, checked by vCPU thread.
31    interrupt_request: bool,
32    // Events that need to be triggered when an ISR is cleared. The outer Vec is indexed by GSI,
33    // and the inner Vec is an unordered list of registered resample events for the GSI.
34    resample_events: Vec<Vec<Event>>,
35    // Index 0 (aka PicSelect::Primary) is the primary pic, the rest are secondary.
36    pics: [PicState; 2],
37}
38
39// Register offsets.
40const PIC_PRIMARY: u64 = 0x20;
41const PIC_PRIMARY_COMMAND: u64 = PIC_PRIMARY;
42const PIC_PRIMARY_DATA: u64 = PIC_PRIMARY + 1;
43const PIC_PRIMARY_ELCR: u64 = 0x4d0;
44
45const PIC_SECONDARY: u64 = 0xa0;
46const PIC_SECONDARY_COMMAND: u64 = PIC_SECONDARY;
47const PIC_SECONDARY_DATA: u64 = PIC_SECONDARY + 1;
48const PIC_SECONDARY_ELCR: u64 = 0x4d1;
49
50const LEVEL_HIGH: bool = true;
51const LEVEL_LOW: bool = false;
52const INVALID_PRIORITY: u8 = 8;
53const SPURIOUS_IRQ: u8 = 0x07;
54const PRIMARY_PIC_CASCADE_PIN: u8 = 2;
55const PRIMARY_PIC_CASCADE_PIN_MASK: u8 = 0x04;
56const PRIMARY_PIC_MAX_IRQ: u8 = 7;
57
58// Command Words
59const ICW1_MASK: u8 = 0x10;
60const OCW3_MASK: u8 = 0x08;
61
62// ICW1 bits
63const ICW1_NEED_ICW4: u8 = 0x01; // ICW4 needed
64const ICW1_SINGLE_PIC_MODE: u8 = 0x02;
65const ICW1_LEVEL_TRIGGER_MODE: u8 = 0x08;
66
67const ICW2_IRQ_BASE_MASK: u8 = 0xf8;
68
69const ICW4_SPECIAL_FULLY_NESTED_MODE: u8 = 0x10;
70const ICW4_AUTO_EOI: u8 = 0x02;
71
72// OCW2 bits
73const OCW2_IRQ_MASK: u8 = 0x07;
74const OCW2_COMMAND_MASK: u8 = 0xe0;
75#[derive(Debug, Clone, Copy, PartialEq, enumn::N)]
76enum Ocw2 {
77    RotateAutoEoiClear = 0x00,
78    NonSpecificEoi = 0x20,
79    NoOp = 0x40,
80    SpecificEoi = 0x60,
81    RotateAutoEoiSet = 0x80,
82    RotateNonSpecificEoi = 0xa0,
83    SetPriority = 0xc0,
84    RotateSpecificEoi = 0xe0,
85}
86
87// OCW3 bits
88const OCW3_POLL_COMMAND: u8 = 0x04;
89const OCW3_READ_REGISTER: u8 = 0x02;
90// OCW3_READ_IRR (0x00) intentionally omitted.
91const OCW3_READ_ISR: u8 = 0x01;
92const OCW3_SPECIAL_MASK: u8 = 0x40;
93const OCW3_SPECIAL_MASK_VALUE: u8 = 0x20;
94
95impl BusDevice for Pic {
96    fn device_id(&self) -> DeviceId {
97        CrosvmDeviceId::Pic.into()
98    }
99
100    fn debug_label(&self) -> String {
101        "userspace PIC".to_string()
102    }
103
104    fn write(&mut self, info: BusAccessInfo, data: &[u8]) {
105        if data.len() != 1 {
106            warn!("PIC: Bad write size: {}", data.len());
107            return;
108        }
109        match info.address {
110            PIC_PRIMARY_COMMAND => self.pic_write_command(PicSelect::Primary, data[0]),
111            PIC_PRIMARY_DATA => self.pic_write_data(PicSelect::Primary, data[0]),
112            PIC_PRIMARY_ELCR => self.pic_write_elcr(PicSelect::Primary, data[0]),
113            PIC_SECONDARY_COMMAND => self.pic_write_command(PicSelect::Secondary, data[0]),
114            PIC_SECONDARY_DATA => self.pic_write_data(PicSelect::Secondary, data[0]),
115            PIC_SECONDARY_ELCR => self.pic_write_elcr(PicSelect::Secondary, data[0]),
116            _ => warn!("PIC: Invalid write to {}", info),
117        }
118    }
119
120    fn read(&mut self, info: BusAccessInfo, data: &mut [u8]) {
121        if data.len() != 1 {
122            warn!("PIC: Bad read size: {}", data.len());
123            return;
124        }
125        data[0] = match info.address {
126            PIC_PRIMARY_COMMAND => self.pic_read_command(PicSelect::Primary),
127            PIC_PRIMARY_DATA => self.pic_read_data(PicSelect::Primary),
128            PIC_PRIMARY_ELCR => self.pic_read_elcr(PicSelect::Primary),
129            PIC_SECONDARY_COMMAND => self.pic_read_command(PicSelect::Secondary),
130            PIC_SECONDARY_DATA => self.pic_read_data(PicSelect::Secondary),
131            PIC_SECONDARY_ELCR => self.pic_read_elcr(PicSelect::Secondary),
132            _ => {
133                warn!("PIC: Invalid read from {}", info);
134                return;
135            }
136        };
137    }
138}
139
140impl Pic {
141    pub fn new() -> Pic {
142        let mut primary_pic: PicState = Default::default();
143        let mut secondary_pic: PicState = Default::default();
144        // These two masks are taken from KVM code (but do not appear in the 8259 specification).
145
146        // These IRQ lines are edge triggered, and so have 0 bits in the masks:
147        //   - IRQs 0, 1, 8, and 13 are dedicated to special I/O devices on the system board.
148        //   - IRQ 2 is the primary pic's cascade line.
149        // The primary pic has IRQs 0-7.
150        primary_pic.elcr_mask = !((1 << 0) | (1 << 1) | (1 << 2));
151        // The secondary PIC has IRQs 8-15, so we subtract 8 from the IRQ number to get the bit
152        // that should be masked here. In this case, bits 8 - 8 = 0 and 13 - 8 = 5.
153        secondary_pic.elcr_mask = !((1 << 0) | (1 << 5));
154
155        Pic {
156            interrupt_request: false,
157            pics: [primary_pic, secondary_pic],
158            resample_events: Vec::new(),
159        }
160    }
161
162    /// Get the current state of the primary or secondary PIC
163    pub fn get_pic_state(&self, select: PicSelect) -> PicState {
164        self.pics[select as usize]
165    }
166
167    /// Set the current state of the primary or secondary PIC
168    pub fn set_pic_state(&mut self, select: PicSelect, state: &PicState) {
169        self.pics[select as usize] = *state;
170    }
171
172    pub fn register_resample_events(&mut self, resample_events: Vec<Vec<Event>>) {
173        self.resample_events = resample_events;
174    }
175
176    pub fn service_irq(&mut self, irq: u8, level: bool) -> bool {
177        assert!(irq <= 15, "Unexpectedly high value irq: {irq} vs 15");
178
179        let pic = if irq <= PRIMARY_PIC_MAX_IRQ {
180            PicSelect::Primary
181        } else {
182            PicSelect::Secondary
183        };
184        Pic::set_irq_internal(&mut self.pics[pic as usize], irq & 7, level);
185
186        self.update_irq()
187    }
188
189    /// Determines whether the (primary) PIC is completely masked.
190    pub fn masked(&self) -> bool {
191        self.pics[PicSelect::Primary as usize].imr == 0xFF
192    }
193
194    /// Determines whether the PIC has an interrupt ready.
195    pub fn has_interrupt(&self) -> bool {
196        self.get_irq(PicSelect::Primary).is_some()
197    }
198
199    /// Determines whether the PIC has fired an interrupt to LAPIC.
200    pub fn interrupt_requested(&self) -> bool {
201        self.interrupt_request
202    }
203
204    /// Determines the external interrupt number that the PIC is prepared to inject, if any.
205    pub fn get_external_interrupt(&mut self) -> Option<u8> {
206        self.interrupt_request = false;
207        // If there is no interrupt request, return `None` to avoid the interrupt entirely.
208        // The architecturally correct behavior in this case is to inject a spurious interrupt.
209        // Although this case only occurs as a result of a race condition where the interrupt
210        // might also be avoided entirely.  The KVM unit test OS, which several unit tests rely
211        // upon, doesn't properly handle spurious interrupts.  Also spurious interrupts are much
212        // more common in this code than real hardware because the hardware race is much much much
213        // smaller.
214        let irq_primary = self.get_irq(PicSelect::Primary)?;
215
216        self.interrupt_ack(PicSelect::Primary, irq_primary);
217        let int_num = if irq_primary == PRIMARY_PIC_CASCADE_PIN {
218            // IRQ on secondary pic.
219            let irq_secondary = if let Some(irq) = self.get_irq(PicSelect::Secondary) {
220                self.interrupt_ack(PicSelect::Secondary, irq);
221                irq
222            } else {
223                SPURIOUS_IRQ
224            };
225            self.pics[PicSelect::Secondary as usize].irq_base + irq_secondary
226        } else {
227            self.pics[PicSelect::Primary as usize].irq_base + irq_primary
228        };
229
230        self.update_irq();
231        Some(int_num)
232    }
233
234    fn pic_read_command(&mut self, pic_type: PicSelect) -> u8 {
235        if self.pics[pic_type as usize].poll {
236            let (ret, update_irq_needed) = self.poll_read(pic_type);
237            self.pics[pic_type as usize].poll = false;
238
239            if update_irq_needed {
240                self.update_irq();
241            }
242
243            ret
244        } else if self.pics[pic_type as usize].read_reg_select {
245            self.pics[pic_type as usize].isr
246        } else {
247            self.pics[pic_type as usize].irr
248        }
249    }
250
251    fn pic_read_data(&mut self, pic_type: PicSelect) -> u8 {
252        if self.pics[pic_type as usize].poll {
253            let (ret, update_needed) = self.poll_read(pic_type);
254            self.pics[pic_type as usize].poll = false;
255            if update_needed {
256                self.update_irq();
257            }
258            ret
259        } else {
260            self.pics[pic_type as usize].imr
261        }
262    }
263
264    fn pic_read_elcr(&mut self, pic_type: PicSelect) -> u8 {
265        self.pics[pic_type as usize].elcr
266    }
267
268    fn pic_write_command(&mut self, pic_type: PicSelect, value: u8) {
269        if value & ICW1_MASK != 0 {
270            self.init_command_word_1(pic_type, value);
271        } else if value & OCW3_MASK != 0 {
272            Pic::operation_command_word_3(&mut self.pics[pic_type as usize], value);
273        } else {
274            self.operation_command_word_2(pic_type, value);
275        }
276    }
277
278    fn pic_write_data(&mut self, pic_type: PicSelect, value: u8) {
279        match self.pics[pic_type as usize].init_state {
280            PicInitState::Icw1 => {
281                self.pics[pic_type as usize].imr = value;
282                self.update_irq();
283            }
284            PicInitState::Icw2 => {
285                self.pics[pic_type as usize].irq_base = value & ICW2_IRQ_BASE_MASK;
286                self.pics[pic_type as usize].init_state = PicInitState::Icw3;
287            }
288            PicInitState::Icw3 => {
289                if self.pics[pic_type as usize].use_4_byte_icw {
290                    self.pics[pic_type as usize].init_state = PicInitState::Icw4;
291                } else {
292                    self.pics[pic_type as usize].init_state = PicInitState::Icw1;
293                }
294            }
295            PicInitState::Icw4 => {
296                self.pics[pic_type as usize].special_fully_nested_mode =
297                    (value & ICW4_SPECIAL_FULLY_NESTED_MODE) != 0;
298                self.pics[pic_type as usize].auto_eoi = (value & ICW4_AUTO_EOI) != 0;
299                self.pics[pic_type as usize].init_state = PicInitState::Icw1;
300            }
301        }
302    }
303
304    fn pic_write_elcr(&mut self, pic_type: PicSelect, value: u8) {
305        self.pics[pic_type as usize].elcr = value & !self.pics[pic_type as usize].elcr;
306    }
307
308    fn reset_pic(&mut self, pic_type: PicSelect) {
309        let pic = &mut self.pics[pic_type as usize];
310
311        let edge_irr = pic.irr & !pic.elcr;
312
313        pic.last_irr = 0;
314        pic.irr &= pic.elcr;
315        pic.imr = 0;
316        pic.priority_add = 0;
317        pic.special_mask = false;
318        pic.read_reg_select = false;
319        if !pic.use_4_byte_icw {
320            pic.special_fully_nested_mode = false;
321            pic.auto_eoi = false;
322        }
323        pic.init_state = PicInitState::Icw2;
324
325        for irq in 0..8 {
326            if edge_irr & (1 << irq) != 0 {
327                self.clear_isr(pic_type, irq);
328            }
329        }
330    }
331
332    /// Determine the priority and whether an update_irq call is needed.
333    fn poll_read(&mut self, pic_type: PicSelect) -> (u8, bool) {
334        if let Some(irq) = self.get_irq(pic_type) {
335            if pic_type == PicSelect::Secondary {
336                self.pics[PicSelect::Primary as usize].isr &= !PRIMARY_PIC_CASCADE_PIN_MASK;
337                self.pics[PicSelect::Primary as usize].irr &= !PRIMARY_PIC_CASCADE_PIN_MASK;
338            }
339            self.pics[pic_type as usize].irr &= !(1 << irq);
340            self.clear_isr(pic_type, irq);
341            let update_irq_needed =
342                pic_type == PicSelect::Secondary && irq != PRIMARY_PIC_CASCADE_PIN;
343            (irq, update_irq_needed)
344        } else {
345            // Spurious interrupt
346            (SPURIOUS_IRQ, true)
347        }
348    }
349
350    fn get_irq(&self, pic_type: PicSelect) -> Option<u8> {
351        let pic = &self.pics[pic_type as usize];
352        let mut irq_bitmap = pic.irr & !pic.imr;
353        let priority = Pic::get_priority(pic, irq_bitmap)?;
354
355        // If the primary is in fully-nested mode, the IRQ coming from the secondary is not taken
356        // into account for the priority computation below.
357        irq_bitmap = pic.isr;
358        if pic_type == PicSelect::Primary && pic.special_fully_nested_mode {
359            irq_bitmap &= !PRIMARY_PIC_CASCADE_PIN_MASK;
360        }
361        let new_priority = Pic::get_priority(pic, irq_bitmap).unwrap_or(INVALID_PRIORITY);
362        if priority < new_priority {
363            // Higher priority found. IRQ should be generated.
364            Some((priority + pic.priority_add) & 7)
365        } else {
366            None
367        }
368    }
369
370    fn clear_isr(&mut self, pic_type: PicSelect, irq: u8) {
371        let pic = &mut self.pics[pic_type as usize];
372
373        assert!(irq <= 7, "Unexpectedly high value for irq: {irq} vs 7");
374        pic.isr &= !(1 << irq);
375        Pic::set_irq_internal(pic, irq, false);
376        let irq = if pic_type == PicSelect::Primary {
377            irq
378        } else {
379            irq + 8
380        };
381        if let Some(resample_events) = self.resample_events.get(irq as usize) {
382            for resample_evt in resample_events {
383                resample_evt.signal().unwrap();
384            }
385        }
386    }
387
388    fn update_irq(&mut self) -> bool {
389        if self.get_irq(PicSelect::Secondary).is_some() {
390            // If secondary pic has an IRQ request, signal primary's cascade pin.
391            Pic::set_irq_internal(
392                &mut self.pics[PicSelect::Primary as usize],
393                PRIMARY_PIC_CASCADE_PIN,
394                LEVEL_HIGH,
395            );
396            Pic::set_irq_internal(
397                &mut self.pics[PicSelect::Primary as usize],
398                PRIMARY_PIC_CASCADE_PIN,
399                LEVEL_LOW,
400            );
401        }
402
403        if self.get_irq(PicSelect::Primary).is_some() {
404            self.interrupt_request = true;
405            // Note: this does not check if the interrupt is succesfully injected into
406            // the CPU, just whether or not one is fired.
407            true
408        } else {
409            false
410        }
411    }
412
413    /// Set Irq level. If edge is detected, then IRR is set to 1.
414    fn set_irq_internal(pic: &mut PicState, irq: u8, level: bool) {
415        assert!(irq <= 7, "Unexpectedly high value for irq: {irq} vs 7");
416        let irq_bitmap = 1 << irq;
417        if (pic.elcr & irq_bitmap) != 0 {
418            // Level-triggered.
419            if level {
420                // Same IRQ already requested.
421                pic.irr |= irq_bitmap;
422                pic.last_irr |= irq_bitmap;
423            } else {
424                pic.irr &= !irq_bitmap;
425                pic.last_irr &= !irq_bitmap;
426            }
427        } else {
428            // Edge-triggered
429            if level {
430                if (pic.last_irr & irq_bitmap) == 0 {
431                    // Raising edge detected.
432                    pic.irr |= irq_bitmap;
433                }
434                pic.last_irr |= irq_bitmap;
435            } else {
436                pic.last_irr &= !irq_bitmap;
437            }
438        }
439    }
440
441    fn get_priority(pic: &PicState, irq_bitmap: u8) -> Option<u8> {
442        if irq_bitmap == 0 {
443            None
444        } else {
445            // Find the highest priority bit in irq_bitmap considering the priority
446            // rotation mechanism (priority_add).
447            let mut priority = 0;
448            let mut priority_mask = 1 << ((priority + pic.priority_add) & 7);
449            while (irq_bitmap & priority_mask) == 0 {
450                priority += 1;
451                priority_mask = 1 << ((priority + pic.priority_add) & 7);
452            }
453            Some(priority)
454        }
455    }
456
457    /// Move interrupt from IRR to ISR to indicate that the interrupt is injected. If
458    /// auto EOI is set, then ISR is immediately cleared (since the purpose of EOI is
459    /// to clear ISR bit).
460    fn interrupt_ack(&mut self, pic_type: PicSelect, irq: u8) {
461        let pic = &mut self.pics[pic_type as usize];
462
463        assert!(irq <= 7, "Unexpectedly high value for irq: {irq} vs 7");
464
465        let irq_bitmap = 1 << irq;
466        pic.isr |= irq_bitmap;
467
468        if (pic.elcr & irq_bitmap) == 0 {
469            pic.irr &= !irq_bitmap;
470        }
471
472        if pic.auto_eoi {
473            if pic.rotate_on_auto_eoi {
474                pic.priority_add = (irq + 1) & 7;
475            }
476            self.clear_isr(pic_type, irq);
477        }
478    }
479
480    fn init_command_word_1(&mut self, pic_type: PicSelect, value: u8) {
481        let pic = &mut self.pics[pic_type as usize];
482        pic.use_4_byte_icw = (value & ICW1_NEED_ICW4) != 0;
483        if (value & ICW1_SINGLE_PIC_MODE) != 0 {
484            debug!("PIC: Single PIC mode not supported.");
485        }
486        if (value & ICW1_LEVEL_TRIGGER_MODE) != 0 {
487            debug!("PIC: Level triggered IRQ not supported.");
488        }
489        self.reset_pic(pic_type);
490    }
491
492    fn operation_command_word_2(&mut self, pic_type: PicSelect, value: u8) {
493        let mut irq = value & OCW2_IRQ_MASK;
494        if let Some(cmd) = Ocw2::n(value & OCW2_COMMAND_MASK) {
495            match cmd {
496                Ocw2::RotateAutoEoiSet => self.pics[pic_type as usize].rotate_on_auto_eoi = true,
497                Ocw2::RotateAutoEoiClear => self.pics[pic_type as usize].rotate_on_auto_eoi = false,
498                Ocw2::NonSpecificEoi | Ocw2::RotateNonSpecificEoi => {
499                    if let Some(priority) = Pic::get_priority(
500                        &self.pics[pic_type as usize],
501                        self.pics[pic_type as usize].isr,
502                    ) {
503                        irq = (priority + self.pics[pic_type as usize].priority_add) & 7;
504                        if cmd == Ocw2::RotateNonSpecificEoi {
505                            self.pics[pic_type as usize].priority_add = (irq + 1) & 7;
506                        }
507                        self.clear_isr(pic_type, irq);
508                        self.update_irq();
509                    }
510                }
511                Ocw2::SpecificEoi => {
512                    self.clear_isr(pic_type, irq);
513                    self.update_irq();
514                }
515                Ocw2::SetPriority => {
516                    self.pics[pic_type as usize].priority_add = (irq + 1) & 7;
517                    self.update_irq();
518                }
519                Ocw2::RotateSpecificEoi => {
520                    self.pics[pic_type as usize].priority_add = (irq + 1) & 7;
521                    self.clear_isr(pic_type, irq);
522                    self.update_irq();
523                }
524                Ocw2::NoOp => {} /* noop */
525            }
526        }
527    }
528
529    fn operation_command_word_3(pic: &mut PicState, value: u8) {
530        if value & OCW3_POLL_COMMAND != 0 {
531            pic.poll = true;
532        }
533        if value & OCW3_READ_REGISTER != 0 {
534            // Select to read ISR or IRR
535            pic.read_reg_select = value & OCW3_READ_ISR != 0;
536        }
537        if value & OCW3_SPECIAL_MASK != 0 {
538            pic.special_mask = value & OCW3_SPECIAL_MASK_VALUE != 0;
539        }
540    }
541}
542
543impl Default for Pic {
544    fn default() -> Self {
545        Self::new()
546    }
547}
548
549/// The PIC is only used in very early boot on x86_64, and snapshots are not
550/// generally taken during that time, so we can safely skip the PIC for now.
551impl Suspendable for Pic {
552    fn sleep(&mut self) -> anyhow::Result<()> {
553        Ok(())
554    }
555
556    fn wake(&mut self) -> anyhow::Result<()> {
557        Ok(())
558    }
559
560    fn snapshot(&mut self) -> anyhow::Result<AnySnapshot> {
561        AnySnapshot::to_any(())
562    }
563
564    fn restore(&mut self, _data: AnySnapshot) -> anyhow::Result<()> {
565        Ok(())
566    }
567}
568
569#[cfg(test)]
570mod tests {
571    // ICW4: Special fully nested mode with no auto EOI.
572    const FULLY_NESTED_NO_AUTO_EOI: u8 = 0x11;
573    use super::*;
574
575    struct TestData {
576        pic: Pic,
577    }
578
579    fn pic_bus_address(address: u64) -> BusAccessInfo {
580        // The PIC is added to the io_bus in three locations, so the offset depends on which
581        // address range the address is in. The PIC implementation currently does not use the
582        // offset, but we're setting it accurately here in case it does in the future.
583        let base_address = if (PIC_PRIMARY..PIC_PRIMARY + 0x2).contains(&address) {
584            PIC_PRIMARY
585        } else if (PIC_SECONDARY..PIC_SECONDARY + 0x2).contains(&address) {
586            PIC_SECONDARY
587        } else if (PIC_PRIMARY_ELCR..PIC_PRIMARY_ELCR + 0x2).contains(&address) {
588            PIC_PRIMARY_ELCR
589        } else {
590            panic!("invalid PIC address: {address:#x}");
591        };
592        BusAccessInfo {
593            offset: address - base_address,
594            address,
595            id: 0,
596        }
597    }
598
599    fn set_up() -> TestData {
600        let mut pic = Pic::new();
601        // Use edge-triggered mode.
602        pic.write(pic_bus_address(PIC_PRIMARY_ELCR), &[0]);
603        pic.write(pic_bus_address(PIC_SECONDARY_ELCR), &[0]);
604        TestData { pic }
605    }
606
607    /// Convenience wrapper to initialize PIC using 4 ICWs. Validity of values is NOT checked.
608    fn icw_init(pic: &mut Pic, pic_type: PicSelect, icw1: u8, icw2: u8, icw3: u8, icw4: u8) {
609        let command_offset = match pic_type {
610            PicSelect::Primary => PIC_PRIMARY_COMMAND,
611            PicSelect::Secondary => PIC_SECONDARY_COMMAND,
612        };
613        let data_offset = match pic_type {
614            PicSelect::Primary => PIC_PRIMARY_DATA,
615            PicSelect::Secondary => PIC_SECONDARY_DATA,
616        };
617
618        pic.write(pic_bus_address(command_offset), &[icw1]);
619        pic.write(pic_bus_address(data_offset), &[icw2]);
620        pic.write(pic_bus_address(data_offset), &[icw3]);
621        pic.write(pic_bus_address(data_offset), &[icw4]);
622    }
623
624    /// Convenience function for primary ICW init.
625    fn icw_init_primary(pic: &mut Pic) {
626        // ICW1 0x11: Edge trigger, cascade mode, ICW4 needed.
627        // ICW2 0x08: Interrupt vector base address 0x08.
628        // ICW3 0xff: Value written does not matter.
629        // ICW4 0x13: Special fully nested mode, auto EOI.
630        icw_init(pic, PicSelect::Primary, 0x11, 0x08, 0xff, 0x13);
631    }
632
633    /// Convenience function for secondary ICW init.
634    fn icw_init_secondary(pic: &mut Pic) {
635        // ICW1 0x11: Edge trigger, cascade mode, ICW4 needed.
636        // ICW2 0x70: Interrupt vector base address 0x70.
637        // ICW3 0xff: Value written does not matter.
638        // ICW4 0x13: Special fully nested mode, auto EOI.
639        icw_init(pic, PicSelect::Secondary, 0x11, 0x70, 0xff, 0x13);
640    }
641
642    /// Convenience function for initializing ICW with custom value for ICW4.
643    fn icw_init_both_with_icw4(pic: &mut Pic, icw4: u8) {
644        // ICW1 0x11: Edge trigger, cascade mode, ICW4 needed.
645        // ICW2 0x08: Interrupt vector base address 0x08.
646        // ICW3 0xff: Value written does not matter.
647        icw_init(pic, PicSelect::Primary, 0x11, 0x08, 0xff, icw4);
648        // ICW1 0x11: Edge trigger, cascade mode, ICW4 needed.
649        // ICW2 0x70: Interrupt vector base address 0x70.
650        // ICW3 0xff: Value written does not matter.
651        icw_init(pic, PicSelect::Secondary, 0x11, 0x70, 0xff, icw4);
652    }
653
654    fn icw_init_both(pic: &mut Pic) {
655        icw_init_primary(pic);
656        icw_init_secondary(pic);
657    }
658
659    /// Test that elcr register can be written and read correctly.
660    #[test]
661    fn write_read_elcr() {
662        let mut data = set_up();
663        let data_write = [0x5f];
664        let mut data_read = [0];
665
666        data.pic
667            .write(pic_bus_address(PIC_PRIMARY_ELCR), &data_write);
668        data.pic
669            .read(pic_bus_address(PIC_PRIMARY_ELCR), &mut data_read);
670        assert_eq!(data_read, data_write);
671
672        data.pic
673            .write(pic_bus_address(PIC_SECONDARY_ELCR), &data_write);
674        data.pic
675            .read(pic_bus_address(PIC_SECONDARY_ELCR), &mut data_read);
676        assert_eq!(data_read, data_write);
677    }
678
679    /// Test the three-word ICW.
680    #[test]
681    fn icw_2_step() {
682        let mut data = set_up();
683
684        // ICW1
685        let mut data_write = [0x10];
686        data.pic
687            .write(pic_bus_address(PIC_PRIMARY_COMMAND), &data_write);
688
689        data_write[0] = 0x08;
690        data.pic
691            .write(pic_bus_address(PIC_PRIMARY_DATA), &data_write);
692
693        data_write[0] = 0xff;
694        data.pic
695            .write(pic_bus_address(PIC_PRIMARY_DATA), &data_write);
696
697        assert_eq!(
698            data.pic.pics[PicSelect::Primary as usize].init_state,
699            PicInitState::Icw1
700        );
701        assert_eq!(data.pic.pics[PicSelect::Primary as usize].irq_base, 0x08);
702        assert_eq!(
703            data.pic.pics[PicSelect::Primary as usize].use_4_byte_icw,
704            false
705        );
706    }
707
708    /// Verify that PIC is in expected state after initialization.
709    #[test]
710    fn initial_values() {
711        let mut data = set_up();
712        icw_init_primary(&mut data.pic);
713
714        let primary_pic = &data.pic.pics[PicSelect::Primary as usize];
715        assert_eq!(primary_pic.last_irr, 0x00);
716        assert_eq!(primary_pic.irr, 0x00);
717        assert_eq!(primary_pic.imr, 0x00);
718        assert_eq!(primary_pic.isr, 0x00);
719        assert_eq!(primary_pic.priority_add, 0);
720        assert_eq!(primary_pic.irq_base, 0x08);
721        assert_eq!(primary_pic.read_reg_select, false);
722        assert_eq!(primary_pic.poll, false);
723        assert_eq!(primary_pic.special_mask, false);
724        assert_eq!(primary_pic.init_state, PicInitState::Icw1);
725        assert_eq!(primary_pic.auto_eoi, true);
726        assert_eq!(primary_pic.rotate_on_auto_eoi, false);
727        assert_eq!(primary_pic.special_fully_nested_mode, true);
728        assert_eq!(primary_pic.use_4_byte_icw, true);
729        assert_eq!(primary_pic.elcr, 0x00);
730        assert_eq!(primary_pic.elcr_mask, 0xf8);
731    }
732
733    /// Verify effect that OCW has on PIC registers & state.
734    #[test]
735    fn ocw() {
736        let mut data = set_up();
737
738        icw_init_secondary(&mut data.pic);
739
740        // OCW1: Write to IMR.
741        data.pic.write(pic_bus_address(PIC_SECONDARY_DATA), &[0x5f]);
742
743        // OCW2: Set rotate on auto EOI.
744        data.pic
745            .write(pic_bus_address(PIC_SECONDARY_COMMAND), &[0x80]);
746
747        // OCW2: Set priority.
748        data.pic
749            .write(pic_bus_address(PIC_SECONDARY_COMMAND), &[0xc0]);
750
751        // OCW3: Change flags.
752        data.pic
753            .write(pic_bus_address(PIC_SECONDARY_COMMAND), &[0x6b]);
754
755        let mut data_read = [0];
756        data.pic
757            .read(pic_bus_address(PIC_SECONDARY_DATA), &mut data_read);
758        assert_eq!(data_read, [0x5f]);
759
760        let secondary_pic = &data.pic.pics[PicSelect::Secondary as usize];
761
762        // Check OCW1 result.
763        assert_eq!(secondary_pic.imr, 0x5f);
764
765        // Check OCW2 result.
766        assert!(secondary_pic.rotate_on_auto_eoi);
767        assert_eq!(secondary_pic.priority_add, 1);
768
769        // Check OCW3 result.
770        assert!(secondary_pic.special_mask);
771        assert_eq!(secondary_pic.poll, false);
772        assert!(secondary_pic.read_reg_select);
773    }
774
775    /// Verify that we can set and clear the AutoRotate bit in OCW.
776    #[test]
777    fn ocw_auto_rotate_set_and_clear() {
778        let mut data = set_up();
779
780        icw_init_secondary(&mut data.pic);
781
782        // OCW2: Set rotate on auto EOI.
783        data.pic
784            .write(pic_bus_address(PIC_SECONDARY_COMMAND), &[0x80]);
785
786        let secondary_pic = &data.pic.pics[PicSelect::Secondary as usize];
787        assert!(secondary_pic.rotate_on_auto_eoi);
788
789        // OCW2: Clear rotate on auto EOI.
790        data.pic
791            .write(pic_bus_address(PIC_SECONDARY_COMMAND), &[0x00]);
792
793        let secondary_pic = &data.pic.pics[PicSelect::Secondary as usize];
794        assert!(!secondary_pic.rotate_on_auto_eoi);
795    }
796
797    /// Test basic auto EOI case.
798    #[test]
799    fn auto_eoi() {
800        let mut data = set_up();
801
802        icw_init_both(&mut data.pic);
803
804        // TODO(mutexlox): Verify APIC interaction when it is implemented.
805        data.pic.service_irq(/* irq= */ 12, /* level= */ true);
806
807        // Check that IRQ is requesting acknowledgment.
808        assert_eq!(data.pic.pics[PicSelect::Secondary as usize].irr, (1 << 4));
809        assert_eq!(data.pic.pics[PicSelect::Secondary as usize].isr, 0);
810        assert_eq!(data.pic.pics[PicSelect::Primary as usize].irr, (1 << 2));
811        assert_eq!(data.pic.pics[PicSelect::Primary as usize].isr, 0);
812
813        // 0x70 is interrupt base on secondary PIC. 0x70 + 4 is the interrupt entry number.
814        assert_eq!(data.pic.get_external_interrupt(), Some(0x70 + 4));
815
816        // Check that IRQ is acknowledged and EOI is automatically done.
817        assert_eq!(data.pic.pics[PicSelect::Secondary as usize].irr, 0);
818        assert_eq!(data.pic.pics[PicSelect::Secondary as usize].isr, 0);
819        assert_eq!(data.pic.pics[PicSelect::Primary as usize].irr, 0);
820        assert_eq!(data.pic.pics[PicSelect::Primary as usize].isr, 0);
821    }
822
823    /// Test with fully-nested mode on. When the secondary PIC has an IRQ in service, it shouldn't
824    /// be locked out by the primary's priority logic.
825    /// This means that the secondary should still be able to request a higher-priority IRQ.
826    /// Auto EOI is off in order to keep IRQ in service.
827    #[test]
828    fn fully_nested_mode_on() {
829        let mut data = set_up();
830
831        icw_init_both_with_icw4(&mut data.pic, FULLY_NESTED_NO_AUTO_EOI);
832
833        // TODO(mutexlox): Verify APIC interaction when it is implemented.
834        data.pic.service_irq(/* irq= */ 12, /* level= */ true);
835        assert_eq!(data.pic.get_external_interrupt(), Some(0x70 + 4));
836
837        // TODO(mutexlox): Verify APIC interaction when it is implemented.
838        // Request higher-priority IRQ on secondary.
839        data.pic.service_irq(/* irq= */ 8, /* level= */ true);
840        assert_eq!(data.pic.get_external_interrupt(), Some(0x70 + 0));
841
842        // Check that IRQ is ack'd and EOI is automatically done.
843        assert_eq!(data.pic.pics[PicSelect::Secondary as usize].irr, 0);
844        assert_eq!(
845            data.pic.pics[PicSelect::Secondary as usize].isr,
846            (1 << 4) + (1 << 0)
847        );
848        assert_eq!(data.pic.pics[PicSelect::Primary as usize].irr, 0);
849        assert_eq!(data.pic.pics[PicSelect::Primary as usize].isr, 1 << 2);
850    }
851
852    /// Test with fully-nested mode off. When the secondary PIC has an IRQ in service, it should
853    /// NOT be able to request another higher-priority IRQ.
854    /// Auto EOI is off in order to keep IRQ in service.
855    #[test]
856    fn fully_nested_mode_off() {
857        let mut data = set_up();
858
859        // ICW4 0x01: No special fully nested mode, no auto EOI.
860        icw_init_both_with_icw4(&mut data.pic, 0x01);
861
862        // TODO(mutexlox): Verify APIC interaction when it is implemented.
863        data.pic.service_irq(/* irq= */ 12, /* level= */ true);
864        assert_eq!(data.pic.get_external_interrupt(), Some(0x70 + 4));
865
866        data.pic.service_irq(/* irq= */ 8, /* level= */ true);
867        // Primary cannot get any IRQ, so this should not provide any interrupt.
868        assert_eq!(data.pic.get_external_interrupt(), None);
869
870        assert_eq!(data.pic.pics[PicSelect::Secondary as usize].irr, 1 << 0);
871        assert_eq!(data.pic.pics[PicSelect::Secondary as usize].isr, 1 << 4);
872        assert_eq!(data.pic.pics[PicSelect::Primary as usize].irr, 1 << 2);
873        assert_eq!(data.pic.pics[PicSelect::Primary as usize].isr, 1 << 2);
874
875        // 2 EOIs will cause 2 interrupts.
876        // TODO(mutexlox): Verify APIC interaction when it is implemented.
877
878        // OCW2: Non-specific EOI, one for primary and one for secondary.
879        data.pic
880            .write(pic_bus_address(PIC_PRIMARY_COMMAND), &[0x20]);
881        data.pic
882            .write(pic_bus_address(PIC_SECONDARY_COMMAND), &[0x20]);
883
884        // Now that the first IRQ is no longer in service, the second IRQ can be ack'd.
885        assert_eq!(data.pic.get_external_interrupt(), Some(0x70 + 0));
886
887        assert_eq!(data.pic.pics[PicSelect::Secondary as usize].irr, 0);
888        assert_eq!(data.pic.pics[PicSelect::Secondary as usize].isr, 1 << 0);
889        assert_eq!(data.pic.pics[PicSelect::Primary as usize].irr, 0);
890        assert_eq!(data.pic.pics[PicSelect::Primary as usize].isr, 1 << 2);
891    }
892
893    /// Write IMR to mask an IRQ. The masked IRQ can't be served until unmasked.
894    #[test]
895    fn mask_irq() {
896        let mut data = set_up();
897
898        icw_init_both_with_icw4(&mut data.pic, FULLY_NESTED_NO_AUTO_EOI);
899
900        // OCW2: Mask IRQ line 6 on secondary (IRQ 14).
901        data.pic.write(pic_bus_address(PIC_SECONDARY_DATA), &[0x40]);
902
903        data.pic.service_irq(/* irq= */ 14, /* level= */ true);
904        assert_eq!(data.pic.get_external_interrupt(), None);
905
906        assert_eq!(data.pic.pics[PicSelect::Secondary as usize].irr, 1 << 6);
907        assert_eq!(data.pic.pics[PicSelect::Secondary as usize].isr, 0);
908        assert_eq!(data.pic.pics[PicSelect::Primary as usize].irr, 0);
909        assert_eq!(data.pic.pics[PicSelect::Primary as usize].isr, 0);
910
911        // OCW2: Unmask IRQ line 6 on secondary (IRQ 14)
912        // TODO(mutexlox): Verify APIC interaction when it is implemented.
913        data.pic.write(pic_bus_address(PIC_SECONDARY_DATA), &[0x00]);
914
915        // Previously-masked interrupt can now be served.
916        assert_eq!(data.pic.get_external_interrupt(), Some(0x70 + 6));
917
918        assert_eq!(data.pic.pics[PicSelect::Secondary as usize].irr, 0);
919        assert_eq!(data.pic.pics[PicSelect::Secondary as usize].isr, 1 << 6);
920        assert_eq!(data.pic.pics[PicSelect::Primary as usize].irr, 0);
921        assert_eq!(data.pic.pics[PicSelect::Primary as usize].isr, 1 << 2);
922    }
923
924    /// Write IMR to mask multiple IRQs. They masked IRQs cannot be served until they're unmasked.
925    /// The highest priority IRQ must be served first, no matter the original order of request.
926    /// (To simplify the test, we won't check irr and isr and so we'll leave auto EOI on.)
927    #[test]
928    fn mask_multiple_irq() {
929        let mut data = set_up();
930        icw_init_both(&mut data.pic);
931
932        // OCW2: Mask *all* IRQ lines on primary and secondary.
933        data.pic.write(pic_bus_address(PIC_PRIMARY_DATA), &[0xff]);
934        data.pic.write(pic_bus_address(PIC_SECONDARY_DATA), &[0xff]);
935
936        data.pic.service_irq(/* irq= */ 14, /* level= */ true);
937        data.pic.service_irq(/* irq= */ 4, /* level= */ true);
938        data.pic.service_irq(/* irq= */ 12, /* level= */ true);
939
940        // Primary cannot get any IRQs since they're all masked.
941        assert_eq!(data.pic.get_external_interrupt(), None);
942
943        // OCW2: Unmask IRQ lines on secondary.
944        data.pic.write(pic_bus_address(PIC_SECONDARY_DATA), &[0x00]);
945
946        // Cascade line is masked, so the primary *still* cannot get any IRQs.
947        assert_eq!(data.pic.get_external_interrupt(), None);
948
949        // Unmask cascade line on primary.
950        // TODO(mutexlox): Verify APIC interaction when it is implemented.
951        data.pic.write(pic_bus_address(PIC_PRIMARY_DATA), &[0xfb]);
952
953        // Previously-masked IRQs should now be served in order of priority.
954        // TODO(mutexlox): Verify APIC interaction when it is implemented.
955        assert_eq!(data.pic.get_external_interrupt(), Some(0x70 + 4));
956        assert_eq!(data.pic.get_external_interrupt(), Some(0x70 + 6));
957
958        // Unmask all other IRQ lines on primary.
959        // TODO(mutexlox): Verify APIC interaction when it is implemented.
960        data.pic.write(pic_bus_address(PIC_PRIMARY_DATA), &[0x00]);
961        assert_eq!(data.pic.get_external_interrupt(), Some(0x08 + 4));
962    }
963
964    /// Test OCW3 poll (reading irr and isr).
965    #[test]
966    fn ocw3() {
967        let mut data = set_up();
968        icw_init_both_with_icw4(&mut data.pic, FULLY_NESTED_NO_AUTO_EOI);
969
970        // TODO(mutexlox): Verify APIC interaction when it is implemented.
971        // Poplate some data on irr/isr. IRQ4 will be in isr and IRQ5 in irr.
972        data.pic.service_irq(/* irq= */ 5, /* level= */ true);
973        data.pic.service_irq(/* irq= */ 4, /* level= */ true);
974        assert_eq!(data.pic.get_external_interrupt(), Some(0x08 + 4));
975
976        // Read primary IRR.
977        data.pic
978            .write(pic_bus_address(PIC_PRIMARY_COMMAND), &[0x0a]);
979        let mut data_read = [0];
980        data.pic
981            .read(pic_bus_address(PIC_PRIMARY_COMMAND), &mut data_read);
982        assert_eq!(data_read[0], 1 << 5);
983
984        // Read primary ISR.
985        data.pic
986            .write(pic_bus_address(PIC_PRIMARY_COMMAND), &[0x0b]);
987        data_read = [0];
988        data.pic
989            .read(pic_bus_address(PIC_PRIMARY_COMMAND), &mut data_read);
990        assert_eq!(data_read[0], 1 << 4);
991
992        // Non-sepcific EOI to end IRQ4.  Then, PIC should signal CPU about IRQ5.
993        // TODO(mutexlox): Verify APIC interaction when it is implemented.
994        data.pic
995            .write(pic_bus_address(PIC_PRIMARY_COMMAND), &[0x20]);
996
997        // Poll command on primary.
998        data.pic
999            .write(pic_bus_address(PIC_PRIMARY_COMMAND), &[0x0c]);
1000        data_read = [0];
1001        data.pic
1002            .read(pic_bus_address(PIC_PRIMARY_COMMAND), &mut data_read);
1003        assert_eq!(data_read[0], 5);
1004    }
1005
1006    /// Assert on primary PIC's IRQ2 without any IRQ on secondary asserted. This should result in a
1007    /// spurious IRQ on secondary.
1008    #[test]
1009    fn fake_irq_on_primary_irq2() {
1010        let mut data = set_up();
1011        icw_init_both_with_icw4(&mut data.pic, FULLY_NESTED_NO_AUTO_EOI);
1012
1013        // TODO(mutexlox): Verify APIC interaction when it is implemented.
1014        data.pic.service_irq(/* irq= */ 2, /* level= */ true);
1015        // 0x70 is secondary IRQ base, 7 is for a spurious IRQ.
1016        assert_eq!(data.pic.get_external_interrupt(), Some(0x70 + 7));
1017    }
1018
1019    /// Raising the same IRQ line twice in edge trigger mode should only send one IRQ request out.
1020    #[test]
1021    fn edge_trigger_mode() {
1022        let mut data = set_up();
1023        icw_init_both_with_icw4(&mut data.pic, FULLY_NESTED_NO_AUTO_EOI);
1024
1025        // TODO(mutexlox): Verify APIC interaction when it is implemented.
1026        data.pic.service_irq(/* irq= */ 4, /* level= */ true);
1027        // get_external_interrupt clears the irr so it is possible to request the same IRQ again.
1028        assert_eq!(data.pic.get_external_interrupt(), Some(0x08 + 4));
1029
1030        data.pic.service_irq(/* irq= */ 4, /* level= */ true);
1031
1032        // In edge triggered mode, there should be no IRQ after this EOI.
1033        // TODO(mutexlox): Verify APIC interaction when it is implemented.
1034        data.pic
1035            .write(pic_bus_address(PIC_PRIMARY_COMMAND), &[0x20]);
1036    }
1037
1038    /// Raising the same IRQ line twice in level-triggered mode should send two IRQ requests out.
1039    #[test]
1040    fn level_trigger_mode() {
1041        let mut data = set_up();
1042        icw_init_both_with_icw4(&mut data.pic, FULLY_NESTED_NO_AUTO_EOI);
1043
1044        // Turn IRQ4 to level-triggered mode.
1045        data.pic.write(pic_bus_address(PIC_PRIMARY_ELCR), &[0x10]);
1046
1047        // TODO(mutexlox): Verify APIC interaction when it is implemented.
1048        data.pic.service_irq(/* irq= */ 4, /* level= */ true);
1049        // get_external_interrupt clears the irr so it is possible to request the same IRQ again.
1050        assert_eq!(data.pic.get_external_interrupt(), Some(0x08 + 4));
1051
1052        data.pic.service_irq(/* irq= */ 4, /* level= */ true);
1053
1054        // In level-triggered mode, there should be another IRQ request after this EOI.
1055        // TODO(mutexlox): Verify APIC interaction when it is implemented.
1056        data.pic
1057            .write(pic_bus_address(PIC_PRIMARY_COMMAND), &[0x20]);
1058    }
1059
1060    /// Specific EOI command in OCW2.
1061    #[test]
1062    fn specific_eoi() {
1063        let mut data = set_up();
1064        icw_init_both_with_icw4(&mut data.pic, FULLY_NESTED_NO_AUTO_EOI);
1065
1066        // TODO(mutexlox): Verify APIC interaction when it is implemented.
1067        data.pic.service_irq(/* irq= */ 4, /* level= */ true);
1068        assert_eq!(data.pic.get_external_interrupt(), Some(0x08 + 4));
1069
1070        // Specific EOI command on IRQ3. Primary PIC's ISR should be unaffected since it's targeted
1071        // at the wrong IRQ number.
1072        data.pic
1073            .write(pic_bus_address(PIC_PRIMARY_COMMAND), &[0x63]);
1074        assert_eq!(data.pic.pics[PicSelect::Primary as usize].isr, 1 << 4);
1075
1076        // Specific EOI command on IRQ4. Primary PIC's ISR should now be cleared.
1077        data.pic
1078            .write(pic_bus_address(PIC_PRIMARY_COMMAND), &[0x64]);
1079        assert_eq!(data.pic.pics[PicSelect::Primary as usize].isr, 0);
1080    }
1081
1082    /// Test rotate on auto EOI.
1083    #[test]
1084    fn rotate_on_auto_eoi() {
1085        let mut data = set_up();
1086        icw_init_both(&mut data.pic);
1087
1088        // OCW3: Clear rotate on auto EOI mode.
1089        data.pic
1090            .write(pic_bus_address(PIC_PRIMARY_COMMAND), &[0x00]);
1091
1092        // TODO(mutexlox): Verify APIC interaction when it is implemented.
1093        data.pic.service_irq(/* irq= */ 5, /* level= */ true);
1094        assert_eq!(data.pic.get_external_interrupt(), Some(0x08 + 5));
1095        data.pic.service_irq(/* irq= */ 5, /* level= */ false);
1096
1097        // EOI automatically happened. Now priority should not be rotated.
1098        assert_eq!(data.pic.pics[PicSelect::Primary as usize].isr, 0);
1099        assert_eq!(data.pic.pics[PicSelect::Primary as usize].imr, 0);
1100        assert_eq!(data.pic.pics[PicSelect::Primary as usize].last_irr, 0);
1101        assert_eq!(data.pic.pics[PicSelect::Primary as usize].priority_add, 0);
1102
1103        // OCW2: Set rotate on auto EOI mode.
1104        data.pic
1105            .write(pic_bus_address(PIC_PRIMARY_COMMAND), &[0x80]);
1106
1107        // TODO(mutexlox): Verify APIC interaction when it is implemented.
1108        data.pic.service_irq(/* irq= */ 5, /* level */ true);
1109        assert_eq!(data.pic.get_external_interrupt(), Some(0x08 + 5));
1110        data.pic.service_irq(/* irq= */ 5, /* level= */ false);
1111
1112        // EOI automatically happened, and the priority *should* be rotated.
1113        assert_eq!(data.pic.pics[PicSelect::Primary as usize].isr, 0);
1114        assert_eq!(data.pic.pics[PicSelect::Primary as usize].priority_add, 6);
1115    }
1116
1117    /// Test rotate on specific (non-auto) EOI.
1118    #[test]
1119    fn rotate_on_specific_eoi() {
1120        let mut data = set_up();
1121        icw_init_both_with_icw4(&mut data.pic, FULLY_NESTED_NO_AUTO_EOI);
1122
1123        // TODO(mutexlox): Verify APIC interaction when it is implemented.
1124        data.pic.service_irq(/* irq= */ 5, /* level= */ true);
1125        assert_eq!(data.pic.get_external_interrupt(), Some(0x08 + 5));
1126        data.pic.service_irq(/* irq= */ 5, /* level= */ false);
1127
1128        // Rotate on specific EOI IRQ4. Since this is a different IRQ number, Should not have an
1129        // effect on isr.
1130        data.pic
1131            .write(pic_bus_address(PIC_PRIMARY_COMMAND), &[0xe4]);
1132
1133        assert_eq!(data.pic.pics[PicSelect::Primary as usize].isr, 1 << 5);
1134
1135        // Rotate on specific EOI IRQ5. This should clear the isr.
1136        data.pic
1137            .write(pic_bus_address(PIC_PRIMARY_COMMAND), &[0xe5]);
1138
1139        assert_eq!(data.pic.pics[PicSelect::Primary as usize].isr, 0);
1140        assert_eq!(data.pic.pics[PicSelect::Primary as usize].priority_add, 6);
1141    }
1142
1143    /// Test rotate on non-specific EOI.
1144    #[test]
1145    fn rotate_non_specific_eoi() {
1146        let mut data = set_up();
1147        icw_init_both_with_icw4(&mut data.pic, FULLY_NESTED_NO_AUTO_EOI);
1148
1149        // TODO(mutexlox): Verify APIC interaction when it is implemented.
1150        data.pic.service_irq(/* irq= */ 5, /* level= */ true);
1151        assert_eq!(data.pic.get_external_interrupt(), Some(0x08 + 5));
1152        data.pic.service_irq(/* irq= */ 5, /* level= */ false);
1153
1154        // Rotate on non-specific EOI.
1155        data.pic
1156            .write(pic_bus_address(PIC_PRIMARY_COMMAND), &[0xa0]);
1157
1158        // The EOI should have cleared isr.
1159        assert_eq!(data.pic.pics[PicSelect::Primary as usize].isr, 0);
1160        assert_eq!(data.pic.pics[PicSelect::Primary as usize].priority_add, 6);
1161    }
1162
1163    /// Tests cascade IRQ that happens on secondary PIC.
1164    #[test]
1165    fn cascade_irq() {
1166        let mut data = set_up();
1167        icw_init_both_with_icw4(&mut data.pic, FULLY_NESTED_NO_AUTO_EOI);
1168
1169        // TODO(mutexlox): Verify APIC interaction when it is implemented.
1170        data.pic.service_irq(/* irq= */ 12, /* level= */ true);
1171
1172        assert_eq!(data.pic.pics[PicSelect::Primary as usize].irr, 1 << 2);
1173        assert_eq!(data.pic.pics[PicSelect::Secondary as usize].irr, 1 << 4);
1174
1175        assert_eq!(data.pic.get_external_interrupt(), Some(0x70 + 4));
1176
1177        // Check that the IRQ is now acknowledged after get_external_interrupt().
1178        assert_eq!(data.pic.pics[PicSelect::Secondary as usize].irr, 0);
1179        assert_eq!(data.pic.pics[PicSelect::Secondary as usize].isr, 1 << 4);
1180
1181        // OCW2: Two non-specific EOIs to primary rather than secondary.
1182        // We need two non-specific EOIs:
1183        //   - The first resets bit 2 in the primary isr (the highest-priority bit that was set
1184        //     before the EOI)
1185        //   - The second resets the secondary PIC's highest-priority isr bit.
1186        data.pic
1187            .write(pic_bus_address(PIC_PRIMARY_COMMAND), &[0x20]);
1188        // Rotate non-specific EOI.
1189        data.pic
1190            .write(pic_bus_address(PIC_SECONDARY_COMMAND), &[0xa0]);
1191
1192        assert_eq!(data.pic.pics[PicSelect::Primary as usize].isr, 0);
1193        assert_eq!(data.pic.pics[PicSelect::Secondary as usize].isr, 0);
1194        assert_eq!(data.pic.pics[PicSelect::Secondary as usize].priority_add, 5);
1195    }
1196}