mirror of
https://chromium.googlesource.com/crosvm/crosvm
synced 2025-02-07 02:46:51 +00:00
These parameters multiplied the number of type arguments to build_vm unnecessarily and complicated the thread of execution in the programmers head. Closures also complicate the borrow rules, making things much harder to change. This change uses the results of the closures (e.g. PCI devices, IRQ chips) as parameters instead. The rest of this change follows naturally from pulling on that thread until tests pass. As a result of the removal of several type arguments, the code size was reduced by ~100KiB on a 5MiB build. BUG=b:185170486 TEST=./test_all Change-Id: I6bcc5eb1b1f3031d4328bb4a81ddef618d04767b Reviewed-on: https://chromium-review.googlesource.com/c/chromiumos/platform/crosvm/+/2829136 Tested-by: kokoro <noreply+kokoro@google.com> Commit-Queue: Zach Reizner <zachr@chromium.org> Reviewed-by: Tomasz Jeznach <tjeznach@chromium.org> Reviewed-by: Daniel Verkamp <dverkamp@chromium.org>
261 lines
9 KiB
Rust
261 lines
9 KiB
Rust
// 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.
|
|
|
|
use base::Result;
|
|
use hypervisor::{IoapicState, LapicState, PicSelect, PicState, PitState};
|
|
|
|
use crate::IrqChip;
|
|
|
|
pub trait IrqChipX86_64: IrqChip {
|
|
// Clones this trait as a `Box` version of itself.
|
|
fn try_box_clone(&self) -> Result<Box<dyn IrqChipX86_64>>;
|
|
|
|
// Get this as the super-trait IrqChip.
|
|
fn as_irq_chip(&self) -> &dyn IrqChip;
|
|
|
|
// Get this as the mutable super-trait IrqChip.
|
|
fn as_irq_chip_mut(&mut self) -> &mut dyn IrqChip;
|
|
|
|
/// 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<()>;
|
|
|
|
/// Returns true if the PIT uses port 0x61 for the PC speaker, false if 0x61 is unused.
|
|
fn pit_uses_speaker_port(&self) -> bool;
|
|
}
|
|
|
|
#[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(mut chip: impl IrqChipX86_64) {
|
|
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(mut chip: impl IrqChipX86_64) {
|
|
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(mut chip: impl IrqChipX86_64) {
|
|
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(mut chip: impl IrqChipX86_64) {
|
|
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(chip: impl IrqChipX86_64) {
|
|
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(mut chip: impl IrqChipX86_64) {
|
|
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);
|
|
}
|
|
|
|
pub fn test_get_lapic(chip: impl IrqChipX86_64) {
|
|
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(mut chip: impl IrqChipX86_64) {
|
|
// 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);
|
|
}
|
|
|
|
/// Helper function for checking the pic interrupt status
|
|
fn check_pic_interrupts(chip: &impl IrqChipX86_64, 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(chip: &impl IrqChipX86_64, 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(mut chip: impl IrqChipX86_64) {
|
|
// 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);
|
|
}
|
|
}
|