2020-05-11 15:13:50 +00:00
|
|
|
// Copyright 2020 The Chromium OS Authors. All rights reserved.
|
|
|
|
// Use of this source code is governed by a BSD-style license that can be
|
|
|
|
// found in the LICENSE file.
|
|
|
|
|
2020-08-03 03:09:41 +00:00
|
|
|
use base::Result;
|
2020-05-11 15:13:50 +00:00
|
|
|
use hypervisor::{IoapicState, LapicState, PicSelect, PicState, PitState, VcpuX86_64};
|
|
|
|
|
|
|
|
use crate::IrqChip;
|
|
|
|
|
|
|
|
pub trait IrqChipX86_64<V: VcpuX86_64>: IrqChip<V> {
|
|
|
|
/// Get the current state of the PIC
|
|
|
|
fn get_pic_state(&self, select: PicSelect) -> Result<PicState>;
|
|
|
|
|
|
|
|
/// Set the current state of the PIC
|
|
|
|
fn set_pic_state(&mut self, select: PicSelect, state: &PicState) -> Result<()>;
|
|
|
|
|
|
|
|
/// Get the current state of the IOAPIC
|
|
|
|
fn get_ioapic_state(&self) -> Result<IoapicState>;
|
|
|
|
|
|
|
|
/// Set the current state of the IOAPIC
|
|
|
|
fn set_ioapic_state(&mut self, state: &IoapicState) -> Result<()>;
|
|
|
|
|
|
|
|
/// Get the current state of the specified VCPU's local APIC
|
|
|
|
fn get_lapic_state(&self, vcpu_id: usize) -> Result<LapicState>;
|
|
|
|
|
|
|
|
/// Set the current state of the specified VCPU's local APIC
|
|
|
|
fn set_lapic_state(&mut self, vcpu_id: usize, state: &LapicState) -> Result<()>;
|
|
|
|
|
|
|
|
/// Retrieves the state of the PIT.
|
|
|
|
fn get_pit(&self) -> Result<PitState>;
|
|
|
|
|
|
|
|
/// Sets the state of the PIT.
|
|
|
|
fn set_pit(&mut self, state: &PitState) -> Result<()>;
|
|
|
|
}
|
2020-06-19 23:36:24 +00:00
|
|
|
|
|
|
|
#[cfg(test)]
|
|
|
|
/// This module contains tests that apply to any implementations of IrqChipX86_64
|
|
|
|
pub(super) mod tests {
|
|
|
|
use super::*;
|
|
|
|
use hypervisor::{IrqRoute, IrqSource, IrqSourceChip};
|
|
|
|
|
|
|
|
pub fn test_get_pic<V: VcpuX86_64>(mut chip: impl IrqChipX86_64<V>) {
|
|
|
|
let state = chip
|
|
|
|
.get_pic_state(PicSelect::Primary)
|
|
|
|
.expect("could not get pic state");
|
|
|
|
|
|
|
|
// Default is that no irq lines are asserted
|
|
|
|
assert_eq!(state.irr, 0);
|
|
|
|
|
|
|
|
// Assert Irq Line 0
|
|
|
|
chip.service_irq(0, true).expect("could not service irq");
|
|
|
|
|
|
|
|
let state = chip
|
|
|
|
.get_pic_state(PicSelect::Primary)
|
|
|
|
.expect("could not get pic state");
|
|
|
|
|
|
|
|
// Bit 0 should now be 1
|
|
|
|
assert_eq!(state.irr, 1);
|
|
|
|
}
|
|
|
|
|
|
|
|
pub fn test_set_pic<V: VcpuX86_64>(mut chip: impl IrqChipX86_64<V>) {
|
|
|
|
let mut state = chip
|
|
|
|
.get_pic_state(PicSelect::Primary)
|
|
|
|
.expect("could not get pic state");
|
|
|
|
|
|
|
|
// set bits 0 and 1
|
|
|
|
state.irr = 3;
|
|
|
|
|
|
|
|
chip.set_pic_state(PicSelect::Primary, &state)
|
|
|
|
.expect("could not set the pic state");
|
|
|
|
|
|
|
|
let state = chip
|
|
|
|
.get_pic_state(PicSelect::Primary)
|
|
|
|
.expect("could not get pic state");
|
|
|
|
|
|
|
|
// Bits 1 and 0 should now be 1
|
|
|
|
assert_eq!(state.irr, 3);
|
|
|
|
}
|
|
|
|
|
|
|
|
pub fn test_get_ioapic<V: VcpuX86_64>(mut chip: impl IrqChipX86_64<V>) {
|
|
|
|
let state = chip.get_ioapic_state().expect("could not get ioapic state");
|
|
|
|
|
|
|
|
// Default is that no irq lines are asserted
|
|
|
|
assert_eq!(state.current_interrupt_level_bitmap, 0);
|
|
|
|
|
|
|
|
// Default routing entries has routes 0..24 routed to vectors 0..24
|
|
|
|
for i in 0..24 {
|
|
|
|
// when the ioapic is reset by kvm, it defaults to all zeroes except the
|
|
|
|
// interrupt mask is set to 1, which is bit 16
|
|
|
|
assert_eq!(state.redirect_table[i].get(0, 64), 1 << 16);
|
|
|
|
}
|
|
|
|
|
|
|
|
// Assert Irq Line 1
|
|
|
|
chip.service_irq(1, true).expect("could not set irq line");
|
|
|
|
|
|
|
|
let state = chip.get_ioapic_state().expect("could not get ioapic state");
|
|
|
|
|
|
|
|
// Bit 1 should now be 1
|
|
|
|
assert_eq!(state.current_interrupt_level_bitmap, 2);
|
|
|
|
}
|
|
|
|
|
|
|
|
pub fn test_set_ioapic<V: VcpuX86_64>(mut chip: impl IrqChipX86_64<V>) {
|
|
|
|
let mut state = chip.get_ioapic_state().expect("could not get ioapic state");
|
|
|
|
|
|
|
|
// set a vector in the redirect table
|
|
|
|
state.redirect_table[2].set_vector(15);
|
|
|
|
// set the irq line status on that entry
|
|
|
|
state.current_interrupt_level_bitmap = 4;
|
|
|
|
|
|
|
|
chip.set_ioapic_state(&state)
|
|
|
|
.expect("could not set the ioapic state");
|
|
|
|
|
|
|
|
let state = chip.get_ioapic_state().expect("could not get ioapic state");
|
|
|
|
|
|
|
|
// verify that get_ioapic_state returns what we set
|
|
|
|
assert_eq!(state.redirect_table[2].get_vector(), 15);
|
|
|
|
assert_eq!(state.current_interrupt_level_bitmap, 4);
|
|
|
|
}
|
|
|
|
|
|
|
|
pub fn test_get_pit<V: VcpuX86_64>(chip: impl IrqChipX86_64<V>) {
|
|
|
|
let state = chip.get_pit().expect("failed to get pit state");
|
|
|
|
|
|
|
|
assert_eq!(state.flags, 0);
|
|
|
|
// assert reset state of pit
|
|
|
|
for i in 0..3 {
|
|
|
|
// initial count of 0 sets it to 0x10000;
|
|
|
|
assert_eq!(state.channels[i].count, 0x10000);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
pub fn test_set_pit<V: VcpuX86_64>(mut chip: impl IrqChipX86_64<V>) {
|
|
|
|
let mut state = chip.get_pit().expect("failed to get pit state");
|
|
|
|
|
|
|
|
// set some values
|
|
|
|
state.channels[0].count = 500;
|
|
|
|
state.channels[0].mode = 1;
|
|
|
|
|
|
|
|
// Setting the pit should initialize the one-shot timer
|
|
|
|
chip.set_pit(&state).expect("failed to set pit state");
|
|
|
|
|
|
|
|
let state = chip.get_pit().expect("failed to get pit state");
|
|
|
|
|
|
|
|
// check the values we set
|
|
|
|
assert_eq!(state.channels[0].count, 500);
|
|
|
|
assert_eq!(state.channels[0].mode, 1);
|
|
|
|
}
|
|
|
|
|
2020-06-23 20:58:12 +00:00
|
|
|
pub fn test_get_lapic<V: VcpuX86_64>(chip: impl IrqChipX86_64<V>) {
|
|
|
|
let state = chip.get_lapic_state(0).expect("failed to get lapic state");
|
|
|
|
|
|
|
|
// Checking some APIC reg defaults for KVM:
|
|
|
|
// DFR default is 0xffffffff
|
|
|
|
assert_eq!(state.regs[0xe], 0xffffffff);
|
|
|
|
// SPIV default is 0xff
|
|
|
|
assert_eq!(state.regs[0xf], 0xff);
|
|
|
|
}
|
|
|
|
|
|
|
|
pub fn test_set_lapic<V: VcpuX86_64>(mut chip: impl IrqChipX86_64<V>) {
|
|
|
|
// Get default state
|
|
|
|
let mut state = chip.get_lapic_state(0).expect("failed to get lapic state");
|
|
|
|
|
|
|
|
// ESR should start out as 0
|
|
|
|
assert_eq!(state.regs[8], 0);
|
|
|
|
// Set a value in the ESR
|
|
|
|
state.regs[8] = 1 << 8;
|
|
|
|
chip.set_lapic_state(0, &state)
|
|
|
|
.expect("failed to set lapic state");
|
|
|
|
|
|
|
|
// check that new ESR value stuck
|
|
|
|
let state = chip.get_lapic_state(0).expect("failed to get lapic state");
|
|
|
|
assert_eq!(state.regs[8], 1 << 8);
|
|
|
|
}
|
|
|
|
|
2020-06-19 23:36:24 +00:00
|
|
|
/// Helper function for checking the pic interrupt status
|
|
|
|
fn check_pic_interrupts<V: VcpuX86_64>(
|
|
|
|
chip: &impl IrqChipX86_64<V>,
|
|
|
|
select: PicSelect,
|
|
|
|
value: u8,
|
|
|
|
) {
|
|
|
|
let state = chip
|
|
|
|
.get_pic_state(select)
|
|
|
|
.expect("could not get ioapic state");
|
|
|
|
|
|
|
|
assert_eq!(state.irr, value);
|
|
|
|
}
|
|
|
|
|
|
|
|
/// Helper function for checking the ioapic interrupt status
|
|
|
|
fn check_ioapic_interrupts<V: VcpuX86_64>(chip: &impl IrqChipX86_64<V>, value: u32) {
|
|
|
|
let state = chip.get_ioapic_state().expect("could not get ioapic state");
|
|
|
|
|
|
|
|
// since the irq route goes nowhere the bitmap should still be 0
|
|
|
|
assert_eq!(state.current_interrupt_level_bitmap, value);
|
|
|
|
}
|
|
|
|
|
|
|
|
pub fn test_route_irq<V: VcpuX86_64>(mut chip: impl IrqChipX86_64<V>) {
|
|
|
|
// clear out irq routes
|
|
|
|
chip.set_irq_routes(&[])
|
|
|
|
.expect("failed to set empty irq routes");
|
|
|
|
// assert Irq Line 1
|
|
|
|
chip.service_irq(1, true).expect("could not set irq line");
|
|
|
|
|
|
|
|
// no pic or ioapic interrupts should be asserted
|
|
|
|
check_pic_interrupts(&chip, PicSelect::Primary, 0);
|
|
|
|
check_ioapic_interrupts(&chip, 0);
|
|
|
|
|
|
|
|
// now we route gsi 1 to pin 3 of the ioapic and pin 6 of the primary pic
|
|
|
|
chip.route_irq(IrqRoute {
|
|
|
|
gsi: 1,
|
|
|
|
source: IrqSource::Irqchip {
|
|
|
|
chip: IrqSourceChip::Ioapic,
|
|
|
|
pin: 3,
|
|
|
|
},
|
|
|
|
})
|
|
|
|
.expect("failed to assert irq route");
|
|
|
|
// re-assert Irq Line 1
|
|
|
|
chip.service_irq(1, true).expect("could not set irq line");
|
|
|
|
|
|
|
|
// no pic line should be asserted, ioapic pin 3 should be asserted
|
|
|
|
check_pic_interrupts(&chip, PicSelect::Primary, 0);
|
|
|
|
check_ioapic_interrupts(&chip, 1 << 3);
|
|
|
|
|
|
|
|
// de-assert Irq Line 1
|
|
|
|
chip.service_irq(1, false).expect("could not set irq line");
|
|
|
|
|
|
|
|
// no pic or ioapic interrupts should be asserted
|
|
|
|
check_pic_interrupts(&chip, PicSelect::Primary, 0);
|
|
|
|
check_ioapic_interrupts(&chip, 0);
|
|
|
|
|
|
|
|
// add pic route
|
|
|
|
chip.route_irq(IrqRoute {
|
|
|
|
gsi: 2,
|
|
|
|
source: IrqSource::Irqchip {
|
|
|
|
chip: IrqSourceChip::PicPrimary,
|
|
|
|
pin: 6,
|
|
|
|
},
|
|
|
|
})
|
|
|
|
.expect("failed to route irq");
|
|
|
|
|
|
|
|
// re-assert Irq Line 1, it should still affect only the ioapic
|
|
|
|
chip.service_irq(1, true).expect("could not set irq line");
|
|
|
|
|
|
|
|
// no pic line should be asserted, ioapic pin 3 should be asserted
|
|
|
|
check_pic_interrupts(&chip, PicSelect::Primary, 0);
|
|
|
|
check_ioapic_interrupts(&chip, 1 << 3);
|
|
|
|
|
|
|
|
// assert Irq Line 2
|
|
|
|
chip.service_irq(2, true).expect("could not set irq line");
|
|
|
|
|
|
|
|
// pic pin 6 should be asserted, ioapic pin 3 should be asserted
|
|
|
|
check_pic_interrupts(&chip, PicSelect::Primary, 1 << 6);
|
|
|
|
check_ioapic_interrupts(&chip, 1 << 3);
|
|
|
|
}
|
|
|
|
}
|