use base::debug;
use base::warn;
use base::Event;
use hypervisor::PicInitState;
use hypervisor::PicSelect;
use hypervisor::PicState;
use crate::bus::BusAccessInfo;
use crate::pci::CrosvmDeviceId;
use crate::BusDevice;
use crate::DeviceId;
use crate::Suspendable;
pub struct Pic {
interrupt_request: bool,
resample_events: Vec<Vec<Event>>,
pics: [PicState; 2],
}
const PIC_PRIMARY: u64 = 0x20;
const PIC_PRIMARY_COMMAND: u64 = PIC_PRIMARY;
const PIC_PRIMARY_DATA: u64 = PIC_PRIMARY + 1;
const PIC_PRIMARY_ELCR: u64 = 0x4d0;
const PIC_SECONDARY: u64 = 0xa0;
const PIC_SECONDARY_COMMAND: u64 = PIC_SECONDARY;
const PIC_SECONDARY_DATA: u64 = PIC_SECONDARY + 1;
const PIC_SECONDARY_ELCR: u64 = 0x4d1;
const LEVEL_HIGH: bool = true;
const LEVEL_LOW: bool = false;
const INVALID_PRIORITY: u8 = 8;
const SPURIOUS_IRQ: u8 = 0x07;
const PRIMARY_PIC_CASCADE_PIN: u8 = 2;
const PRIMARY_PIC_CASCADE_PIN_MASK: u8 = 0x04;
const PRIMARY_PIC_MAX_IRQ: u8 = 7;
const ICW1_MASK: u8 = 0x10;
const OCW3_MASK: u8 = 0x08;
const ICW1_NEED_ICW4: u8 = 0x01; const ICW1_SINGLE_PIC_MODE: u8 = 0x02;
const ICW1_LEVEL_TRIGGER_MODE: u8 = 0x08;
const ICW2_IRQ_BASE_MASK: u8 = 0xf8;
const ICW4_SPECIAL_FULLY_NESTED_MODE: u8 = 0x10;
const ICW4_AUTO_EOI: u8 = 0x02;
const OCW2_IRQ_MASK: u8 = 0x07;
const OCW2_COMMAND_MASK: u8 = 0xe0;
#[derive(Debug, Clone, Copy, PartialEq, enumn::N)]
enum Ocw2 {
RotateAutoEoiClear = 0x00,
NonSpecificEoi = 0x20,
NoOp = 0x40,
SpecificEoi = 0x60,
RotateAutoEoiSet = 0x80,
RotateNonSpecificEoi = 0xa0,
SetPriority = 0xc0,
RotateSpecificEoi = 0xe0,
}
const OCW3_POLL_COMMAND: u8 = 0x04;
const OCW3_READ_REGISTER: u8 = 0x02;
const OCW3_READ_ISR: u8 = 0x01;
const OCW3_SPECIAL_MASK: u8 = 0x40;
const OCW3_SPECIAL_MASK_VALUE: u8 = 0x20;
impl BusDevice for Pic {
fn device_id(&self) -> DeviceId {
CrosvmDeviceId::Pic.into()
}
fn debug_label(&self) -> String {
"userspace PIC".to_string()
}
fn write(&mut self, info: BusAccessInfo, data: &[u8]) {
if data.len() != 1 {
warn!("PIC: Bad write size: {}", data.len());
return;
}
match info.address {
PIC_PRIMARY_COMMAND => self.pic_write_command(PicSelect::Primary, data[0]),
PIC_PRIMARY_DATA => self.pic_write_data(PicSelect::Primary, data[0]),
PIC_PRIMARY_ELCR => self.pic_write_elcr(PicSelect::Primary, data[0]),
PIC_SECONDARY_COMMAND => self.pic_write_command(PicSelect::Secondary, data[0]),
PIC_SECONDARY_DATA => self.pic_write_data(PicSelect::Secondary, data[0]),
PIC_SECONDARY_ELCR => self.pic_write_elcr(PicSelect::Secondary, data[0]),
_ => warn!("PIC: Invalid write to {}", info),
}
}
fn read(&mut self, info: BusAccessInfo, data: &mut [u8]) {
if data.len() != 1 {
warn!("PIC: Bad read size: {}", data.len());
return;
}
data[0] = match info.address {
PIC_PRIMARY_COMMAND => self.pic_read_command(PicSelect::Primary),
PIC_PRIMARY_DATA => self.pic_read_data(PicSelect::Primary),
PIC_PRIMARY_ELCR => self.pic_read_elcr(PicSelect::Primary),
PIC_SECONDARY_COMMAND => self.pic_read_command(PicSelect::Secondary),
PIC_SECONDARY_DATA => self.pic_read_data(PicSelect::Secondary),
PIC_SECONDARY_ELCR => self.pic_read_elcr(PicSelect::Secondary),
_ => {
warn!("PIC: Invalid read from {}", info);
return;
}
};
}
}
impl Pic {
pub fn new() -> Pic {
let mut primary_pic: PicState = Default::default();
let mut secondary_pic: PicState = Default::default();
primary_pic.elcr_mask = !((1 << 0) | (1 << 1) | (1 << 2));
secondary_pic.elcr_mask = !((1 << 0) | (1 << 5));
Pic {
interrupt_request: false,
pics: [primary_pic, secondary_pic],
resample_events: Vec::new(),
}
}
pub fn get_pic_state(&self, select: PicSelect) -> PicState {
self.pics[select as usize]
}
pub fn set_pic_state(&mut self, select: PicSelect, state: &PicState) {
self.pics[select as usize] = *state;
}
pub fn register_resample_events(&mut self, resample_events: Vec<Vec<Event>>) {
self.resample_events = resample_events;
}
pub fn service_irq(&mut self, irq: u8, level: bool) -> bool {
assert!(irq <= 15, "Unexpectedly high value irq: {} vs 15", irq);
let pic = if irq <= PRIMARY_PIC_MAX_IRQ {
PicSelect::Primary
} else {
PicSelect::Secondary
};
Pic::set_irq_internal(&mut self.pics[pic as usize], irq & 7, level);
self.update_irq()
}
pub fn masked(&self) -> bool {
self.pics[PicSelect::Primary as usize].imr == 0xFF
}
pub fn has_interrupt(&self) -> bool {
self.get_irq(PicSelect::Primary).is_some()
}
pub fn interrupt_requested(&self) -> bool {
self.interrupt_request
}
pub fn get_external_interrupt(&mut self) -> Option<u8> {
self.interrupt_request = false;
let irq_primary = self.get_irq(PicSelect::Primary)?;
self.interrupt_ack(PicSelect::Primary, irq_primary);
let int_num = if irq_primary == PRIMARY_PIC_CASCADE_PIN {
let irq_secondary = if let Some(irq) = self.get_irq(PicSelect::Secondary) {
self.interrupt_ack(PicSelect::Secondary, irq);
irq
} else {
SPURIOUS_IRQ
};
self.pics[PicSelect::Secondary as usize].irq_base + irq_secondary
} else {
self.pics[PicSelect::Primary as usize].irq_base + irq_primary
};
self.update_irq();
Some(int_num)
}
fn pic_read_command(&mut self, pic_type: PicSelect) -> u8 {
if self.pics[pic_type as usize].poll {
let (ret, update_irq_needed) = self.poll_read(pic_type);
self.pics[pic_type as usize].poll = false;
if update_irq_needed {
self.update_irq();
}
ret
} else if self.pics[pic_type as usize].read_reg_select {
self.pics[pic_type as usize].isr
} else {
self.pics[pic_type as usize].irr
}
}
fn pic_read_data(&mut self, pic_type: PicSelect) -> u8 {
if self.pics[pic_type as usize].poll {
let (ret, update_needed) = self.poll_read(pic_type);
self.pics[pic_type as usize].poll = false;
if update_needed {
self.update_irq();
}
ret
} else {
self.pics[pic_type as usize].imr
}
}
fn pic_read_elcr(&mut self, pic_type: PicSelect) -> u8 {
self.pics[pic_type as usize].elcr
}
fn pic_write_command(&mut self, pic_type: PicSelect, value: u8) {
if value & ICW1_MASK != 0 {
self.init_command_word_1(pic_type, value);
} else if value & OCW3_MASK != 0 {
Pic::operation_command_word_3(&mut self.pics[pic_type as usize], value);
} else {
self.operation_command_word_2(pic_type, value);
}
}
fn pic_write_data(&mut self, pic_type: PicSelect, value: u8) {
match self.pics[pic_type as usize].init_state {
PicInitState::Icw1 => {
self.pics[pic_type as usize].imr = value;
self.update_irq();
}
PicInitState::Icw2 => {
self.pics[pic_type as usize].irq_base = value & ICW2_IRQ_BASE_MASK;
self.pics[pic_type as usize].init_state = PicInitState::Icw3;
}
PicInitState::Icw3 => {
if self.pics[pic_type as usize].use_4_byte_icw {
self.pics[pic_type as usize].init_state = PicInitState::Icw4;
} else {
self.pics[pic_type as usize].init_state = PicInitState::Icw1;
}
}
PicInitState::Icw4 => {
self.pics[pic_type as usize].special_fully_nested_mode =
(value & ICW4_SPECIAL_FULLY_NESTED_MODE) != 0;
self.pics[pic_type as usize].auto_eoi = (value & ICW4_AUTO_EOI) != 0;
self.pics[pic_type as usize].init_state = PicInitState::Icw1;
}
}
}
fn pic_write_elcr(&mut self, pic_type: PicSelect, value: u8) {
self.pics[pic_type as usize].elcr = value & !self.pics[pic_type as usize].elcr;
}
fn reset_pic(&mut self, pic_type: PicSelect) {
let pic = &mut self.pics[pic_type as usize];
let edge_irr = pic.irr & !pic.elcr;
pic.last_irr = 0;
pic.irr &= pic.elcr;
pic.imr = 0;
pic.priority_add = 0;
pic.special_mask = false;
pic.read_reg_select = false;
if !pic.use_4_byte_icw {
pic.special_fully_nested_mode = false;
pic.auto_eoi = false;
}
pic.init_state = PicInitState::Icw2;
for irq in 0..8 {
if edge_irr & (1 << irq) != 0 {
self.clear_isr(pic_type, irq);
}
}
}
fn poll_read(&mut self, pic_type: PicSelect) -> (u8, bool) {
if let Some(irq) = self.get_irq(pic_type) {
if pic_type == PicSelect::Secondary {
self.pics[PicSelect::Primary as usize].isr &= !PRIMARY_PIC_CASCADE_PIN_MASK;
self.pics[PicSelect::Primary as usize].irr &= !PRIMARY_PIC_CASCADE_PIN_MASK;
}
self.pics[pic_type as usize].irr &= !(1 << irq);
self.clear_isr(pic_type, irq);
let update_irq_needed =
pic_type == PicSelect::Secondary && irq != PRIMARY_PIC_CASCADE_PIN;
(irq, update_irq_needed)
} else {
(SPURIOUS_IRQ, true)
}
}
fn get_irq(&self, pic_type: PicSelect) -> Option<u8> {
let pic = &self.pics[pic_type as usize];
let mut irq_bitmap = pic.irr & !pic.imr;
let priority = Pic::get_priority(pic, irq_bitmap)?;
irq_bitmap = pic.isr;
if pic_type == PicSelect::Primary && pic.special_fully_nested_mode {
irq_bitmap &= !PRIMARY_PIC_CASCADE_PIN_MASK;
}
let new_priority = Pic::get_priority(pic, irq_bitmap).unwrap_or(INVALID_PRIORITY);
if priority < new_priority {
Some((priority + pic.priority_add) & 7)
} else {
None
}
}
fn clear_isr(&mut self, pic_type: PicSelect, irq: u8) {
let pic = &mut self.pics[pic_type as usize];
assert!(irq <= 7, "Unexpectedly high value for irq: {} vs 7", irq);
pic.isr &= !(1 << irq);
Pic::set_irq_internal(pic, irq, false);
let irq = if pic_type == PicSelect::Primary {
irq
} else {
irq + 8
};
if let Some(resample_events) = self.resample_events.get(irq as usize) {
for resample_evt in resample_events {
resample_evt.signal().unwrap();
}
}
}
fn update_irq(&mut self) -> bool {
if self.get_irq(PicSelect::Secondary).is_some() {
Pic::set_irq_internal(
&mut self.pics[PicSelect::Primary as usize],
PRIMARY_PIC_CASCADE_PIN,
LEVEL_HIGH,
);
Pic::set_irq_internal(
&mut self.pics[PicSelect::Primary as usize],
PRIMARY_PIC_CASCADE_PIN,
LEVEL_LOW,
);
}
if self.get_irq(PicSelect::Primary).is_some() {
self.interrupt_request = true;
true
} else {
false
}
}
fn set_irq_internal(pic: &mut PicState, irq: u8, level: bool) {
assert!(irq <= 7, "Unexpectedly high value for irq: {} vs 7", irq);
let irq_bitmap = 1 << irq;
if (pic.elcr & irq_bitmap) != 0 {
if level {
pic.irr |= irq_bitmap;
pic.last_irr |= irq_bitmap;
} else {
pic.irr &= !irq_bitmap;
pic.last_irr &= !irq_bitmap;
}
} else {
if level {
if (pic.last_irr & irq_bitmap) == 0 {
pic.irr |= irq_bitmap;
}
pic.last_irr |= irq_bitmap;
} else {
pic.last_irr &= !irq_bitmap;
}
}
}
fn get_priority(pic: &PicState, irq_bitmap: u8) -> Option<u8> {
if irq_bitmap == 0 {
None
} else {
let mut priority = 0;
let mut priority_mask = 1 << ((priority + pic.priority_add) & 7);
while (irq_bitmap & priority_mask) == 0 {
priority += 1;
priority_mask = 1 << ((priority + pic.priority_add) & 7);
}
Some(priority)
}
}
fn interrupt_ack(&mut self, pic_type: PicSelect, irq: u8) {
let pic = &mut self.pics[pic_type as usize];
assert!(irq <= 7, "Unexpectedly high value for irq: {} vs 7", irq);
let irq_bitmap = 1 << irq;
pic.isr |= irq_bitmap;
if (pic.elcr & irq_bitmap) == 0 {
pic.irr &= !irq_bitmap;
}
if pic.auto_eoi {
if pic.rotate_on_auto_eoi {
pic.priority_add = (irq + 1) & 7;
}
self.clear_isr(pic_type, irq);
}
}
fn init_command_word_1(&mut self, pic_type: PicSelect, value: u8) {
let pic = &mut self.pics[pic_type as usize];
pic.use_4_byte_icw = (value & ICW1_NEED_ICW4) != 0;
if (value & ICW1_SINGLE_PIC_MODE) != 0 {
debug!("PIC: Single PIC mode not supported.");
}
if (value & ICW1_LEVEL_TRIGGER_MODE) != 0 {
debug!("PIC: Level triggered IRQ not supported.");
}
self.reset_pic(pic_type);
}
fn operation_command_word_2(&mut self, pic_type: PicSelect, value: u8) {
let mut irq = value & OCW2_IRQ_MASK;
if let Some(cmd) = Ocw2::n(value & OCW2_COMMAND_MASK) {
match cmd {
Ocw2::RotateAutoEoiSet => self.pics[pic_type as usize].rotate_on_auto_eoi = true,
Ocw2::RotateAutoEoiClear => self.pics[pic_type as usize].rotate_on_auto_eoi = false,
Ocw2::NonSpecificEoi | Ocw2::RotateNonSpecificEoi => {
if let Some(priority) = Pic::get_priority(
&self.pics[pic_type as usize],
self.pics[pic_type as usize].isr,
) {
irq = (priority + self.pics[pic_type as usize].priority_add) & 7;
if cmd == Ocw2::RotateNonSpecificEoi {
self.pics[pic_type as usize].priority_add = (irq + 1) & 7;
}
self.clear_isr(pic_type, irq);
self.update_irq();
}
}
Ocw2::SpecificEoi => {
self.clear_isr(pic_type, irq);
self.update_irq();
}
Ocw2::SetPriority => {
self.pics[pic_type as usize].priority_add = (irq + 1) & 7;
self.update_irq();
}
Ocw2::RotateSpecificEoi => {
self.pics[pic_type as usize].priority_add = (irq + 1) & 7;
self.clear_isr(pic_type, irq);
self.update_irq();
}
Ocw2::NoOp => {} }
}
}
fn operation_command_word_3(pic: &mut PicState, value: u8) {
if value & OCW3_POLL_COMMAND != 0 {
pic.poll = true;
}
if value & OCW3_READ_REGISTER != 0 {
pic.read_reg_select = value & OCW3_READ_ISR != 0;
}
if value & OCW3_SPECIAL_MASK != 0 {
pic.special_mask = value & OCW3_SPECIAL_MASK_VALUE != 0;
}
}
}
impl Default for Pic {
fn default() -> Self {
Self::new()
}
}
impl Suspendable for Pic {
fn sleep(&mut self) -> anyhow::Result<()> {
Ok(())
}
fn wake(&mut self) -> anyhow::Result<()> {
Ok(())
}
fn snapshot(&mut self) -> anyhow::Result<serde_json::Value> {
Ok(serde_json::Value::Null)
}
fn restore(&mut self, _data: serde_json::Value) -> anyhow::Result<()> {
Ok(())
}
}
#[cfg(test)]
mod tests {
const FULLY_NESTED_NO_AUTO_EOI: u8 = 0x11;
use super::*;
struct TestData {
pic: Pic,
}
fn pic_bus_address(address: u64) -> BusAccessInfo {
let base_address = if (PIC_PRIMARY..PIC_PRIMARY + 0x2).contains(&address) {
PIC_PRIMARY
} else if (PIC_SECONDARY..PIC_SECONDARY + 0x2).contains(&address) {
PIC_SECONDARY
} else if (PIC_PRIMARY_ELCR..PIC_PRIMARY_ELCR + 0x2).contains(&address) {
PIC_PRIMARY_ELCR
} else {
panic!("invalid PIC address: {:#x}", address);
};
BusAccessInfo {
offset: address - base_address,
address,
id: 0,
}
}
fn set_up() -> TestData {
let mut pic = Pic::new();
pic.write(pic_bus_address(PIC_PRIMARY_ELCR), &[0]);
pic.write(pic_bus_address(PIC_SECONDARY_ELCR), &[0]);
TestData { pic }
}
fn icw_init(pic: &mut Pic, pic_type: PicSelect, icw1: u8, icw2: u8, icw3: u8, icw4: u8) {
let command_offset = match pic_type {
PicSelect::Primary => PIC_PRIMARY_COMMAND,
PicSelect::Secondary => PIC_SECONDARY_COMMAND,
};
let data_offset = match pic_type {
PicSelect::Primary => PIC_PRIMARY_DATA,
PicSelect::Secondary => PIC_SECONDARY_DATA,
};
pic.write(pic_bus_address(command_offset), &[icw1]);
pic.write(pic_bus_address(data_offset), &[icw2]);
pic.write(pic_bus_address(data_offset), &[icw3]);
pic.write(pic_bus_address(data_offset), &[icw4]);
}
fn icw_init_primary(pic: &mut Pic) {
icw_init(pic, PicSelect::Primary, 0x11, 0x08, 0xff, 0x13);
}
fn icw_init_secondary(pic: &mut Pic) {
icw_init(pic, PicSelect::Secondary, 0x11, 0x70, 0xff, 0x13);
}
fn icw_init_both_with_icw4(pic: &mut Pic, icw4: u8) {
icw_init(pic, PicSelect::Primary, 0x11, 0x08, 0xff, icw4);
icw_init(pic, PicSelect::Secondary, 0x11, 0x70, 0xff, icw4);
}
fn icw_init_both(pic: &mut Pic) {
icw_init_primary(pic);
icw_init_secondary(pic);
}
#[test]
fn write_read_elcr() {
let mut data = set_up();
let data_write = [0x5f];
let mut data_read = [0];
data.pic
.write(pic_bus_address(PIC_PRIMARY_ELCR), &data_write);
data.pic
.read(pic_bus_address(PIC_PRIMARY_ELCR), &mut data_read);
assert_eq!(data_read, data_write);
data.pic
.write(pic_bus_address(PIC_SECONDARY_ELCR), &data_write);
data.pic
.read(pic_bus_address(PIC_SECONDARY_ELCR), &mut data_read);
assert_eq!(data_read, data_write);
}
#[test]
fn icw_2_step() {
let mut data = set_up();
let mut data_write = [0x10];
data.pic
.write(pic_bus_address(PIC_PRIMARY_COMMAND), &data_write);
data_write[0] = 0x08;
data.pic
.write(pic_bus_address(PIC_PRIMARY_DATA), &data_write);
data_write[0] = 0xff;
data.pic
.write(pic_bus_address(PIC_PRIMARY_DATA), &data_write);
assert_eq!(
data.pic.pics[PicSelect::Primary as usize].init_state,
PicInitState::Icw1
);
assert_eq!(data.pic.pics[PicSelect::Primary as usize].irq_base, 0x08);
assert_eq!(
data.pic.pics[PicSelect::Primary as usize].use_4_byte_icw,
false
);
}
#[test]
fn initial_values() {
let mut data = set_up();
icw_init_primary(&mut data.pic);
let primary_pic = &data.pic.pics[PicSelect::Primary as usize];
assert_eq!(primary_pic.last_irr, 0x00);
assert_eq!(primary_pic.irr, 0x00);
assert_eq!(primary_pic.imr, 0x00);
assert_eq!(primary_pic.isr, 0x00);
assert_eq!(primary_pic.priority_add, 0);
assert_eq!(primary_pic.irq_base, 0x08);
assert_eq!(primary_pic.read_reg_select, false);
assert_eq!(primary_pic.poll, false);
assert_eq!(primary_pic.special_mask, false);
assert_eq!(primary_pic.init_state, PicInitState::Icw1);
assert_eq!(primary_pic.auto_eoi, true);
assert_eq!(primary_pic.rotate_on_auto_eoi, false);
assert_eq!(primary_pic.special_fully_nested_mode, true);
assert_eq!(primary_pic.use_4_byte_icw, true);
assert_eq!(primary_pic.elcr, 0x00);
assert_eq!(primary_pic.elcr_mask, 0xf8);
}
#[test]
fn ocw() {
let mut data = set_up();
icw_init_secondary(&mut data.pic);
data.pic.write(pic_bus_address(PIC_SECONDARY_DATA), &[0x5f]);
data.pic
.write(pic_bus_address(PIC_SECONDARY_COMMAND), &[0x80]);
data.pic
.write(pic_bus_address(PIC_SECONDARY_COMMAND), &[0xc0]);
data.pic
.write(pic_bus_address(PIC_SECONDARY_COMMAND), &[0x6b]);
let mut data_read = [0];
data.pic
.read(pic_bus_address(PIC_SECONDARY_DATA), &mut data_read);
assert_eq!(data_read, [0x5f]);
let secondary_pic = &data.pic.pics[PicSelect::Secondary as usize];
assert_eq!(secondary_pic.imr, 0x5f);
assert!(secondary_pic.rotate_on_auto_eoi);
assert_eq!(secondary_pic.priority_add, 1);
assert!(secondary_pic.special_mask);
assert_eq!(secondary_pic.poll, false);
assert!(secondary_pic.read_reg_select);
}
#[test]
fn ocw_auto_rotate_set_and_clear() {
let mut data = set_up();
icw_init_secondary(&mut data.pic);
data.pic
.write(pic_bus_address(PIC_SECONDARY_COMMAND), &[0x80]);
let secondary_pic = &data.pic.pics[PicSelect::Secondary as usize];
assert!(secondary_pic.rotate_on_auto_eoi);
data.pic
.write(pic_bus_address(PIC_SECONDARY_COMMAND), &[0x00]);
let secondary_pic = &data.pic.pics[PicSelect::Secondary as usize];
assert!(!secondary_pic.rotate_on_auto_eoi);
}
#[test]
fn auto_eoi() {
let mut data = set_up();
icw_init_both(&mut data.pic);
data.pic.service_irq(12, true);
assert_eq!(data.pic.pics[PicSelect::Secondary as usize].irr, (1 << 4));
assert_eq!(data.pic.pics[PicSelect::Secondary as usize].isr, 0);
assert_eq!(data.pic.pics[PicSelect::Primary as usize].irr, (1 << 2));
assert_eq!(data.pic.pics[PicSelect::Primary as usize].isr, 0);
assert_eq!(data.pic.get_external_interrupt(), Some(0x70 + 4));
assert_eq!(data.pic.pics[PicSelect::Secondary as usize].irr, 0);
assert_eq!(data.pic.pics[PicSelect::Secondary as usize].isr, 0);
assert_eq!(data.pic.pics[PicSelect::Primary as usize].irr, 0);
assert_eq!(data.pic.pics[PicSelect::Primary as usize].isr, 0);
}
#[test]
fn fully_nested_mode_on() {
let mut data = set_up();
icw_init_both_with_icw4(&mut data.pic, FULLY_NESTED_NO_AUTO_EOI);
data.pic.service_irq(12, true);
assert_eq!(data.pic.get_external_interrupt(), Some(0x70 + 4));
data.pic.service_irq(8, true);
assert_eq!(data.pic.get_external_interrupt(), Some(0x70 + 0));
assert_eq!(data.pic.pics[PicSelect::Secondary as usize].irr, 0);
assert_eq!(
data.pic.pics[PicSelect::Secondary as usize].isr,
(1 << 4) + (1 << 0)
);
assert_eq!(data.pic.pics[PicSelect::Primary as usize].irr, 0);
assert_eq!(data.pic.pics[PicSelect::Primary as usize].isr, 1 << 2);
}
#[test]
fn fully_nested_mode_off() {
let mut data = set_up();
icw_init_both_with_icw4(&mut data.pic, 0x01);
data.pic.service_irq(12, true);
assert_eq!(data.pic.get_external_interrupt(), Some(0x70 + 4));
data.pic.service_irq(8, true);
assert_eq!(data.pic.get_external_interrupt(), None);
assert_eq!(data.pic.pics[PicSelect::Secondary as usize].irr, 1 << 0);
assert_eq!(data.pic.pics[PicSelect::Secondary as usize].isr, 1 << 4);
assert_eq!(data.pic.pics[PicSelect::Primary as usize].irr, 1 << 2);
assert_eq!(data.pic.pics[PicSelect::Primary as usize].isr, 1 << 2);
data.pic
.write(pic_bus_address(PIC_PRIMARY_COMMAND), &[0x20]);
data.pic
.write(pic_bus_address(PIC_SECONDARY_COMMAND), &[0x20]);
assert_eq!(data.pic.get_external_interrupt(), Some(0x70 + 0));
assert_eq!(data.pic.pics[PicSelect::Secondary as usize].irr, 0);
assert_eq!(data.pic.pics[PicSelect::Secondary as usize].isr, 1 << 0);
assert_eq!(data.pic.pics[PicSelect::Primary as usize].irr, 0);
assert_eq!(data.pic.pics[PicSelect::Primary as usize].isr, 1 << 2);
}
#[test]
fn mask_irq() {
let mut data = set_up();
icw_init_both_with_icw4(&mut data.pic, FULLY_NESTED_NO_AUTO_EOI);
data.pic.write(pic_bus_address(PIC_SECONDARY_DATA), &[0x40]);
data.pic.service_irq(14, true);
assert_eq!(data.pic.get_external_interrupt(), None);
assert_eq!(data.pic.pics[PicSelect::Secondary as usize].irr, 1 << 6);
assert_eq!(data.pic.pics[PicSelect::Secondary as usize].isr, 0);
assert_eq!(data.pic.pics[PicSelect::Primary as usize].irr, 0);
assert_eq!(data.pic.pics[PicSelect::Primary as usize].isr, 0);
data.pic.write(pic_bus_address(PIC_SECONDARY_DATA), &[0x00]);
assert_eq!(data.pic.get_external_interrupt(), Some(0x70 + 6));
assert_eq!(data.pic.pics[PicSelect::Secondary as usize].irr, 0);
assert_eq!(data.pic.pics[PicSelect::Secondary as usize].isr, 1 << 6);
assert_eq!(data.pic.pics[PicSelect::Primary as usize].irr, 0);
assert_eq!(data.pic.pics[PicSelect::Primary as usize].isr, 1 << 2);
}
#[test]
fn mask_multiple_irq() {
let mut data = set_up();
icw_init_both(&mut data.pic);
data.pic.write(pic_bus_address(PIC_PRIMARY_DATA), &[0xff]);
data.pic.write(pic_bus_address(PIC_SECONDARY_DATA), &[0xff]);
data.pic.service_irq(14, true);
data.pic.service_irq(4, true);
data.pic.service_irq(12, true);
assert_eq!(data.pic.get_external_interrupt(), None);
data.pic.write(pic_bus_address(PIC_SECONDARY_DATA), &[0x00]);
assert_eq!(data.pic.get_external_interrupt(), None);
data.pic.write(pic_bus_address(PIC_PRIMARY_DATA), &[0xfb]);
assert_eq!(data.pic.get_external_interrupt(), Some(0x70 + 4));
assert_eq!(data.pic.get_external_interrupt(), Some(0x70 + 6));
data.pic.write(pic_bus_address(PIC_PRIMARY_DATA), &[0x00]);
assert_eq!(data.pic.get_external_interrupt(), Some(0x08 + 4));
}
#[test]
fn ocw3() {
let mut data = set_up();
icw_init_both_with_icw4(&mut data.pic, FULLY_NESTED_NO_AUTO_EOI);
data.pic.service_irq(5, true);
data.pic.service_irq(4, true);
assert_eq!(data.pic.get_external_interrupt(), Some(0x08 + 4));
data.pic
.write(pic_bus_address(PIC_PRIMARY_COMMAND), &[0x0a]);
let mut data_read = [0];
data.pic
.read(pic_bus_address(PIC_PRIMARY_COMMAND), &mut data_read);
assert_eq!(data_read[0], 1 << 5);
data.pic
.write(pic_bus_address(PIC_PRIMARY_COMMAND), &[0x0b]);
data_read = [0];
data.pic
.read(pic_bus_address(PIC_PRIMARY_COMMAND), &mut data_read);
assert_eq!(data_read[0], 1 << 4);
data.pic
.write(pic_bus_address(PIC_PRIMARY_COMMAND), &[0x20]);
data.pic
.write(pic_bus_address(PIC_PRIMARY_COMMAND), &[0x0c]);
data_read = [0];
data.pic
.read(pic_bus_address(PIC_PRIMARY_COMMAND), &mut data_read);
assert_eq!(data_read[0], 5);
}
#[test]
fn fake_irq_on_primary_irq2() {
let mut data = set_up();
icw_init_both_with_icw4(&mut data.pic, FULLY_NESTED_NO_AUTO_EOI);
data.pic.service_irq(2, true);
assert_eq!(data.pic.get_external_interrupt(), Some(0x70 + 7));
}
#[test]
fn edge_trigger_mode() {
let mut data = set_up();
icw_init_both_with_icw4(&mut data.pic, FULLY_NESTED_NO_AUTO_EOI);
data.pic.service_irq(4, true);
assert_eq!(data.pic.get_external_interrupt(), Some(0x08 + 4));
data.pic.service_irq(4, true);
data.pic
.write(pic_bus_address(PIC_PRIMARY_COMMAND), &[0x20]);
}
#[test]
fn level_trigger_mode() {
let mut data = set_up();
icw_init_both_with_icw4(&mut data.pic, FULLY_NESTED_NO_AUTO_EOI);
data.pic.write(pic_bus_address(PIC_PRIMARY_ELCR), &[0x10]);
data.pic.service_irq(4, true);
assert_eq!(data.pic.get_external_interrupt(), Some(0x08 + 4));
data.pic.service_irq(4, true);
data.pic
.write(pic_bus_address(PIC_PRIMARY_COMMAND), &[0x20]);
}
#[test]
fn specific_eoi() {
let mut data = set_up();
icw_init_both_with_icw4(&mut data.pic, FULLY_NESTED_NO_AUTO_EOI);
data.pic.service_irq(4, true);
assert_eq!(data.pic.get_external_interrupt(), Some(0x08 + 4));
data.pic
.write(pic_bus_address(PIC_PRIMARY_COMMAND), &[0x63]);
assert_eq!(data.pic.pics[PicSelect::Primary as usize].isr, 1 << 4);
data.pic
.write(pic_bus_address(PIC_PRIMARY_COMMAND), &[0x64]);
assert_eq!(data.pic.pics[PicSelect::Primary as usize].isr, 0);
}
#[test]
fn rotate_on_auto_eoi() {
let mut data = set_up();
icw_init_both(&mut data.pic);
data.pic
.write(pic_bus_address(PIC_PRIMARY_COMMAND), &[0x00]);
data.pic.service_irq(5, true);
assert_eq!(data.pic.get_external_interrupt(), Some(0x08 + 5));
data.pic.service_irq(5, false);
assert_eq!(data.pic.pics[PicSelect::Primary as usize].isr, 0);
assert_eq!(data.pic.pics[PicSelect::Primary as usize].imr, 0);
assert_eq!(data.pic.pics[PicSelect::Primary as usize].last_irr, 0);
assert_eq!(data.pic.pics[PicSelect::Primary as usize].priority_add, 0);
data.pic
.write(pic_bus_address(PIC_PRIMARY_COMMAND), &[0x80]);
data.pic.service_irq(5, true);
assert_eq!(data.pic.get_external_interrupt(), Some(0x08 + 5));
data.pic.service_irq(5, false);
assert_eq!(data.pic.pics[PicSelect::Primary as usize].isr, 0);
assert_eq!(data.pic.pics[PicSelect::Primary as usize].priority_add, 6);
}
#[test]
fn rotate_on_specific_eoi() {
let mut data = set_up();
icw_init_both_with_icw4(&mut data.pic, FULLY_NESTED_NO_AUTO_EOI);
data.pic.service_irq(5, true);
assert_eq!(data.pic.get_external_interrupt(), Some(0x08 + 5));
data.pic.service_irq(5, false);
data.pic
.write(pic_bus_address(PIC_PRIMARY_COMMAND), &[0xe4]);
assert_eq!(data.pic.pics[PicSelect::Primary as usize].isr, 1 << 5);
data.pic
.write(pic_bus_address(PIC_PRIMARY_COMMAND), &[0xe5]);
assert_eq!(data.pic.pics[PicSelect::Primary as usize].isr, 0);
assert_eq!(data.pic.pics[PicSelect::Primary as usize].priority_add, 6);
}
#[test]
fn rotate_non_specific_eoi() {
let mut data = set_up();
icw_init_both_with_icw4(&mut data.pic, FULLY_NESTED_NO_AUTO_EOI);
data.pic.service_irq(5, true);
assert_eq!(data.pic.get_external_interrupt(), Some(0x08 + 5));
data.pic.service_irq(5, false);
data.pic
.write(pic_bus_address(PIC_PRIMARY_COMMAND), &[0xa0]);
assert_eq!(data.pic.pics[PicSelect::Primary as usize].isr, 0);
assert_eq!(data.pic.pics[PicSelect::Primary as usize].priority_add, 6);
}
#[test]
fn cascade_irq() {
let mut data = set_up();
icw_init_both_with_icw4(&mut data.pic, FULLY_NESTED_NO_AUTO_EOI);
data.pic.service_irq(12, true);
assert_eq!(data.pic.pics[PicSelect::Primary as usize].irr, 1 << 2);
assert_eq!(data.pic.pics[PicSelect::Secondary as usize].irr, 1 << 4);
assert_eq!(data.pic.get_external_interrupt(), Some(0x70 + 4));
assert_eq!(data.pic.pics[PicSelect::Secondary as usize].irr, 0);
assert_eq!(data.pic.pics[PicSelect::Secondary as usize].isr, 1 << 4);
data.pic
.write(pic_bus_address(PIC_PRIMARY_COMMAND), &[0x20]);
data.pic
.write(pic_bus_address(PIC_SECONDARY_COMMAND), &[0xa0]);
assert_eq!(data.pic.pics[PicSelect::Primary as usize].isr, 0);
assert_eq!(data.pic.pics[PicSelect::Secondary as usize].isr, 0);
assert_eq!(data.pic.pics[PicSelect::Secondary as usize].priority_add, 5);
}
}