mptable: Add ability to allocate pci interrupts

PCI devices will require interrupts, allow this by passing a vector of
IRQs to the mptable so the guest kernel can find the IRQs.

Change-Id: I9fa8a2ed0a34089e631441570521082ffde9c4ef
Reviewed-on: https://chromium-review.googlesource.com/1072578
Commit-Ready: Dylan Reid <dgreid@chromium.org>
Tested-by: Dylan Reid <dgreid@chromium.org>
Reviewed-by: Dylan Reid <dgreid@chromium.org>
This commit is contained in:
Dylan Reid 2018-05-23 16:45:29 -07:00 committed by chrome-bot
parent b847858e66
commit a697d31fc6
5 changed files with 81 additions and 23 deletions

View file

@ -20,7 +20,7 @@ use std::io::stdout;
use std::sync::{Arc, Mutex};
use std::ffi::CStr;
use devices::Bus;
use devices::{Bus, PciInterruptPin};
use sys_util::{EventFd, GuestAddress, GuestMemory};
use resources::{AddressRanges, SystemAllocator};
use std::os::unix::io::FromRawFd;
@ -171,8 +171,12 @@ impl arch::LinuxArch for AArch64 {
Ok(())
}
fn setup_system_memory(mem: &GuestMemory, mem_size: u64, vcpu_count: u32,
cmdline: &CStr) -> Result<()> {
fn setup_system_memory(mem: &GuestMemory,
mem_size: u64,
vcpu_count: u32,
cmdline: &CStr,
_pci_irqs: Vec<(u32, PciInterruptPin)>) -> Result<()> {
// TODO(dgreid) set up PCI in the device tree.
fdt::create_fdt(AARCH64_FDT_MAX_SIZE as usize,
mem,
vcpu_count,

View file

@ -41,10 +41,12 @@ pub trait LinuxArch {
/// * `mem_size` - The size in bytes of system memory
/// * `vcpu_count` - Number of virtual CPUs the guest will have
/// * `cmdline` - the kernel commandline
/// * `pci_irqs` - Any PCI irqs that need to be configured (Interrupt Line, PCI pin).
fn setup_system_memory(mem: &GuestMemory,
mem_size: u64,
vcpu_count: u32,
cmdline: &CStr) -> Result<()>;
cmdline: &CStr,
pci_irqs: Vec<(u32, devices::PciInterruptPin)>) -> Result<()>;
/// Creates a new VM object and initializes architecture specific devices
///

View file

@ -910,8 +910,8 @@ pub fn run_config(cfg: Config) -> Result<()> {
// kernel loading
Arch::load_kernel(&mem, &mut kernel_image).map_err(|e| Error::LoadKernel(e))?;
Arch::setup_system_memory(&mem, mem_size as u64, vcpu_count,
&CString::new(cmdline).unwrap()).
map_err(|e| Error::SetupSystemMemory(e))?;
&CString::new(cmdline).unwrap(), Vec::new())
.map_err(|e| Error::SetupSystemMemory(e))?;
setup_vcpu_signal_handler()?;
for (cpu_id, vcpu) in vcpus.into_iter().enumerate() {

View file

@ -69,6 +69,7 @@ use std::io::stdout;
use bootparam::boot_params;
use bootparam::E820_RAM;
use devices::PciInterruptPin;
use sys_util::{EventFd, GuestAddress, GuestMemory};
use resources::{AddressRanges, SystemAllocator};
use kvm::*;
@ -139,7 +140,8 @@ fn configure_system(guest_mem: &GuestMemory,
kernel_addr: GuestAddress,
cmdline_addr: GuestAddress,
cmdline_size: usize,
num_cpus: u8)
num_cpus: u8,
pci_irqs: Vec<(u32, PciInterruptPin)>)
-> Result<()> {
const EBDA_START: u64 = 0x0009fc00;
const KERNEL_BOOT_FLAG_MAGIC: u16 = 0xaa55;
@ -150,7 +152,7 @@ fn configure_system(guest_mem: &GuestMemory,
let end_32bit_gap_start = GuestAddress(FIRST_ADDR_PAST_32BITS - MEM_32BIT_GAP_SIZE);
// Note that this puts the mptable at 0x0 in guest physical memory.
mptable::setup_mptable(guest_mem, num_cpus)?;
mptable::setup_mptable(guest_mem, num_cpus, pci_irqs)?;
let mut params: boot_params = Default::default();
@ -250,11 +252,12 @@ impl arch::LinuxArch for X8664arch {
/// * `vcpu_count` - Number of virtual CPUs the guest will have.
/// * `cmdline` - the kernel commandline
fn setup_system_memory(mem: &GuestMemory, _mem_size: u64,
vcpu_count: u32, cmdline: &CStr) -> Result<()> {
vcpu_count: u32, cmdline: &CStr,
pci_irqs: Vec<(u32, PciInterruptPin)>) -> Result<()> {
kernel_loader::load_cmdline(mem, GuestAddress(CMDLINE_OFFSET), cmdline)?;
configure_system(mem, GuestAddress(KERNEL_START_OFFSET),
GuestAddress(CMDLINE_OFFSET),
cmdline.to_bytes().len() + 1, vcpu_count as u8)?;
cmdline.to_bytes().len() + 1, vcpu_count as u8, pci_irqs)?;
Ok(())
}

View file

@ -11,6 +11,7 @@ use std::fmt::{self, Display};
use libc::c_char;
use devices::PciInterruptPin;
use sys_util::{GuestAddress, GuestMemory};
use mpspec::*;
@ -79,6 +80,7 @@ const MPC_SPEC: i8 = 4;
const MPC_OEM: [c_char; 8] = char_array!(c_char; 'C', 'R', 'O', 'S', 'V', 'M', ' ', ' ');
const MPC_PRODUCT_ID: [c_char; 12] = ['0' as c_char; 12];
const BUS_TYPE_ISA: [u8; 6] = char_array!(u8; 'I', 'S', 'A', ' ', ' ', ' ');
const BUS_TYPE_PCI: [u8; 6] = char_array!(u8; 'P', 'C', 'I', ' ', ' ', ' ');
const IO_APIC_DEFAULT_PHYS_BASE: u32 = 0xfec00000; // source: linux/arch/x86/include/asm/apicdef.h
const APIC_DEFAULT_PHYS_BASE: u32 = 0xfee00000; // source: linux/arch/x86/include/asm/apicdef.h
const APIC_VERSION: u8 = 0x14;
@ -105,13 +107,17 @@ fn mpf_intel_compute_checksum(v: &mpf_intel) -> u8 {
fn compute_mp_size(num_cpus: u8) -> usize {
mem::size_of::<mpf_intel>() + mem::size_of::<mpc_table>() +
mem::size_of::<mpc_cpu>() * (num_cpus as usize) + mem::size_of::<mpc_ioapic>() +
mem::size_of::<mpc_bus>() + mem::size_of::<mpc_intsrc>() +
mem::size_of::<mpc_bus>() * 2 + mem::size_of::<mpc_intsrc>() +
mem::size_of::<mpc_intsrc>() * 16 +
mem::size_of::<mpc_lintsrc>() * 2
}
/// Performs setup of the MP table for the given `num_cpus`.
pub fn setup_mptable(mem: &GuestMemory, num_cpus: u8) -> Result<()> {
pub fn setup_mptable(mem: &GuestMemory, num_cpus: u8,
pci_irqs: Vec<(u32, PciInterruptPin)>)
-> Result<()> {
const PCI_BUS_ID: u8 = 0;
const ISA_BUS_ID: u8 = 1;
// Used to keep track of the next base pointer into the MP table.
let mut base_mp = GuestAddress(MPTABLE_START);
@ -188,7 +194,18 @@ pub fn setup_mptable(mem: &GuestMemory, num_cpus: u8) -> Result<()> {
let size = mem::size_of::<mpc_bus>();
let mut mpc_bus = mpc_bus::default();
mpc_bus.type_ = MP_BUS as u8;
mpc_bus.busid = 0;
mpc_bus.busid = PCI_BUS_ID;
mpc_bus.bustype = BUS_TYPE_PCI;
mem.write_obj_at_addr(mpc_bus, base_mp)
.map_err(|_| Error::WriteMpcBus)?;
base_mp = base_mp.unchecked_add(size as u64);
checksum = checksum.wrapping_add(compute_checksum(&mpc_bus));
}
{
let size = mem::size_of::<mpc_bus>();
let mut mpc_bus = mpc_bus::default();
mpc_bus.type_ = MP_BUS as u8;
mpc_bus.busid = ISA_BUS_ID;
mpc_bus.bustype = BUS_TYPE_ISA;
mem.write_obj_at_addr(mpc_bus, base_mp)
.map_err(|_| Error::WriteMpcBus)?;
@ -201,7 +218,7 @@ pub fn setup_mptable(mem: &GuestMemory, num_cpus: u8) -> Result<()> {
mpc_intsrc.type_ = MP_INTSRC as u8;
mpc_intsrc.irqtype = mp_irq_source_types_mp_INT as u8;
mpc_intsrc.irqflag = MP_IRQDIR_DEFAULT as u16;
mpc_intsrc.srcbus = 0;
mpc_intsrc.srcbus = ISA_BUS_ID;
mpc_intsrc.srcbusirq = 0;
mpc_intsrc.dstapic = 0;
mpc_intsrc.dstirq = 0;
@ -211,13 +228,13 @@ pub fn setup_mptable(mem: &GuestMemory, num_cpus: u8) -> Result<()> {
checksum = checksum.wrapping_add(compute_checksum(&mpc_intsrc));
}
// Per kvm_setup_default_irq_routing() in kernel
for i in 0..16 {
for i in 0..5 {
let size = mem::size_of::<mpc_intsrc>();
let mut mpc_intsrc = mpc_intsrc::default();
mpc_intsrc.type_ = MP_INTSRC as u8;
mpc_intsrc.irqtype = mp_irq_source_types_mp_INT as u8;
mpc_intsrc.irqflag = MP_IRQDIR_DEFAULT as u16;
mpc_intsrc.srcbus = 0;
mpc_intsrc.srcbus = ISA_BUS_ID;
mpc_intsrc.srcbusirq = i;
mpc_intsrc.dstapic = ioapicid;
mpc_intsrc.dstirq = i;
@ -226,13 +243,45 @@ pub fn setup_mptable(mem: &GuestMemory, num_cpus: u8) -> Result<()> {
base_mp = base_mp.unchecked_add(size as u64);
checksum = checksum.wrapping_add(compute_checksum(&mpc_intsrc));
}
// Insert PCI interrupts after platform IRQs.
for (i, pci_irq) in pci_irqs.iter().enumerate() {
let size = mem::size_of::<mpc_intsrc>();
let mut mpc_intsrc = mpc_intsrc::default();
mpc_intsrc.type_ = MP_INTSRC as u8;
mpc_intsrc.irqtype = mp_irq_source_types_mp_INT as u8;
mpc_intsrc.irqflag = MP_IRQDIR_DEFAULT as u16;
mpc_intsrc.srcbus = PCI_BUS_ID;
mpc_intsrc.srcbusirq = 1 << 2 | pci_irq.1.to_mask() as u8; // slot <<2 | int A(0)
mpc_intsrc.dstapic = ioapicid;
mpc_intsrc.dstirq = 5 + i as u8;
mem.write_obj_at_addr(mpc_intsrc, base_mp)
.map_err(|_| Error::WriteMpcIntsrc)?;
base_mp = base_mp.unchecked_add(size as u64);
checksum = checksum.wrapping_add(compute_checksum(&mpc_intsrc));
}
// Finally insert ISA interrupts.
for i in 5 + pci_irqs.len()..16 {
let size = mem::size_of::<mpc_intsrc>();
let mut mpc_intsrc = mpc_intsrc::default();
mpc_intsrc.type_ = MP_INTSRC as u8;
mpc_intsrc.irqtype = mp_irq_source_types_mp_INT as u8;
mpc_intsrc.irqflag = MP_IRQDIR_DEFAULT as u16;
mpc_intsrc.srcbus = ISA_BUS_ID;
mpc_intsrc.srcbusirq = i as u8;
mpc_intsrc.dstapic = ioapicid;
mpc_intsrc.dstirq = i as u8;
mem.write_obj_at_addr(mpc_intsrc, base_mp)
.map_err(|_| Error::WriteMpcIntsrc)?;
base_mp = base_mp.unchecked_add(size as u64);
checksum = checksum.wrapping_add(compute_checksum(&mpc_intsrc));
}
{
let size = mem::size_of::<mpc_lintsrc>();
let mut mpc_lintsrc = mpc_lintsrc::default();
mpc_lintsrc.type_ = MP_LINTSRC as u8;
mpc_lintsrc.irqtype = mp_irq_source_types_mp_ExtINT as u8;
mpc_lintsrc.irqflag = MP_IRQDIR_DEFAULT as u16;
mpc_lintsrc.srcbusid = 0;
mpc_lintsrc.srcbusid = ISA_BUS_ID;
mpc_lintsrc.srcbusirq = 0;
mpc_lintsrc.destapic = 0;
mpc_lintsrc.destapiclint = 0;
@ -247,7 +296,7 @@ pub fn setup_mptable(mem: &GuestMemory, num_cpus: u8) -> Result<()> {
mpc_lintsrc.type_ = MP_LINTSRC as u8;
mpc_lintsrc.irqtype = mp_irq_source_types_mp_NMI as u8;
mpc_lintsrc.irqflag = MP_IRQDIR_DEFAULT as u16;
mpc_lintsrc.srcbusid = 0;
mpc_lintsrc.srcbusid = ISA_BUS_ID;
mpc_lintsrc.srcbusirq = 0;
mpc_lintsrc.destapic = 0xFF; // Per SeaBIOS
mpc_lintsrc.destapiclint = 1;
@ -299,7 +348,7 @@ mod tests {
let mem = GuestMemory::new(&[(GuestAddress(MPTABLE_START),
compute_mp_size(num_cpus) as u64)]).unwrap();
setup_mptable(&mem, num_cpus).unwrap();
setup_mptable(&mem, num_cpus, Vec::new()).unwrap();
}
#[test]
@ -308,7 +357,7 @@ mod tests {
let mem = GuestMemory::new(&[(GuestAddress(MPTABLE_START),
(compute_mp_size(num_cpus) - 1) as u64)]).unwrap();
assert!(setup_mptable(&mem, num_cpus).is_err());
assert!(setup_mptable(&mem, num_cpus, Vec::new()).is_err());
}
#[test]
@ -317,7 +366,7 @@ mod tests {
let mem = GuestMemory::new(&[(GuestAddress(MPTABLE_START),
compute_mp_size(num_cpus) as u64)]).unwrap();
setup_mptable(&mem, num_cpus).unwrap();
setup_mptable(&mem, num_cpus, Vec::new()).unwrap();
let mpf_intel = mem.read_obj_from_addr(GuestAddress(MPTABLE_START)).unwrap();
@ -330,7 +379,7 @@ mod tests {
let mem = GuestMemory::new(&[(GuestAddress(MPTABLE_START),
compute_mp_size(num_cpus) as u64)]).unwrap();
setup_mptable(&mem, num_cpus).unwrap();
setup_mptable(&mem, num_cpus, Vec::new()).unwrap();
let mpf_intel: mpf_intel = mem.read_obj_from_addr(GuestAddress(MPTABLE_START)).unwrap();
let mpc_offset = GuestAddress(mpf_intel.physptr as u64);
@ -362,7 +411,7 @@ mod tests {
compute_mp_size(MAX_CPUS) as u64)]).unwrap();
for i in 0..MAX_CPUS {
setup_mptable(&mem, i).unwrap();
setup_mptable(&mem, i, Vec::new()).unwrap();
let mpf_intel: mpf_intel = mem.read_obj_from_addr(GuestAddress(MPTABLE_START)).unwrap();
let mpc_offset = GuestAddress(mpf_intel.physptr as u64);