From 66163544231428d88b876a462da33842aa6f340b Mon Sep 17 00:00:00 2001 From: Hsuan Hsu Date: Sun, 5 Jul 2020 22:56:07 +0800 Subject: [PATCH] arch-arm: Add basic support for KVM_CAP_ARM_USER_IRQ KVM_CAP_ARM_USER_IRQ is a KVM extension introduced in newer versions of Linux (>= 4.12). It supports delivering interrupt from the kernel-space timer to the user-space GIC, which means that it will be unnecessary to use the memory-mapped timer and emulate it in gem5 anymore. Using the option provided by this change, Linux is able to boot with 1 CPU successfully, and the speed is slightly faster then the memory- mapped timer option. However, multicore seems to hang during boot and still needs more investigation to be enabled. JIRA: https://gem5.atlassian.net/browse/GEM5-663 Change-Id: I146bbcce3cf66f8f5ebee04ea5f1b9f54868721a Signed-off-by: Hsuan Hsu Reviewed-on: https://gem5-review.googlesource.com/c/public/gem5/+/30921 Tested-by: kokoro Reviewed-by: Andreas Sandberg Maintainer: Andreas Sandberg --- src/arch/arm/kvm/base_cpu.cc | 33 +++++++++++++++++++++++++++++++-- src/arch/arm/kvm/base_cpu.hh | 18 ++++++++++++++++++ 2 files changed, 49 insertions(+), 2 deletions(-) diff --git a/src/arch/arm/kvm/base_cpu.cc b/src/arch/arm/kvm/base_cpu.cc index c028106b3..04c5d0fd0 100644 --- a/src/arch/arm/kvm/base_cpu.cc +++ b/src/arch/arm/kvm/base_cpu.cc @@ -41,7 +41,9 @@ #include "arch/arm/interrupts.hh" #include "debug/KvmInt.hh" +#include "dev/arm/generic_timer.hh" #include "params/BaseArmKvmCPU.hh" +#include "params/GenericTimer.hh" #define INTERRUPT_ID(type, vcpu, irq) ( \ ((type) << KVM_ARM_IRQ_TYPE_SHIFT) | \ @@ -57,7 +59,8 @@ BaseArmKvmCPU::BaseArmKvmCPU(BaseArmKvmCPUParams *params) : BaseKvmCPU(params), - irqAsserted(false), fiqAsserted(false) + irqAsserted(false), fiqAsserted(false), + virtTimerPin(nullptr), prevDeviceIRQLevel(0) { } @@ -82,6 +85,10 @@ BaseArmKvmCPU::startup() target_config.features[0] |= (1 << KVM_ARM_VCPU_EL1_32BIT); } kvmArmVCpuInit(target_config); + + if (!vm.hasKernelIRQChip()) + virtTimerPin = static_cast(system)\ + ->getGenericTimer()->params()->int_virt->get(tc); } Tick @@ -113,7 +120,29 @@ BaseArmKvmCPU::kvmRun(Tick ticks) irqAsserted = simIRQ; fiqAsserted = simFIQ; - return BaseKvmCPU::kvmRun(ticks); + Tick kvmRunTicks = BaseKvmCPU::kvmRun(ticks); + + if (!vm.hasKernelIRQChip()) { + uint64_t device_irq_level = + getKvmRunState()->s.regs.device_irq_level; + + if (!(prevDeviceIRQLevel & KVM_ARM_DEV_EL1_VTIMER) && + (device_irq_level & KVM_ARM_DEV_EL1_VTIMER)) { + + DPRINTF(KvmInt, "In-kernel vtimer IRQ asserted\n"); + prevDeviceIRQLevel |= KVM_ARM_DEV_EL1_VTIMER; + virtTimerPin->raise(); + + } else if ((prevDeviceIRQLevel & KVM_ARM_DEV_EL1_VTIMER) && + !(device_irq_level & KVM_ARM_DEV_EL1_VTIMER)) { + + DPRINTF(KvmInt, "In-kernel vtimer IRQ disasserted\n"); + prevDeviceIRQLevel &= ~KVM_ARM_DEV_EL1_VTIMER; + virtTimerPin->clear(); + } + } + + return kvmRunTicks; } const BaseArmKvmCPU::RegIndexVector & diff --git a/src/arch/arm/kvm/base_cpu.hh b/src/arch/arm/kvm/base_cpu.hh index 4ee35ca92..028cd3903 100644 --- a/src/arch/arm/kvm/base_cpu.hh +++ b/src/arch/arm/kvm/base_cpu.hh @@ -41,6 +41,7 @@ #include #include "cpu/kvm/base.hh" +#include "dev/arm/base_gic.hh" struct BaseArmKvmCPUParams; @@ -61,6 +62,23 @@ class BaseArmKvmCPU : public BaseKvmCPU /** Cached state of the FIQ line */ bool fiqAsserted; + /** + * If the user-space GIC and the kernel-space timer are used + * simultaneously, set up this interrupt pin to forward interrupt from + * the timer to the GIC when timer IRQ level change is intercepted. + */ + ArmInterruptPin *virtTimerPin; + + /** + * KVM records whether each in-kernel device IRQ is asserted or + * disasserted in the kvmRunState->s.regs.device_irq_level bit map, + * and guarantees at least one KVM exit when the level changes. We + * use only the KVM_ARM_DEV_EL1_VTIMER bit field currently to track + * the level of the in-kernel timer, and preserve the last level in + * this class member. + */ + uint64_t prevDeviceIRQLevel; + protected: typedef std::vector RegIndexVector; -- 2.30.2