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 <hsuan.hsu@mediatek.com>
Reviewed-on: https://gem5-review.googlesource.com/c/public/gem5/+/30921
Tested-by: kokoro <noreply+kokoro@google.com>
Reviewed-by: Andreas Sandberg <andreas.sandberg@arm.com>
Maintainer: Andreas Sandberg <andreas.sandberg@arm.com>
This commit is contained in:
Hsuan Hsu
2020-07-05 22:56:07 +08:00
committed by Hsuan Hsu
parent d4e9a34590
commit 6616354423
2 changed files with 49 additions and 2 deletions

View File

@@ -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<ArmSystem *>(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 &

View File

@@ -41,6 +41,7 @@
#include <vector>
#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<uint64_t> RegIndexVector;