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