• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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 Suspendable for Pic {
sleep(&mut self) -> anyhow::Result<()>543     fn sleep(&mut self) -> anyhow::Result<()> {
544         Ok(())
545     }
546 
wake(&mut self) -> anyhow::Result<()>547     fn wake(&mut self) -> anyhow::Result<()> {
548         Ok(())
549     }
550 }
551 
552 #[cfg(test)]
553 mod tests {
554     // ICW4: Special fully nested mode with no auto EOI.
555     const FULLY_NESTED_NO_AUTO_EOI: u8 = 0x11;
556     use super::*;
557 
558     struct TestData {
559         pic: Pic,
560     }
561 
pic_bus_address(address: u64) -> BusAccessInfo562     fn pic_bus_address(address: u64) -> BusAccessInfo {
563         // The PIC is added to the io_bus in three locations, so the offset depends on which
564         // address range the address is in. The PIC implementation currently does not use the
565         // offset, but we're setting it accurately here in case it does in the future.
566         let base_address = if (PIC_PRIMARY..PIC_PRIMARY + 0x2).contains(&address) {
567             PIC_PRIMARY
568         } else if (PIC_SECONDARY..PIC_SECONDARY + 0x2).contains(&address) {
569             PIC_SECONDARY
570         } else if (PIC_PRIMARY_ELCR..PIC_PRIMARY_ELCR + 0x2).contains(&address) {
571             PIC_PRIMARY_ELCR
572         } else {
573             panic!("invalid PIC address: {:#x}", address);
574         };
575         BusAccessInfo {
576             offset: address - base_address,
577             address,
578             id: 0,
579         }
580     }
581 
set_up() -> TestData582     fn set_up() -> TestData {
583         let mut pic = Pic::new();
584         // Use edge-triggered mode.
585         pic.write(pic_bus_address(PIC_PRIMARY_ELCR), &[0]);
586         pic.write(pic_bus_address(PIC_SECONDARY_ELCR), &[0]);
587         TestData { pic }
588     }
589 
590     /// 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)591     fn icw_init(pic: &mut Pic, pic_type: PicSelect, icw1: u8, icw2: u8, icw3: u8, icw4: u8) {
592         let command_offset = match pic_type {
593             PicSelect::Primary => PIC_PRIMARY_COMMAND,
594             PicSelect::Secondary => PIC_SECONDARY_COMMAND,
595         };
596         let data_offset = match pic_type {
597             PicSelect::Primary => PIC_PRIMARY_DATA,
598             PicSelect::Secondary => PIC_SECONDARY_DATA,
599         };
600 
601         pic.write(pic_bus_address(command_offset), &[icw1]);
602         pic.write(pic_bus_address(data_offset), &[icw2]);
603         pic.write(pic_bus_address(data_offset), &[icw3]);
604         pic.write(pic_bus_address(data_offset), &[icw4]);
605     }
606 
607     /// Convenience function for primary ICW init.
icw_init_primary(pic: &mut Pic)608     fn icw_init_primary(pic: &mut Pic) {
609         // ICW1 0x11: Edge trigger, cascade mode, ICW4 needed.
610         // ICW2 0x08: Interrupt vector base address 0x08.
611         // ICW3 0xff: Value written does not matter.
612         // ICW4 0x13: Special fully nested mode, auto EOI.
613         icw_init(pic, PicSelect::Primary, 0x11, 0x08, 0xff, 0x13);
614     }
615 
616     /// Convenience function for secondary ICW init.
icw_init_secondary(pic: &mut Pic)617     fn icw_init_secondary(pic: &mut Pic) {
618         // ICW1 0x11: Edge trigger, cascade mode, ICW4 needed.
619         // ICW2 0x70: Interrupt vector base address 0x70.
620         // ICW3 0xff: Value written does not matter.
621         // ICW4 0x13: Special fully nested mode, auto EOI.
622         icw_init(pic, PicSelect::Secondary, 0x11, 0x70, 0xff, 0x13);
623     }
624 
625     /// Convenience function for initializing ICW with custom value for ICW4.
icw_init_both_with_icw4(pic: &mut Pic, icw4: u8)626     fn icw_init_both_with_icw4(pic: &mut Pic, icw4: u8) {
627         // ICW1 0x11: Edge trigger, cascade mode, ICW4 needed.
628         // ICW2 0x08: Interrupt vector base address 0x08.
629         // ICW3 0xff: Value written does not matter.
630         icw_init(pic, PicSelect::Primary, 0x11, 0x08, 0xff, icw4);
631         // ICW1 0x11: Edge trigger, cascade mode, ICW4 needed.
632         // ICW2 0x70: Interrupt vector base address 0x70.
633         // ICW3 0xff: Value written does not matter.
634         icw_init(pic, PicSelect::Secondary, 0x11, 0x70, 0xff, icw4);
635     }
636 
icw_init_both(pic: &mut Pic)637     fn icw_init_both(pic: &mut Pic) {
638         icw_init_primary(pic);
639         icw_init_secondary(pic);
640     }
641 
642     /// Test that elcr register can be written and read correctly.
643     #[test]
write_read_elcr()644     fn write_read_elcr() {
645         let mut data = set_up();
646         let data_write = [0x5f];
647         let mut data_read = [0];
648 
649         data.pic
650             .write(pic_bus_address(PIC_PRIMARY_ELCR), &data_write);
651         data.pic
652             .read(pic_bus_address(PIC_PRIMARY_ELCR), &mut data_read);
653         assert_eq!(data_read, data_write);
654 
655         data.pic
656             .write(pic_bus_address(PIC_SECONDARY_ELCR), &data_write);
657         data.pic
658             .read(pic_bus_address(PIC_SECONDARY_ELCR), &mut data_read);
659         assert_eq!(data_read, data_write);
660     }
661 
662     /// Test the three-word ICW.
663     #[test]
icw_2_step()664     fn icw_2_step() {
665         let mut data = set_up();
666 
667         // ICW1
668         let mut data_write = [0x10];
669         data.pic
670             .write(pic_bus_address(PIC_PRIMARY_COMMAND), &data_write);
671 
672         data_write[0] = 0x08;
673         data.pic
674             .write(pic_bus_address(PIC_PRIMARY_DATA), &data_write);
675 
676         data_write[0] = 0xff;
677         data.pic
678             .write(pic_bus_address(PIC_PRIMARY_DATA), &data_write);
679 
680         assert_eq!(
681             data.pic.pics[PicSelect::Primary as usize].init_state,
682             PicInitState::Icw1
683         );
684         assert_eq!(data.pic.pics[PicSelect::Primary as usize].irq_base, 0x08);
685         assert_eq!(
686             data.pic.pics[PicSelect::Primary as usize].use_4_byte_icw,
687             false
688         );
689     }
690 
691     /// Verify that PIC is in expected state after initialization.
692     #[test]
initial_values()693     fn initial_values() {
694         let mut data = set_up();
695         icw_init_primary(&mut data.pic);
696 
697         let primary_pic = &data.pic.pics[PicSelect::Primary as usize];
698         assert_eq!(primary_pic.last_irr, 0x00);
699         assert_eq!(primary_pic.irr, 0x00);
700         assert_eq!(primary_pic.imr, 0x00);
701         assert_eq!(primary_pic.isr, 0x00);
702         assert_eq!(primary_pic.priority_add, 0);
703         assert_eq!(primary_pic.irq_base, 0x08);
704         assert_eq!(primary_pic.read_reg_select, false);
705         assert_eq!(primary_pic.poll, false);
706         assert_eq!(primary_pic.special_mask, false);
707         assert_eq!(primary_pic.init_state, PicInitState::Icw1);
708         assert_eq!(primary_pic.auto_eoi, true);
709         assert_eq!(primary_pic.rotate_on_auto_eoi, false);
710         assert_eq!(primary_pic.special_fully_nested_mode, true);
711         assert_eq!(primary_pic.use_4_byte_icw, true);
712         assert_eq!(primary_pic.elcr, 0x00);
713         assert_eq!(primary_pic.elcr_mask, 0xf8);
714     }
715 
716     /// Verify effect that OCW has on PIC registers & state.
717     #[test]
ocw()718     fn ocw() {
719         let mut data = set_up();
720 
721         icw_init_secondary(&mut data.pic);
722 
723         // OCW1: Write to IMR.
724         data.pic.write(pic_bus_address(PIC_SECONDARY_DATA), &[0x5f]);
725 
726         // OCW2: Set rotate on auto EOI.
727         data.pic
728             .write(pic_bus_address(PIC_SECONDARY_COMMAND), &[0x80]);
729 
730         // OCW2: Set priority.
731         data.pic
732             .write(pic_bus_address(PIC_SECONDARY_COMMAND), &[0xc0]);
733 
734         // OCW3: Change flags.
735         data.pic
736             .write(pic_bus_address(PIC_SECONDARY_COMMAND), &[0x6b]);
737 
738         let mut data_read = [0];
739         data.pic
740             .read(pic_bus_address(PIC_SECONDARY_DATA), &mut data_read);
741         assert_eq!(data_read, [0x5f]);
742 
743         let secondary_pic = &data.pic.pics[PicSelect::Secondary as usize];
744 
745         // Check OCW1 result.
746         assert_eq!(secondary_pic.imr, 0x5f);
747 
748         // Check OCW2 result.
749         assert!(secondary_pic.rotate_on_auto_eoi);
750         assert_eq!(secondary_pic.priority_add, 1);
751 
752         // Check OCW3 result.
753         assert!(secondary_pic.special_mask);
754         assert_eq!(secondary_pic.poll, false);
755         assert!(secondary_pic.read_reg_select);
756     }
757 
758     /// Verify that we can set and clear the AutoRotate bit in OCW.
759     #[test]
ocw_auto_rotate_set_and_clear()760     fn ocw_auto_rotate_set_and_clear() {
761         let mut data = set_up();
762 
763         icw_init_secondary(&mut data.pic);
764 
765         // OCW2: Set rotate on auto EOI.
766         data.pic
767             .write(pic_bus_address(PIC_SECONDARY_COMMAND), &[0x80]);
768 
769         let secondary_pic = &data.pic.pics[PicSelect::Secondary as usize];
770         assert!(secondary_pic.rotate_on_auto_eoi);
771 
772         // OCW2: Clear rotate on auto EOI.
773         data.pic
774             .write(pic_bus_address(PIC_SECONDARY_COMMAND), &[0x00]);
775 
776         let secondary_pic = &data.pic.pics[PicSelect::Secondary as usize];
777         assert!(!secondary_pic.rotate_on_auto_eoi);
778     }
779 
780     /// Test basic auto EOI case.
781     #[test]
auto_eoi()782     fn auto_eoi() {
783         let mut data = set_up();
784 
785         icw_init_both(&mut data.pic);
786 
787         // TODO(mutexlox): Verify APIC interaction when it is implemented.
788         data.pic.service_irq(/*irq=*/ 12, /*level=*/ true);
789 
790         // Check that IRQ is requesting acknowledgment.
791         assert_eq!(data.pic.pics[PicSelect::Secondary as usize].irr, (1 << 4));
792         assert_eq!(data.pic.pics[PicSelect::Secondary as usize].isr, 0);
793         assert_eq!(data.pic.pics[PicSelect::Primary as usize].irr, (1 << 2));
794         assert_eq!(data.pic.pics[PicSelect::Primary as usize].isr, 0);
795 
796         // 0x70 is interrupt base on secondary PIC. 0x70 + 4 is the interrupt entry number.
797         assert_eq!(data.pic.get_external_interrupt(), Some(0x70 + 4));
798 
799         // Check that IRQ is acknowledged and EOI is automatically done.
800         assert_eq!(data.pic.pics[PicSelect::Secondary as usize].irr, 0);
801         assert_eq!(data.pic.pics[PicSelect::Secondary as usize].isr, 0);
802         assert_eq!(data.pic.pics[PicSelect::Primary as usize].irr, 0);
803         assert_eq!(data.pic.pics[PicSelect::Primary as usize].isr, 0);
804     }
805 
806     /// Test with fully-nested mode on. When the secondary PIC has an IRQ in service, it shouldn't
807     /// be locked out by the primary's priority logic.
808     /// This means that the secondary should still be able to request a higher-priority IRQ.
809     /// Auto EOI is off in order to keep IRQ in service.
810     #[test]
fully_nested_mode_on()811     fn fully_nested_mode_on() {
812         let mut data = set_up();
813 
814         icw_init_both_with_icw4(&mut data.pic, FULLY_NESTED_NO_AUTO_EOI);
815 
816         // TODO(mutexlox): Verify APIC interaction when it is implemented.
817         data.pic.service_irq(/*irq=*/ 12, /*level=*/ true);
818         assert_eq!(data.pic.get_external_interrupt(), Some(0x70 + 4));
819 
820         // TODO(mutexlox): Verify APIC interaction when it is implemented.
821         // Request higher-priority IRQ on secondary.
822         data.pic.service_irq(/*irq=*/ 8, /*level=*/ true);
823         assert_eq!(data.pic.get_external_interrupt(), Some(0x70 + 0));
824 
825         // Check that IRQ is ack'd and EOI is automatically done.
826         assert_eq!(data.pic.pics[PicSelect::Secondary as usize].irr, 0);
827         assert_eq!(
828             data.pic.pics[PicSelect::Secondary as usize].isr,
829             (1 << 4) + (1 << 0)
830         );
831         assert_eq!(data.pic.pics[PicSelect::Primary as usize].irr, 0);
832         assert_eq!(data.pic.pics[PicSelect::Primary as usize].isr, 1 << 2);
833     }
834 
835     /// Test with fully-nested mode off. When the secondary PIC has an IRQ in service, it should
836     /// NOT be able to request another higher-priority IRQ.
837     /// Auto EOI is off in order to keep IRQ in service.
838     #[test]
fully_nested_mode_off()839     fn fully_nested_mode_off() {
840         let mut data = set_up();
841 
842         // ICW4 0x01: No special fully nested mode, no auto EOI.
843         icw_init_both_with_icw4(&mut data.pic, 0x01);
844 
845         // TODO(mutexlox): Verify APIC interaction when it is implemented.
846         data.pic.service_irq(/*irq=*/ 12, /*level=*/ true);
847         assert_eq!(data.pic.get_external_interrupt(), Some(0x70 + 4));
848 
849         data.pic.service_irq(/*irq=*/ 8, /*level=*/ true);
850         // Primary cannot get any IRQ, so this should not provide any interrupt.
851         assert_eq!(data.pic.get_external_interrupt(), None);
852 
853         assert_eq!(data.pic.pics[PicSelect::Secondary as usize].irr, 1 << 0);
854         assert_eq!(data.pic.pics[PicSelect::Secondary as usize].isr, 1 << 4);
855         assert_eq!(data.pic.pics[PicSelect::Primary as usize].irr, 1 << 2);
856         assert_eq!(data.pic.pics[PicSelect::Primary as usize].isr, 1 << 2);
857 
858         // 2 EOIs will cause 2 interrupts.
859         // TODO(mutexlox): Verify APIC interaction when it is implemented.
860 
861         // OCW2: Non-specific EOI, one for primary and one for secondary.
862         data.pic
863             .write(pic_bus_address(PIC_PRIMARY_COMMAND), &[0x20]);
864         data.pic
865             .write(pic_bus_address(PIC_SECONDARY_COMMAND), &[0x20]);
866 
867         // Now that the first IRQ is no longer in service, the second IRQ can be ack'd.
868         assert_eq!(data.pic.get_external_interrupt(), Some(0x70 + 0));
869 
870         assert_eq!(data.pic.pics[PicSelect::Secondary as usize].irr, 0);
871         assert_eq!(data.pic.pics[PicSelect::Secondary as usize].isr, 1 << 0);
872         assert_eq!(data.pic.pics[PicSelect::Primary as usize].irr, 0);
873         assert_eq!(data.pic.pics[PicSelect::Primary as usize].isr, 1 << 2);
874     }
875 
876     /// Write IMR to mask an IRQ. The masked IRQ can't be served until unmasked.
877     #[test]
mask_irq()878     fn mask_irq() {
879         let mut data = set_up();
880 
881         icw_init_both_with_icw4(&mut data.pic, FULLY_NESTED_NO_AUTO_EOI);
882 
883         // OCW2: Mask IRQ line 6 on secondary (IRQ 14).
884         data.pic.write(pic_bus_address(PIC_SECONDARY_DATA), &[0x40]);
885 
886         data.pic.service_irq(/*irq=*/ 14, /*level=*/ true);
887         assert_eq!(data.pic.get_external_interrupt(), None);
888 
889         assert_eq!(data.pic.pics[PicSelect::Secondary as usize].irr, 1 << 6);
890         assert_eq!(data.pic.pics[PicSelect::Secondary as usize].isr, 0);
891         assert_eq!(data.pic.pics[PicSelect::Primary as usize].irr, 0);
892         assert_eq!(data.pic.pics[PicSelect::Primary as usize].isr, 0);
893 
894         // OCW2: Unmask IRQ line 6 on secondary (IRQ 14)
895         // TODO(mutexlox): Verify APIC interaction when it is implemented.
896         data.pic.write(pic_bus_address(PIC_SECONDARY_DATA), &[0x00]);
897 
898         // Previously-masked interrupt can now be served.
899         assert_eq!(data.pic.get_external_interrupt(), Some(0x70 + 6));
900 
901         assert_eq!(data.pic.pics[PicSelect::Secondary as usize].irr, 0);
902         assert_eq!(data.pic.pics[PicSelect::Secondary as usize].isr, 1 << 6);
903         assert_eq!(data.pic.pics[PicSelect::Primary as usize].irr, 0);
904         assert_eq!(data.pic.pics[PicSelect::Primary as usize].isr, 1 << 2);
905     }
906 
907     /// Write IMR to mask multiple IRQs. They masked IRQs cannot be served until they're unmasked.
908     /// The highest priority IRQ must be served first, no matter the original order of request.
909     /// (To simplify the test, we won't check irr and isr and so we'll leave auto EOI on.)
910     #[test]
mask_multiple_irq()911     fn mask_multiple_irq() {
912         let mut data = set_up();
913         icw_init_both(&mut data.pic);
914 
915         // OCW2: Mask *all* IRQ lines on primary and secondary.
916         data.pic.write(pic_bus_address(PIC_PRIMARY_DATA), &[0xff]);
917         data.pic.write(pic_bus_address(PIC_SECONDARY_DATA), &[0xff]);
918 
919         data.pic.service_irq(/*irq=*/ 14, /*level=*/ true);
920         data.pic.service_irq(/*irq=*/ 4, /*level=*/ true);
921         data.pic.service_irq(/*irq=*/ 12, /*level=*/ true);
922 
923         // Primary cannot get any IRQs since they're all masked.
924         assert_eq!(data.pic.get_external_interrupt(), None);
925 
926         // OCW2: Unmask IRQ lines on secondary.
927         data.pic.write(pic_bus_address(PIC_SECONDARY_DATA), &[0x00]);
928 
929         // Cascade line is masked, so the primary *still* cannot get any IRQs.
930         assert_eq!(data.pic.get_external_interrupt(), None);
931 
932         // Unmask cascade line on primary.
933         // TODO(mutexlox): Verify APIC interaction when it is implemented.
934         data.pic.write(pic_bus_address(PIC_PRIMARY_DATA), &[0xfb]);
935 
936         // Previously-masked IRQs should now be served in order of priority.
937         // TODO(mutexlox): Verify APIC interaction when it is implemented.
938         assert_eq!(data.pic.get_external_interrupt(), Some(0x70 + 4));
939         assert_eq!(data.pic.get_external_interrupt(), Some(0x70 + 6));
940 
941         // Unmask all other IRQ lines on primary.
942         // TODO(mutexlox): Verify APIC interaction when it is implemented.
943         data.pic.write(pic_bus_address(PIC_PRIMARY_DATA), &[0x00]);
944         assert_eq!(data.pic.get_external_interrupt(), Some(0x08 + 4));
945     }
946 
947     /// Test OCW3 poll (reading irr and isr).
948     #[test]
ocw3()949     fn ocw3() {
950         let mut data = set_up();
951         icw_init_both_with_icw4(&mut data.pic, FULLY_NESTED_NO_AUTO_EOI);
952 
953         // TODO(mutexlox): Verify APIC interaction when it is implemented.
954         // Poplate some data on irr/isr. IRQ4 will be in isr and IRQ5 in irr.
955         data.pic.service_irq(/*irq=*/ 5, /*level=*/ true);
956         data.pic.service_irq(/*irq=*/ 4, /*level=*/ true);
957         assert_eq!(data.pic.get_external_interrupt(), Some(0x08 + 4));
958 
959         // Read primary IRR.
960         data.pic
961             .write(pic_bus_address(PIC_PRIMARY_COMMAND), &[0x0a]);
962         let mut data_read = [0];
963         data.pic
964             .read(pic_bus_address(PIC_PRIMARY_COMMAND), &mut data_read);
965         assert_eq!(data_read[0], 1 << 5);
966 
967         // Read primary ISR.
968         data.pic
969             .write(pic_bus_address(PIC_PRIMARY_COMMAND), &[0x0b]);
970         data_read = [0];
971         data.pic
972             .read(pic_bus_address(PIC_PRIMARY_COMMAND), &mut data_read);
973         assert_eq!(data_read[0], 1 << 4);
974 
975         // Non-sepcific EOI to end IRQ4.  Then, PIC should signal CPU about IRQ5.
976         // TODO(mutexlox): Verify APIC interaction when it is implemented.
977         data.pic
978             .write(pic_bus_address(PIC_PRIMARY_COMMAND), &[0x20]);
979 
980         // Poll command on primary.
981         data.pic
982             .write(pic_bus_address(PIC_PRIMARY_COMMAND), &[0x0c]);
983         data_read = [0];
984         data.pic
985             .read(pic_bus_address(PIC_PRIMARY_COMMAND), &mut data_read);
986         assert_eq!(data_read[0], 5);
987     }
988 
989     /// Assert on primary PIC's IRQ2 without any IRQ on secondary asserted. This should result in a
990     /// spurious IRQ on secondary.
991     #[test]
fake_irq_on_primary_irq2()992     fn fake_irq_on_primary_irq2() {
993         let mut data = set_up();
994         icw_init_both_with_icw4(&mut data.pic, FULLY_NESTED_NO_AUTO_EOI);
995 
996         // TODO(mutexlox): Verify APIC interaction when it is implemented.
997         data.pic.service_irq(/*irq=*/ 2, /*level=*/ true);
998         // 0x70 is secondary IRQ base, 7 is for a spurious IRQ.
999         assert_eq!(data.pic.get_external_interrupt(), Some(0x70 + 7));
1000     }
1001 
1002     /// Raising the same IRQ line twice in edge trigger mode should only send one IRQ request out.
1003     #[test]
edge_trigger_mode()1004     fn edge_trigger_mode() {
1005         let mut data = set_up();
1006         icw_init_both_with_icw4(&mut data.pic, FULLY_NESTED_NO_AUTO_EOI);
1007 
1008         // TODO(mutexlox): Verify APIC interaction when it is implemented.
1009         data.pic.service_irq(/*irq=*/ 4, /*level=*/ true);
1010         // get_external_interrupt clears the irr so it is possible to request the same IRQ again.
1011         assert_eq!(data.pic.get_external_interrupt(), Some(0x08 + 4));
1012 
1013         data.pic.service_irq(/*irq=*/ 4, /*level=*/ true);
1014 
1015         // In edge triggered mode, there should be no IRQ after this EOI.
1016         // TODO(mutexlox): Verify APIC interaction when it is implemented.
1017         data.pic
1018             .write(pic_bus_address(PIC_PRIMARY_COMMAND), &[0x20]);
1019     }
1020 
1021     /// Raising the same IRQ line twice in level-triggered mode should send two IRQ requests out.
1022     #[test]
level_trigger_mode()1023     fn level_trigger_mode() {
1024         let mut data = set_up();
1025         icw_init_both_with_icw4(&mut data.pic, FULLY_NESTED_NO_AUTO_EOI);
1026 
1027         // Turn IRQ4 to level-triggered mode.
1028         data.pic.write(pic_bus_address(PIC_PRIMARY_ELCR), &[0x10]);
1029 
1030         // TODO(mutexlox): Verify APIC interaction when it is implemented.
1031         data.pic.service_irq(/*irq=*/ 4, /*level=*/ true);
1032         // get_external_interrupt clears the irr so it is possible to request the same IRQ again.
1033         assert_eq!(data.pic.get_external_interrupt(), Some(0x08 + 4));
1034 
1035         data.pic.service_irq(/*irq=*/ 4, /*level=*/ true);
1036 
1037         // In level-triggered mode, there should be another IRQ request after this EOI.
1038         // TODO(mutexlox): Verify APIC interaction when it is implemented.
1039         data.pic
1040             .write(pic_bus_address(PIC_PRIMARY_COMMAND), &[0x20]);
1041     }
1042 
1043     /// Specific EOI command in OCW2.
1044     #[test]
specific_eoi()1045     fn specific_eoi() {
1046         let mut data = set_up();
1047         icw_init_both_with_icw4(&mut data.pic, FULLY_NESTED_NO_AUTO_EOI);
1048 
1049         // TODO(mutexlox): Verify APIC interaction when it is implemented.
1050         data.pic.service_irq(/*irq=*/ 4, /*level=*/ true);
1051         assert_eq!(data.pic.get_external_interrupt(), Some(0x08 + 4));
1052 
1053         // Specific EOI command on IRQ3. Primary PIC's ISR should be unaffected since it's targeted
1054         // at the wrong IRQ number.
1055         data.pic
1056             .write(pic_bus_address(PIC_PRIMARY_COMMAND), &[0x63]);
1057         assert_eq!(data.pic.pics[PicSelect::Primary as usize].isr, 1 << 4);
1058 
1059         // Specific EOI command on IRQ4. Primary PIC's ISR should now be cleared.
1060         data.pic
1061             .write(pic_bus_address(PIC_PRIMARY_COMMAND), &[0x64]);
1062         assert_eq!(data.pic.pics[PicSelect::Primary as usize].isr, 0);
1063     }
1064 
1065     /// Test rotate on auto EOI.
1066     #[test]
rotate_on_auto_eoi()1067     fn rotate_on_auto_eoi() {
1068         let mut data = set_up();
1069         icw_init_both(&mut data.pic);
1070 
1071         // OCW3: Clear rotate on auto EOI mode.
1072         data.pic
1073             .write(pic_bus_address(PIC_PRIMARY_COMMAND), &[0x00]);
1074 
1075         // TODO(mutexlox): Verify APIC interaction when it is implemented.
1076         data.pic.service_irq(/*irq=*/ 5, /*level=*/ true);
1077         assert_eq!(data.pic.get_external_interrupt(), Some(0x08 + 5));
1078         data.pic.service_irq(/*irq=*/ 5, /*level=*/ false);
1079 
1080         // EOI automatically happened. Now priority should not be rotated.
1081         assert_eq!(data.pic.pics[PicSelect::Primary as usize].isr, 0);
1082         assert_eq!(data.pic.pics[PicSelect::Primary as usize].imr, 0);
1083         assert_eq!(data.pic.pics[PicSelect::Primary as usize].last_irr, 0);
1084         assert_eq!(data.pic.pics[PicSelect::Primary as usize].priority_add, 0);
1085 
1086         // OCW2: Set rotate on auto EOI mode.
1087         data.pic
1088             .write(pic_bus_address(PIC_PRIMARY_COMMAND), &[0x80]);
1089 
1090         // TODO(mutexlox): Verify APIC interaction when it is implemented.
1091         data.pic.service_irq(/*irq=*/ 5, /*level*/ true);
1092         assert_eq!(data.pic.get_external_interrupt(), Some(0x08 + 5));
1093         data.pic.service_irq(/*irq=*/ 5, /*level=*/ false);
1094 
1095         // EOI automatically happened, and the priority *should* be rotated.
1096         assert_eq!(data.pic.pics[PicSelect::Primary as usize].isr, 0);
1097         assert_eq!(data.pic.pics[PicSelect::Primary as usize].priority_add, 6);
1098     }
1099 
1100     /// Test rotate on specific (non-auto) EOI.
1101     #[test]
rotate_on_specific_eoi()1102     fn rotate_on_specific_eoi() {
1103         let mut data = set_up();
1104         icw_init_both_with_icw4(&mut data.pic, FULLY_NESTED_NO_AUTO_EOI);
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         // Rotate on specific EOI IRQ4. Since this is a different IRQ number, Should not have an
1112         // effect on isr.
1113         data.pic
1114             .write(pic_bus_address(PIC_PRIMARY_COMMAND), &[0xe4]);
1115 
1116         assert_eq!(data.pic.pics[PicSelect::Primary as usize].isr, 1 << 5);
1117 
1118         // Rotate on specific EOI IRQ5. This should clear the isr.
1119         data.pic
1120             .write(pic_bus_address(PIC_PRIMARY_COMMAND), &[0xe5]);
1121 
1122         assert_eq!(data.pic.pics[PicSelect::Primary as usize].isr, 0);
1123         assert_eq!(data.pic.pics[PicSelect::Primary as usize].priority_add, 6);
1124     }
1125 
1126     /// Test rotate on non-specific EOI.
1127     #[test]
rotate_non_specific_eoi()1128     fn rotate_non_specific_eoi() {
1129         let mut data = set_up();
1130         icw_init_both_with_icw4(&mut data.pic, FULLY_NESTED_NO_AUTO_EOI);
1131 
1132         // TODO(mutexlox): Verify APIC interaction when it is implemented.
1133         data.pic.service_irq(/*irq=*/ 5, /*level=*/ true);
1134         assert_eq!(data.pic.get_external_interrupt(), Some(0x08 + 5));
1135         data.pic.service_irq(/*irq=*/ 5, /*level=*/ false);
1136 
1137         // Rotate on non-specific EOI.
1138         data.pic
1139             .write(pic_bus_address(PIC_PRIMARY_COMMAND), &[0xa0]);
1140 
1141         // The EOI should have cleared isr.
1142         assert_eq!(data.pic.pics[PicSelect::Primary as usize].isr, 0);
1143         assert_eq!(data.pic.pics[PicSelect::Primary as usize].priority_add, 6);
1144     }
1145 
1146     /// Tests cascade IRQ that happens on secondary PIC.
1147     #[test]
cascade_irq()1148     fn cascade_irq() {
1149         let mut data = set_up();
1150         icw_init_both_with_icw4(&mut data.pic, FULLY_NESTED_NO_AUTO_EOI);
1151 
1152         // TODO(mutexlox): Verify APIC interaction when it is implemented.
1153         data.pic.service_irq(/*irq=*/ 12, /*level=*/ true);
1154 
1155         assert_eq!(data.pic.pics[PicSelect::Primary as usize].irr, 1 << 2);
1156         assert_eq!(data.pic.pics[PicSelect::Secondary as usize].irr, 1 << 4);
1157 
1158         assert_eq!(data.pic.get_external_interrupt(), Some(0x70 + 4));
1159 
1160         // Check that the IRQ is now acknowledged after get_external_interrupt().
1161         assert_eq!(data.pic.pics[PicSelect::Secondary as usize].irr, 0);
1162         assert_eq!(data.pic.pics[PicSelect::Secondary as usize].isr, 1 << 4);
1163 
1164         // OCW2: Two non-specific EOIs to primary rather than secondary.
1165         // We need two non-specific EOIs:
1166         //   - The first resets bit 2 in the primary isr (the highest-priority bit that was set
1167         //     before the EOI)
1168         //   - The second resets the secondary PIC's highest-priority isr bit.
1169         data.pic
1170             .write(pic_bus_address(PIC_PRIMARY_COMMAND), &[0x20]);
1171         // Rotate non-specific EOI.
1172         data.pic
1173             .write(pic_bus_address(PIC_SECONDARY_COMMAND), &[0xa0]);
1174 
1175         assert_eq!(data.pic.pics[PicSelect::Primary as usize].isr, 0);
1176         assert_eq!(data.pic.pics[PicSelect::Secondary as usize].isr, 0);
1177         assert_eq!(data.pic.pics[PicSelect::Secondary as usize].priority_add, 5);
1178     }
1179 }
1180