Add logic to setup PIC/IOAPIC.

TODO: Route irqfd to PIC/IOAPIC to make them fully work.

BUG=chromium:908689
TEST=None

Change-Id: I301287b1cf32cfccffce6c52ebbb5e123931178e
Reviewed-on: https://chromium-review.googlesource.com/c/chromiumos/platform/crosvm/+/1945796
Reviewed-by: Daniel Verkamp <dverkamp@chromium.org>
Tested-by: kokoro <noreply+kokoro@google.com>
Commit-Queue: Zhuocheng Ding <zhuocheng.ding@intel.corp-partner.google.com>
This commit is contained in:
Zhuocheng Ding 2019-12-02 15:50:20 +08:00 committed by Commit Bot
parent 50740cece4
commit f2e90bf0b0
11 changed files with 80 additions and 11 deletions

3
Cargo.lock generated
View file

@ -16,6 +16,7 @@ dependencies = [
"resources 0.1.0",
"sync 0.1.0",
"sys_util 0.1.0",
"vm_control 0.1.0",
]
[[package]]
@ -37,6 +38,7 @@ dependencies = [
"resources 0.1.0",
"sync 0.1.0",
"sys_util 0.1.0",
"vm_control 0.1.0",
]
[[package]]
@ -776,6 +778,7 @@ dependencies = [
"resources 0.1.0",
"sync 0.1.0",
"sys_util 0.1.0",
"vm_control 0.1.0",
]
[metadata]

View file

@ -17,3 +17,4 @@ remain = "*"
resources = { path = "../resources" }
sync = { path = "../sync" }
sys_util = { path = "../sys_util" }
vm_control = { path = "../vm_control" }

View file

@ -21,6 +21,7 @@ use remain::sorted;
use resources::SystemAllocator;
use sync::Mutex;
use sys_util::{EventFd, GuestAddress, GuestMemory, GuestMemoryError};
use vm_control::VmIrqRequestSocket;
use kvm::*;
use kvm_sys::kvm_device_attr;
@ -195,6 +196,7 @@ impl arch::LinuxArch for AArch64 {
fn build_vm<F, E>(
mut components: VmComponents,
_split_irqchip: bool,
_ioapic_device_socket: VmIrqRequestSocket,
serial_parameters: &BTreeMap<u8, SerialParameters>,
serial_jail: Option<Minijail>,
create_devices: F,
@ -314,6 +316,7 @@ impl arch::LinuxArch for AArch64 {
vcpus,
vcpu_affinity,
irq_chip,
split_irqchip: None,
io_bus,
mmio_bus,
pid_debug_label_map,

View file

@ -13,3 +13,4 @@ libc = "*"
resources = { path = "../resources" }
sync = { path = "../sync" }
sys_util = { path = "../sys_util" }
vm_control = { path = "../vm_control" }

View file

@ -25,6 +25,7 @@ use kvm::{IoeventAddress, Kvm, Vcpu, Vm};
use resources::SystemAllocator;
use sync::Mutex;
use sys_util::{syslog, EventFd, GuestAddress, GuestMemory, GuestMemoryError};
use vm_control::VmIrqRequestSocket;
pub enum VmImage {
Kernel(File),
@ -60,6 +61,7 @@ pub struct RunnableLinuxVm {
pub vcpus: Vec<Vcpu>,
pub vcpu_affinity: Vec<usize>,
pub irq_chip: Option<File>,
pub split_irqchip: Option<(Arc<Mutex<devices::Pic>>, Arc<Mutex<devices::Ioapic>>)>,
pub io_bus: Bus,
pub mmio_bus: Bus,
pub pid_debug_label_map: BTreeMap<u32, String>,
@ -88,6 +90,7 @@ pub trait LinuxArch {
fn build_vm<F, E>(
components: VmComponents,
split_irqchip: bool,
ioapic_device_socket: VmIrqRequestSocket,
serial_parameters: &BTreeMap<u8, SerialParameters>,
serial_jail: Option<Minijail>,
create_devices: F,

View file

@ -40,11 +40,9 @@ pub enum DeliveryStatus {
}
const IOAPIC_VERSION_ID: u32 = 0x00170011;
#[allow(dead_code)]
const IOAPIC_BASE_ADDRESS: u32 = 0xfec00000;
pub const IOAPIC_BASE_ADDRESS: u64 = 0xfec00000;
// The Intel manual does not specify this size, but KVM uses it.
#[allow(dead_code)]
const IOAPIC_MEM_LENGTH_BYTES: usize = 0x100;
pub const IOAPIC_MEM_LENGTH_BYTES: u64 = 0x100;
// Constants for IOAPIC direct register offset.
const IOAPIC_REG_ID: u8 = 0x00;
@ -52,10 +50,10 @@ const IOAPIC_REG_VERSION: u8 = 0x01;
const IOAPIC_REG_ARBITRATION_ID: u8 = 0x02;
// Register offsets
pub const IOREGSEL_OFF: u8 = 0x0;
pub const IOREGSEL_DUMMY_UPPER_32_BITS_OFF: u8 = 0x4;
pub const IOWIN_OFF: u8 = 0x10;
pub const IOWIN_SCALE: u8 = 0x2;
const IOREGSEL_OFF: u8 = 0x0;
const IOREGSEL_DUMMY_UPPER_32_BITS_OFF: u8 = 0x4;
const IOWIN_OFF: u8 = 0x10;
const IOWIN_SCALE: u8 = 0x2;
/// Given an IRQ and whether or not the selector should refer to the high bits, return a selector
/// suitable to use as an offset to read to/write from.

View file

@ -28,7 +28,7 @@ pub use self::bus::Error as BusError;
pub use self::bus::{Bus, BusDevice, BusRange, BusResumeDevice};
pub use self::cmos::Cmos;
pub use self::i8042::I8042Device;
pub use self::ioapic::Ioapic;
pub use self::ioapic::{Ioapic, IOAPIC_BASE_ADDRESS, IOAPIC_MEM_LENGTH_BYTES};
pub use self::pci::{
Ac97Dev, PciConfigIo, PciConfigMmio, PciDevice, PciDeviceError, PciInterruptPin, PciRoot,
VfioPciDevice,

View file

@ -1121,6 +1121,9 @@ pub enum VcpuExit {
size: usize,
data: [u8; 8],
},
IoapicEoi {
vector: u8,
},
Unknown,
Exception,
Hypercall,
@ -1811,6 +1814,12 @@ impl RunnableVcpu {
Ok(VcpuExit::MmioRead { address, size })
}
}
KVM_EXIT_IOAPIC_EOI => {
// Safe because the exit_reason (which comes from the kernel) told us which
// union field to use.
let vector = unsafe { run.__bindgen_anon_1.eoi.vector };
Ok(VcpuExit::IoapicEoi { vector })
}
KVM_EXIT_UNKNOWN => Ok(VcpuExit::Unknown),
KVM_EXIT_EXCEPTION => Ok(VcpuExit::Exception),
KVM_EXIT_HYPERCALL => Ok(VcpuExit::Hypercall),

View file

@ -1356,6 +1356,7 @@ fn run_vcpu(
start_barrier: Arc<Barrier>,
io_bus: devices::Bus,
mmio_bus: devices::Bus,
split_irqchip: Option<(Arc<Mutex<devices::Pic>>, Arc<Mutex<devices::Ioapic>>)>,
exit_evt: EventFd,
requires_kvmclock_ctrl: bool,
run_mode_arc: Arc<VcpuRunMode>,
@ -1417,6 +1418,13 @@ fn run_vcpu(
}) => {
mmio_bus.write(address, &data[..size]);
}
Ok(VcpuExit::IoapicEoi{vector}) => {
if let Some((_, ioapic)) = &split_irqchip {
ioapic.lock().end_of_interrupt(vector);
} else {
panic!("userspace ioapic not found in split irqchip mode, should be impossible.");
}
},
Ok(VcpuExit::Hlt) => break,
Ok(VcpuExit::Shutdown) => break,
Ok(VcpuExit::FailEntry {
@ -1589,10 +1597,15 @@ pub fn run_config(cfg: Config) -> Result<()> {
msg_socket::pair::<VmMemoryResponse, VmMemoryRequest>().map_err(Error::CreateSocket)?;
control_sockets.push(TaggedControlSocket::VmMemory(gpu_host_socket));
let (ioapic_host_socket, ioapic_device_socket) =
msg_socket::pair::<VmIrqResponse, VmIrqRequest>().map_err(Error::CreateSocket)?;
control_sockets.push(TaggedControlSocket::VmIrq(ioapic_host_socket));
let sandbox = cfg.sandbox;
let linux = Arch::build_vm(
components,
cfg.split_irqchip,
ioapic_device_socket,
&cfg.serial_parameters,
simple_jail(&cfg, "serial")?,
|mem, vm, sys_allocator, exit_evt| {
@ -1739,6 +1752,7 @@ fn run_control(
vcpu_thread_barrier.clone(),
linux.io_bus.clone(),
linux.mmio_bus.clone(),
linux.split_irqchip.clone(),
linux.exit_evt.try_clone().map_err(Error::CloneEventFd)?,
linux.vm.check_extension(Cap::KvmclockCtrl),
run_mode_arc.clone(),

View file

@ -20,3 +20,4 @@ resources = { path = "../resources" }
sync = { path = "../sync" }
sys_util = { path = "../sys_util" }
acpi_tables = {path = "../acpi_tables" }
vm_control = { path = "../vm_control" }

View file

@ -55,13 +55,17 @@ use std::sync::Arc;
use crate::bootparam::boot_params;
use arch::{RunnableLinuxVm, VmComponents, VmImage};
use devices::{get_serial_tty_string, PciConfigIo, PciDevice, PciInterruptPin, SerialParameters};
use devices::{
get_serial_tty_string, Ioapic, PciConfigIo, PciDevice, PciInterruptPin, Pic, SerialParameters,
IOAPIC_BASE_ADDRESS, IOAPIC_MEM_LENGTH_BYTES,
};
use io_jail::Minijail;
use kvm::*;
use remain::sorted;
use resources::SystemAllocator;
use sync::Mutex;
use sys_util::{Clock, EventFd, GuestAddress, GuestMemory, GuestMemoryError};
use vm_control::VmIrqRequestSocket;
#[sorted]
#[derive(Debug)]
@ -73,6 +77,7 @@ pub enum Error {
CreateDevices(Box<dyn StdError>),
CreateEventFd(sys_util::Error),
CreateFdt(arch::fdt::Error),
CreateIoapicDevice(sys_util::Error),
CreateIrqChip(sys_util::Error),
CreateKvm(sys_util::Error),
CreatePciRoot(arch::DeviceRegistrationError),
@ -120,6 +125,7 @@ impl Display for Error {
CreateDevices(e) => write!(f, "error creating devices: {}", e),
CreateEventFd(e) => write!(f, "unable to make an EventFd: {}", e),
CreateFdt(e) => write!(f, "failed to create fdt: {}", e),
CreateIoapicDevice(e) => write!(f, "failed to create IOAPIC device: {}", e),
CreateIrqChip(e) => write!(f, "failed to create irq chip: {}", e),
CreateKvm(e) => write!(f, "failed to open /dev/kvm: {}", e),
CreatePciRoot(e) => write!(f, "failed to create a PCI root hub: {}", e),
@ -314,6 +320,7 @@ impl arch::LinuxArch for X8664arch {
fn build_vm<F, E>(
mut components: VmComponents,
split_irqchip: bool,
ioapic_device_socket: VmIrqRequestSocket,
serial_parameters: &BTreeMap<u8, SerialParameters>,
serial_jail: Option<Minijail>,
create_devices: F,
@ -362,6 +369,23 @@ impl arch::LinuxArch for X8664arch {
let exit_evt = EventFd::new().map_err(Error::CreateEventFd)?;
let split_irqchip = if split_irqchip {
let pic = Arc::new(Mutex::new(Pic::new()));
let ioapic = Arc::new(Mutex::new(
Ioapic::new(&mut vm, ioapic_device_socket).map_err(Error::CreateIoapicDevice)?,
));
mmio_bus
.insert(
ioapic.clone(),
IOAPIC_BASE_ADDRESS,
IOAPIC_MEM_LENGTH_BYTES,
false,
)
.unwrap();
Some((pic, ioapic))
} else {
None
};
let pci_devices = create_devices(&mem, &mut vm, &mut resources, &exit_evt)
.map_err(|e| Error::CreateDevices(Box::new(e)))?;
let (pci, pci_irqs, pid_debug_label_map) =
@ -376,7 +400,7 @@ impl arch::LinuxArch for X8664arch {
let mut io_bus = Self::setup_io_bus(
&mut vm,
split_irqchip,
split_irqchip.is_some(),
exit_evt.try_clone().map_err(Error::CloneEventFd)?,
Some(pci_bus.clone()),
components.memory_size,
@ -394,6 +418,17 @@ impl arch::LinuxArch for X8664arch {
None => None,
};
if let Some((pic, _)) = &split_irqchip {
io_bus.insert(pic.clone(), 0x20, 0x2, true).unwrap();
io_bus.insert(pic.clone(), 0xa0, 0x2, true).unwrap();
io_bus.insert(pic.clone(), 0x4d0, 0x2, true).unwrap();
let mut irq_num = resources.allocate_irq().unwrap();
while irq_num < kvm::NUM_IOAPIC_PINS as u32 {
irq_num = resources.allocate_irq().unwrap();
}
}
match components.vm_image {
VmImage::Bios(ref mut bios) => Self::load_bios(&mem, bios)?,
VmImage::Kernel(ref mut kernel_image) => {
@ -447,6 +482,7 @@ impl arch::LinuxArch for X8664arch {
vcpus,
vcpu_affinity,
irq_chip,
split_irqchip,
io_bus,
mmio_bus,
pid_debug_label_map,