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