2017-04-28 13:47:25 +02:00
|
|
|
From 3227a2bdc7a494194a6a4f7d5653ff178ecb1b2f Mon Sep 17 00:00:00 2001
|
2017-04-12 21:10:15 +02:00
|
|
|
From: Alexander Graf <agraf@suse.de>
|
|
|
|
Date: Mon, 19 Sep 2016 10:02:55 +0200
|
|
|
|
Subject: [PATCH] ARM: KVM: Enable in-kernel timers with user space gic
|
|
|
|
|
|
|
|
When running with KVM enabled, you can choose between emulating the
|
|
|
|
gic in kernel or user space. If the kernel supports in-kernel virtualization
|
|
|
|
of the interrupt controller, it will default to that. If not, if will
|
|
|
|
default to user space emulation.
|
|
|
|
|
|
|
|
Unfortunately when running in user mode gic emulation, we miss out on
|
|
|
|
timer events which are only available from kernel space. This patch leverages
|
|
|
|
the new kernel/user space pending line synchronization for those timer events.
|
|
|
|
|
|
|
|
Signed-off-by: Alexander Graf <agraf@suse.de>
|
|
|
|
---
|
|
|
|
hw/arm/virt.c | 10 ++++++++++
|
|
|
|
target/arm/cpu.h | 3 +++
|
|
|
|
target/arm/kvm.c | 31 ++++++++++++++++++++++++++++++-
|
|
|
|
3 files changed, 43 insertions(+), 1 deletion(-)
|
|
|
|
|
|
|
|
diff --git a/hw/arm/virt.c b/hw/arm/virt.c
|
2017-04-28 13:47:25 +02:00
|
|
|
index 5f62a03..a1d24a4 100644
|
2017-04-12 21:10:15 +02:00
|
|
|
--- a/hw/arm/virt.c
|
|
|
|
+++ b/hw/arm/virt.c
|
|
|
|
@@ -609,6 +609,16 @@ static void create_gic(VirtMachineState *vms, qemu_irq *pic)
|
|
|
|
} else if (type == 2) {
|
|
|
|
create_v2m(vms, pic);
|
|
|
|
}
|
|
|
|
+
|
|
|
|
+#ifdef CONFIG_KVM
|
|
|
|
+ if (kvm_enabled() && !kvm_irqchip_in_kernel()) {
|
|
|
|
+ if (!kvm_check_extension(kvm_state, KVM_CAP_ARM_USER_IRQ)) {
|
|
|
|
+ error_report("KVM with user space irqchip only works when the "
|
|
|
|
+ "host kernel supports KVM_CAP_ARM_USER_IRQ");
|
|
|
|
+ exit(1);
|
|
|
|
+ }
|
|
|
|
+ }
|
|
|
|
+#endif
|
|
|
|
}
|
|
|
|
|
|
|
|
static void create_uart(const VirtMachineState *vms, qemu_irq *pic, int uart,
|
|
|
|
diff --git a/target/arm/cpu.h b/target/arm/cpu.h
|
2017-04-28 13:47:25 +02:00
|
|
|
index a8aabce..19cb596 100644
|
2017-04-12 21:10:15 +02:00
|
|
|
--- a/target/arm/cpu.h
|
|
|
|
+++ b/target/arm/cpu.h
|
|
|
|
@@ -702,6 +702,9 @@ struct ARMCPU {
|
|
|
|
|
|
|
|
ARMELChangeHook *el_change_hook;
|
|
|
|
void *el_change_hook_opaque;
|
|
|
|
+
|
|
|
|
+ /* Used to synchronize KVM and QEMU timer levels */
|
|
|
|
+ uint64_t device_irq_level;
|
|
|
|
};
|
|
|
|
|
|
|
|
static inline ARMCPU *arm_env_get_cpu(CPUARMState *env)
|
|
|
|
diff --git a/target/arm/kvm.c b/target/arm/kvm.c
|
2017-04-28 13:47:25 +02:00
|
|
|
index 4555468..dccb326 100644
|
2017-04-12 21:10:15 +02:00
|
|
|
--- a/target/arm/kvm.c
|
|
|
|
+++ b/target/arm/kvm.c
|
|
|
|
@@ -174,6 +174,12 @@ int kvm_arch_init(MachineState *ms, KVMState *s)
|
|
|
|
*/
|
|
|
|
kvm_async_interrupts_allowed = true;
|
|
|
|
|
|
|
|
+ /*
|
|
|
|
+ * PSCI wakes up secondary cores, so we always need to
|
|
|
|
+ * have vCPUs waiting in kernel space
|
|
|
|
+ */
|
|
|
|
+ kvm_halt_in_kernel_allowed = true;
|
|
|
|
+
|
|
|
|
cap_has_mp_state = kvm_check_extension(s, KVM_CAP_MP_STATE);
|
|
|
|
|
|
|
|
type_register_static(&host_arm_cpu_type_info);
|
|
|
|
@@ -528,10 +534,33 @@ void kvm_arch_pre_run(CPUState *cs, struct kvm_run *run)
|
|
|
|
|
|
|
|
MemTxAttrs kvm_arch_post_run(CPUState *cs, struct kvm_run *run)
|
|
|
|
{
|
|
|
|
+ ARMCPU *cpu;
|
|
|
|
+
|
|
|
|
+ if (kvm_irqchip_in_kernel()) {
|
|
|
|
+ /*
|
|
|
|
+ * We only need to sync timer states with user-space interrupt
|
|
|
|
+ * controllers, so return early and save cycles if we don't.
|
|
|
|
+ */
|
|
|
|
+ return MEMTXATTRS_UNSPECIFIED;
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+ cpu = ARM_CPU(cs);
|
|
|
|
+
|
|
|
|
+ /* Synchronize our internal vtimer irq line with the kvm one */
|
|
|
|
+ if (run->s.regs.device_irq_level != cpu->device_irq_level) {
|
|
|
|
+ qemu_mutex_lock_iothread();
|
|
|
|
+ qemu_set_irq(cpu->gt_timer_outputs[GTIMER_VIRT],
|
|
|
|
+ run->s.regs.device_irq_level & KVM_ARM_DEV_EL1_VTIMER);
|
|
|
|
+ qemu_set_irq(cpu->gt_timer_outputs[GTIMER_PHYS],
|
|
|
|
+ run->s.regs.device_irq_level & KVM_ARM_DEV_EL1_PTIMER);
|
|
|
|
+ /* TODO: Handle changes in PMU as well */
|
|
|
|
+ cpu->device_irq_level = run->s.regs.device_irq_level;
|
|
|
|
+ qemu_mutex_unlock_iothread();
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
return MEMTXATTRS_UNSPECIFIED;
|
|
|
|
}
|
|
|
|
|
|
|
|
-
|
|
|
|
int kvm_arch_handle_exit(CPUState *cs, struct kvm_run *run)
|
|
|
|
{
|
|
|
|
int ret = 0;
|