Merge branch kvm-arm64/oslock into kvmarm-master/next
* kvm-arm64/oslock: : . : Debug OS-Lock emulation courtesy of Oliver Upton. From the cover letter: : : "KVM does not implement the debug architecture to the letter of the : specification. One such issue is the fact that KVM treats the OS Lock as : RAZ/WI, rather than emulating its behavior on hardware. This series adds : emulation support for the OS Lock to KVM. Emulation is warranted as the : OS Lock affects debug exceptions taken from all ELs, and is not limited : to only the context of the guest." : . selftests: KVM: Test OS lock behavior selftests: KVM: Add OSLSR_EL1 to the list of blessed regs KVM: arm64: Emulate the OS Lock KVM: arm64: Allow guest to set the OSLK bit KVM: arm64: Stash OSLSR_EL1 in the cpu context KVM: arm64: Correctly treat writes to OSLSR_EL1 as undefined Signed-off-by: Marc Zyngier <maz@kernel.org>
This commit is contained in:
commit
b61fa004a4
@ -171,6 +171,7 @@ enum vcpu_sysreg {
|
||||
PAR_EL1, /* Physical Address Register */
|
||||
MDSCR_EL1, /* Monitor Debug System Control Register */
|
||||
MDCCINT_EL1, /* Monitor Debug Comms Channel Interrupt Enable Reg */
|
||||
OSLSR_EL1, /* OS Lock Status Register */
|
||||
DISR_EL1, /* Deferred Interrupt Status Register */
|
||||
|
||||
/* Performance Monitors Registers */
|
||||
@ -725,6 +726,10 @@ void kvm_arm_vcpu_init_debug(struct kvm_vcpu *vcpu);
|
||||
void kvm_arm_setup_debug(struct kvm_vcpu *vcpu);
|
||||
void kvm_arm_clear_debug(struct kvm_vcpu *vcpu);
|
||||
void kvm_arm_reset_debug_ptr(struct kvm_vcpu *vcpu);
|
||||
|
||||
#define kvm_vcpu_os_lock_enabled(vcpu) \
|
||||
(!!(__vcpu_sys_reg(vcpu, OSLSR_EL1) & SYS_OSLSR_OSLK))
|
||||
|
||||
int kvm_arm_vcpu_arch_set_attr(struct kvm_vcpu *vcpu,
|
||||
struct kvm_device_attr *attr);
|
||||
int kvm_arm_vcpu_arch_get_attr(struct kvm_vcpu *vcpu,
|
||||
|
@ -128,8 +128,16 @@
|
||||
#define SYS_DBGWVRn_EL1(n) sys_reg(2, 0, 0, n, 6)
|
||||
#define SYS_DBGWCRn_EL1(n) sys_reg(2, 0, 0, n, 7)
|
||||
#define SYS_MDRAR_EL1 sys_reg(2, 0, 1, 0, 0)
|
||||
|
||||
#define SYS_OSLAR_EL1 sys_reg(2, 0, 1, 0, 4)
|
||||
#define SYS_OSLAR_OSLK BIT(0)
|
||||
|
||||
#define SYS_OSLSR_EL1 sys_reg(2, 0, 1, 1, 4)
|
||||
#define SYS_OSLSR_OSLM_MASK (BIT(3) | BIT(0))
|
||||
#define SYS_OSLSR_OSLM_NI 0
|
||||
#define SYS_OSLSR_OSLM_IMPLEMENTED BIT(3)
|
||||
#define SYS_OSLSR_OSLK BIT(1)
|
||||
|
||||
#define SYS_OSDLR_EL1 sys_reg(2, 0, 1, 3, 4)
|
||||
#define SYS_DBGPRCR_EL1 sys_reg(2, 0, 1, 4, 4)
|
||||
#define SYS_DBGCLAIMSET_EL1 sys_reg(2, 0, 7, 8, 6)
|
||||
|
@ -105,9 +105,11 @@ static void kvm_arm_setup_mdcr_el2(struct kvm_vcpu *vcpu)
|
||||
* - Userspace is using the hardware to debug the guest
|
||||
* (KVM_GUESTDBG_USE_HW is set).
|
||||
* - The guest is not using debug (KVM_ARM64_DEBUG_DIRTY is clear).
|
||||
* - The guest has enabled the OS Lock (debug exceptions are blocked).
|
||||
*/
|
||||
if ((vcpu->guest_debug & KVM_GUESTDBG_USE_HW) ||
|
||||
!(vcpu->arch.flags & KVM_ARM64_DEBUG_DIRTY))
|
||||
!(vcpu->arch.flags & KVM_ARM64_DEBUG_DIRTY) ||
|
||||
kvm_vcpu_os_lock_enabled(vcpu))
|
||||
vcpu->arch.mdcr_el2 |= MDCR_EL2_TDA;
|
||||
|
||||
trace_kvm_arm_set_dreg32("MDCR_EL2", vcpu->arch.mdcr_el2);
|
||||
@ -160,8 +162,8 @@ void kvm_arm_setup_debug(struct kvm_vcpu *vcpu)
|
||||
|
||||
kvm_arm_setup_mdcr_el2(vcpu);
|
||||
|
||||
/* Is Guest debugging in effect? */
|
||||
if (vcpu->guest_debug) {
|
||||
/* Check if we need to use the debug registers. */
|
||||
if (vcpu->guest_debug || kvm_vcpu_os_lock_enabled(vcpu)) {
|
||||
/* Save guest debug state */
|
||||
save_guest_debug_regs(vcpu);
|
||||
|
||||
@ -223,6 +225,19 @@ void kvm_arm_setup_debug(struct kvm_vcpu *vcpu)
|
||||
trace_kvm_arm_set_regset("WAPTS", get_num_wrps(),
|
||||
&vcpu->arch.debug_ptr->dbg_wcr[0],
|
||||
&vcpu->arch.debug_ptr->dbg_wvr[0]);
|
||||
|
||||
/*
|
||||
* The OS Lock blocks debug exceptions in all ELs when it is
|
||||
* enabled. If the guest has enabled the OS Lock, constrain its
|
||||
* effects to the guest. Emulate the behavior by clearing
|
||||
* MDSCR_EL1.MDE. In so doing, we ensure that host debug
|
||||
* exceptions are unaffected by guest configuration of the OS
|
||||
* Lock.
|
||||
*/
|
||||
} else if (kvm_vcpu_os_lock_enabled(vcpu)) {
|
||||
mdscr = vcpu_read_sys_reg(vcpu, MDSCR_EL1);
|
||||
mdscr &= ~DBG_MDSCR_MDE;
|
||||
vcpu_write_sys_reg(vcpu, mdscr, MDSCR_EL1);
|
||||
}
|
||||
}
|
||||
|
||||
@ -244,7 +259,10 @@ void kvm_arm_clear_debug(struct kvm_vcpu *vcpu)
|
||||
{
|
||||
trace_kvm_arm_clear_debug(vcpu->guest_debug);
|
||||
|
||||
if (vcpu->guest_debug) {
|
||||
/*
|
||||
* Restore the guest's debug registers if we were using them.
|
||||
*/
|
||||
if (vcpu->guest_debug || kvm_vcpu_os_lock_enabled(vcpu)) {
|
||||
restore_guest_debug_regs(vcpu);
|
||||
|
||||
/*
|
||||
|
@ -44,6 +44,10 @@
|
||||
* 64bit interface.
|
||||
*/
|
||||
|
||||
static int reg_from_user(u64 *val, const void __user *uaddr, u64 id);
|
||||
static int reg_to_user(void __user *uaddr, const u64 *val, u64 id);
|
||||
static u64 sys_reg_to_index(const struct sys_reg_desc *reg);
|
||||
|
||||
static bool read_from_write_only(struct kvm_vcpu *vcpu,
|
||||
struct sys_reg_params *params,
|
||||
const struct sys_reg_desc *r)
|
||||
@ -287,16 +291,55 @@ static bool trap_loregion(struct kvm_vcpu *vcpu,
|
||||
return trap_raz_wi(vcpu, p, r);
|
||||
}
|
||||
|
||||
static bool trap_oslar_el1(struct kvm_vcpu *vcpu,
|
||||
struct sys_reg_params *p,
|
||||
const struct sys_reg_desc *r)
|
||||
{
|
||||
u64 oslsr;
|
||||
|
||||
if (!p->is_write)
|
||||
return read_from_write_only(vcpu, p, r);
|
||||
|
||||
/* Forward the OSLK bit to OSLSR */
|
||||
oslsr = __vcpu_sys_reg(vcpu, OSLSR_EL1) & ~SYS_OSLSR_OSLK;
|
||||
if (p->regval & SYS_OSLAR_OSLK)
|
||||
oslsr |= SYS_OSLSR_OSLK;
|
||||
|
||||
__vcpu_sys_reg(vcpu, OSLSR_EL1) = oslsr;
|
||||
return true;
|
||||
}
|
||||
|
||||
static bool trap_oslsr_el1(struct kvm_vcpu *vcpu,
|
||||
struct sys_reg_params *p,
|
||||
const struct sys_reg_desc *r)
|
||||
{
|
||||
if (p->is_write) {
|
||||
return ignore_write(vcpu, p);
|
||||
} else {
|
||||
p->regval = (1 << 3);
|
||||
return true;
|
||||
}
|
||||
if (p->is_write)
|
||||
return write_to_read_only(vcpu, p, r);
|
||||
|
||||
p->regval = __vcpu_sys_reg(vcpu, r->reg);
|
||||
return true;
|
||||
}
|
||||
|
||||
static int set_oslsr_el1(struct kvm_vcpu *vcpu, const struct sys_reg_desc *rd,
|
||||
const struct kvm_one_reg *reg, void __user *uaddr)
|
||||
{
|
||||
u64 id = sys_reg_to_index(rd);
|
||||
u64 val;
|
||||
int err;
|
||||
|
||||
err = reg_from_user(&val, uaddr, id);
|
||||
if (err)
|
||||
return err;
|
||||
|
||||
/*
|
||||
* The only modifiable bit is the OSLK bit. Refuse the write if
|
||||
* userspace attempts to change any other bit in the register.
|
||||
*/
|
||||
if ((val ^ rd->val) & ~SYS_OSLSR_OSLK)
|
||||
return -EINVAL;
|
||||
|
||||
__vcpu_sys_reg(vcpu, rd->reg) = val;
|
||||
return 0;
|
||||
}
|
||||
|
||||
static bool trap_dbgauthstatus_el1(struct kvm_vcpu *vcpu,
|
||||
@ -1164,10 +1207,6 @@ static bool access_raz_id_reg(struct kvm_vcpu *vcpu,
|
||||
return __access_id_reg(vcpu, p, r, true);
|
||||
}
|
||||
|
||||
static int reg_from_user(u64 *val, const void __user *uaddr, u64 id);
|
||||
static int reg_to_user(void __user *uaddr, const u64 *val, u64 id);
|
||||
static u64 sys_reg_to_index(const struct sys_reg_desc *reg);
|
||||
|
||||
/* Visibility overrides for SVE-specific control registers */
|
||||
static unsigned int sve_visibility(const struct kvm_vcpu *vcpu,
|
||||
const struct sys_reg_desc *rd)
|
||||
@ -1418,9 +1457,9 @@ static unsigned int mte_visibility(const struct kvm_vcpu *vcpu,
|
||||
* Debug handling: We do trap most, if not all debug related system
|
||||
* registers. The implementation is good enough to ensure that a guest
|
||||
* can use these with minimal performance degradation. The drawback is
|
||||
* that we don't implement any of the external debug, none of the
|
||||
* OSlock protocol. This should be revisited if we ever encounter a
|
||||
* more demanding guest...
|
||||
* that we don't implement any of the external debug architecture.
|
||||
* This should be revisited if we ever encounter a more demanding
|
||||
* guest...
|
||||
*/
|
||||
static const struct sys_reg_desc sys_reg_descs[] = {
|
||||
{ SYS_DESC(SYS_DC_ISW), access_dcsw },
|
||||
@ -1447,8 +1486,9 @@ static const struct sys_reg_desc sys_reg_descs[] = {
|
||||
DBG_BCR_BVR_WCR_WVR_EL1(15),
|
||||
|
||||
{ SYS_DESC(SYS_MDRAR_EL1), trap_raz_wi },
|
||||
{ SYS_DESC(SYS_OSLAR_EL1), trap_raz_wi },
|
||||
{ SYS_DESC(SYS_OSLSR_EL1), trap_oslsr_el1 },
|
||||
{ SYS_DESC(SYS_OSLAR_EL1), trap_oslar_el1 },
|
||||
{ SYS_DESC(SYS_OSLSR_EL1), trap_oslsr_el1, reset_val, OSLSR_EL1,
|
||||
SYS_OSLSR_OSLM_IMPLEMENTED, .set_user = set_oslsr_el1, },
|
||||
{ SYS_DESC(SYS_OSDLR_EL1), trap_raz_wi },
|
||||
{ SYS_DESC(SYS_DBGPRCR_EL1), trap_raz_wi },
|
||||
{ SYS_DESC(SYS_DBGCLAIMSET_EL1), trap_raz_wi },
|
||||
@ -1920,10 +1960,10 @@ static const struct sys_reg_desc cp14_regs[] = {
|
||||
|
||||
DBGBXVR(0),
|
||||
/* DBGOSLAR */
|
||||
{ Op1( 0), CRn( 1), CRm( 0), Op2( 4), trap_raz_wi },
|
||||
{ Op1( 0), CRn( 1), CRm( 0), Op2( 4), trap_oslar_el1 },
|
||||
DBGBXVR(1),
|
||||
/* DBGOSLSR */
|
||||
{ Op1( 0), CRn( 1), CRm( 1), Op2( 4), trap_oslsr_el1 },
|
||||
{ Op1( 0), CRn( 1), CRm( 1), Op2( 4), trap_oslsr_el1, NULL, OSLSR_EL1 },
|
||||
DBGBXVR(2),
|
||||
DBGBXVR(3),
|
||||
/* DBGOSDLR */
|
||||
|
@ -23,7 +23,7 @@
|
||||
#define SPSR_D (1 << 9)
|
||||
#define SPSR_SS (1 << 21)
|
||||
|
||||
extern unsigned char sw_bp, hw_bp, bp_svc, bp_brk, hw_wp, ss_start;
|
||||
extern unsigned char sw_bp, sw_bp2, hw_bp, hw_bp2, bp_svc, bp_brk, hw_wp, ss_start;
|
||||
static volatile uint64_t sw_bp_addr, hw_bp_addr;
|
||||
static volatile uint64_t wp_addr, wp_data_addr;
|
||||
static volatile uint64_t svc_addr;
|
||||
@ -47,6 +47,14 @@ static void reset_debug_state(void)
|
||||
isb();
|
||||
}
|
||||
|
||||
static void enable_os_lock(void)
|
||||
{
|
||||
write_sysreg(1, oslar_el1);
|
||||
isb();
|
||||
|
||||
GUEST_ASSERT(read_sysreg(oslsr_el1) & 2);
|
||||
}
|
||||
|
||||
static void install_wp(uint64_t addr)
|
||||
{
|
||||
uint32_t wcr;
|
||||
@ -99,6 +107,7 @@ static void guest_code(void)
|
||||
GUEST_SYNC(0);
|
||||
|
||||
/* Software-breakpoint */
|
||||
reset_debug_state();
|
||||
asm volatile("sw_bp: brk #0");
|
||||
GUEST_ASSERT_EQ(sw_bp_addr, PC(sw_bp));
|
||||
|
||||
@ -152,6 +161,51 @@ static void guest_code(void)
|
||||
GUEST_ASSERT_EQ(ss_addr[1], PC(ss_start) + 4);
|
||||
GUEST_ASSERT_EQ(ss_addr[2], PC(ss_start) + 8);
|
||||
|
||||
GUEST_SYNC(6);
|
||||
|
||||
/* OS Lock does not block software-breakpoint */
|
||||
reset_debug_state();
|
||||
enable_os_lock();
|
||||
sw_bp_addr = 0;
|
||||
asm volatile("sw_bp2: brk #0");
|
||||
GUEST_ASSERT_EQ(sw_bp_addr, PC(sw_bp2));
|
||||
|
||||
GUEST_SYNC(7);
|
||||
|
||||
/* OS Lock blocking hardware-breakpoint */
|
||||
reset_debug_state();
|
||||
enable_os_lock();
|
||||
install_hw_bp(PC(hw_bp2));
|
||||
hw_bp_addr = 0;
|
||||
asm volatile("hw_bp2: nop");
|
||||
GUEST_ASSERT_EQ(hw_bp_addr, 0);
|
||||
|
||||
GUEST_SYNC(8);
|
||||
|
||||
/* OS Lock blocking watchpoint */
|
||||
reset_debug_state();
|
||||
enable_os_lock();
|
||||
write_data = '\0';
|
||||
wp_data_addr = 0;
|
||||
install_wp(PC(write_data));
|
||||
write_data = 'x';
|
||||
GUEST_ASSERT_EQ(write_data, 'x');
|
||||
GUEST_ASSERT_EQ(wp_data_addr, 0);
|
||||
|
||||
GUEST_SYNC(9);
|
||||
|
||||
/* OS Lock blocking single-step */
|
||||
reset_debug_state();
|
||||
enable_os_lock();
|
||||
ss_addr[0] = 0;
|
||||
install_ss();
|
||||
ss_idx = 0;
|
||||
asm volatile("mrs x0, esr_el1\n\t"
|
||||
"add x0, x0, #1\n\t"
|
||||
"msr daifset, #8\n\t"
|
||||
: : : "x0");
|
||||
GUEST_ASSERT_EQ(ss_addr[0], 0);
|
||||
|
||||
GUEST_DONE();
|
||||
}
|
||||
|
||||
@ -223,7 +277,7 @@ int main(int argc, char *argv[])
|
||||
vm_install_sync_handler(vm, VECTOR_SYNC_CURRENT,
|
||||
ESR_EC_SVC64, guest_svc_handler);
|
||||
|
||||
for (stage = 0; stage < 7; stage++) {
|
||||
for (stage = 0; stage < 11; stage++) {
|
||||
vcpu_run(vm, VCPU_ID);
|
||||
|
||||
switch (get_ucall(vm, VCPU_ID, &uc)) {
|
||||
|
@ -760,6 +760,7 @@ static __u64 base_regs[] = {
|
||||
ARM64_SYS_REG(2, 0, 0, 15, 5),
|
||||
ARM64_SYS_REG(2, 0, 0, 15, 6),
|
||||
ARM64_SYS_REG(2, 0, 0, 15, 7),
|
||||
ARM64_SYS_REG(2, 0, 1, 1, 4), /* OSLSR_EL1 */
|
||||
ARM64_SYS_REG(2, 4, 0, 7, 0), /* DBGVCR32_EL2 */
|
||||
ARM64_SYS_REG(3, 0, 0, 0, 5), /* MPIDR_EL1 */
|
||||
ARM64_SYS_REG(3, 0, 0, 1, 0), /* ID_PFR0_EL1 */
|
||||
|
Loading…
x
Reference in New Issue
Block a user