Commit b61fa004 authored by Marc Zyngier's avatar Marc Zyngier
Browse files

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: default avatarMarc Zyngier <maz@kernel.org>
parents dfd42fac 05c9324d
Loading
Loading
Loading
Loading
+5 −0
Original line number Diff line number Diff line
@@ -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,
+8 −0
Original line number Diff line number Diff line
@@ -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)
+22 −4
Original line number Diff line number Diff line
@@ -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);

		/*
+57 −17
Original line number Diff line number Diff line
@@ -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);
	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 */
+56 −2
Original line number Diff line number Diff line
@@ -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)) {
Loading