mirror of
https://gitee.com/openharmony/kernel_linux
synced 2025-05-24 15:46:39 +00:00

Current KVM world switch code is unintentionally setting wrong bits to CNTHCTL_EL2 when E2H == 1, which may allow guest OS to access physical timer. Bit positions of CNTHCTL_EL2 are changing depending on HCR_EL2.E2H bit. EL1PCEN and EL1PCTEN are 1st and 0th bits when E2H is not set, but they are 11th and 10th bits respectively when E2H is set. In fact, on VHE we only need to set those bits once, not for every world switch. This is because the host kernel runs in EL2 with HCR_EL2.TGE == 1, which makes those bits have no effect for the host kernel execution. So we just set those bits once for guests, and that's it. Signed-off-by: Jintack Lim <jintack@cs.columbia.edu> Reviewed-by: Marc Zyngier <marc.zyngier@arm.com> Signed-off-by: Marc Zyngier <marc.zyngier@arm.com>
78 lines
2.2 KiB
C
78 lines
2.2 KiB
C
/*
|
|
* Copyright (C) 2012-2015 - ARM Ltd
|
|
* Author: Marc Zyngier <marc.zyngier@arm.com>
|
|
*
|
|
* This program is free software; you can redistribute it and/or modify
|
|
* it under the terms of the GNU General Public License version 2 as
|
|
* published by the Free Software Foundation.
|
|
*
|
|
* This program is distributed in the hope that it will be useful,
|
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
* GNU General Public License for more details.
|
|
*
|
|
* You should have received a copy of the GNU General Public License
|
|
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
|
*/
|
|
|
|
#include <clocksource/arm_arch_timer.h>
|
|
#include <linux/compiler.h>
|
|
#include <linux/kvm_host.h>
|
|
|
|
#include <asm/kvm_hyp.h>
|
|
|
|
/* vcpu is already in the HYP VA space */
|
|
void __hyp_text __timer_save_state(struct kvm_vcpu *vcpu)
|
|
{
|
|
struct arch_timer_cpu *timer = &vcpu->arch.timer_cpu;
|
|
u64 val;
|
|
|
|
if (timer->enabled) {
|
|
timer->cntv_ctl = read_sysreg_el0(cntv_ctl);
|
|
timer->cntv_cval = read_sysreg_el0(cntv_cval);
|
|
}
|
|
|
|
/* Disable the virtual timer */
|
|
write_sysreg_el0(0, cntv_ctl);
|
|
|
|
/*
|
|
* We don't need to do this for VHE since the host kernel runs in EL2
|
|
* with HCR_EL2.TGE ==1, which makes those bits have no impact.
|
|
*/
|
|
if (!has_vhe()) {
|
|
/* Allow physical timer/counter access for the host */
|
|
val = read_sysreg(cnthctl_el2);
|
|
val |= CNTHCTL_EL1PCTEN | CNTHCTL_EL1PCEN;
|
|
write_sysreg(val, cnthctl_el2);
|
|
}
|
|
|
|
/* Clear cntvoff for the host */
|
|
write_sysreg(0, cntvoff_el2);
|
|
}
|
|
|
|
void __hyp_text __timer_restore_state(struct kvm_vcpu *vcpu)
|
|
{
|
|
struct kvm *kvm = kern_hyp_va(vcpu->kvm);
|
|
struct arch_timer_cpu *timer = &vcpu->arch.timer_cpu;
|
|
u64 val;
|
|
|
|
/* Those bits are already configured at boot on VHE-system */
|
|
if (!has_vhe()) {
|
|
/*
|
|
* Disallow physical timer access for the guest
|
|
* Physical counter access is allowed
|
|
*/
|
|
val = read_sysreg(cnthctl_el2);
|
|
val &= ~CNTHCTL_EL1PCEN;
|
|
val |= CNTHCTL_EL1PCTEN;
|
|
write_sysreg(val, cnthctl_el2);
|
|
}
|
|
|
|
if (timer->enabled) {
|
|
write_sysreg(kvm->arch.timer.cntvoff, cntvoff_el2);
|
|
write_sysreg_el0(timer->cntv_cval, cntv_cval);
|
|
isb();
|
|
write_sysreg_el0(timer->cntv_ctl, cntv_ctl);
|
|
}
|
|
}
|