From 0e166f2c517f8c08657789b5618cc53161b1aa40 Mon Sep 17 00:00:00 2001 From: Djordje Lukic Date: Fri, 31 May 2024 19:27:20 +0200 Subject: [PATCH] Emulate GICv3 on macos Signed-off-by: Djordje Lukic Signed-off-by: David Gageot Signed-off-by: Piotr Stankiewicz --- Cargo.lock | 1 + src/arch/src/aarch64/layout.rs | 2 + src/arch/src/aarch64/macos/gic.rs | 9 +- src/arch/src/aarch64/macos/gicv3.rs | 94 ++++ src/arch/src/aarch64/macos/mod.rs | 2 + src/arch/src/aarch64/macos/sysreg.rs | 122 +++++ src/devices/src/legacy/aarch64/gpio.rs | 10 +- src/devices/src/legacy/aarch64/serial.rs | 11 +- src/devices/src/legacy/gic.rs | 356 ------------- src/devices/src/legacy/gicv3.rs | 486 ++++++++++++++++++ src/devices/src/legacy/mod.rs | 16 +- src/devices/src/legacy/vcpu.rs | 220 ++++++++ src/devices/src/virtio/balloon/device.rs | 11 +- src/devices/src/virtio/block/device.rs | 9 +- src/devices/src/virtio/block/worker.rs | 10 +- src/devices/src/virtio/console/device.rs | 6 +- .../src/virtio/console/irq_signaler.rs | 11 +- src/devices/src/virtio/fs/device.rs | 9 +- src/devices/src/virtio/fs/worker.rs | 10 +- src/devices/src/virtio/gpu/device.rs | 9 +- src/devices/src/virtio/gpu/virtio_gpu.rs | 8 +- src/devices/src/virtio/gpu/worker.rs | 8 +- src/devices/src/virtio/net/device.rs | 9 +- src/devices/src/virtio/net/worker.rs | 10 +- src/devices/src/virtio/rng/device.rs | 11 +- src/devices/src/virtio/snd/device.rs | 9 +- src/devices/src/virtio/snd/mod.rs | 4 +- src/devices/src/virtio/snd/worker.rs | 8 +- src/devices/src/virtio/vsock/device.rs | 9 +- src/devices/src/virtio/vsock/muxer.rs | 8 +- src/devices/src/virtio/vsock/muxer_thread.rs | 8 +- src/devices/src/virtio/vsock/timesync.rs | 8 +- src/hvf/Cargo.toml | 2 + src/hvf/src/lib.rs | 395 +++++++------- src/vmm/src/builder.rs | 37 +- src/vmm/src/device_manager/hvf/mmio.rs | 21 +- src/vmm/src/macos/vstate.rs | 24 +- 37 files changed, 1294 insertions(+), 689 deletions(-) create mode 100644 src/arch/src/aarch64/macos/gicv3.rs create mode 100644 src/arch/src/aarch64/macos/sysreg.rs delete mode 100644 src/devices/src/legacy/gic.rs create mode 100644 src/devices/src/legacy/gicv3.rs create mode 100644 src/devices/src/legacy/vcpu.rs diff --git a/Cargo.lock b/Cargo.lock index 547a43fd..5d1d5e22 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -599,6 +599,7 @@ checksum = "9a3a5bfb195931eeb336b2a7b4d761daec841b97f947d34394601737a7bba5e4" name = "hvf" version = "0.1.0" dependencies = [ + "arch", "crossbeam-channel", "env_logger", "log", diff --git a/src/arch/src/aarch64/layout.rs b/src/arch/src/aarch64/layout.rs index 8c0bd112..b22f1c4a 100644 --- a/src/arch/src/aarch64/layout.rs +++ b/src/arch/src/aarch64/layout.rs @@ -84,6 +84,8 @@ pub const GTIMER_HYP: u32 = 14; pub const GTIMER_VIRT: u32 = 11; pub const GTIMER_PHYS: u32 = 12; +pub const VTIMER_IRQ: u32 = GTIMER_VIRT + 16; + /// Below this address will reside the GIC, above this address will reside the MMIO devices. #[cfg(not(feature = "efi"))] pub const MAPPED_IO_START: u64 = 1 << 30; // 1 GB diff --git a/src/arch/src/aarch64/macos/gic.rs b/src/arch/src/aarch64/macos/gic.rs index 6195f22e..5566d2db 100644 --- a/src/arch/src/aarch64/macos/gic.rs +++ b/src/arch/src/aarch64/macos/gic.rs @@ -3,7 +3,7 @@ use std::{boxed::Box, result}; -use super::gicv2::GICv2; +use super::gicv3::GICv3; /// Errors thrown while setting up the GIC. #[derive(Debug)] @@ -70,10 +70,7 @@ pub trait GICDevice: Send { } } -/// Create a GIC device. -/// -/// It will try to create by default a GICv3 device. If that fails it will try -/// to fall-back to a GICv2 device. +/// Create a GICv3 device. pub fn create_gic(vcpu_count: u64) -> Result> { - GICv2::new(vcpu_count) + GICv3::new(vcpu_count) } diff --git a/src/arch/src/aarch64/macos/gicv3.rs b/src/arch/src/aarch64/macos/gicv3.rs new file mode 100644 index 00000000..a717e1bb --- /dev/null +++ b/src/arch/src/aarch64/macos/gicv3.rs @@ -0,0 +1,94 @@ +use std::{boxed::Box, result}; + +use super::gic::{Error, GICDevice}; + +type Result = result::Result; + +/// This is just a placeholder for building the FDT entry. +/// The actual emulated GICv3 is in devices/legacy. +pub struct GICv3 { + /// GIC device properties, to be used for setting up the fdt entry + properties: [u64; 4], + + /// Number of CPUs handled by the device + vcpu_count: u64, +} + +impl GICv3 { + const SZ_64K: u64 = 0x0001_0000; + + // Device trees specific constants + const GIC_V3_MAINT_IRQ: u32 = 8; + + /// Get the address of the GICv3 distributor. + pub fn get_dist_addr() -> u64 { + super::super::layout::MAPPED_IO_START - 3 * GICv3::SZ_64K + } + + /// Get the size of the GIC_v3 distributor. + pub const fn get_dist_size() -> u64 { + GICv3::SZ_64K + } + + /// Get the address of the GIC redistributors. + pub const fn compute_redists_addr(vcpu_count: u64) -> u64 { + super::super::layout::MAPPED_IO_START + - 3 * GICv3::SZ_64K + - GICv3::compute_redists_size(vcpu_count) + } + + pub fn get_redists_addr(&self) -> u64 { + Self::compute_redists_addr(self.vcpu_count) + } + + /// Get the size of the GIC redistributors. + pub const fn compute_redists_size(vcpu_count: u64) -> u64 { + vcpu_count * GICv3::get_redist_size() + } + + pub fn get_redists_size(&self) -> u64 { + GICv3::compute_redists_size(self.vcpu_count) + } + + pub const fn get_redist_size() -> u64 { + 2 * GICv3::SZ_64K + } +} + +impl GICDevice for GICv3 { + fn device_properties(&self) -> &[u64] { + &self.properties + } + + fn vcpu_count(&self) -> u64 { + self.vcpu_count + } + + fn fdt_compatibility(&self) -> &str { + "arm,gic-v3" + } + + fn fdt_maint_irq(&self) -> u32 { + GICv3::GIC_V3_MAINT_IRQ + } + + fn version() -> u32 { + 0 + } + + fn create_device(vcpu_count: u64) -> Box { + Box::new(GICv3 { + properties: [ + GICv3::get_dist_addr(), + GICv3::get_dist_size(), + GICv3::compute_redists_addr(vcpu_count), + GICv3::compute_redists_size(vcpu_count), + ], + vcpu_count, + }) + } + + fn init_device_attributes(_gic_device: &Box) -> Result<()> { + Ok(()) + } +} diff --git a/src/arch/src/aarch64/macos/mod.rs b/src/arch/src/aarch64/macos/mod.rs index c289d8c4..90ce234e 100644 --- a/src/arch/src/aarch64/macos/mod.rs +++ b/src/arch/src/aarch64/macos/mod.rs @@ -1,4 +1,6 @@ #[allow(clippy::new_ret_no_self)] pub mod gic; pub mod gicv2; +pub mod gicv3; pub mod regs; +pub mod sysreg; diff --git a/src/arch/src/aarch64/macos/sysreg.rs b/src/arch/src/aarch64/macos/sysreg.rs new file mode 100644 index 00000000..92a5b25f --- /dev/null +++ b/src/arch/src/aarch64/macos/sysreg.rs @@ -0,0 +1,122 @@ +pub const SYSREG_OP0_SHIFT: u32 = 20; +pub const SYSREG_OP0_MASK: u32 = 0x3; +pub const SYSREG_OP1_SHIFT: u32 = 14; +pub const SYSREG_OP1_MASK: u32 = 0x7; +pub const SYSREG_CRN_SHIFT: u32 = 10; +pub const SYSREG_CRN_MASK: u32 = 0xf; +pub const SYSREG_CRM_SHIFT: u32 = 1; +pub const SYSREG_CRM_MASK: u32 = 0xf; +pub const SYSREG_OP2_SHIFT: u32 = 17; +pub const SYSREG_OP2_MASK: u32 = 0x7; + +#[macro_export] +macro_rules! arm64_sys_reg { + ($name: tt, $op0: tt, $op1: tt, $op2: tt, $crn: tt, $crm: tt) => { + pub const $name: u32 = ($op0 as u32) << SYSREG_OP0_SHIFT + | ($op2 as u32) << SYSREG_OP2_SHIFT + | ($op1 as u32) << SYSREG_OP1_SHIFT + | ($crn as u32) << SYSREG_CRN_SHIFT + | ($crm as u32) << SYSREG_CRM_SHIFT; + }; +} + +arm64_sys_reg!( + SYSREG_MASK, + SYSREG_OP0_MASK, + SYSREG_OP1_MASK, + SYSREG_OP2_MASK, + SYSREG_CRN_MASK, + SYSREG_CRM_MASK +); + +arm64_sys_reg!(SYSREG_OSLAR_EL1, 2, 0, 4, 1, 0); +arm64_sys_reg!(SYSREG_OSDLR_EL1, 2, 0, 4, 1, 3); + +arm64_sys_reg!(SYSREG_ICC_AP0R0_EL1, 3, 0, 4, 12, 8); +arm64_sys_reg!(SYSREG_ICC_AP0R1_EL1, 3, 0, 5, 12, 8); +arm64_sys_reg!(SYSREG_ICC_AP0R2_EL1, 3, 0, 6, 12, 8); +arm64_sys_reg!(SYSREG_ICC_AP0R3_EL1, 3, 0, 7, 12, 8); +arm64_sys_reg!(SYSREG_ICC_AP1R0_EL1, 3, 0, 0, 12, 9); +arm64_sys_reg!(SYSREG_ICC_AP1R1_EL1, 3, 0, 1, 12, 9); +arm64_sys_reg!(SYSREG_ICC_AP1R2_EL1, 3, 0, 2, 12, 9); +arm64_sys_reg!(SYSREG_ICC_AP1R3_EL1, 3, 0, 3, 12, 9); +arm64_sys_reg!(SYSREG_ICC_ASGI1R_EL1, 3, 0, 6, 12, 11); +arm64_sys_reg!(SYSREG_ICC_BPR0_EL1, 3, 0, 3, 12, 8); +arm64_sys_reg!(SYSREG_ICC_BPR1_EL1, 3, 0, 3, 12, 12); +arm64_sys_reg!(SYSREG_ICC_CTLR_EL1, 3, 0, 4, 12, 12); +arm64_sys_reg!(SYSREG_ICC_DIR_EL1, 3, 0, 1, 12, 11); +arm64_sys_reg!(SYSREG_ICC_EOIR0_EL1, 3, 0, 1, 12, 8); +arm64_sys_reg!(SYSREG_ICC_EOIR1_EL1, 3, 0, 1, 12, 12); +arm64_sys_reg!(SYSREG_ICC_HPPIR0_EL1, 3, 0, 2, 12, 8); +arm64_sys_reg!(SYSREG_ICC_HPPIR1_EL1, 3, 0, 2, 12, 12); +arm64_sys_reg!(SYSREG_ICC_IAR0_EL1, 3, 0, 0, 12, 8); +arm64_sys_reg!(SYSREG_ICC_IAR1_EL1, 3, 0, 0, 12, 12); +arm64_sys_reg!(SYSREG_ICC_IGRPEN0_EL1, 3, 0, 6, 12, 12); +arm64_sys_reg!(SYSREG_ICC_IGRPEN1_EL1, 3, 0, 7, 12, 12); +arm64_sys_reg!(SYSREG_ICC_PMR_EL1, 3, 0, 0, 4, 6); +arm64_sys_reg!(SYSREG_ICC_SGI1R_EL1, 3, 0, 5, 12, 11); +arm64_sys_reg!(SYSREG_ICC_SRE_EL1, 3, 0, 5, 12, 12); + +// ICC_CTLR_EL1 (https://developer.arm.com/documentation/ddi0595/2021-06/AArch64-Registers/ICC-CTLR-EL1--Interrupt-Controller-Control-Register--EL1-) +pub const ICC_CTLR_EL1_RSS_SHIFT: u32 = 18; +pub const ICC_CTLR_EL1_A3V_SHIFT: u32 = 15; +pub const ICC_CTLR_EL1_ID_BITS_SHIFT: u32 = 11; +pub const ICC_CTLR_EL1_PRI_BITS_SHIFT: u32 = 8; + +pub fn icc_reg_name(addr: u32) -> Option<&'static str> { + match addr { + SYSREG_ICC_IAR0_EL1 => Some("SYSREG_ICC_IAR0_EL1"), + SYSREG_ICC_IAR1_EL1 => Some("SYSREG_ICC_IAR1_EL1"), + SYSREG_ICC_EOIR0_EL1 => Some("SYSREG_ICC_EOIR0_EL1"), + SYSREG_ICC_EOIR1_EL1 => Some("SYSREG_ICC_EOIR1_EL1"), + SYSREG_ICC_AP0R0_EL1 => Some("SYSREG_ICC_AP0R0_EL1"), + SYSREG_ICC_AP0R1_EL1 => Some("SYSREG_ICC_AP0R1_EL1"), + SYSREG_ICC_AP0R2_EL1 => Some("SYSREG_ICC_AP0R2_EL1"), + SYSREG_ICC_AP0R3_EL1 => Some("SYSREG_ICC_AP0R3_EL1"), + SYSREG_ICC_AP1R0_EL1 => Some("SYSREG_ICC_AP1R0_EL1"), + SYSREG_ICC_AP1R1_EL1 => Some("SYSREG_ICC_AP1R1_EL1"), + SYSREG_ICC_AP1R2_EL1 => Some("SYSREG_ICC_AP1R2_EL1"), + SYSREG_ICC_AP1R3_EL1 => Some("SYSREG_ICC_AP1R3_EL1"), + SYSREG_ICC_ASGI1R_EL1 => Some("SYSREG_ICC_ASGI1R_EL1"), + SYSREG_ICC_BPR0_EL1 => Some("SYSREG_ICC_BPR0_EL1"), + SYSREG_ICC_BPR1_EL1 => Some("SYSREG_ICC_BPR1_EL1"), + SYSREG_ICC_CTLR_EL1 => Some("SYSREG_ICC_CTLR_EL1"), + SYSREG_ICC_DIR_EL1 => Some("SYSREG_ICC_DIR_EL1"), + SYSREG_ICC_HPPIR0_EL1 => Some("SYSREG_ICC_HPPIR0_EL1"), + SYSREG_ICC_HPPIR1_EL1 => Some("SYSREG_ICC_HPPIR1_EL1"), + SYSREG_ICC_IGRPEN0_EL1 => Some("SYSREG_ICC_IGRPEN0_EL1"), + SYSREG_ICC_IGRPEN1_EL1 => Some("SYSREG_ICC_IGRPEN1_EL1"), + SYSREG_ICC_PMR_EL1 => Some("SYSREG_ICC_PMR_EL1"), + SYSREG_ICC_SGI1R_EL1 => Some("SYSREG_ICC_SGI1R_EL1"), + SYSREG_ICC_SRE_EL1 => Some("SYSREG_ICC_SRE_EL1"), + _ => None, + } +} + +pub fn sysreg_op0(sysreg: u32) -> u32 { + (sysreg >> SYSREG_OP0_SHIFT) & SYSREG_OP0_MASK +} + +pub fn sysreg_op1(sysreg: u32) -> u32 { + (sysreg >> SYSREG_OP1_SHIFT) & SYSREG_OP1_MASK +} + +pub fn sysreg_op2(sysreg: u32) -> u32 { + (sysreg >> SYSREG_OP2_SHIFT) & SYSREG_OP2_MASK +} + +pub fn sysreg_crn(sysreg: u32) -> u32 { + (sysreg >> SYSREG_CRN_SHIFT) & SYSREG_CRN_MASK +} + +pub fn sysreg_crm(sysreg: u32) -> u32 { + (sysreg >> SYSREG_CRM_SHIFT) & SYSREG_CRM_MASK +} + +pub fn is_id_sysreg(reg: u32) -> bool { + sysreg_op0(reg) == 3 + && sysreg_op1(reg) == 0 + && sysreg_crn(reg) == 0 + && sysreg_crm(reg) >= 1 + && sysreg_crm(reg) < 8 +} diff --git a/src/devices/src/legacy/aarch64/gpio.rs b/src/devices/src/legacy/aarch64/gpio.rs index e39bda0e..a732c78f 100644 --- a/src/devices/src/legacy/aarch64/gpio.rs +++ b/src/devices/src/legacy/aarch64/gpio.rs @@ -10,7 +10,6 @@ use std::fmt; use std::os::fd::AsRawFd; use std::result; -use std::sync::{Arc, Mutex}; use polly::event_manager::{EventManager, Subscriber}; use utils::byte_order::{read_le_u32, write_le_u32}; @@ -18,7 +17,7 @@ use utils::epoll::{EpollEvent, EventSet}; use utils::eventfd::EventFd; use crate::bus::BusDevice; -use crate::legacy::Gic; +use crate::legacy::GicV3; const OFS_DATA: u64 = 0x400; // Data Register const GPIODIR: u64 = 0x400; // Direction Register @@ -74,7 +73,7 @@ pub struct Gpio { afsel: u32, // GPIO irq_field interrupt_evt: EventFd, - intc: Option>>, + intc: Option, irq_line: Option, shutdown_efd: EventFd, } @@ -98,11 +97,12 @@ impl Gpio { } } - pub fn set_intc(&mut self, intc: Arc>) { + pub fn set_intc(&mut self, intc: GicV3) { self.intc = Some(intc); } pub fn set_irq_line(&mut self, irq: u32) { + debug!("SET_IRQ_LINE (GPIO)={}", irq); self.irq_line = Some(irq); } @@ -166,7 +166,7 @@ impl Gpio { fn trigger_gpio_interrupt(&self) { if let Some(intc) = &self.intc { - intc.lock().unwrap().set_irq(self.irq_line.unwrap()); + intc.set_irq(self.irq_line.unwrap()); } else if let Err(e) = self.interrupt_evt.write(1) { error!("Failed to signal used queue: {:?}", e); } diff --git a/src/devices/src/legacy/aarch64/serial.rs b/src/devices/src/legacy/aarch64/serial.rs index fd884c20..b0532e3d 100644 --- a/src/devices/src/legacy/aarch64/serial.rs +++ b/src/devices/src/legacy/aarch64/serial.rs @@ -8,7 +8,6 @@ use std::collections::VecDeque; use std::fmt; -use std::sync::{Arc, Mutex}; use std::{io, result}; use polly::event_manager::{EventManager, Subscriber}; @@ -17,8 +16,7 @@ use utils::epoll::{EpollEvent, EventSet}; use utils::eventfd::EventFd; use crate::bus::BusDevice; -use crate::legacy::Gic; -use crate::legacy::ReadableFd; +use crate::legacy::{GicV3, ReadableFd}; /* Registers */ const UARTDR: u64 = 0; @@ -91,7 +89,7 @@ pub struct Serial { read_trigger: u32, out: Option>, input: Option>, - intc: Option>>, + intc: Option, irq_line: Option, } @@ -179,11 +177,12 @@ impl Serial { Self::new(interrupt_evt, None, None) } - pub fn set_intc(&mut self, intc: Arc>) { + pub fn set_intc(&mut self, intc: GicV3) { self.intc = Some(intc); } pub fn set_irq_line(&mut self, irq: u32) { + debug!("SET_IRQ_LINE (SERIAL)={}", irq); self.irq_line = Some(irq); } @@ -308,7 +307,7 @@ impl Serial { fn trigger_interrupt(&mut self) -> result::Result<(), io::Error> { if let Some(intc) = &self.intc { - intc.lock().unwrap().set_irq(self.irq_line.unwrap()); + intc.set_irq(self.irq_line.unwrap()); Ok(()) } else { self.interrupt_evt.write(1) diff --git a/src/devices/src/legacy/gic.rs b/src/devices/src/legacy/gic.rs deleted file mode 100644 index dbbaa2ca..00000000 --- a/src/devices/src/legacy/gic.rs +++ /dev/null @@ -1,356 +0,0 @@ -// Copyright 2021 Red Hat, Inc. -// SPDX-License-Identifier: Apache-2.0 - -use crossbeam_channel::Sender; -use std::collections::VecDeque; -use std::convert::TryInto; -use std::sync::{Arc, Mutex}; - -use arch::aarch64::gicv2::GICv2; -use arch::aarch64::layout::GTIMER_VIRT; -use hvf::{vcpu_request_exit, vcpu_set_vtimer_mask}; - -use crate::bus::BusDevice; - -const IRQ_NUM: u32 = 64; -const MAX_CPUS: u64 = 8; - -enum VcpuStatus { - Running, - Waiting, -} - -struct VcpuInfo { - status: VcpuStatus, - pending_irqs: VecDeque, - wfe_sender: Option>, -} - -pub struct VcpuList { - vcpus: Vec>, - vtimer_irq: u32, -} - -impl Default for VcpuList { - fn default() -> Self { - Self::new() - } -} - -impl VcpuList { - pub fn new() -> Self { - let mut vcpus = Vec::with_capacity(MAX_CPUS as usize); - for _ in 0..MAX_CPUS { - vcpus.push(Mutex::new(VcpuInfo { - status: VcpuStatus::Running, - pending_irqs: VecDeque::new(), - wfe_sender: None, - })); - } - - Self { - vcpus, - vtimer_irq: GTIMER_VIRT + 16, - } - } - - fn set_irq_common(&self, vcpuid: u8, irq_line: u32) { - let vcpu = &mut self.vcpus[vcpuid as usize].lock().unwrap(); - vcpu.pending_irqs.push_back(irq_line); - - match vcpu.status { - VcpuStatus::Waiting => { - vcpu.wfe_sender - .as_mut() - .unwrap() - .send(vcpuid as u32) - .unwrap(); - vcpu.status = VcpuStatus::Running; - } - VcpuStatus::Running => { - vcpu_request_exit(vcpuid as u64).unwrap(); - } - } - } - - pub fn set_vtimer_irq(&self, vcpuid: u64) { - assert!(vcpuid < MAX_CPUS); - self.set_irq_common(vcpuid as u8, self.vtimer_irq); - } - - pub fn register(&self, vcpuid: u64, wfe_sender: Sender) { - assert!(vcpuid < MAX_CPUS); - let vcpu = &mut self.vcpus[vcpuid as usize].lock().unwrap(); - vcpu.wfe_sender = Some(wfe_sender); - } - - pub fn should_wait(&self, vcpuid: u64) -> bool { - assert!(vcpuid < MAX_CPUS); - let vcpu = &mut self.vcpus[vcpuid as usize].lock().unwrap(); - if vcpu.pending_irqs.is_empty() { - vcpu.status = VcpuStatus::Waiting; - true - } else { - false - } - } - - pub fn has_pending_irq(&self, vcpuid: u64) -> bool { - assert!(vcpuid < MAX_CPUS); - let vcpu = &mut self.vcpus[vcpuid as usize].lock().unwrap(); - !vcpu.pending_irqs.is_empty() - } - - pub fn get_pending_irq(&self, vcpuid: u8) -> u32 { - let vcpu = &mut self.vcpus[vcpuid as usize].lock().unwrap(); - vcpu.pending_irqs.pop_front().unwrap_or(1023) - } -} - -pub struct Gic { - cpu_size: u64, - ctlr: u32, - irq_cfg: [u8; IRQ_NUM as usize], - vcpu_list: Arc, - vcpu_count: u8, - irq_target: [u8; IRQ_NUM as usize], - vtimer_irq: u32, -} - -impl Gic { - pub fn new(vcpu_list: Arc) -> Self { - Self { - cpu_size: GICv2::get_cpu_size(), - ctlr: 0, - irq_cfg: [0; IRQ_NUM as usize], - vcpu_list, - vcpu_count: 0, - irq_target: [0; IRQ_NUM as usize], - vtimer_irq: GTIMER_VIRT + 16, - } - } - - /// Get the address of the GICv2 distributor + CPU interface. - pub const fn get_addr() -> u64 { - // The CPU interface mapping starts before the distributor, so use it here. - GICv2::get_cpu_addr() - } - - /// Get the size of the GICv2 distributor + CPU interface. - pub const fn get_size() -> u64 { - GICv2::get_dist_size() + GICv2::get_cpu_size() - } - - pub fn add_vcpu(&mut self) { - self.vcpu_count += 1; - } - - fn set_sgi_irq(&self, vcpuid: u8, irq_line: u32) { - assert!(irq_line < 16); - self.vcpu_list.set_irq_common(vcpuid, irq_line); - } - - pub fn set_irq(&self, irq_line: u32) { - for vcpuid in 0..self.vcpu_count { - if (self.irq_target[irq_line as usize] & (1 << vcpuid)) == 0 { - continue; - } - - debug!("signaling irq={} to vcpuid={}", irq_line, vcpuid); - self.vcpu_list.set_irq_common(vcpuid, irq_line); - } - } - - fn handle_dist_read8(&mut self, vcpuid: u64, offset: u64, _data: &mut [u8]) { - debug!("GIC DIST read8 vcpuid={} offset=0x{:x}", vcpuid, offset); - } - - fn handle_dist_read16(&mut self, vcpuid: u64, offset: u64, _data: &mut [u8]) { - debug!("GIC DIST read16 vcpuid={} offset=0x{:x}", vcpuid, offset); - } - - fn handle_dist_read32(&mut self, vcpuid: u64, offset: u64, data: &mut [u8]) { - debug!("GIC DIST read32 vcpuid={} offset=0x{:x}", vcpuid, offset); - let mut val: u32 = 0; - match offset { - 0x0 => val = self.ctlr, - 0x4 => val = (IRQ_NUM / 32) - 1, - 0x800..=0x8c1 => val = 1 << vcpuid, - 0xc00..=0xf00 => { - let first_irq = (offset - 0xc00) * 4; - for i in 0..=15 { - let irq = first_irq + i; - val |= (self.irq_cfg[irq as usize] as u32) << ((15 - i) * 2); - } - } - _ => {} - } - for (i, b) in val.to_le_bytes().iter().enumerate() { - data[i] = *b; - } - debug!("data={:?}", data); - } - - fn handle_dist_write8(&mut self, vcpuid: u64, offset: u64, data: &[u8]) { - debug!( - "GIC DIST write8 vcpuid={} offset=0x{:x}, data={:?}", - vcpuid, offset, data - ); - } - - fn handle_dist_write16(&mut self, vcpuid: u64, offset: u64, data: &[u8]) { - debug!( - "GIC DIST write16 vcpuid={} offset=0x{:x}, data={:?}", - vcpuid, offset, data - ); - } - - fn handle_dist_write32(&mut self, vcpuid: u64, offset: u64, data: &[u8]) { - debug!( - "GIC DIST write32 vcpuid={} offset=0x{:x}, data={:?}", - vcpuid, offset, data - ); - let val: u32 = u32::from_le_bytes(data.try_into().unwrap()); - match offset { - 0x0 => self.ctlr = val, - 0x800..=0xbf8 => { - let first_irq = offset - 0x800; - for i in 0..=3 { - let irq = first_irq + i; - let cpumask: u8 = ((val >> ((3 - i) * 8)) & 0xf) as u8; - debug!("Configuring irq {} to cpumask {}", irq, cpumask); - self.irq_target[irq as usize] = cpumask; - } - } - 0xc00..=0xeff => { - let first_irq = (offset - 0xc00) * 4; - for i in 0..=15 { - let irq = first_irq + i; - let cfg: u8 = ((val >> ((15 - i) * 2)) & 0x3) as u8; - debug!("Configuring irq {} to cfg {}", irq, cfg); - self.irq_cfg[irq as usize] = cfg; - } - } - 0xf00 => { - debug!("SGI requested by vcpuid={}", vcpuid); - let irq = val & 0xf; - let filter = val & 0x3000000; - match filter { - 0b01 => { - for cpu in 0..self.vcpu_count { - if cpu != vcpuid as u8 { - self.set_sgi_irq(cpu, irq); - } - } - } - 0b10 => self.set_sgi_irq(vcpuid as u8, irq), - _ => { - let target_cpus = (val & 0xff0000) >> 16; - for vcpuid in 0..self.vcpu_count { - if (target_cpus & (1 << vcpuid)) != 0 { - debug!("signal irq={} to vcpu: {}", irq, vcpuid); - self.set_sgi_irq(vcpuid, irq); - } - } - } - } - } - _ => {} - } - } - - fn handle_cpu_read8(&mut self, vcpuid: u64, offset: u64, _data: &mut [u8]) { - debug!("GIC CPU read8 vcpuid={} offset=0x{:x}", vcpuid, offset); - } - - fn handle_cpu_read16(&mut self, vcpuid: u64, offset: u64, _data: &mut [u8]) { - debug!("GIC CPU read16 vcpuid={} offset=0x{:x}", vcpuid, offset); - } - - fn handle_cpu_read32(&mut self, vcpuid: u64, offset: u64, data: &mut [u8]) { - debug!("GIC CPU read32 vcpuid={} offset=0x{:x}", vcpuid, offset); - assert!(vcpuid < MAX_CPUS); - - let mut val = 0; - if offset == 0xc { - val = self.vcpu_list.get_pending_irq(vcpuid as u8); - } - for (i, b) in val.to_le_bytes().iter().enumerate() { - data[i] = *b; - } - debug!( - "data={:?} val={}", - data, - u32::from_le_bytes((data as &[u8]).try_into().unwrap()) - ); - } - - fn handle_cpu_write8(&mut self, vcpuid: u64, offset: u64, data: &[u8]) { - debug!( - "GIC CPU write8 vcpuid={} offset=0x{:x}, data={:?}", - vcpuid, offset, data - ); - } - - fn handle_cpu_write16(&mut self, vcpuid: u64, offset: u64, data: &[u8]) { - debug!( - "GIC CPU write16 vcpuid={} offset=0x{:x}, data={:?}", - vcpuid, offset, data - ); - } - - fn handle_cpu_write32(&mut self, vcpuid: u64, offset: u64, data: &[u8]) { - debug!( - "GIC CPU write32 vcpuid={} offset=0x{:x}, data={:?}", - vcpuid, offset, data - ); - let val: u32 = u32::from_le_bytes(data.try_into().unwrap()); - - if offset == 0x10 { - let irq = val & 0x3FF; - if irq < IRQ_NUM && irq == self.vtimer_irq { - vcpu_set_vtimer_mask(vcpuid, false).unwrap(); - } - } - } -} - -impl BusDevice for Gic { - fn read(&mut self, vcpuid: u64, offset: u64, data: &mut [u8]) { - if offset >= self.cpu_size { - let offset = offset - self.cpu_size; - match data.len() { - 1 => self.handle_dist_read8(vcpuid, offset, data), - 2 => self.handle_dist_read16(vcpuid, offset, data), - 4 => self.handle_dist_read32(vcpuid, offset, data), - _ => panic!("GIC DIST unsupported read size"), - } - } else { - match data.len() { - 1 => self.handle_cpu_read8(vcpuid, offset, data), - 2 => self.handle_cpu_read16(vcpuid, offset, data), - 4 => self.handle_cpu_read32(vcpuid, offset, data), - _ => panic!("GIC CPU unsupported read size"), - } - } - } - - fn write(&mut self, vcpuid: u64, offset: u64, data: &[u8]) { - if offset >= self.cpu_size { - let offset = offset - self.cpu_size; - match data.len() { - 1 => self.handle_dist_write8(vcpuid, offset, data), - 2 => self.handle_dist_write16(vcpuid, offset, data), - 4 => self.handle_dist_write32(vcpuid, offset, data), - _ => panic!("GIC DIST unsupported read size"), - } - } else { - match data.len() { - 1 => self.handle_cpu_write8(vcpuid, offset, data), - 2 => self.handle_cpu_write16(vcpuid, offset, data), - 4 => self.handle_cpu_write32(vcpuid, offset, data), - _ => panic!("GIC CPU unsupported write size"), - } - } - } -} diff --git a/src/devices/src/legacy/gicv3.rs b/src/devices/src/legacy/gicv3.rs new file mode 100644 index 00000000..2d033f69 --- /dev/null +++ b/src/devices/src/legacy/gicv3.rs @@ -0,0 +1,486 @@ +use std::convert::TryInto; +use std::sync::{Arc, Mutex}; + +use arch::aarch64::gicv3::GICv3; + +use crate::bus::BusDevice; +use crate::legacy::VcpuList; + +const IRQ_NUM: u32 = 288; +const MAXIRQ: u32 = 1020; +const BITMAP_SZ: usize = (MAXIRQ as usize + 31) / 32; + +const GIC_INTERNAL: u32 = 32; + +const GICD_CTLR: u64 = 0x0000; +const GICD_TYPER: u64 = 0x0004; +const GICD_IIDR: u64 = 0x0008; +const GICD_STATUSR: u64 = 0x0010; +const GICD_IGROUPR: u64 = 0x0080; +const GICD_ISENABLER: u64 = 0x0100; +const GICD_ICENABLER: u64 = 0x0180; +const GICD_ISPENDR: u64 = 0x0200; +const GICD_ICPENDR: u64 = 0x0280; +const GICD_ISACTIVER: u64 = 0x0300; +const GICD_ICACTIVER: u64 = 0x0380; +const GICD_IPRIORITYR: u64 = 0x0400; +const GICD_ITARGETSR: u64 = 0x0800; +const GICD_ICFGR: u64 = 0x0C00; +const GICD_SGIR: u64 = 0x0F00; +const GICD_IROUTER: u64 = 0x6000; +const GICD_IDREGS: u64 = 0xFFD0; + +/* GICD_CTLR fields */ +const GICD_CTLR_EN_GRP0: u32 = 1 << 0; +const GICD_CTLR_EN_GRP1NS: u32 = 1 << 1; /* GICv3 5.3.20 */ +/* Bit 4 is ARE if the system doesn't support TrustZone, ARE_S otherwise */ +const GICD_CTLR_ARE: u32 = 1 << 4; +const GICD_CTLR_DS: u32 = 1 << 6; + +/* + * Redistributor registers, offsets from RD_base + */ +const GICR_CTLR: u64 = 0x0000; +const GICR_TYPER: u64 = 0x0008; +const GICR_WAKER: u64 = 0x0014; +const GICR_IDREGS: u64 = 0xFFD0; + +const GICR_WAKER_PROCESSOR_SLEEP: u32 = 1 << 1; +const GICR_WAKER_CHILDREN_ASLEEP: u32 = 1 << 2; + +/* + * Redistributor frame offsets from RD_base + */ +const GICR_SGI_OFFSET: u64 = 0x10000; + +/* SGI and PPI Redistributor registers, offsets from RD_base */ +const GICR_IGROUPR0: u64 = GICR_SGI_OFFSET + 0x0080; +const GICR_ISENABLER0: u64 = GICR_SGI_OFFSET + 0x0100; +const GICR_ICENABLER0: u64 = GICR_SGI_OFFSET + 0x0180; +const GICR_ICACTIVER0: u64 = GICR_SGI_OFFSET + 0x0380; +const GICR_IPRIORITYR: u64 = GICR_SGI_OFFSET + 0x0400; +const GICR_ICFGR1: u64 = GICR_SGI_OFFSET + 0x0C04; + +/* Distributor register fields */ +// GICD_TYPER (https://developer.arm.com/documentation/ddi0601/2020-12/External-Registers/GICD-TYPER--Interrupt-Controller-Type-Register?lang=en) +const GICD_TYPER_RSS_SHIFT: u64 = 26; +const GICD_TYPER_NO1N_SHIFT: u64 = 25; +const GICD_TYPER_A3V_SHIFT: u64 = 24; +const GICD_TYPER_ID_BITS_SHIFT: u64 = 19; +const GICD_TYPER_LPIS_SHIFT: u64 = 17; +const GICD_TYPER_IT_LINES_NUMBER_SHIFT: u64 = 0; + +/* Redistributor register fields */ +// GICR_TYPER (https://developer.arm.com/documentation/ddi0601/2020-12/External-Registers/GICR-TYPER--Redistributor-Type-Register?lang=en) +const GICR_TYPER_AFFINITY_VALUE: u64 = 32; +const GICR_TYPER_COMMON_LPI_AFF_SHIFT: u64 = 24; +const GICR_TYPER_PROCESSOR_NUMBER_SHIFT: u64 = 8; +const GICR_TYPER_LAST_SHIFT: u64 = 4; + +/* CoreSight PIDR0 values for ARM GICv3 implementations */ +const GICV3_PIDR0_DIST: u8 = 0x92; +const GICV3_PIDR0_REDIST: u8 = 0x93; + +#[derive(Clone)] +pub struct GicV3 { + gic: Arc>, +} + +impl GicV3 { + pub fn new(vcpu_list: Arc) -> Self { + Self { + gic: Arc::new(Mutex::new(GicV3Internal::new(vcpu_list))), + } + } + + pub fn get_mmio_addr(&self) -> u64 { + self.gic.lock().unwrap().get_mmio_addr() + } + + pub fn get_mmio_size(&self) -> u64 { + self.gic.lock().unwrap().get_mmio_size() + } + + pub fn set_irq(&self, irq_line: u32) { + self.gic.lock().unwrap().set_irq(irq_line) + } + + pub fn as_device(&self) -> Arc> { + self.gic.clone() + } +} + +impl BusDevice for GicV3 { + fn read(&mut self, vcpuid: u64, offset: u64, data: &mut [u8]) { + self.gic.lock().unwrap().read(vcpuid, offset, data) + } + + fn write(&mut self, vcpuid: u64, offset: u64, data: &[u8]) { + self.gic.lock().unwrap().write(vcpuid, offset, data) + } +} + +struct GicV3Internal { + gicd_ctlr: u32, + vcpu_list: Arc, + revision: u8, + edge_trigger: [u32; BITMAP_SZ], + gicr_waker: u32, + gicd_irouter: [u64; MAXIRQ as usize], +} + +impl GicV3Internal { + pub fn new(vcpu_list: Arc) -> Self { + Self { + gicd_ctlr: GICD_CTLR_DS | GICD_CTLR_ARE, + vcpu_list, + revision: 3, + edge_trigger: [0; BITMAP_SZ], + gicr_waker: GICR_WAKER_PROCESSOR_SLEEP | GICR_WAKER_CHILDREN_ASLEEP, + gicd_irouter: [0; MAXIRQ as usize], + } + } + + pub fn get_mmio_addr(&self) -> u64 { + GICv3::compute_redists_addr(self.vcpu_list.get_cpu_count()) + } + + pub fn get_mmio_size(&self) -> u64 { + GICv3::get_dist_size() + GICv3::compute_redists_size(self.vcpu_list.get_cpu_count()) + } + + pub fn set_irq(&self, irq_line: u32) { + assert!(irq_line < MAXIRQ, "[GICv3] intid out of range"); + // TODO(p1-0tr): extract full MPID, but for now Aff0 will do + let mpid = self.gicd_irouter[irq_line as usize] & 0xff; + self.vcpu_list.set_irq_common(mpid, irq_line); + } + + fn handle_dist_read32(&self, _vcpuid: u64, offset: u64, data: &mut [u8]) { + let mut val: u32 = 0; + match offset { + GICD_CTLR => val = self.gicd_ctlr, + GICD_TYPER => { + let itlinesnumber = (IRQ_NUM / 32) - 1; + val = (1 << GICD_TYPER_RSS_SHIFT) + | (1 << GICD_TYPER_NO1N_SHIFT) + | (1 << GICD_TYPER_A3V_SHIFT) + | (1 << GICD_TYPER_LPIS_SHIFT) + | (0xf << GICD_TYPER_ID_BITS_SHIFT) + | (itlinesnumber << GICD_TYPER_IT_LINES_NUMBER_SHIFT); + } + GICD_IIDR => val = 0x43b, + GICD_STATUSR => {} + _ if (GICD_IGROUPR..GICD_IGROUPR + 0x7f).contains(&offset) => {} + _ if (GICD_ISENABLER..GICD_ISENABLER + 0x7f).contains(&offset) => {} + _ if (GICD_ICENABLER..GICD_ICENABLER + 0x7f).contains(&offset) => {} + _ if (GICD_ISPENDR..GICD_ISPENDR + 0x7f).contains(&offset) => {} + _ if (GICD_ICPENDR..GICD_ICPENDR + 0x7f).contains(&offset) => {} + _ if (GICD_ISACTIVER..GICD_ISACTIVER + 0x7f).contains(&offset) => {} + _ if (GICD_ICACTIVER..GICD_ICACTIVER + 0x7f).contains(&offset) => {} + _ if (GICD_IPRIORITYR..GICD_IPRIORITYR + 0x3ff).contains(&offset) => {} + _ if (GICD_ITARGETSR..GICD_ITARGETSR + 0x3ff).contains(&offset) => { + panic!("[GICv3] only affinity routing is implemented"); + } + _ if (GICD_ICFGR..GICD_ICFGR + 0xff).contains(&offset) => { + let irq = ((offset - GICD_ICFGR) * 4) as u32; + if !(GIC_INTERNAL..IRQ_NUM).contains(&irq) { + val = 0; + } else { + let mut value = self.edge_trigger[((irq & !0x1f) / 32) as usize]; + value = extract32(value, if (irq & 0x1f) != 0 { 16 } else { 0 }, 16); + value = half_shuffle32(value) << 1; + val = value; + } + } + _ if (GICD_IDREGS..GICD_IDREGS + 0x2f).contains(&offset) => { + /* Return the value of the CoreSight ID register at the specified + * offset from the first ID register (as found in the distributor + * and redistributor register banks). + * These values indicate an ARM implementation of a GICv3 or v4. + */ + let gicd_ids: [u8; 12] = [ + 0x44, 0x00, 0x00, 0x00, 0x92, 0xB4, 0x0B, 0x00, 0x0D, 0xF0, 0x05, 0xB1, + ]; + let mut id: u32; + let regoffset = (offset - GICD_IDREGS) / 4; + + if regoffset == 4 { + id = GICV3_PIDR0_DIST as u32; + } else { + id = gicd_ids[regoffset as usize] as u32; + if regoffset == 6 { + /* PIDR2 bits [7:4] are the GIC architecture revision */ + id |= (self.revision as u32) << 4; + } + } + + val = id; + } + GICD_SGIR => {} + 0xc => { + // invalid guest read on Qemu + } + _ => panic!("Unknown GIC DIST read32 offset=0x{:x}", offset), + } + for (i, b) in val.to_le_bytes().iter().enumerate() { + data[i] = *b; + } + debug!("[GICv3] -> read32 DIST offset={} val={}", offset, val); + } + + fn handle_dist_write32(&mut self, _vcpuid: u64, offset: u64, data: &[u8]) { + debug!( + "[GICv3] write32 DIST offset={} val={}", + offset, + u32::from_le_bytes(data.try_into().unwrap()) + ); + + let val: u32 = u32::from_le_bytes(data.try_into().unwrap()); + match offset { + GICD_CTLR => { + let mask = GICD_CTLR_EN_GRP0 | GICD_CTLR_EN_GRP1NS; + self.gicd_ctlr = (self.gicd_ctlr & !mask) | (val & mask); + } + _ if (GICD_IGROUPR..GICD_IGROUPR + 0x7f).contains(&offset) => {} + _ if (GICD_ISENABLER..GICD_ISENABLER + 0x7f).contains(&offset) => {} + _ if (GICD_ICENABLER..GICD_ICENABLER + 0x7f).contains(&offset) => {} + _ if (GICD_ISPENDR..GICD_ISPENDR + 0x7f).contains(&offset) => {} + _ if (GICD_ICPENDR..GICD_ICPENDR + 0x7f).contains(&offset) => {} + _ if (GICD_ISACTIVER..GICD_ISACTIVER + 0x7f).contains(&offset) => {} + _ if (GICD_ICACTIVER..GICD_ICACTIVER + 0x7f).contains(&offset) => {} + _ if (GICD_IPRIORITYR..GICD_IPRIORITYR + 0x3ff).contains(&offset) => {} + _ if (GICD_ITARGETSR..GICD_ITARGETSR + 0x3ff).contains(&offset) => { + panic!("[GICv3] only affinity routing is implemented"); + } + _ if (GICD_ICFGR..GICD_ICFGR + 0xff).contains(&offset) => { + /* Here only the odd bits are used; even bits are RES0 */ + let irq = ((offset - GICD_ICFGR) * 4) as u32; + let mut mask: u32; + + if !(GIC_INTERNAL..IRQ_NUM).contains(&irq) { + return; + } + + /* Since our edge_trigger bitmap is one bit per irq, our input + * 32-bits will compress down into 16 bits which we need + * to write into the bitmap. + */ + let mut value = half_unshuffle32(val >> 1); + mask = 0xFFFFFFFFu32; + if (irq as u64) & 0x1fu64 != 0u64 { + value <<= 16; + mask &= 0xffff0000u32; + } else { + mask &= 0xffff; + } + let idx = (irq & !0x1f) / 32; + let oldval = self.edge_trigger[idx as usize]; + value = (oldval & !mask) | (value & mask); + self.edge_trigger[idx as usize] = value; + } + _ => panic!("Unknown GIC DIST write32 offset=0x{:x}", offset), + } + } + + fn handle_dist_write64(&mut self, _vcpuid: u64, offset: u64, data: &[u8]) { + let val = u64::from_le_bytes(data.try_into().unwrap()); + debug!( + "[GICv3] write64 DIST offset=0x{:x} value=0x{:x}", + offset, val + ); + match offset { + _ if (GICD_IROUTER..GICD_IROUTER + 0x1fdf).contains(&offset) => { + let intid = ((offset - GICD_IROUTER) / 8) as usize; + self.gicd_irouter[intid] = val; + } + _ => panic!("Unknown GIC DIST write64 offset=0x{:x}", offset), + } + } + + fn handle_redist_read32(&self, _vcpuid: u64, offset: u64, data: &mut [u8]) { + let mut val: u32 = 0; + match offset { + GICR_CTLR => { + val = 2; + } + GICD_IIDR => { + val = 0x43b; + } + GICD_STATUSR => {} + GICR_WAKER => { + val = self.gicr_waker; + } + _ if (GICR_IPRIORITYR..GICR_IPRIORITYR + 0x3ff).contains(&offset) => {} + _ if (GICR_IDREGS..GICR_IDREGS + 0x2f).contains(&offset) => { + /* Return the value of the CoreSight ID register at the specified + * offset from the first ID register (as found in the distributor + * and redistributor register banks). + * These values indicate an ARM implementation of a GICv3 or v4. + */ + let gicd_ids: [u8; 12] = [ + 0x44, 0x00, 0x00, 0x00, 0x92, 0xB4, 0x0B, 0x00, 0x0D, 0xF0, 0x05, 0xB1, + ]; + let mut id: u32; + let regoffset = (offset - GICR_IDREGS) / 4; + + if regoffset == 4 { + id = GICV3_PIDR0_REDIST as u32; + } else { + id = gicd_ids[regoffset as usize] as u32; + if regoffset == 6 { + /* PIDR2 bits [7:4] are the GIC architecture revision */ + id |= (self.revision as u32) << 4; + } + } + + val = id; + } + GICR_ICFGR1 => {} + _ => panic!("Unknown GIC REDIST read32 offset=0x{:x}", offset), + } + for (i, b) in val.to_le_bytes().iter().enumerate() { + data[i] = *b; + } + + debug!("[GICv3] -> read32 REDIST offset={} val={}", offset, val); + } + + fn handle_redist_read64(&self, vcpuid: u64, offset: u64, data: &mut [u8]) { + let val = match offset { + GICR_TYPER => { + let mut typer = (vcpuid << GICR_TYPER_AFFINITY_VALUE) + | (1 << GICR_TYPER_COMMON_LPI_AFF_SHIFT) + | (vcpuid << GICR_TYPER_PROCESSOR_NUMBER_SHIFT); + // Assume we have one redistributor range, set last bit for last CPU. + if vcpuid == self.vcpu_list.get_cpu_count() - 1 { + typer |= 1u64 << GICR_TYPER_LAST_SHIFT; + } + typer + } + _ => panic!("Unknown GIC REDIST read64 offset=0x{:x}", offset), + }; + for (i, b) in val.to_le_bytes().iter().enumerate() { + data[i] = *b; + } + + debug!("[GICv3] -> read64 REDIST offset={} val={}", offset, val); + } + + fn handle_redist_write32(&mut self, _vcpuid: u64, offset: u64, data: &[u8]) { + debug!( + "[GICv3] write32 REDIST offset={} val={}", + offset, + u32::from_le_bytes(data.try_into().unwrap()) + ); + + let mut val: u32 = u32::from_le_bytes(data.try_into().unwrap()); + match offset { + GICR_WAKER => { + val &= GICR_WAKER_PROCESSOR_SLEEP; + if (val & GICR_WAKER_PROCESSOR_SLEEP) != 0 { + val |= GICR_WAKER_CHILDREN_ASLEEP; + } + self.gicr_waker = val; + } + GICR_IGROUPR0 | GICR_ISENABLER0 | GICR_ICENABLER0 | GICR_ICACTIVER0 => {} + _ if (GICR_IPRIORITYR..GICR_IPRIORITYR + 0x1f).contains(&offset) => {} + _ => panic!("Unknown GIC REDIST write32 offset=0x{:x}", offset), + } + } +} + +impl BusDevice for GicV3Internal { + fn read(&mut self, vcpuid: u64, offset: u64, data: &mut [u8]) { + if offset >= GICv3::compute_redists_size(self.vcpu_list.get_cpu_count()) { + let offset = offset - GICv3::compute_redists_size(self.vcpu_list.get_cpu_count()); + match data.len() { + 1 => panic!("GIC DIST read8 vcpuid={} offset=0x{:x}", vcpuid, offset), + 2 => panic!("GIC DIST read16 vcpuid={} offset=0x{:x}", vcpuid, offset), + 4 => self.handle_dist_read32(vcpuid, offset, data), + 8 => panic!("GIC DIST read64 vcpuid={} offset=0x{:x}", vcpuid, offset), + _ => panic!("GIC DIST unsupported read size"), + } + } else { + let vcpuid = offset / GICv3::get_redist_size(); + let offset = offset % GICv3::get_redist_size(); + + match data.len() { + 1 => panic!("GIC REDIST read8 vcpuid={} offset=0x{:x}", vcpuid, offset), + 2 => panic!("GIC REDIST read16 vcpuid={} offset=0x{:x}", vcpuid, offset), + 4 => self.handle_redist_read32(vcpuid, offset, data), + 8 => self.handle_redist_read64(vcpuid, offset, data), + _ => panic!("GIC REDIST unsupported read size"), + } + } + } + + fn write(&mut self, vcpuid: u64, offset: u64, data: &[u8]) { + if offset >= GICv3::compute_redists_size(self.vcpu_list.get_cpu_count()) { + let offset = offset - GICv3::compute_redists_size(self.vcpu_list.get_cpu_count()); + match data.len() { + 1 => panic!( + "GIC DIST write8 vcpuid={} offset=0x{:x}, data={:?}", + vcpuid, offset, data + ), + 2 => panic!( + "GIC DIST write16 vcpuid={} offset=0x{:x}, data={:?}", + vcpuid, offset, data + ), + 4 => self.handle_dist_write32(vcpuid, offset, data), + 8 => self.handle_dist_write64(vcpuid, offset, data), + _ => panic!("GIC DIST unsupported read size"), + } + } else { + let vcpuid = offset / GICv3::get_redist_size(); + let offset = offset % GICv3::get_redist_size(); + + match data.len() { + 1 => panic!( + "GIC REDIST write8 vcpuid={} offset=0x{:x}, data={:?}", + vcpuid, offset, data + ), + 2 => panic!( + "GIC REDIST write16 vcpuid={} offset=0x{:x}, data={:?}", + vcpuid, offset, data + ), + 4 => self.handle_redist_write32(vcpuid, offset, data), + 8 => panic!( + "GIC REDIST write64 vcpuid={} offset=0x{:x}, data={:?}", + vcpuid, offset, data + ), + _ => panic!("GIC REDIST unsupported write size"), + } + } + } +} + +fn half_shuffle32(val: u32) -> u32 { + /* This algorithm is from _Hacker's Delight_ section 7-2 "Shuffling Bits". + * It ignores any bits set in the top half of the input. + */ + let mut x = val; + x = ((x & 0xFF00) << 8) | (x & 0x00FF); + x = ((x << 4) | x) & 0x0F0F0F0F; + x = ((x << 2) | x) & 0x33333333; + x = ((x << 1) | x) & 0x55555555; + x +} + +fn half_unshuffle32(val: u32) -> u32 { + /* This algorithm is from _Hacker's Delight_ section 7-2 "Shuffling Bits". + * where it is called an inverse half shuffle. + */ + let mut x = val; + x &= 0x55555555; + x = ((x >> 1) | x) & 0x33333333; + x = ((x >> 2) | x) & 0x0F0F0F0F; + x = ((x >> 4) | x) & 0x00FF00FF; + x = ((x >> 8) | x) & 0x0000FFFF; + x +} + +fn extract32(value: u32, start: u32, length: u32) -> u32 { + assert!(length <= 32 - start); + (value >> start) & ((!0u32) >> (32 - length)) +} diff --git a/src/devices/src/legacy/mod.rs b/src/devices/src/legacy/mod.rs index c1912d43..ee04fb25 100644 --- a/src/devices/src/legacy/mod.rs +++ b/src/devices/src/legacy/mod.rs @@ -6,11 +6,12 @@ // found in the THIRD-PARTY file. #[cfg(target_os = "macos")] -#[allow(non_camel_case_types)] -mod gic; +mod gicv3; mod i8042; #[cfg(target_arch = "aarch64")] mod rtc_pl031; +#[cfg(target_os = "macos")] +mod vcpu; #[cfg(target_arch = "x86_64")] mod x86_64; #[cfg(target_arch = "x86_64")] @@ -23,7 +24,7 @@ use aarch64::gpio; use aarch64::serial; #[cfg(target_os = "macos")] -pub use self::gic::{Gic, VcpuList}; +pub use self::gicv3::GicV3; #[cfg(target_arch = "aarch64")] pub use self::gpio::Gpio; pub use self::i8042::Error as I8042DeviceError; @@ -31,6 +32,8 @@ pub use self::i8042::I8042Device; #[cfg(target_arch = "aarch64")] pub use self::rtc_pl031::RTC; pub use self::serial::Serial; +#[cfg(target_os = "macos")] +pub use self::vcpu::VcpuList; // Cannot use multiple types as bounds for a trait object, so we define our own trait // which is a composition of the desired bounds. In this case, io::Read and AsRawFd. @@ -39,9 +42,10 @@ pub use self::serial::Serial; pub trait ReadableFd: std::io::Read + std::os::fd::AsRawFd {} #[cfg(target_os = "linux")] -pub struct Gic {} +#[derive(Clone)] +pub struct GicV3 {} #[cfg(target_os = "linux")] -impl Gic { - pub fn set_irq(&mut self, _irq: u32) {} +impl GicV3 { + pub fn set_irq(&self, _irq: u32) {} } diff --git a/src/devices/src/legacy/vcpu.rs b/src/devices/src/legacy/vcpu.rs new file mode 100644 index 00000000..965a36aa --- /dev/null +++ b/src/devices/src/legacy/vcpu.rs @@ -0,0 +1,220 @@ +use crossbeam_channel::Sender; +use std::collections::VecDeque; +use std::sync::Mutex; + +use arch::aarch64::layout::VTIMER_IRQ; +use arch::aarch64::sysreg::*; +use hvf::{vcpu_request_exit, Vcpus}; + +// See https://developer.arm.com/documentation/ddi0595/2020-12/AArch64-Registers/ICC-IAR0-EL1--Interrupt-Controller-Interrupt-Acknowledge-Register-0 +const GIC_INTID_SPURIOUS: u32 = 1023; + +enum VcpuStatus { + Running, + Waiting, +} + +struct PerCPUInterruptControllerState { + vcpuid: u64, + status: VcpuStatus, + pending_irqs: VecDeque, + wfe_sender: Option>, +} + +impl PerCPUInterruptControllerState { + fn set_irq_common(&mut self, irq: u32) { + debug!( + "[GICv3] SET_IRQ_COMMON vcpuid={}, irq_line={}", + self.vcpuid, irq + ); + self.pending_irqs.push_back(irq); + + match self.status { + VcpuStatus::Waiting => { + self.wfe_sender + .as_mut() + .unwrap() + .send(self.vcpuid as u32) + .unwrap(); + self.status = VcpuStatus::Running; + } + VcpuStatus::Running => { + vcpu_request_exit(self.vcpuid).unwrap(); + } + } + } + + fn should_wait(&mut self) -> bool { + if self.pending_irqs.is_empty() { + self.status = VcpuStatus::Waiting; + return true; + } + false + } + + fn has_pending_irq(&self) -> bool { + !self.pending_irqs.is_empty() + } + + fn get_pending_irq(&mut self) -> u32 { + self.pending_irqs.pop_front().unwrap_or(GIC_INTID_SPURIOUS) + } +} + +pub struct VcpuList { + cpu_count: u64, + vcpus: Vec>, +} + +impl VcpuList { + pub fn new(cpu_count: u64) -> Self { + let mut vcpus = Vec::with_capacity(cpu_count as usize); + for vcpuid in 0..cpu_count { + vcpus.push(Mutex::new(PerCPUInterruptControllerState { + vcpuid, + status: VcpuStatus::Running, + pending_irqs: VecDeque::new(), + wfe_sender: None, + })); + } + + Self { cpu_count, vcpus } + } + + pub fn get_cpu_count(&self) -> u64 { + self.cpu_count + } + + pub fn set_irq_common(&self, vcpuid: u64, irq: u32) { + assert!(vcpuid < self.cpu_count); + self.vcpus[vcpuid as usize] + .lock() + .unwrap() + .set_irq_common(irq); + } + + pub fn set_sgi_irq(&self, vcpuid: u64, irq: u32) { + assert!(vcpuid < self.cpu_count); + assert!(irq < 16); + self.vcpus[vcpuid as usize] + .lock() + .unwrap() + .set_irq_common(irq); + } + + pub fn register(&self, vcpuid: u64, wfe_sender: Sender) { + assert!(vcpuid < self.cpu_count); + self.vcpus[vcpuid as usize].lock().unwrap().wfe_sender = Some(wfe_sender); + } +} + +impl Vcpus for VcpuList { + fn set_vtimer_irq(&self, vcpuid: u64) { + assert!(vcpuid < self.cpu_count); + self.vcpus[vcpuid as usize] + .lock() + .unwrap() + .set_irq_common(VTIMER_IRQ); + } + + fn should_wait(&self, vcpuid: u64) -> bool { + assert!(vcpuid < self.cpu_count); + self.vcpus[vcpuid as usize].lock().unwrap().should_wait() + } + + fn has_pending_irq(&self, vcpuid: u64) -> bool { + assert!(vcpuid < self.cpu_count); + self.vcpus[vcpuid as usize] + .lock() + .unwrap() + .has_pending_irq() + } + + fn get_pending_irq(&self, vcpuid: u64) -> u32 { + assert!(vcpuid < self.cpu_count); + self.vcpus[vcpuid as usize] + .lock() + .unwrap() + .get_pending_irq() + } + + fn handle_sysreg_read(&self, vcpuid: u64, reg: u32) -> Option { + assert!(vcpuid < self.cpu_count); + + if is_id_sysreg(reg) { + return Some(0); + } + + match reg { + SYSREG_ICC_IAR1_EL1 => Some( + self.vcpus[vcpuid as usize] + .lock() + .unwrap() + .get_pending_irq() as u64, + ), + SYSREG_ICC_PMR_EL1 => Some(0), + SYSREG_ICC_CTLR_EL1 => Some( + (1 << ICC_CTLR_EL1_RSS_SHIFT) + | (1 << ICC_CTLR_EL1_A3V_SHIFT) + | (1 << ICC_CTLR_EL1_ID_BITS_SHIFT) + | (4 << ICC_CTLR_EL1_PRI_BITS_SHIFT), + ), + _ => None, + } + } + + fn handle_sysreg_write(&self, vcpuid: u64, reg: u32, val: u64) -> bool { + assert!(vcpuid < self.cpu_count); + + if is_id_sysreg(reg) { + return true; + } + + match reg { + SYSREG_ICC_SGI1R_EL1 => { + let target_list = val & 0xffff; + let intid = ((val >> 24) & 0xf) as u32; + let irm = (val & (1 << 40)) >> 40; + let is_broadcast = irm == 1; + let aff3aff2aff1 = val & ((0xff << 48) | (0xff << 32) | (0xff << 16)); + let rs = (val & (0xf << 44)) >> 44; + + debug!( + "vCPU {} GenerateSoftwareInterrupt={} (0x{:x})", + vcpuid, intid, val + ); + + // A flat core hierarchy should be good enough, but if we ever start using + // Aff[123] MPIDR fields (currently MPID is configured via DT), GICv3 support + // will need to be added. + assert_eq!( + aff3aff2aff1, 0, + "[GICv3] only flat core hierarchy supported for now" + ); + + assert!( + !is_broadcast, + "[GICv3] SGI broadcast is not implemented yet" + ); + + // for each core in target list + for target_id in 0u64..=15u64 { + if (target_list >> target_id) & 1 == 1 { + self.set_sgi_irq(rs * 16 + target_id, intid) + } + } + + true + } + SYSREG_ICC_EOIR1_EL1 + | SYSREG_ICC_IGRPEN1_EL1 + | SYSREG_ICC_PMR_EL1 + | SYSREG_ICC_BPR1_EL1 + | SYSREG_ICC_CTLR_EL1 + | SYSREG_ICC_AP1R0_EL1 + | SYSREG_OSLAR_EL1 + | SYSREG_OSDLR_EL1 => true, + _ => false, + } + } +} diff --git a/src/devices/src/virtio/balloon/device.rs b/src/devices/src/virtio/balloon/device.rs index ba28b23f..7669e884 100644 --- a/src/devices/src/virtio/balloon/device.rs +++ b/src/devices/src/virtio/balloon/device.rs @@ -3,7 +3,7 @@ use std::convert::TryInto; use std::io::Write; use std::result; use std::sync::atomic::{AtomicUsize, Ordering}; -use std::sync::{Arc, Mutex}; +use std::sync::Arc; use utils::eventfd::EventFd; use vm_memory::{ByteValued, GuestMemory, GuestMemoryMmap}; @@ -13,7 +13,7 @@ use super::super::{ VIRTIO_MMIO_INT_VRING, }; use super::{defs, defs::uapi}; -use crate::legacy::Gic; +use crate::legacy::GicV3; use crate::Error as DeviceError; // Inflate queue. @@ -59,7 +59,7 @@ pub struct Balloon { pub(crate) activate_evt: EventFd, pub(crate) device_state: DeviceState, config: VirtioBalloonConfig, - intc: Option>>, + intc: Option, irq_line: Option, } @@ -102,7 +102,7 @@ impl Balloon { defs::BALLOON_DEV_ID } - pub fn set_intc(&mut self, intc: Arc>) { + pub fn set_intc(&mut self, intc: GicV3) { self.intc = Some(intc); } @@ -111,7 +111,7 @@ impl Balloon { self.interrupt_status .fetch_or(VIRTIO_MMIO_INT_VRING as usize, Ordering::SeqCst); if let Some(intc) = &self.intc { - intc.lock().unwrap().set_irq(self.irq_line.unwrap()); + intc.set_irq(self.irq_line.unwrap()); Ok(()) } else { self.interrupt_evt.write(1).map_err(|e| { @@ -196,6 +196,7 @@ impl VirtioDevice for Balloon { } fn set_irq_line(&mut self, irq: u32) { + debug!("SET_IRQ_LINE (BALLOON)={}", irq); self.irq_line = Some(irq); } diff --git a/src/devices/src/virtio/block/device.rs b/src/devices/src/virtio/block/device.rs index f8038a51..de8dafe2 100644 --- a/src/devices/src/virtio/block/device.rs +++ b/src/devices/src/virtio/block/device.rs @@ -16,7 +16,7 @@ use std::os::macos::fs::MetadataExt; use std::path::PathBuf; use std::result; use std::sync::atomic::AtomicUsize; -use std::sync::{Arc, Mutex}; +use std::sync::Arc; use std::thread::JoinHandle; use imago::file::File as ImagoFile; @@ -35,7 +35,7 @@ use super::{ Error, QUEUE_SIZES, SECTOR_SHIFT, SECTOR_SIZE, }; -use crate::legacy::Gic; +use crate::legacy::GicV3; use crate::virtio::{block::ImageType, ActivateError}; /// Configuration options for disk caching. @@ -189,7 +189,7 @@ pub struct Block { pub(crate) partuuid: Option, // Interrupt specific fields. - intc: Option>>, + intc: Option, irq_line: Option, } @@ -271,7 +271,7 @@ impl Block { }) } - pub fn set_intc(&mut self, intc: Arc>) { + pub fn set_intc(&mut self, intc: GicV3) { self.intc = Some(intc); } @@ -318,6 +318,7 @@ impl VirtioDevice for Block { } fn set_irq_line(&mut self, irq: u32) { + debug!("SET_IRQ_LINE (BLOCK)={}", irq); self.irq_line = Some(irq); } diff --git a/src/devices/src/virtio/block/worker.rs b/src/devices/src/virtio/block/worker.rs index 96d14314..19d7d213 100644 --- a/src/devices/src/virtio/block/worker.rs +++ b/src/devices/src/virtio/block/worker.rs @@ -1,4 +1,4 @@ -use crate::legacy::Gic; +use crate::legacy::GicV3; use crate::virtio::descriptor_utils::{Reader, Writer}; use crate::Error as DeviceError; @@ -9,7 +9,7 @@ use std::io::{self, Write}; use std::os::fd::AsRawFd; use std::result; use std::sync::atomic::{AtomicUsize, Ordering}; -use std::sync::{Arc, Mutex}; +use std::sync::Arc; use std::thread; use utils::epoll::{ControlOperation, Epoll, EpollEvent, EventSet}; use utils::eventfd::EventFd; @@ -51,7 +51,7 @@ pub struct BlockWorker { queue_evt: EventFd, interrupt_status: Arc, interrupt_evt: EventFd, - intc: Option>>, + intc: Option, irq_line: Option, mem: GuestMemoryMmap, @@ -66,7 +66,7 @@ impl BlockWorker { queue_evt: EventFd, interrupt_status: Arc, interrupt_evt: EventFd, - intc: Option>>, + intc: Option, irq_line: Option, mem: GuestMemoryMmap, disk: DiskProperties, @@ -271,7 +271,7 @@ impl BlockWorker { self.interrupt_status .fetch_or(VIRTIO_MMIO_INT_VRING as usize, Ordering::SeqCst); if let Some(intc) = &self.intc { - intc.lock().unwrap().set_irq(self.irq_line.unwrap()); + intc.set_irq(self.irq_line.unwrap()); } else { self.interrupt_evt.write(1).map_err(|e| { error!("Failed to signal used queue: {:?}", e); diff --git a/src/devices/src/virtio/console/device.rs b/src/devices/src/virtio/console/device.rs index e1193ec6..6bbdda83 100644 --- a/src/devices/src/virtio/console/device.rs +++ b/src/devices/src/virtio/console/device.rs @@ -4,7 +4,7 @@ use std::iter::zip; use std::mem::{size_of, size_of_val}; use std::os::unix::io::{AsRawFd, RawFd}; use std::sync::atomic::AtomicUsize; -use std::sync::{Arc, Mutex}; +use std::sync::Arc; use libc::TIOCGWINSZ; use nix::ioctl_read_bad; @@ -15,7 +15,7 @@ use super::super::{ ActivateError, ActivateResult, ConsoleError, DeviceState, Queue as VirtQueue, VirtioDevice, }; use super::{defs, defs::control_event, defs::uapi}; -use crate::legacy::Gic; +use crate::legacy::GicV3; use crate::virtio::console::console_control::{ ConsoleControl, VirtioConsoleControl, VirtioConsoleResize, }; @@ -142,7 +142,7 @@ impl Console { defs::CONSOLE_DEV_ID } - pub fn set_intc(&mut self, intc: Arc>) { + pub fn set_intc(&mut self, intc: GicV3) { self.irq.set_intc(intc) } diff --git a/src/devices/src/virtio/console/irq_signaler.rs b/src/devices/src/virtio/console/irq_signaler.rs index 5deb5971..e84aa376 100644 --- a/src/devices/src/virtio/console/irq_signaler.rs +++ b/src/devices/src/virtio/console/irq_signaler.rs @@ -1,16 +1,16 @@ use std::sync::atomic::{AtomicUsize, Ordering}; -use std::sync::{Arc, Mutex}; +use std::sync::Arc; use utils::eventfd::EventFd; -use crate::legacy::Gic; +use crate::legacy::GicV3; use crate::virtio::{VIRTIO_MMIO_INT_CONFIG, VIRTIO_MMIO_INT_VRING}; #[derive(Clone)] pub struct IRQSignaler { interrupt_status: Arc, interrupt_evt: Arc, - intc: Option>>, + intc: Option, irq_line: Option, } @@ -32,7 +32,7 @@ impl IRQSignaler { self.interrupt_status .fetch_or(VIRTIO_MMIO_INT_VRING as usize, Ordering::SeqCst); if let Some(intc) = &self.intc { - intc.lock().unwrap().set_irq(self.irq_line.unwrap()); + intc.set_irq(self.irq_line.unwrap()); } else if let Err(e) = self.interrupt_evt.write(1) { error!("Failed to signal used queue: {e:?}"); } @@ -55,11 +55,12 @@ impl IRQSignaler { self.interrupt_status.clone() } - pub fn set_intc(&mut self, intc: Arc>) { + pub fn set_intc(&mut self, intc: GicV3) { self.intc = Some(intc); } pub fn set_irq_line(&mut self, irq: u32) { + debug!("SET_IRQ_LINE (SIGNALER)={}", irq); self.irq_line = Some(irq); } } diff --git a/src/devices/src/virtio/fs/device.rs b/src/devices/src/virtio/fs/device.rs index fa9ffc8f..9d7a21e0 100644 --- a/src/devices/src/virtio/fs/device.rs +++ b/src/devices/src/virtio/fs/device.rs @@ -3,7 +3,7 @@ use crossbeam_channel::Sender; use std::cmp; use std::io::Write; use std::sync::atomic::{AtomicU64, AtomicUsize, Ordering}; -use std::sync::{Arc, Mutex}; +use std::sync::Arc; use std::thread::JoinHandle; #[cfg(target_os = "macos")] @@ -19,7 +19,7 @@ use super::passthrough; use super::worker::FsWorker; use super::ExportTable; use super::{defs, defs::uapi}; -use crate::legacy::Gic; +use crate::legacy::GicV3; #[derive(Copy, Clone)] #[repr(C, packed)] @@ -46,7 +46,7 @@ pub struct Fs { acked_features: u64, interrupt_status: Arc, interrupt_evt: EventFd, - intc: Option>>, + intc: Option, irq_line: Option, device_state: DeviceState, config: VirtioFsConfig, @@ -114,7 +114,7 @@ impl Fs { defs::FS_DEV_ID } - pub fn set_intc(&mut self, intc: Arc>) { + pub fn set_intc(&mut self, intc: GicV3) { self.intc = Some(intc); } @@ -175,6 +175,7 @@ impl VirtioDevice for Fs { } fn set_irq_line(&mut self, irq: u32) { + debug!("SET_IRQ_LINE (FS)={}", irq); self.irq_line = Some(irq); } diff --git a/src/devices/src/virtio/fs/worker.rs b/src/devices/src/virtio/fs/worker.rs index cfd058e1..a0b93e08 100644 --- a/src/devices/src/virtio/fs/worker.rs +++ b/src/devices/src/virtio/fs/worker.rs @@ -5,7 +5,7 @@ use hvf::MemoryMapping; use std::os::fd::AsRawFd; use std::sync::atomic::{AtomicUsize, Ordering}; -use std::sync::{Arc, Mutex}; +use std::sync::Arc; use std::thread; use utils::epoll::{ControlOperation, Epoll, EpollEvent, EventSet}; @@ -17,7 +17,7 @@ use super::defs::{HPQ_INDEX, REQ_INDEX}; use super::descriptor_utils::{Reader, Writer}; use super::passthrough::{self, PassthroughFs}; use super::server::Server; -use crate::legacy::Gic; +use crate::legacy::GicV3; use crate::virtio::VirtioShmRegion; pub struct FsWorker { @@ -25,7 +25,7 @@ pub struct FsWorker { queue_evts: Vec, interrupt_status: Arc, interrupt_evt: EventFd, - intc: Option>>, + intc: Option, irq_line: Option, mem: GuestMemoryMmap, @@ -43,7 +43,7 @@ impl FsWorker { queue_evts: Vec, interrupt_status: Arc, interrupt_evt: EventFd, - intc: Option>>, + intc: Option, irq_line: Option, mem: GuestMemoryMmap, shm_region: Option, @@ -184,7 +184,7 @@ impl FsWorker { self.interrupt_status .fetch_or(VIRTIO_MMIO_INT_VRING as usize, Ordering::SeqCst); if let Some(intc) = &self.intc { - intc.lock().unwrap().set_irq(self.irq_line.unwrap()); + intc.set_irq(self.irq_line.unwrap()); } else if let Err(e) = self.interrupt_evt.write(1) { error!("Failed to signal used queue: {:?}", e); } diff --git a/src/devices/src/virtio/gpu/device.rs b/src/devices/src/virtio/gpu/device.rs index f8ee59bd..68593dcf 100644 --- a/src/devices/src/virtio/gpu/device.rs +++ b/src/devices/src/virtio/gpu/device.rs @@ -15,7 +15,7 @@ use super::defs; use super::defs::uapi; use super::defs::uapi::virtio_gpu_config; use super::worker::Worker; -use crate::legacy::Gic; +use crate::legacy::GicV3; use crate::Error as DeviceError; #[cfg(target_os = "macos")] use hvf::MemoryMapping; @@ -44,7 +44,7 @@ pub struct Gpu { pub(crate) activate_evt: EventFd, pub(crate) device_state: DeviceState, shm_region: Option, - intc: Option>>, + intc: Option, irq_line: Option, pub(crate) sender: Option>, virgl_flags: u32, @@ -110,7 +110,7 @@ impl Gpu { defs::GPU_DEV_ID } - pub fn set_intc(&mut self, intc: Arc>) { + pub fn set_intc(&mut self, intc: GicV3) { self.intc = Some(intc); } @@ -128,7 +128,7 @@ impl Gpu { self.interrupt_status .fetch_or(VIRTIO_MMIO_INT_VRING as usize, Ordering::SeqCst); if let Some(intc) = &self.intc { - intc.lock().unwrap().set_irq(self.irq_line.unwrap()); + intc.set_irq(self.irq_line.unwrap()); Ok(()) } else { self.interrupt_evt.write(1).map_err(|e| { @@ -232,6 +232,7 @@ impl VirtioDevice for Gpu { } fn set_irq_line(&mut self, irq: u32) { + debug!("SET_IRQ_LINE (GPU)={}", irq); self.irq_line = Some(irq); } diff --git a/src/devices/src/virtio/gpu/virtio_gpu.rs b/src/devices/src/virtio/gpu/virtio_gpu.rs index bc5efc8f..b760786b 100644 --- a/src/devices/src/virtio/gpu/virtio_gpu.rs +++ b/src/devices/src/virtio/gpu/virtio_gpu.rs @@ -38,7 +38,7 @@ use super::protocol::{ }; use super::{GpuError, Result}; -use crate::legacy::Gic; +use crate::legacy::GicV3; use crate::virtio::fs::ExportTable; use crate::virtio::gpu::protocol::VIRTIO_GPU_FLAG_INFO_RING_IDX; use crate::virtio::{VirtioShmRegion, VIRTIO_MMIO_INT_VRING}; @@ -117,7 +117,7 @@ impl VirtioGpu { fence_state: Arc>, interrupt_status: Arc, interrupt_evt: EventFd, - intc: Option>>, + intc: Option, irq_line: Option, ) -> RutabagaFenceHandler { RutabagaFenceHandler::new(move |completed_fence: RutabagaFence| { @@ -157,7 +157,7 @@ impl VirtioGpu { interrupt_status.fetch_or(VIRTIO_MMIO_INT_VRING as usize, Ordering::SeqCst); if let Some(intc) = &intc { - intc.lock().unwrap().set_irq(irq_line.unwrap()); + intc.set_irq(irq_line.unwrap()); } else if let Err(e) = interrupt_evt.write(1) { error!("Failed to signal used queue: {:?}", e); } @@ -178,7 +178,7 @@ impl VirtioGpu { queue_ctl: Arc>, interrupt_status: Arc, interrupt_evt: EventFd, - intc: Option>>, + intc: Option, irq_line: Option, virgl_flags: u32, #[cfg(target_os = "macos")] map_sender: Sender, diff --git a/src/devices/src/virtio/gpu/worker.rs b/src/devices/src/virtio/gpu/worker.rs index 9fb709c5..a8c6f0ce 100644 --- a/src/devices/src/virtio/gpu/worker.rs +++ b/src/devices/src/virtio/gpu/worker.rs @@ -21,7 +21,7 @@ use super::protocol::{ virtio_gpu_ctrl_hdr, virtio_gpu_mem_entry, GpuCommand, GpuResponse, VirtioGpuResult, }; use super::virtio_gpu::VirtioGpu; -use crate::legacy::Gic; +use crate::legacy::GicV3; use crate::virtio::fs::ExportTable; use crate::virtio::gpu::protocol::{VIRTIO_GPU_FLAG_FENCE, VIRTIO_GPU_FLAG_INFO_RING_IDX}; use crate::virtio::gpu::virtio_gpu::VirtioGpuRing; @@ -34,7 +34,7 @@ pub struct Worker { queue_ctl: Arc>, interrupt_status: Arc, interrupt_evt: EventFd, - intc: Option>>, + intc: Option, irq_line: Option, shm_region: VirtioShmRegion, virgl_flags: u32, @@ -51,7 +51,7 @@ impl Worker { queue_ctl: Arc>, interrupt_status: Arc, interrupt_evt: EventFd, - intc: Option>>, + intc: Option, irq_line: Option, shm_region: VirtioShmRegion, virgl_flags: u32, @@ -110,7 +110,7 @@ impl Worker { self.interrupt_status .fetch_or(VIRTIO_MMIO_INT_VRING as usize, Ordering::SeqCst); if let Some(intc) = &self.intc { - intc.lock().unwrap().set_irq(self.irq_line.unwrap()); + intc.set_irq(self.irq_line.unwrap()); Ok(()) } else { self.interrupt_evt.write(1).map_err(|e| { diff --git a/src/devices/src/virtio/net/device.rs b/src/devices/src/virtio/net/device.rs index e8c76acb..62695c1d 100644 --- a/src/devices/src/virtio/net/device.rs +++ b/src/devices/src/virtio/net/device.rs @@ -4,7 +4,7 @@ // Portions Copyright 2017 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 THIRD-PARTY file. -use crate::legacy::Gic; +use crate::legacy::GicV3; use crate::virtio::net::{Error, Result}; use crate::virtio::net::{QUEUE_SIZES, RX_INDEX, TX_INDEX}; use crate::virtio::queue::Error as QueueError; @@ -19,7 +19,7 @@ use std::io::Write; use std::os::fd::RawFd; use std::path::PathBuf; use std::sync::atomic::AtomicUsize; -use std::sync::{Arc, Mutex}; +use std::sync::Arc; use utils::eventfd::{EventFd, EFD_NONBLOCK}; use virtio_bindings::virtio_net::{ VIRTIO_NET_F_CSUM, VIRTIO_NET_F_GUEST_CSUM, VIRTIO_NET_F_GUEST_TSO4, VIRTIO_NET_F_GUEST_UFO, @@ -84,7 +84,7 @@ pub struct Net { pub(crate) device_state: DeviceState, - intc: Option>>, + intc: Option, irq_line: Option, config: VirtioNetConfig, @@ -143,7 +143,7 @@ impl Net { &self.id } - pub fn set_intc(&mut self, intc: Arc>) { + pub fn set_intc(&mut self, intc: GicV3) { self.intc = Some(intc); } } @@ -186,6 +186,7 @@ impl VirtioDevice for Net { } fn set_irq_line(&mut self, irq: u32) { + debug!("SET_IRQ_LINE (NET)={}", irq); self.irq_line = Some(irq); } diff --git a/src/devices/src/virtio/net/worker.rs b/src/devices/src/virtio/net/worker.rs index d07f905e..599154f4 100644 --- a/src/devices/src/virtio/net/worker.rs +++ b/src/devices/src/virtio/net/worker.rs @@ -1,4 +1,4 @@ -use crate::legacy::Gic; +use crate::legacy::GicV3; use crate::virtio::net::gvproxy::Gvproxy; use crate::virtio::net::passt::Passt; use crate::virtio::net::{MAX_BUFFER_SIZE, QUEUE_SIZE, RX_INDEX, TX_INDEX}; @@ -11,7 +11,7 @@ use super::device::{FrontendError, RxError, TxError, VirtioNetBackend}; use std::os::fd::AsRawFd; use std::sync::atomic::AtomicUsize; use std::sync::atomic::Ordering; -use std::sync::{Arc, Mutex}; +use std::sync::Arc; use std::thread; use std::{cmp, mem, result}; use utils::epoll::{ControlOperation, Epoll, EpollEvent, EventSet}; @@ -36,7 +36,7 @@ pub struct NetWorker { queue_evts: Vec, interrupt_status: Arc, interrupt_evt: EventFd, - intc: Option>>, + intc: Option, irq_line: Option, mem: GuestMemoryMmap, @@ -58,7 +58,7 @@ impl NetWorker { queue_evts: Vec, interrupt_status: Arc, interrupt_evt: EventFd, - intc: Option>>, + intc: Option, irq_line: Option, mem: GuestMemoryMmap, cfg_backend: VirtioNetBackend, @@ -383,7 +383,7 @@ impl NetWorker { self.interrupt_status .fetch_or(VIRTIO_MMIO_INT_VRING as usize, Ordering::SeqCst); if let Some(intc) = &self.intc { - intc.lock().unwrap().set_irq(self.irq_line.unwrap()); + intc.set_irq(self.irq_line.unwrap()); Ok(()) } else { self.interrupt_evt.write(1).map_err(|e| { diff --git a/src/devices/src/virtio/rng/device.rs b/src/devices/src/virtio/rng/device.rs index d87f696d..eafcb06b 100644 --- a/src/devices/src/virtio/rng/device.rs +++ b/src/devices/src/virtio/rng/device.rs @@ -1,6 +1,6 @@ use std::result; use std::sync::atomic::{AtomicUsize, Ordering}; -use std::sync::{Arc, Mutex}; +use std::sync::Arc; use rand::{rngs::OsRng, RngCore}; use utils::eventfd::EventFd; @@ -11,7 +11,7 @@ use super::super::{ VIRTIO_MMIO_INT_VRING, }; use super::{defs, defs::uapi}; -use crate::legacy::Gic; +use crate::legacy::GicV3; use crate::Error as DeviceError; // Request queue. @@ -33,7 +33,7 @@ pub struct Rng { pub(crate) interrupt_evt: EventFd, pub(crate) activate_evt: EventFd, pub(crate) device_state: DeviceState, - intc: Option>>, + intc: Option, irq_line: Option, } @@ -71,7 +71,7 @@ impl Rng { defs::RNG_DEV_ID } - pub fn set_intc(&mut self, intc: Arc>) { + pub fn set_intc(&mut self, intc: GicV3) { self.intc = Some(intc); } @@ -80,7 +80,7 @@ impl Rng { self.interrupt_status .fetch_or(VIRTIO_MMIO_INT_VRING as usize, Ordering::SeqCst); if let Some(intc) = &self.intc { - intc.lock().unwrap().set_irq(self.irq_line.unwrap()); + intc.set_irq(self.irq_line.unwrap()); Ok(()) } else { self.interrupt_evt.write(1).map_err(|e| { @@ -162,6 +162,7 @@ impl VirtioDevice for Rng { } fn set_irq_line(&mut self, irq: u32) { + debug!("SET_IRQ_LINE (RNG)={}", irq); self.irq_line = Some(irq); } diff --git a/src/devices/src/virtio/snd/device.rs b/src/devices/src/virtio/snd/device.rs index 9ecb8478..88d378eb 100644 --- a/src/devices/src/virtio/snd/device.rs +++ b/src/devices/src/virtio/snd/device.rs @@ -12,7 +12,7 @@ use super::virtio_sound::VirtioSoundConfig; use super::worker::SndWorker; use super::{defs, defs::uapi, defs::QUEUE_INDEXES, Error}; -use crate::legacy::Gic; +use crate::legacy::GicV3; use crate::virtio::DeviceState; // Supported features. @@ -31,7 +31,7 @@ pub struct Snd { pub(crate) interrupt_evt: EventFd, pub(crate) activate_evt: EventFd, pub(crate) device_state: DeviceState, - intc: Option>>, + intc: Option, irq_line: Option, worker_thread: Option>, worker_stopfd: EventFd, @@ -76,7 +76,7 @@ impl Snd { defs::SND_DEV_ID } - pub fn set_intc(&mut self, intc: Arc>) { + pub fn set_intc(&mut self, intc: GicV3) { self.intc = Some(intc); } } @@ -119,6 +119,7 @@ impl VirtioDevice for Snd { } fn set_irq_line(&mut self, irq: u32) { + debug!("SET_IRQ_LINE (SND)={}", irq); self.irq_line = Some(irq); } @@ -179,7 +180,7 @@ impl VirtioDevice for Snd { queue_evts, self.interrupt_status.clone(), self.interrupt_evt.try_clone().unwrap(), - self.intc.clone(), + self.intc, self.irq_line, mem.clone(), self.worker_stopfd.try_clone().unwrap(), diff --git a/src/devices/src/virtio/snd/mod.rs b/src/devices/src/virtio/snd/mod.rs index 9016f4b4..b8aee868 100644 --- a/src/devices/src/virtio/snd/mod.rs +++ b/src/devices/src/virtio/snd/mod.rs @@ -182,7 +182,7 @@ pub struct Vring { queue: Queue, interrupt_evt: EventFd, interrupt_status: Arc, - intc: Option>>, + intc: Option, irq_line: Option, } @@ -194,7 +194,7 @@ impl Vring { std::sync::atomic::Ordering::SeqCst, ); if let Some(intc) = &self.intc { - intc.lock().unwrap().set_irq(self.irq_line.unwrap()); + intc.set_irq(self.irq_line.unwrap()); } else if let Err(e) = self.interrupt_evt.write(1) { error!("Failed to signal used queue: {:?}", e); } diff --git a/src/devices/src/virtio/snd/worker.rs b/src/devices/src/virtio/snd/worker.rs index f2600807..9d42ea5b 100644 --- a/src/devices/src/virtio/snd/worker.rs +++ b/src/devices/src/virtio/snd/worker.rs @@ -23,7 +23,7 @@ use super::{ BackendType, Direction, Error, VirtioSoundChmapInfo, VirtioSoundJackInfo, Vring, VIRTIO_SND_CHMAP_FL, VIRTIO_SND_CHMAP_FR, VIRTIO_SND_CHMAP_MAX_SIZE, VIRTIO_SND_CHMAP_NONE, }; -use crate::legacy::Gic; +use crate::legacy::GicV3; use crate::virtio::snd::stream::Buffer; use crate::virtio::snd::{ControlMessageKind, IOMessage}; use crate::virtio::DescriptorChain; @@ -33,7 +33,7 @@ pub struct SndWorker { queue_evts: Vec, interrupt_status: Arc, interrupt_evt: EventFd, - intc: Option>>, + intc: Option, irq_line: Option, mem: GuestMemoryMmap, @@ -52,7 +52,7 @@ impl SndWorker { queue_evts: Vec, interrupt_status: Arc, interrupt_evt: EventFd, - intc: Option>>, + intc: Option, irq_line: Option, mem: GuestMemoryMmap, stop_fd: EventFd, @@ -249,7 +249,7 @@ impl SndWorker { self.interrupt_status .fetch_or(VIRTIO_MMIO_INT_VRING as usize, Ordering::SeqCst); if let Some(intc) = &self.intc { - intc.lock().unwrap().set_irq(self.irq_line.unwrap()); + intc.set_irq(self.irq_line.unwrap()); } else if let Err(e) = self.interrupt_evt.write(1) { error!("Failed to signal used queue: {:?}", e); } diff --git a/src/devices/src/virtio/vsock/device.rs b/src/devices/src/virtio/vsock/device.rs index 09fa479e..127ded48 100644 --- a/src/devices/src/virtio/vsock/device.rs +++ b/src/devices/src/virtio/vsock/device.rs @@ -23,7 +23,7 @@ use super::super::{ use super::muxer::VsockMuxer; use super::packet::VsockPacket; use super::{defs, defs::uapi}; -use crate::legacy::Gic; +use crate::legacy::GicV3; pub(crate) const RXQ_INDEX: usize = 0; pub(crate) const TXQ_INDEX: usize = 1; @@ -50,7 +50,7 @@ pub struct Vsock { pub(crate) interrupt_evt: EventFd, pub(crate) activate_evt: EventFd, pub(crate) device_state: DeviceState, - intc: Option>>, + intc: Option, irq_line: Option, } @@ -116,7 +116,7 @@ impl Vsock { defs::VSOCK_DEV_ID } - pub fn set_intc(&mut self, intc: Arc>) { + pub fn set_intc(&mut self, intc: GicV3) { self.intc = Some(intc); } @@ -131,7 +131,7 @@ impl Vsock { self.interrupt_status .fetch_or(VIRTIO_MMIO_INT_VRING as usize, Ordering::SeqCst); if let Some(intc) = &self.intc { - intc.lock().unwrap().set_irq(self.irq_line.unwrap()); + intc.set_irq(self.irq_line.unwrap()); Ok(()) } else { self.interrupt_evt.write(1).map_err(|e| { @@ -273,6 +273,7 @@ impl VirtioDevice for Vsock { } fn set_irq_line(&mut self, irq: u32) { + debug!("SET_IRQ_LINE (VSOCK)={}", irq); self.irq_line = Some(irq); } diff --git a/src/devices/src/virtio/vsock/muxer.rs b/src/devices/src/virtio/vsock/muxer.rs index 5612f83f..0ba9f93f 100644 --- a/src/devices/src/virtio/vsock/muxer.rs +++ b/src/devices/src/virtio/vsock/muxer.rs @@ -4,7 +4,7 @@ use std::path::PathBuf; use std::sync::atomic::{AtomicUsize, Ordering}; use std::sync::{Arc, Mutex, RwLock}; -use super::super::super::legacy::Gic; +use super::super::super::legacy::GicV3; use super::super::Queue as VirtQueue; use super::super::VIRTIO_MMIO_INT_VRING; use super::defs; @@ -107,7 +107,7 @@ pub struct VsockMuxer { epoll: Epoll, interrupt_evt: EventFd, interrupt_status: Arc, - intc: Option>>, + intc: Option, irq_line: Option, proxy_map: ProxyMap, reaper_sender: Option>, @@ -143,7 +143,7 @@ impl VsockMuxer { &mut self, mem: GuestMemoryMmap, queue: Arc>, - intc: Option>>, + intc: Option, irq_line: Option, ) { self.queue = Some(queue.clone()); @@ -241,7 +241,7 @@ impl VsockMuxer { self.interrupt_status .fetch_or(VIRTIO_MMIO_INT_VRING as usize, Ordering::SeqCst); if let Some(intc) = &self.intc { - intc.lock().unwrap().set_irq(self.irq_line.unwrap()); + intc.set_irq(self.irq_line.unwrap()); } else if let Err(e) = self.interrupt_evt.write(1) { warn!("failed to signal used queue: {:?}", e); } diff --git a/src/devices/src/virtio/vsock/muxer_thread.rs b/src/devices/src/virtio/vsock/muxer_thread.rs index 09a32361..d6286ce5 100644 --- a/src/devices/src/virtio/vsock/muxer_thread.rs +++ b/src/devices/src/virtio/vsock/muxer_thread.rs @@ -3,7 +3,7 @@ use std::sync::atomic::{AtomicUsize, Ordering}; use std::sync::{Arc, Mutex}; use std::thread; -use super::super::super::legacy::Gic; +use super::super::super::legacy::GicV3; use super::super::Queue as VirtQueue; use super::super::VIRTIO_MMIO_INT_VRING; use super::muxer::{push_packet, MuxerRx, ProxyMap}; @@ -26,7 +26,7 @@ pub struct MuxerThread { queue: Arc>, interrupt_evt: EventFd, interrupt_status: Arc, - intc: Option>>, + intc: Option, irq_line: Option, reaper_sender: Sender, } @@ -42,7 +42,7 @@ impl MuxerThread { queue: Arc>, interrupt_evt: EventFd, interrupt_status: Arc, - intc: Option>>, + intc: Option, irq_line: Option, reaper_sender: Sender, ) -> Self { @@ -140,7 +140,7 @@ impl MuxerThread { self.interrupt_status .fetch_or(VIRTIO_MMIO_INT_VRING as usize, Ordering::SeqCst); if let Some(intc) = &self.intc { - intc.lock().unwrap().set_irq(self.irq_line.unwrap()); + intc.set_irq(self.irq_line.unwrap()); } else if let Err(e) = self.interrupt_evt.write(1) { warn!("failed to signal used queue: {:?}", e); } diff --git a/src/devices/src/virtio/vsock/timesync.rs b/src/devices/src/virtio/vsock/timesync.rs index c415b621..8df47a8b 100644 --- a/src/devices/src/virtio/vsock/timesync.rs +++ b/src/devices/src/virtio/vsock/timesync.rs @@ -3,7 +3,7 @@ use std::sync::{Arc, Mutex}; use std::thread; use std::time; -use super::super::super::legacy::Gic; +use super::super::super::legacy::GicV3; use super::super::Queue as VirtQueue; use super::super::VIRTIO_MMIO_INT_VRING; use super::defs::uapi; @@ -22,7 +22,7 @@ pub struct TimesyncThread { queue_mutex: Arc>, interrupt_evt: EventFd, interrupt_status: Arc, - intc: Option>>, + intc: Option, irq_line: Option, } @@ -33,7 +33,7 @@ impl TimesyncThread { queue_mutex: Arc>, interrupt_evt: EventFd, interrupt_status: Arc, - intc: Option>>, + intc: Option, irq_line: Option, ) -> Self { Self { @@ -68,7 +68,7 @@ impl TimesyncThread { self.interrupt_status .fetch_or(VIRTIO_MMIO_INT_VRING as usize, Ordering::SeqCst); if let Some(intc) = &self.intc { - intc.lock().unwrap().set_irq(self.irq_line.unwrap()); + intc.set_irq(self.irq_line.unwrap()); } else if let Err(e) = self.interrupt_evt.write(1) { warn!("failed to signal used queue: {:?}", e); } diff --git a/src/hvf/Cargo.toml b/src/hvf/Cargo.toml index 4f43b5c7..31249d30 100644 --- a/src/hvf/Cargo.toml +++ b/src/hvf/Cargo.toml @@ -8,3 +8,5 @@ edition = "2021" crossbeam-channel = "0.5" log = "0.4.0" env_logger = "0.9.0" + +arch = { path = "../arch" } \ No newline at end of file diff --git a/src/hvf/src/lib.rs b/src/hvf/src/lib.rs index 465f7343..79ba02d4 100644 --- a/src/hvf/src/lib.rs +++ b/src/hvf/src/lib.rs @@ -8,12 +8,19 @@ #[allow(non_upper_case_globals)] #[allow(deref_nullptr)] mod bindings; + use bindings::*; +#[cfg(target_arch = "aarch64")] +use std::arch::asm; + use std::convert::TryInto; use std::fmt::{Display, Formatter}; +use std::sync::Arc; use std::time::Duration; +#[cfg(all(target_arch = "aarch64", target_os = "macos"))] +use arch::aarch64::sysreg::{icc_reg_name, SYSREG_MASK}; use crossbeam_channel::Sender; use log::debug; @@ -25,6 +32,10 @@ const HV_EXIT_REASON_CANCELED: hv_exit_reason_t = 0; const HV_EXIT_REASON_EXCEPTION: hv_exit_reason_t = 1; const HV_EXIT_REASON_VTIMER_ACTIVATED: hv_exit_reason_t = 2; +const TMR_CTL_ENABLE: u64 = 1 << 0; +const TMR_CTL_IMASK: u64 = 1 << 1; +const TMR_CTL_ISTATUS: u64 = 1 << 2; + const PSR_MODE_EL1H: u64 = 0x0000_0005; const PSR_F_BIT: u64 = 0x0000_0040; const PSR_I_BIT: u64 = 0x0000_0080; @@ -35,53 +46,11 @@ const PSTATE_FAULT_BITS_64: u64 = PSR_MODE_EL1H | PSR_A_BIT | PSR_F_BIT | PSR_I_ const EC_WFX_TRAP: u64 = 0x1; const EC_AA64_HVC: u64 = 0x16; const EC_AA64_SMC: u64 = 0x17; +#[cfg(all(target_arch = "aarch64", target_os = "macos"))] const EC_SYSTEMREGISTERTRAP: u64 = 0x18; const EC_DATAABORT: u64 = 0x24; const EC_AA64_BKPT: u64 = 0x3c; -macro_rules! arm64_sys_reg { - ($name: tt, $op0: tt, $op1: tt, $op2: tt, $crn: tt, $crm: tt) => { - const $name: u64 = ($op0 as u64) << 20 - | ($op2 as u64) << 17 - | ($op1 as u64) << 14 - | ($crn as u64) << 10 - | ($crm as u64) << 1; - }; -} - -arm64_sys_reg!(SYSREG_MASK, 0x3, 0x7, 0x7, 0xf, 0xf); - -/* -arm64_sys_reg!(SYSREG_CNTPCT_EL0, 3, 3, 1, 14, 0); -arm64_sys_reg!(SYSREG_PMCCNTR_EL0, 3, 3, 0, 9, 13); -arm64_sys_reg!(SYSREG_ICC_AP0R0_EL1, 3, 0, 4, 12, 8); -arm64_sys_reg!(SYSREG_ICC_AP0R1_EL1, 3, 0, 5, 12, 8); -arm64_sys_reg!(SYSREG_ICC_AP0R2_EL1, 3, 0, 6, 12, 8); -arm64_sys_reg!(SYSREG_ICC_AP0R3_EL1, 3, 0, 7, 12, 8); -arm64_sys_reg!(SYSREG_ICC_AP1R0_EL1, 3, 0, 0, 12, 9); -arm64_sys_reg!(SYSREG_ICC_AP1R1_EL1, 3, 0, 1, 12, 9); -arm64_sys_reg!(SYSREG_ICC_AP1R2_EL1, 3, 0, 2, 12, 9); -arm64_sys_reg!(SYSREG_ICC_AP1R3_EL1, 3, 0, 3, 12, 9); -arm64_sys_reg!(SYSREG_ICC_ASGI1R_EL1, 3, 0, 6, 12, 11); -arm64_sys_reg!(SYSREG_ICC_BPR0_EL1, 3, 0, 3, 12, 8); -arm64_sys_reg!(SYSREG_ICC_BPR1_EL1, 3, 0, 3, 12, 12); -arm64_sys_reg!(SYSREG_ICC_CTLR_EL1, 3, 0, 4, 12, 12); -arm64_sys_reg!(SYSREG_ICC_DIR_EL1, 3, 0, 1, 12, 11); -arm64_sys_reg!(SYSREG_ICC_EOIR0_EL1, 3, 0, 1, 12, 8); -arm64_sys_reg!(SYSREG_ICC_EOIR1_EL1, 3, 0, 1, 12, 12); -arm64_sys_reg!(SYSREG_ICC_HPPIR0_EL1, 3, 0, 2, 12, 8); -arm64_sys_reg!(SYSREG_ICC_HPPIR1_EL1, 3, 0, 2, 12, 12); -arm64_sys_reg!(SYSREG_ICC_IAR0_EL1, 3, 0, 0, 12, 8); -arm64_sys_reg!(SYSREG_ICC_IAR1_EL1, 3, 0, 0, 12, 12); -arm64_sys_reg!(SYSREG_ICC_IGRPEN0_EL1, 3, 0, 6, 12, 12); -arm64_sys_reg!(SYSREG_ICC_IGRPEN1_EL1, 3, 0, 7, 12, 12); -arm64_sys_reg!(SYSREG_ICC_PMR_EL1, 3, 0, 0, 4, 6); -arm64_sys_reg!(SYSREG_ICC_RPR_EL1, 3, 0, 3, 12, 11); -arm64_sys_reg!(SYSREG_ICC_SGI0R_EL1, 3, 0, 7, 12, 11); -arm64_sys_reg!(SYSREG_ICC_SGI1R_EL1, 3, 0, 5, 12, 11); -arm64_sys_reg!(SYSREG_ICC_SRE_EL1, 3, 0, 5, 12, 12); -*/ - #[derive(Clone, Debug)] pub enum Error { MemoryMap, @@ -94,7 +63,7 @@ pub enum Error { VcpuRun, VcpuSetPendingIrq, VcpuSetRegister, - VcpuSetSystemRegister, + VcpuSetSystemRegister(u16, u64), VcpuSetVtimerMask, VmCreate, } @@ -114,7 +83,11 @@ impl Display for Error { VcpuRun => write!(f, "Error running HVF vCPU"), VcpuSetPendingIrq => write!(f, "Error setting HVF vCPU pending irq"), VcpuSetRegister => write!(f, "Error setting HVF vCPU register"), - VcpuSetSystemRegister => write!(f, "Error setting HVF vCPU system register"), + VcpuSetSystemRegister(reg, val) => write!( + f, + "Error setting HVF vCPU system register 0x{:#x} to 0x{:#x}", + reg, val + ), VcpuSetVtimerMask => write!(f, "Error setting HVF vCPU vtimer mask"), VmCreate => write!(f, "Error creating HVF VM instance"), } @@ -132,6 +105,15 @@ pub enum InterruptType { Fiq, } +pub trait Vcpus { + fn set_vtimer_irq(&self, vcpuid: u64); + fn should_wait(&self, vcpuid: u64) -> bool; + fn has_pending_irq(&self, vcpuid: u64) -> bool; + fn get_pending_irq(&self, vcpuid: u64) -> u32; + fn handle_sysreg_read(&self, vcpuid: u64, reg: u32) -> Option; + fn handle_sysreg_write(&self, vcpuid: u64, reg: u32, val: u64) -> bool; +} + pub fn vcpu_request_exit(vcpuid: u64) -> Result<(), Error> { let mut vcpu: u64 = vcpuid; let ret = unsafe { hv_vcpus_exit(&mut vcpu, 1) }; @@ -245,16 +227,22 @@ pub struct HvfVcpu<'a> { mmio_buf: [u8; 8], pending_mmio_read: Option, pending_advance_pc: bool, + vtimer_masked: bool, } impl HvfVcpu<'_> { pub fn new() -> Result { let mut vcpuid: hv_vcpu_t = 0; let vcpu_exit_ptr: *mut hv_vcpu_exit_t = std::ptr::null_mut(); - let cntfrq: u64 = 24000000; - // TODO - Once inline assembly is stabilized in Rust, re-enable this: - //unsafe { asm!("mrs {}, cntfrq_el0", out(reg) cntfrq) }; + #[cfg(target_arch = "aarch64")] + let cntfrq = { + let cntfrq: u64; + unsafe { asm!("mrs {}, cntfrq_el0", out(reg) cntfrq) }; + cntfrq + }; + #[cfg(target_arch = "x86_64")] + let cntfrq = 0u64; let ret = unsafe { hv_vcpu_create( @@ -284,6 +272,7 @@ impl HvfVcpu<'_> { mmio_buf: [0; 8], pending_mmio_read: None, pending_advance_pc: false, + vtimer_masked: false, }) } @@ -321,8 +310,8 @@ impl HvfVcpu<'_> { } } - fn write_reg(&self, reg: u32, val: u64) -> Result<(), Error> { - let ret = unsafe { hv_vcpu_set_reg(self.vcpuid, reg, val) }; + pub fn write_reg(&self, rt: u32, val: u64) -> Result<(), Error> { + let ret = unsafe { hv_vcpu_set_reg(self.vcpuid, rt, val) }; if ret != HV_SUCCESS { Err(Error::VcpuSetRegister) } else { @@ -340,17 +329,26 @@ impl HvfVcpu<'_> { } } - #[allow(dead_code)] - fn write_sys_reg(&self, reg: u16, val: u64) -> Result<(), Error> { - let ret = unsafe { hv_vcpu_set_sys_reg(self.vcpuid, reg, val) }; - if ret != HV_SUCCESS { - Err(Error::VcpuSetSystemRegister) - } else { - Ok(()) + fn hvf_sync_vtimer(&mut self, vcpu_list: Arc) { + if !self.vtimer_masked { + return; + } + + let ctl = self + .read_sys_reg(hv_sys_reg_t_HV_SYS_REG_CNTV_CTL_EL0) + .unwrap(); + let irq_state = (ctl & (TMR_CTL_ENABLE | TMR_CTL_IMASK | TMR_CTL_ISTATUS)) + == (TMR_CTL_ENABLE | TMR_CTL_ISTATUS); + vcpu_list.set_vtimer_irq(self.vcpuid); + if !irq_state { + vcpu_set_vtimer_mask(self.vcpuid, false).unwrap(); + self.vtimer_masked = false; } } - pub fn run(&mut self, pending_irq: bool) -> Result { + pub fn run(&mut self, vcpu_list: Arc) -> Result { + let pending_irq = vcpu_list.has_pending_irq(self.vcpuid); + if let Some(mmio_read) = self.pending_mmio_read.take() { if mmio_read.srt < 31 { let val = match mmio_read.len { @@ -364,7 +362,7 @@ impl HvfVcpu<'_> { ), }; - self.write_reg(hv_reg_t_HV_REG_X0 + mmio_read.srt, val)?; + self.write_reg(mmio_read.srt, val)?; } } @@ -384,142 +382,171 @@ impl HvfVcpu<'_> { } match self.vcpu_exit.reason { - HV_EXIT_REASON_CANCELED => Ok(VcpuExit::Canceled), - HV_EXIT_REASON_EXCEPTION => { - let syndrome = self.vcpu_exit.exception.syndrome; - let ec = (syndrome >> 26) & 0x3f; - - match ec { - EC_AA64_HVC => { - let val = self.read_reg(hv_reg_t_HV_REG_X0)?; - - debug!("HVC: 0x{:x}", val); - let ret = match val { - 0x8400_0000 => Some(2), - 0x8400_0006 => Some(2), - 0x8400_0008 => return Ok(VcpuExit::Shutdown), // ARM Power State Coordination Interface SYSTEM_OFF - 0x8400_0009 => return Ok(VcpuExit::Shutdown), // ARM Power State Coordination Interface SYSTEM_RESET - 0xc400_0003 => { - let mpidr = self.read_reg(hv_reg_t_HV_REG_X1)?; - let entry = self.read_reg(hv_reg_t_HV_REG_X2)?; - let context_id = self.read_reg(hv_reg_t_HV_REG_X3)?; - self.write_reg(hv_reg_t_HV_REG_X0, 0)?; - return Ok(VcpuExit::CpuOn(mpidr, entry, context_id)); - } - _ => { - debug!("HVC call unhandled"); - None - } - }; - - if let Some(ret) = ret { - self.write_reg(hv_reg_t_HV_REG_X0, ret)?; - } + HV_EXIT_REASON_EXCEPTION => { /* This is the main one, handle below. */ } + HV_EXIT_REASON_VTIMER_ACTIVATED => { + self.vtimer_masked = true; + return Ok(VcpuExit::VtimerActivated); + } + HV_EXIT_REASON_CANCELED => return Ok(VcpuExit::Canceled), + _ => { + let pc = self.read_reg(hv_reg_t_HV_REG_PC)?; + panic!( + "unexpected exit reason: vcpuid={} 0x{:x} at pc=0x{:x}", + self.id(), + self.vcpu_exit.reason, + pc + ); + } + } - Ok(VcpuExit::HypervisorCall) - } - EC_AA64_SMC => { - debug!("SMC exit"); + self.hvf_sync_vtimer(vcpu_list.clone()); - self.pending_advance_pc = true; - Ok(VcpuExit::SecureMonitorCall) - } - EC_SYSTEMREGISTERTRAP => { - let isread: bool = (syndrome & 1) != 0; - let rt: u32 = ((syndrome >> 5) & 0x1f) as u32; - let reg: u64 = syndrome & SYSREG_MASK; - - debug!("sysreg operation reg={} (op0={} op1={} op2={} crn={} crm={}) isread={:?}", - reg, (reg >> 20) & 0x3, - (reg >> 14) & 0x7, (reg >> 17) & 0x7, - (reg >> 10) & 0xf, (reg >> 1) & 0xf, - isread); - - if isread && rt < 31 { - self.write_reg(rt, 0)?; - } + let syndrome = self.vcpu_exit.exception.syndrome; + let ec = (syndrome >> 26) & 0x3f; + match ec { + EC_AA64_BKPT => { + debug!("vcpu[{}]: BRK exit", self.vcpuid); + Ok(VcpuExit::Breakpoint) + } + EC_DATAABORT => { + let isv: bool = (syndrome & (1 << 24)) != 0; + let iswrite: bool = ((syndrome >> 6) & 1) != 0; + let s1ptw: bool = ((syndrome >> 7) & 1) != 0; + let sas: u32 = ((syndrome >> 22) & 3) as u32; + let len: usize = (1 << sas) as usize; + let srt: u32 = ((syndrome >> 16) & 0x1f) as u32; + let cm: u32 = ((syndrome >> 8) & 0x1) as u32; + + debug!( + "EC_DATAABORT {} {} {} {} {} {} {} {}", + syndrome, isv as u8, iswrite as u8, s1ptw as u8, sas, len, srt, cm + ); - self.pending_advance_pc = true; - Ok(VcpuExit::SystemRegister) + let pa = self.vcpu_exit.exception.physical_address; + self.pending_advance_pc = true; + + if iswrite { + let val = if srt < 31 { + self.read_reg(hv_reg_t_HV_REG_X0 + srt)? + } else { + 0 + }; + + match len { + 1 => self.mmio_buf[0..1].copy_from_slice(&(val as u8).to_le_bytes()), + 4 => self.mmio_buf[0..4].copy_from_slice(&(val as u32).to_le_bytes()), + 8 => self.mmio_buf[0..8].copy_from_slice(&val.to_le_bytes()), + _ => panic!("unsupported mmio len={len}"), + }; + + Ok(VcpuExit::MmioWrite(pa, &self.mmio_buf[0..len])) + } else { + self.pending_mmio_read = Some(MmioRead { addr: pa, srt, len }); + Ok(VcpuExit::MmioRead(pa, &mut self.mmio_buf[0..len])) + } + } + #[cfg(all(target_arch = "aarch64", target_os = "macos"))] + EC_SYSTEMREGISTERTRAP => { + let isread: bool = (syndrome & 1) != 0; + let rt: u32 = ((syndrome >> 5) & 0x1f) as u32; + let reg: u32 = syndrome as u32 & SYSREG_MASK; + debug!( + "EC_SYSTEMREGISTERTRAP isread={}, syndrome={}, rt={}, reg={}, reg_name={}", + isread as u32, + syndrome, + rt, + reg, + icc_reg_name(reg).unwrap_or("non-ICC reg") + ); + + self.pending_advance_pc = true; + + if isread { + assert!(rt < 32); + + // See https://developer.arm.com/documentation/dui0801/l/Overview-of-AArch64-state/Registers-in-AArch64-state + if rt == 31 { + return Ok(VcpuExit::SystemRegister); } - EC_DATAABORT => { - let isv: bool = (syndrome & (1 << 24)) != 0; - let iswrite: bool = ((syndrome >> 6) & 1) != 0; - let s1ptw: bool = ((syndrome >> 7) & 1) != 0; - let sas: u32 = (syndrome as u32 >> 22) & 3; - let len: usize = (1 << sas) as usize; - let srt: u32 = (syndrome as u32 >> 16) & 0x1f; - - debug!("data abort: va={:x}, pa={:x}, isv={}, iswrite={:?}, s1ptrw={}, len={}, srt={}", - self.vcpu_exit.exception.virtual_address, - self.vcpu_exit.exception.physical_address, - isv, iswrite, s1ptw, len, srt); - - let pa = self.vcpu_exit.exception.physical_address; - self.pending_advance_pc = true; - - if iswrite { - let val = if srt < 31 { - self.read_reg(hv_reg_t_HV_REG_X0 + srt)? - } else { - 0u64 - }; - - match len { - 1 => { - self.mmio_buf[0..1].copy_from_slice(&(val as u8).to_le_bytes()) - } - 4 => { - self.mmio_buf[0..4].copy_from_slice(&(val as u32).to_le_bytes()) - } - 8 => self.mmio_buf[0..8].copy_from_slice(&(val).to_le_bytes()), - _ => panic!("unsupported mmio len={len}"), - }; - - Ok(VcpuExit::MmioWrite(pa, &self.mmio_buf[0..len])) - } else { - self.pending_mmio_read = Some(MmioRead { addr: pa, srt, len }); - Ok(VcpuExit::MmioRead(pa, &mut self.mmio_buf[0..len])) + + match vcpu_list.handle_sysreg_read(self.vcpuid, reg) { + Some(val) => { + self.write_reg(rt, val)?; + Ok(VcpuExit::SystemRegister) } + None => panic!( + "UNKNOWN rt={}, reg={} name={}", + rt, + reg, + icc_reg_name(reg).unwrap() + ), } - EC_AA64_BKPT => { - debug!("BRK exit"); - Ok(VcpuExit::Breakpoint) + } else { + assert!(rt < 32); + + // See https://developer.arm.com/documentation/dui0801/l/Overview-of-AArch64-state/Registers-in-AArch64-state + let val = if rt == 31 { 0u64 } else { self.read_reg(rt)? }; + + if vcpu_list.handle_sysreg_write(self.vcpuid, reg, val) { + Ok(VcpuExit::SystemRegister) + } else { + panic!( + "unexpected write: {} name={}", + reg, + icc_reg_name(reg).unwrap_or("non-ICC reg") + ) } - EC_WFX_TRAP => { - debug!("WFX exit"); - let ctl = self.read_sys_reg(hv_sys_reg_t_HV_SYS_REG_CNTV_CTL_EL0)?; - - self.pending_advance_pc = true; - if ((ctl & 1) == 0) || (ctl & 2) != 0 { - Ok(VcpuExit::WaitForEvent) - } else { - let cval = self.read_sys_reg(hv_sys_reg_t_HV_SYS_REG_CNTV_CVAL_EL0)?; - let now = unsafe { mach_absolute_time() }; - - if now > cval { - Ok(VcpuExit::WaitForEventExpired) - } else { - let timeout = Duration::from_nanos( - (cval - now) * (1_000_000_000 / self.cntfrq), - ); - Ok(VcpuExit::WaitForEventTimeout(timeout)) - } - } + } + } + EC_WFX_TRAP => { + let ctl = self.read_sys_reg(hv_sys_reg_t_HV_SYS_REG_CNTV_CTL_EL0)?; + + self.pending_advance_pc = true; + if ((ctl & 1) == 0) || (ctl & 2) != 0 { + return Ok(VcpuExit::WaitForEvent); + } + + // Also CNTV_CVAL & CNTV_CVAL_EL0 + let cval = self.read_sys_reg(hv_sys_reg_t_HV_SYS_REG_CNTV_CVAL_EL0)?; + let now = unsafe { mach_absolute_time() }; + if now > cval { + return Ok(VcpuExit::WaitForEventExpired); + } + + let timeout = Duration::from_nanos((cval - now) * (1_000_000_000 / self.cntfrq)); + Ok(VcpuExit::WaitForEventTimeout(timeout)) + } + EC_AA64_HVC => { + match self.read_reg(hv_reg_t_HV_REG_X0)? { + 0x8400_0000 /* QEMU_PSCI_0_2_FN_PSCI_VERSION */ => { + self.write_reg(hv_reg_t_HV_REG_X0, 2)?; + Ok(VcpuExit::HypervisorCall) + }, + 0x8400_0006 /* QEMU_PSCI_0_2_FN_MIGRATE_INFO_TYPE */ => { + self.write_reg(hv_reg_t_HV_REG_X0, 2)?; + Ok(VcpuExit::HypervisorCall) + }, + 0x8400_0008 /* QEMU_PSCI_0_2_FN_SYSTEM_OFF */ => { + Ok(VcpuExit::Shutdown) + }, + 0x8400_0009 /* QEMU_PSCI_0_2_FN_SYSTEM_RESET */ => { + Ok(VcpuExit::Shutdown) + }, + 0xc400_0003 /* QEMU_PSCI_0_2_FN64_CPU_ON */ => { + let mpidr = self.read_reg(hv_reg_t_HV_REG_X1)?; + let entry = self.read_reg(hv_reg_t_HV_REG_X2)?; + let context_id = self.read_reg(hv_reg_t_HV_REG_X3)?; + self.write_reg(hv_reg_t_HV_REG_X0, 0)?; + Ok(VcpuExit::CpuOn(mpidr, entry, context_id)) } - _ => panic!("unexpected exception: 0x{ec:x}"), + val => panic!("Unexpected val={}", val) } } - HV_EXIT_REASON_VTIMER_ACTIVATED => Ok(VcpuExit::VtimerActivated), - _ => { - let pc = self.read_reg(hv_reg_t_HV_REG_PC)?; - panic!( - "unexpected exit reason: vcpuid={} 0x{:x} at pc=0x{:x}", - self.id(), - self.vcpu_exit.reason, - pc - ); + EC_AA64_SMC => { + self.pending_advance_pc = true; + Ok(VcpuExit::SecureMonitorCall) } + _ => panic!("unexpected exception: 0x{ec:x}"), } } } diff --git a/src/vmm/src/builder.rs b/src/vmm/src/builder.rs index 83120701..8eda82a1 100644 --- a/src/vmm/src/builder.rs +++ b/src/vmm/src/builder.rs @@ -19,9 +19,10 @@ use super::{Error, Vmm}; use crate::device_manager::legacy::PortIODeviceManager; use crate::device_manager::mmio::MMIODeviceManager; use crate::resources::VmResources; +use devices::legacy::GicV3; +use devices::legacy::Serial; #[cfg(target_os = "macos")] use devices::legacy::VcpuList; -use devices::legacy::{Gic, Serial}; #[cfg(feature = "net")] use devices::virtio::Net; use devices::virtio::{port_io, MmioTransport, PortDescription, Vsock}; @@ -525,12 +526,15 @@ pub fn build_microvm( ); #[cfg(target_os = "macos")] - let vcpu_list = Arc::new(VcpuList::new()); + let vcpu_list = { + let cpu_count = vm_resources.vm_config().vcpu_count.unwrap(); + Arc::new(VcpuList::new(cpu_count as u64)) + }; #[cfg(target_os = "linux")] let intc = None; #[cfg(target_os = "macos")] - let intc = Some(Arc::new(Mutex::new(Gic::new(vcpu_list.clone())))); + let intc = Some(GicV3::new(vcpu_list.clone())); #[cfg(all(target_os = "linux", target_arch = "x86_64", not(feature = "tee")))] let boot_ip: GuestAddress = GuestAddress(kernel_bundle.entry_addr); @@ -593,7 +597,6 @@ pub fn build_microvm( &guest_memory, start_addr, &exit_evt, - intc.clone().unwrap(), vcpu_list.clone(), ) .map_err(StartMicrovmError::Internal)?; @@ -1032,11 +1035,11 @@ fn attach_legacy_devices( vm: &Vm, mmio_device_manager: &mut MMIODeviceManager, kernel_cmdline: &mut kernel::cmdline::Cmdline, - intc: Option>>, + intc: Option, serial: Option>>, event_manager: &mut EventManager, shutdown_efd: Option, -) -> std::result::Result<(), StartMicrovmError> { +) -> Result<(), StartMicrovmError> { if let Some(serial) = serial { mmio_device_manager .register_mmio_serial(vm, kernel_cmdline, intc.clone(), serial) @@ -1056,7 +1059,7 @@ fn attach_legacy_devices( if let Some(shutdown_efd) = shutdown_efd { mmio_device_manager - .register_mmio_gpio(vm, intc, event_manager, shutdown_efd) + .register_mmio_gpio(vm, intc.clone(), event_manager, shutdown_efd) .map_err(Error::RegisterMMIODevice) .map_err(StartMicrovmError::Internal)?; } @@ -1125,7 +1128,6 @@ fn create_vcpus_aarch64( guest_mem: &GuestMemoryMmap, entry_addr: GuestAddress, exit_evt: &EventFd, - intc: Arc>, vcpu_list: Arc, ) -> super::Result> { let mut vcpus = Vec::with_capacity(vcpu_config.vcpu_count as usize); @@ -1145,7 +1147,6 @@ fn create_vcpus_aarch64( entry_addr, boot_receiver, exit_evt.try_clone().map_err(Error::EventFd)?, - intc.clone(), vcpu_list.clone(), ) .map_err(Error::Vcpu)?; @@ -1195,7 +1196,7 @@ fn attach_fs_devices( fs_devs: &[FsDeviceConfig], shm_manager: &mut ShmManager, #[cfg(not(feature = "tee"))] export_table: Option, - intc: Option>>, + intc: Option, #[cfg(target_os = "macos")] map_sender: Sender, ) -> std::result::Result<(), StartMicrovmError> { use self::StartMicrovmError::*; @@ -1245,7 +1246,7 @@ fn attach_fs_devices( fn attach_console_devices( vmm: &mut Vmm, event_manager: &mut EventManager, - intc: Option>>, + intc: Option, console_output: Option, ) -> std::result::Result<(), StartMicrovmError> { use self::StartMicrovmError::*; @@ -1345,7 +1346,7 @@ fn attach_console_devices( fn attach_net_devices<'a>( vmm: &mut Vmm, net_devices: impl Iterator>>, - intc: Option>>, + intc: Option, ) -> Result<(), StartMicrovmError> { for net_device in net_devices { let id = net_device.lock().unwrap().id().to_string(); @@ -1368,7 +1369,7 @@ fn attach_unixsock_vsock_device( vmm: &mut Vmm, unix_vsock: &Arc>, event_manager: &mut EventManager, - intc: Option>>, + intc: Option, ) -> std::result::Result<(), StartMicrovmError> { use self::StartMicrovmError::*; @@ -1397,7 +1398,7 @@ fn attach_unixsock_vsock_device( fn attach_balloon_device( vmm: &mut Vmm, event_manager: &mut EventManager, - intc: Option>>, + intc: Option, ) -> std::result::Result<(), StartMicrovmError> { use self::StartMicrovmError::*; @@ -1428,7 +1429,7 @@ fn attach_balloon_device( fn attach_block_devices( vmm: &mut Vmm, block_devs: &BlockBuilder, - intc: Option>>, + intc: Option, ) -> std::result::Result<(), StartMicrovmError> { use self::StartMicrovmError::*; @@ -1455,7 +1456,7 @@ fn attach_block_devices( fn attach_rng_device( vmm: &mut Vmm, event_manager: &mut EventManager, - intc: Option>>, + intc: Option, ) -> std::result::Result<(), StartMicrovmError> { use self::StartMicrovmError::*; @@ -1484,7 +1485,7 @@ fn attach_gpu_device( event_manager: &mut EventManager, shm_manager: &mut ShmManager, #[cfg(not(feature = "tee"))] mut export_table: Option, - intc: Option>>, + intc: Option, virgl_flags: u32, #[cfg(target_os = "macos")] map_sender: Sender, ) -> std::result::Result<(), StartMicrovmError> { @@ -1535,7 +1536,7 @@ fn attach_gpu_device( #[cfg(feature = "snd")] fn attach_snd_device( vmm: &mut Vmm, - intc: Option>>, + intc: Option, ) -> std::result::Result<(), StartMicrovmError> { use self::StartMicrovmError::*; diff --git a/src/vmm/src/device_manager/hvf/mmio.rs b/src/vmm/src/device_manager/hvf/mmio.rs index f0e7e031..f506324b 100644 --- a/src/vmm/src/device_manager/hvf/mmio.rs +++ b/src/vmm/src/device_manager/hvf/mmio.rs @@ -14,7 +14,7 @@ use arch::aarch64::DeviceInfoForFDT; use arch::DeviceType; use devices; -use devices::legacy::Gic; +use devices::legacy::GicV3; use devices::BusDevice; use kernel::cmdline as kernel_cmdline; use polly::event_manager::EventManager; @@ -85,6 +85,7 @@ impl MMIODeviceManager { if cfg!(target_arch = "aarch64") { *mmio_base += MMIO_LEN; } + MMIODeviceManager { mmio_base: *mmio_base, irq: irq_interval.0, @@ -141,7 +142,7 @@ impl MMIODeviceManager { &mut self, _vm: &Vm, cmdline: &mut kernel_cmdline::Cmdline, - intc: Option>>, + intc: Option, serial: Arc>, ) -> Result<()> { if self.irq > self.last_irq { @@ -183,7 +184,7 @@ impl MMIODeviceManager { #[cfg(target_arch = "aarch64")] /// Register a MMIO RTC device. - pub fn register_mmio_rtc(&mut self, _vm: &Vm, _intc: Option>>) -> Result<()> { + pub fn register_mmio_rtc(&mut self, _vm: &Vm, _intc: Option) -> Result<()> { if self.irq > self.last_irq { return Err(Error::IrqsExhausted); } @@ -217,7 +218,7 @@ impl MMIODeviceManager { pub fn register_mmio_gpio( &mut self, _vm: &Vm, - intc: Option>>, + intc: Option, event_manager: &mut EventManager, shutdown_efd: EventFd, ) -> Result<()> { @@ -262,14 +263,12 @@ impl MMIODeviceManager { #[cfg(target_arch = "aarch64")] /// Register a MMIO GIC device. - pub fn register_mmio_gic(&mut self, _vm: &Vm, intc: Option>>) -> Result<()> { + pub fn register_mmio_gic(&mut self, _vm: &Vm, intc: Option) -> Result<()> { if let Some(intc) = intc { + let mmio_addr = intc.get_mmio_addr(); + let mmio_size = intc.get_mmio_size(); self.bus - .insert( - intc, - devices::legacy::Gic::get_addr(), - devices::legacy::Gic::get_size(), - ) + .insert(intc.as_device(), mmio_addr, mmio_size) .map_err(Error::BusError)?; } @@ -282,7 +281,7 @@ impl MMIODeviceManager { &self.id_to_dev_info } - /// Gets the the specified device. + /// Gets the specified device. pub fn get_device( &self, device_type: DeviceType, diff --git a/src/vmm/src/macos/vstate.rs b/src/vmm/src/macos/vstate.rs index ffbcb098..66de5a13 100644 --- a/src/vmm/src/macos/vstate.rs +++ b/src/vmm/src/macos/vstate.rs @@ -10,7 +10,7 @@ use std::fmt::{Display, Formatter}; use std::io; use std::result; #[cfg(not(test))] -use std::sync::{Arc, Mutex}; +use std::sync::Arc; use std::thread; use std::time::Duration; @@ -19,8 +19,8 @@ use crate::vmm_config::machine_config::CpuFeaturesTemplate; use arch::aarch64::gic::GICDevice; use crossbeam_channel::{unbounded, Receiver, RecvTimeoutError, Sender}; -use devices::legacy::{Gic, VcpuList}; -use hvf::{HvfVcpu, HvfVm, VcpuExit}; +use devices::legacy::VcpuList; +use hvf::{HvfVcpu, HvfVm, VcpuExit, Vcpus}; use utils::eventfd::EventFd; use vm_memory::{ Address, GuestAddress, GuestMemory, GuestMemoryError, GuestMemoryMmap, GuestMemoryRegion, @@ -218,7 +218,6 @@ pub struct Vcpu { // The transmitting end of the responses channel owned by the vcpu side. response_sender: Sender, - intc: Arc>, vcpu_list: Arc, } @@ -292,7 +291,6 @@ impl Vcpu { boot_entry_addr: GuestAddress, boot_receiver: Option>, exit_evt: EventFd, - intc: Arc>, vcpu_list: Arc, ) -> Result { let (event_sender, event_receiver) = unbounded(); @@ -311,7 +309,6 @@ impl Vcpu { event_sender: Some(event_sender), response_receiver: Some(response_receiver), response_sender, - intc, vcpu_list, }) } @@ -343,7 +340,7 @@ impl Vcpu { /// * `guest_mem` - The guest memory used by this microvm. /// * `kernel_load_addr` - Offset from `guest_mem` at which the kernel is loaded. pub fn configure_aarch64(&mut self, guest_mem: &GuestMemoryMmap) -> Result<()> { - self.mpidr = (self.id as u64) << 8; + self.mpidr = self.id as u64; self.fdt_addr = arch::aarch64::get_fdt_addr(guest_mem); Ok(()) @@ -380,9 +377,8 @@ impl Vcpu { /// Returns error or enum specifying whether emulation was handled or interrupted. fn run_emulation(&mut self, hvf_vcpu: &mut HvfVcpu) -> Result { let vcpuid = hvf_vcpu.id(); - let pending_irq = self.vcpu_list.has_pending_irq(vcpuid); - match hvf_vcpu.run(pending_irq) { + match hvf_vcpu.run(self.vcpu_list.clone()) { Ok(exit) => match exit { VcpuExit::Breakpoint => { debug!("vCPU {} breakpoint", vcpuid); @@ -397,7 +393,9 @@ impl Vcpu { "CpuOn: mpidr=0x{:x} entry=0x{:x} context_id={}", mpidr, entry, context_id ); - let cpuid: usize = (mpidr >> 8) as usize; + // assuming a flat CPU hierarchy, only the bottom bits of mpidr should be used, + // and cpuid == mpidr + let cpuid: usize = mpidr as usize; if let Some(boot_senders) = &self.boot_senders { if let Some(sender) = boot_senders.get(cpuid - 1) { sender.send(entry).unwrap() @@ -411,6 +409,7 @@ impl Vcpu { } VcpuExit::MmioRead(addr, data) => { if let Some(ref mmio_bus) = self.mmio_bus { + debug!("vCPU {} MMIO read 0x{:x}", vcpuid, addr); mmio_bus.read(vcpuid, addr, data); } Ok(VcpuEmulation::Handled) @@ -451,9 +450,7 @@ impl Vcpu { Ok(VcpuEmulation::WaitForEventTimeout(duration)) } }, - Err(e) => { - panic!("Error running HVF vCPU: {:?}", e); - } + Err(e) => panic!("Error running HVF vCPU: {:?}", e), } } @@ -468,7 +465,6 @@ impl Vcpu { let (wfe_sender, wfe_receiver) = unbounded(); self.vcpu_list.register(hvf_vcpuid, wfe_sender); - self.intc.lock().unwrap().add_vcpu(); let entry_addr = if let Some(boot_receiver) = &self.boot_receiver { boot_receiver.recv().unwrap()