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