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