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