mirror of
https://github.com/Motorhead1991/qemu.git
synced 2025-08-08 18:23:57 -06:00
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 interrupt events which are only available from kernel space, such as the timer. This patch leverages the new kernel/user space pending line synchronization for timer events. It does not handle PMU events yet. Signed-off-by: Alexander Graf <agraf@suse.de> Reviewed-by: Andrew Jones <drjones@redhat.com> Message-id: 1498577737-130264-1-git-send-email-agraf@suse.de Signed-off-by: Peter Maydell <peter.maydell@linaro.org>
This commit is contained in:
parent
f986ee1d43
commit
5d721b785f
6 changed files with 82 additions and 0 deletions
|
@ -706,6 +706,9 @@ struct ARMCPU {
|
|||
void *el_change_hook_opaque;
|
||||
|
||||
int32_t node_id; /* NUMA node this CPU belongs to */
|
||||
|
||||
/* Used to synchronize KVM and QEMU in-kernel device levels */
|
||||
uint8_t device_irq_level;
|
||||
};
|
||||
|
||||
static inline ARMCPU *arm_env_get_cpu(CPUARMState *env)
|
||||
|
|
|
@ -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,6 +534,51 @@ void kvm_arch_pre_run(CPUState *cs, struct kvm_run *run)
|
|||
|
||||
MemTxAttrs kvm_arch_post_run(CPUState *cs, struct kvm_run *run)
|
||||
{
|
||||
ARMCPU *cpu;
|
||||
uint32_t switched_level;
|
||||
|
||||
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 shadowed in-kernel device irq lines with the kvm ones */
|
||||
if (run->s.regs.device_irq_level != cpu->device_irq_level) {
|
||||
switched_level = cpu->device_irq_level ^ run->s.regs.device_irq_level;
|
||||
|
||||
qemu_mutex_lock_iothread();
|
||||
|
||||
if (switched_level & KVM_ARM_DEV_EL1_VTIMER) {
|
||||
qemu_set_irq(cpu->gt_timer_outputs[GTIMER_VIRT],
|
||||
!!(run->s.regs.device_irq_level &
|
||||
KVM_ARM_DEV_EL1_VTIMER));
|
||||
switched_level &= ~KVM_ARM_DEV_EL1_VTIMER;
|
||||
}
|
||||
|
||||
if (switched_level & KVM_ARM_DEV_EL1_PTIMER) {
|
||||
qemu_set_irq(cpu->gt_timer_outputs[GTIMER_PHYS],
|
||||
!!(run->s.regs.device_irq_level &
|
||||
KVM_ARM_DEV_EL1_PTIMER));
|
||||
switched_level &= ~KVM_ARM_DEV_EL1_PTIMER;
|
||||
}
|
||||
|
||||
/* XXX PMU IRQ is missing */
|
||||
|
||||
if (switched_level) {
|
||||
qemu_log_mask(LOG_UNIMP, "%s: unhandled in-kernel device IRQ %x\n",
|
||||
__func__, switched_level);
|
||||
}
|
||||
|
||||
/* We also mark unknown levels as processed to not waste cycles */
|
||||
cpu->device_irq_level = run->s.regs.device_irq_level;
|
||||
qemu_mutex_unlock_iothread();
|
||||
}
|
||||
|
||||
return MEMTXATTRS_UNSPECIFIED;
|
||||
}
|
||||
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue