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