Skip to contentSkip to conversation

Fix single step for traps

PATCH Superseded Julien Thierry 2 participants 10 messages Oct 3, 2017 – Oct 4, 2017

Subsystems (2)

v1 of 4 Superseded Latest is v4 →

lore
Share & tools
lore
#0 lore raw

cover Fix single step for traps

[Reposting series rebased on v4.14-rc3]

When single stepping a trapped/emulated instruction, the instruction not being actually executed, the PE ends up single stepping the instruction we return to after ERET-ing from the trap.

The issue affects traps in the kernel and emulated instructions in KVM guests.

This patch series ensures we properly single step trapped instruction.

First patch is just to avoid raw values when using single stepping registers/bits.
Patches 2 and 3 fix the issue for kernel and kvm respectively.

Julien Thierry (3):
arm64: Use existing defines for mdscr
arm64: Fix single stepping in kernel traps
arm64: kvm: Fix single step for guest skipped instructions

 arch/arm64/include/asm/assembler.h   |  5 +++--
 arch/arm64/include/asm/kvm_asm.h     |  2 ++
 arch/arm64/include/asm/kvm_emulate.h |  2 ++
 arch/arm64/include/asm/traps.h       |  2 ++
 arch/arm64/kernel/armv8_deprecated.c |  8 ++++----
 arch/arm64/kernel/cpufeature.c       |  2 +-
 arch/arm64/kernel/traps.c            | 21 ++++++++++++++++-----
 arch/arm64/kvm/debug.c               | 17 ++++++++++++++++-
 arch/arm64/kvm/hyp/switch.c          | 10 ++++++++++
 9 files changed, 56 insertions(+), 13 deletions(-)

--
1.9.1

#1 lore raw

PATCH 1/3 arm64: Use existing defines for mdscr

Cc Catalin Marinas <[email protected]>
Cc Will Deacon <[email protected]>
Cc Mark Rutland <[email protected]>
---
 arch/arm64/include/asm/assembler.h | 5 +++--
 1 file changed, 3 insertions(+), 2 deletions(-)
arch/arm64/include/asm/assembler.h +3 -2
index d58a625..00a4526 100644
@@ -30,6 +30,7 @@
 #include <asm/pgtable-hwdef.h>
 #include <asm/ptrace.h>
 #include <asm/thread_info.h>
+#include <asm/debug-monitors.h>
 
 /*
  * Enable and disable interrupts.
@@ -65,7 +66,7 @@
 	.macro	disable_step_tsk, flgs, tmp
 	tbz	\flgs, #TIF_SINGLESTEP, 9990f
 	mrs	\tmp, mdscr_el1
-	bic	\tmp, \tmp, #1
+	bic	\tmp, \tmp, #DBG_MDSCR_SS
 	msr	mdscr_el1, \tmp
 	isb	// Synchronise with enable_dbg
 9990:
@@ -75,7 +76,7 @@
 	tbz	\flgs, #TIF_SINGLESTEP, 9990f
 	disable_dbg
 	mrs	\tmp, mdscr_el1
-	orr	\tmp, \tmp, #1
+	orr	\tmp, \tmp, #DBG_MDSCR_SS
 	msr	mdscr_el1, \tmp
 9990:
 	.endm

--
1.9.1

#2 lore raw

PATCH 1/3 arm64: Use existing defines for mdscr

Quoted 4 lines · Julien Thierry
Cc Catalin Marinas <[email protected]>
Cc Will Deacon <[email protected]>
Cc Mark Rutland <[email protected]>
Quoted 27 lines
---
 arch/arm64/include/asm/assembler.h | 5 +++--
 1 file changed, 3 insertions(+), 2 deletions(-)
arch/arm64/include/asm/assembler.h +2 -1
index d58a625..00a4526 100644
@@ -30,6 +30,7 @@
 #include <asm/pgtable-hwdef.h>
 #include <asm/ptrace.h>
 #include <asm/thread_info.h>
+#include <asm/debug-monitors.h>
 
 /*
  * Enable and disable interrupts.
@@ -65,7 +66,7 @@
 	.macro	disable_step_tsk, flgs, tmp
 	tbz	\flgs, #TIF_SINGLESTEP, 9990f
 	mrs	\tmp, mdscr_el1
-	bic	\tmp, \tmp, #1
+	bic	\tmp, \tmp, #DBG_MDSCR_SS
 	msr	mdscr_el1, \tmp
 	isb	// Synchronise with enable_dbg
 9990:

arch/arm64/include/asm/assembler.h line 82

Quoted 8 lines
arch/arm64/include/asm/assembler.h +1 -1
@@ -75,7 +76,7 @@
 	tbz	\flgs, #TIF_SINGLESTEP, 9990f
 	disable_dbg
 	mrs	\tmp, mdscr_el1
-	orr	\tmp, \tmp, #1
+	orr	\tmp, \tmp, #DBG_MDSCR_SS
 	msr	mdscr_el1, \tmp
 9990:
 	.endm

--
Alex Benn?e

#3 lore raw

PATCH 1/3 arm64: Use existing defines for mdscr

In reply to Julien Thierry
Quoted 4 lines · Julien Thierry
Cc Catalin Marinas <[email protected]>
Cc Will Deacon <[email protected]>
Cc Mark Rutland <[email protected]>

Sorry I meant:

Quoted 27 lines
---
 arch/arm64/include/asm/assembler.h | 5 +++--
 1 file changed, 3 insertions(+), 2 deletions(-)
arch/arm64/include/asm/assembler.h +2 -1
index d58a625..00a4526 100644
@@ -30,6 +30,7 @@
 #include <asm/pgtable-hwdef.h>
 #include <asm/ptrace.h>
 #include <asm/thread_info.h>
+#include <asm/debug-monitors.h>
 
 /*
  * Enable and disable interrupts.
@@ -65,7 +66,7 @@
 	.macro	disable_step_tsk, flgs, tmp
 	tbz	\flgs, #TIF_SINGLESTEP, 9990f
 	mrs	\tmp, mdscr_el1
-	bic	\tmp, \tmp, #1
+	bic	\tmp, \tmp, #DBG_MDSCR_SS
 	msr	mdscr_el1, \tmp
 	isb	// Synchronise with enable_dbg
 9990:

arch/arm64/include/asm/assembler.h line 82

Quoted 8 lines
arch/arm64/include/asm/assembler.h +1 -1
@@ -75,7 +76,7 @@
 	tbz	\flgs, #TIF_SINGLESTEP, 9990f
 	disable_dbg
 	mrs	\tmp, mdscr_el1
-	orr	\tmp, \tmp, #1
+	orr	\tmp, \tmp, #DBG_MDSCR_SS
 	msr	mdscr_el1, \tmp
 9990:
 	.endm

--
Alex Benn?e

#4 lore raw

PATCH 2/3 arm64: Fix single stepping in kernel traps

In reply to Julien Thierry

Software Step exception is missing after stepping a trapped instruction.

Ensure SPSR.SS gets set to 0 after emulating/skipping a trapped instruction before doing ERET.

Cc Catalin Marinas <[email protected]>
Cc Will Deacon <[email protected]>
Cc Mark Rutland <[email protected]>
---
 arch/arm64/include/asm/traps.h       |  2 ++
 arch/arm64/kernel/armv8_deprecated.c |  8 ++++----
 arch/arm64/kernel/cpufeature.c       |  2 +-
 arch/arm64/kernel/traps.c            | 21 ++++++++++++++++-----
 4 files changed, 23 insertions(+), 10 deletions(-)
arch/arm64/include/asm/traps.h +2 -0
index d131501..dd7affb 100644
@@ -37,6 +37,8 @@ struct undef_hook {
 
 void arm64_notify_segfault(struct pt_regs *regs, unsigned long addr);
 
+void arm64_skip_trapped_instr(struct pt_regs *regs, unsigned long size);
+
 static inline int __in_irqentry_text(unsigned long ptr)
 {
 	return ptr >= (unsigned long)&__irqentry_text_start &&
arch/arm64/kernel/armv8_deprecated.c +4 -4
index f0e6d71..1f38208 100644
@@ -431,7 +431,7 @@ static int swp_handler(struct pt_regs *regs, u32 instr)
 	pr_warn_ratelimited("\"%s\" (%ld) uses obsolete SWP{B} instruction at 0x%llx\n",
 			current->comm, (unsigned long)current->pid, regs->pc);
 
-	regs->pc += 4;
+	arm64_skip_trapped_instr(regs, 4);
 	return 0;
 
 fault:
@@ -512,7 +512,7 @@ static int cp15barrier_handler(struct pt_regs *regs, u32 instr)
 	pr_warn_ratelimited("\"%s\" (%ld) uses deprecated CP15 Barrier instruction at 0x%llx\n",
 			current->comm, (unsigned long)current->pid, regs->pc);
 
-	regs->pc += 4;
+	arm64_skip_trapped_instr(regs, 4);
 	return 0;
 }
 
@@ -586,14 +586,14 @@ static int compat_setend_handler(struct pt_regs *regs, u32 big_endian)
 static int a32_setend_handler(struct pt_regs *regs, u32 instr)
 {
 	int rc = compat_setend_handler(regs, (instr >> 9) & 1);
-	regs->pc += 4;
+	arm64_skip_trapped_instr(regs, 4);
 	return rc;
 }
 
 static int t16_setend_handler(struct pt_regs *regs, u32 instr)
 {
 	int rc = compat_setend_handler(regs, (instr >> 3) & 1);
-	regs->pc += 2;
+	arm64_skip_trapped_instr(regs, 2);
 	return rc;
 }
 
arch/arm64/kernel/cpufeature.c +1 -1
index cd52d36..2956d5a 100644
@@ -1287,7 +1287,7 @@ static int emulate_mrs(struct pt_regs *regs, u32 insn)
 	if (!rc) {
 		dst = aarch64_insn_decode_register(AARCH64_INSN_REGTYPE_RT, insn);
 		pt_regs_write_reg(regs, dst, val);
-		regs->pc += 4;
+		arm64_skip_trapped_instr(regs, AARCH64_INSN_SIZE);
 	}
 
 	return rc;
arch/arm64/kernel/traps.c +16 -5
index 5ea4b85..ed9d856 100644
@@ -293,6 +293,17 @@ void arm64_notify_die(const char *str, struct pt_regs *regs,
 	}
 }
 
+void arm64_skip_trapped_instr(struct pt_regs *regs, unsigned long size)
+{
+	regs->pc += size;
+
+	/*
+	 * If we were single stepping, we want to get the step exception after
+	 * we return from the skipped exception
+	 */
+	regs->pstate &= ~DBG_SPSR_SS;
+}
+
 static LIST_HEAD(undef_hook);
 static DEFINE_RAW_SPINLOCK(undef_lock);
 
@@ -480,7 +491,7 @@ static void user_cache_maint_handler(unsigned int esr, struct pt_regs *regs)
 	if (ret)
 		arm64_notify_segfault(regs, address);
 	else
-		regs->pc += 4;
+		arm64_skip_trapped_instr(regs, AARCH64_INSN_SIZE);
 }
 
 static void ctr_read_handler(unsigned int esr, struct pt_regs *regs)
@@ -490,7 +501,7 @@ static void ctr_read_handler(unsigned int esr, struct pt_regs *regs)
 
 	pt_regs_write_reg(regs, rt, val);
 
-	regs->pc += 4;
+	arm64_skip_trapped_instr(regs, AARCH64_INSN_SIZE);
 }
 
 static void cntvct_read_handler(unsigned int esr, struct pt_regs *regs)
@@ -498,7 +509,7 @@ static void cntvct_read_handler(unsigned int esr, struct pt_regs *regs)
 	int rt = (esr & ESR_ELx_SYS64_ISS_RT_MASK) >> ESR_ELx_SYS64_ISS_RT_SHIFT;
 
 	pt_regs_write_reg(regs, rt, arch_counter_get_cntvct());
-	regs->pc += 4;
+	arm64_skip_trapped_instr(regs, AARCH64_INSN_SIZE);
 }
 
 static void cntfrq_read_handler(unsigned int esr, struct pt_regs *regs)
@@ -506,7 +517,7 @@ static void cntfrq_read_handler(unsigned int esr, struct pt_regs *regs)
 	int rt = (esr & ESR_ELx_SYS64_ISS_RT_MASK) >> ESR_ELx_SYS64_ISS_RT_SHIFT;
 
 	pt_regs_write_reg(regs, rt, arch_timer_get_rate());
-	regs->pc += 4;
+	arm64_skip_trapped_instr(regs, AARCH64_INSN_SIZE);
 }
 
 struct sys64_hook {
@@ -761,7 +772,7 @@ static int bug_handler(struct pt_regs *regs, unsigned int esr)
 	}
 
 	/* If thread survives, skip over the BUG instruction and continue: */
-	regs->pc += AARCH64_INSN_SIZE;	/* skip BRK and resume */
+	arm64_skip_trapped_instr(regs, AARCH64_INSN_SIZE);
 	return DBG_HOOK_HANDLED;
 }
 

--
1.9.1

#5 lore raw

PATCH 2/3 arm64: Fix single stepping in kernel traps

Quoted 22 lines · Julien Thierry

Software Step exception is missing after stepping a trapped instruction.

Ensure SPSR.SS gets set to 0 after emulating/skipping a trapped instruction before doing ERET.

Cc Catalin Marinas <[email protected]>
Cc Will Deacon <[email protected]>
Cc Mark Rutland <[email protected]>
---
 arch/arm64/include/asm/traps.h       |  2 ++
 arch/arm64/kernel/armv8_deprecated.c |  8 ++++----
 arch/arm64/kernel/cpufeature.c       |  2 +-
 arch/arm64/kernel/traps.c            | 21 ++++++++++++++++-----
 4 files changed, 23 insertions(+), 10 deletions(-)
arch/arm64/include/asm/traps.h +0 -0
index d131501..dd7affb 100644

arch/arm64/include/asm/traps.h struct undef_hook { · line 41

Quoted 5 lines
arch/arm64/include/asm/traps.h +2 -0
@@ -37,6 +37,8 @@ struct undef_hook {
 
 void arm64_notify_segfault(struct pt_regs *regs, unsigned long addr);
 
+void arm64_skip_trapped_instr(struct pt_regs *regs, unsigned long size);
+

Personally I think this is a poor name as it implies there is no effect from the operation. I suspect arm64_update_regs is a little too generic through. Naming things as ever is hard....

Quoted 26 lines
arch/arm64/include/asm/traps.h +0 -0
 static inline int __in_irqentry_text(unsigned long ptr)
 {
 	return ptr >= (unsigned long)&__irqentry_text_start &&
arch/arm64/kernel/armv8_deprecated.c +2 -2
index f0e6d71..1f38208 100644
@@ -431,7 +431,7 @@ static int swp_handler(struct pt_regs *regs, u32 instr)
 	pr_warn_ratelimited("\"%s\" (%ld) uses obsolete SWP{B} instruction at 0x%llx\n",
 			current->comm, (unsigned long)current->pid, regs->pc);
 
-	regs->pc += 4;
+	arm64_skip_trapped_instr(regs, 4);
 	return 0;
 
 fault:
@@ -512,7 +512,7 @@ static int cp15barrier_handler(struct pt_regs *regs, u32 instr)
 	pr_warn_ratelimited("\"%s\" (%ld) uses deprecated CP15 Barrier instruction at 0x%llx\n",
 			current->comm, (unsigned long)current->pid, regs->pc);
 
-	regs->pc += 4;
+	arm64_skip_trapped_instr(regs, 4);
 	return 0;
 }
 

arch/arm64/kernel/armv8_deprecated.c static int compat_setend_handler(struct pt_regs *regs, u32 big_endian) · line 591

Quoted 7 lines
arch/arm64/kernel/armv8_deprecated.c +1 -1
@@ -586,14 +586,14 @@ static int compat_setend_handler(struct pt_regs *regs, u32 big_endian)
 static int a32_setend_handler(struct pt_regs *regs, u32 instr)
 {
 	int rc = compat_setend_handler(regs, (instr >> 9) & 1);
-	regs->pc += 4;
+	arm64_skip_trapped_instr(regs, 4);
 	return rc;
 }

Why didn't we use AARCH64_INSN_SIZE for these here?

arch/arm64/kernel/armv8_deprecated.c static int compat_setend_handler(struct pt_regs *regs, u32 big_endian) · line 598

Quoted 8 lines
arch/arm64/kernel/armv8_deprecated.c +1 -1
 static int t16_setend_handler(struct pt_regs *regs, u32 instr)
 {
 	int rc = compat_setend_handler(regs, (instr >> 3) & 1);
-	regs->pc += 2;
+	arm64_skip_trapped_instr(regs, 2);
 	return rc;
 }

I guess we don't have an equivalent AARCH32_T16_INSN_SIZE?

Quoted 73 lines
arch/arm64/kernel/cpufeature.c +1 -1
index cd52d36..2956d5a 100644
@@ -1287,7 +1287,7 @@ static int emulate_mrs(struct pt_regs *regs, u32 insn)
 	if (!rc) {
 		dst = aarch64_insn_decode_register(AARCH64_INSN_REGTYPE_RT, insn);
 		pt_regs_write_reg(regs, dst, val);
-		regs->pc += 4;
+		arm64_skip_trapped_instr(regs, AARCH64_INSN_SIZE);
 	}
 
 	return rc;
arch/arm64/kernel/traps.c +15 -4
index 5ea4b85..ed9d856 100644
@@ -293,6 +293,17 @@ void arm64_notify_die(const char *str, struct pt_regs *regs,
 	}
 }
 
+void arm64_skip_trapped_instr(struct pt_regs *regs, unsigned long size)
+{
+	regs->pc += size;
+
+	/*
+	 * If we were single stepping, we want to get the step exception after
+	 * we return from the skipped exception
+	 */
+	regs->pstate &= ~DBG_SPSR_SS;
+}
+
 static LIST_HEAD(undef_hook);
 static DEFINE_RAW_SPINLOCK(undef_lock);
 
@@ -480,7 +491,7 @@ static void user_cache_maint_handler(unsigned int esr, struct pt_regs *regs)
 	if (ret)
 		arm64_notify_segfault(regs, address);
 	else
-		regs->pc += 4;
+		arm64_skip_trapped_instr(regs, AARCH64_INSN_SIZE);
 }
 
 static void ctr_read_handler(unsigned int esr, struct pt_regs *regs)
@@ -490,7 +501,7 @@ static void ctr_read_handler(unsigned int esr, struct pt_regs *regs)
 
 	pt_regs_write_reg(regs, rt, val);
 
-	regs->pc += 4;
+	arm64_skip_trapped_instr(regs, AARCH64_INSN_SIZE);
 }
 
 static void cntvct_read_handler(unsigned int esr, struct pt_regs *regs)
@@ -498,7 +509,7 @@ static void cntvct_read_handler(unsigned int esr, struct pt_regs *regs)
 	int rt = (esr & ESR_ELx_SYS64_ISS_RT_MASK) >> ESR_ELx_SYS64_ISS_RT_SHIFT;
 
 	pt_regs_write_reg(regs, rt, arch_counter_get_cntvct());
-	regs->pc += 4;
+	arm64_skip_trapped_instr(regs, AARCH64_INSN_SIZE);
 }
 
 static void cntfrq_read_handler(unsigned int esr, struct pt_regs *regs)
@@ -506,7 +517,7 @@ static void cntfrq_read_handler(unsigned int esr, struct pt_regs *regs)
 	int rt = (esr & ESR_ELx_SYS64_ISS_RT_MASK) >> ESR_ELx_SYS64_ISS_RT_SHIFT;
 
 	pt_regs_write_reg(regs, rt, arch_timer_get_rate());
-	regs->pc += 4;
+	arm64_skip_trapped_instr(regs, AARCH64_INSN_SIZE);
 }
 
 struct sys64_hook {

arch/arm64/kernel/traps.c static int bug_handler(struct pt_regs *regs, unsigned int esr) · line 777

Quoted 7 lines
arch/arm64/kernel/traps.c +1 -1
@@ -761,7 +772,7 @@ static int bug_handler(struct pt_regs *regs, unsigned int esr)
 	}
 
 	/* If thread survives, skip over the BUG instruction and continue: */
-	regs->pc += AARCH64_INSN_SIZE;	/* skip BRK and resume */
+	arm64_skip_trapped_instr(regs, AARCH64_INSN_SIZE);
 	return DBG_HOOK_HANDLED;
 }

It would be nice to find a better name but otherwise:

--
Alex Benn?e

#6 lore raw

PATCH 2/3 arm64: Fix single stepping in kernel traps

Quoted 26 lines · Alex Benn?e
Quoted 23 lines · Julien Thierry

Software Step exception is missing after stepping a trapped instruction.

Ensure SPSR.SS gets set to 0 after emulating/skipping a trapped instruction before doing ERET.

Cc Catalin Marinas <[email protected]>
Cc Will Deacon <[email protected]>
Cc Mark Rutland <[email protected]>
---
  arch/arm64/include/asm/traps.h       |  2 ++
  arch/arm64/kernel/armv8_deprecated.c |  8 ++++----
  arch/arm64/kernel/cpufeature.c       |  2 +-
  arch/arm64/kernel/traps.c            | 21 ++++++++++++++++-----
  4 files changed, 23 insertions(+), 10 deletions(-)
arch/arm64/include/asm/traps.h +0 -0
index d131501..dd7affb 100644
@@ -37,6 +37,8 @@ struct undef_hook {
 
Quoted 8 lines

arch/arm64/include/asm/traps.h struct undef_hook { · line 41

Quoted 4 lines
arch/arm64/include/asm/traps.h +2 -0
  void arm64_notify_segfault(struct pt_regs *regs, unsigned long addr);
 
+void arm64_skip_trapped_instr(struct pt_regs *regs, unsigned long size);
+

Personally I think this is a poor name as it implies there is no effect from the operation. I suspect arm64_update_regs is a little too generic through. Naming things as ever is hard....

What about arm64_step_trapped_instr? Don't know if it is much better but maybe less misleading than just "skip".

Or arm64_setup_next_instr?

Quoted 26 lines
Quoted 26 lines
arch/arm64/include/asm/traps.h +0 -0
  static inline int __in_irqentry_text(unsigned long ptr)
  {
  	return ptr >= (unsigned long)&__irqentry_text_start &&
arch/arm64/kernel/armv8_deprecated.c +2 -2
index f0e6d71..1f38208 100644
@@ -431,7 +431,7 @@ static int swp_handler(struct pt_regs *regs, u32 instr)
  	pr_warn_ratelimited("\"%s\" (%ld) uses obsolete SWP{B} instruction at 0x%llx\n",
  			current->comm, (unsigned long)current->pid, regs->pc);
 
-	regs->pc += 4;
+	arm64_skip_trapped_instr(regs, 4);
  	return 0;
 
  fault:
@@ -512,7 +512,7 @@ static int cp15barrier_handler(struct pt_regs *regs, u32 instr)
  	pr_warn_ratelimited("\"%s\" (%ld) uses deprecated CP15 Barrier instruction at 0x%llx\n",
  			current->comm, (unsigned long)current->pid, regs->pc);
 
-	regs->pc += 4;
+	arm64_skip_trapped_instr(regs, 4);
  	return 0;
  }
 
Quoted 10 lines

arch/arm64/kernel/armv8_deprecated.c static int compat_setend_handler(struct pt_regs *regs, u32 big_endian) · line 591

Quoted 8 lines
arch/arm64/kernel/armv8_deprecated.c +1 -1
@@ -586,14 +586,14 @@ static int compat_setend_handler(struct pt_regs *regs, u32 big_endian)
  static int a32_setend_handler(struct pt_regs *regs, u32 instr)
  {
  	int rc = compat_setend_handler(regs, (instr >> 9) & 1);
-	regs->pc += 4;
+	arm64_skip_trapped_instr(regs, 4);
  	return rc;
  }

Why didn't we use AARCH64_INSN_SIZE for these here?

Because these are AARCH32 instructions, although it is the same size, I thought it would be misleading to use AARCH64 macro.

Quoted 12 lines

arch/arm64/kernel/armv8_deprecated.c static int compat_setend_handler(struct pt_regs *regs, u32 big_endian) · line 598

Quoted 9 lines
arch/arm64/kernel/armv8_deprecated.c +1 -1
  static int t16_setend_handler(struct pt_regs *regs, u32 instr)
  {
  	int rc = compat_setend_handler(regs, (instr >> 3) & 1);
-	regs->pc += 2;
+	arm64_skip_trapped_instr(regs, 2);
  	return rc;
  }

I guess we don't have an equivalent AARCH32_T16_INSN_SIZE?

Yes, I was a bit hesitant because thumb2 can have 16 and 32bits instructions, but I guess having "T16" in the macro name makes it clear it should be used only on 16bits instructions.

I'll add this define, and might as well add AARCH32_INSN_SIZE.

Quoted 83 lines
Quoted 73 lines
arch/arm64/kernel/cpufeature.c +1 -1
index cd52d36..2956d5a 100644
@@ -1287,7 +1287,7 @@ static int emulate_mrs(struct pt_regs *regs, u32 insn)
  	if (!rc) {
  		dst = aarch64_insn_decode_register(AARCH64_INSN_REGTYPE_RT, insn);
  		pt_regs_write_reg(regs, dst, val);
-		regs->pc += 4;
+		arm64_skip_trapped_instr(regs, AARCH64_INSN_SIZE);
  	}
 
  	return rc;
arch/arm64/kernel/traps.c +15 -4
index 5ea4b85..ed9d856 100644
@@ -293,6 +293,17 @@ void arm64_notify_die(const char *str, struct pt_regs *regs,
  	}
  }
 
+void arm64_skip_trapped_instr(struct pt_regs *regs, unsigned long size)
+{
+	regs->pc += size;
+
+	/*
+	 * If we were single stepping, we want to get the step exception after
+	 * we return from the skipped exception
+	 */
+	regs->pstate &= ~DBG_SPSR_SS;
+}
+
  static LIST_HEAD(undef_hook);
  static DEFINE_RAW_SPINLOCK(undef_lock);
 
@@ -480,7 +491,7 @@ static void user_cache_maint_handler(unsigned int esr, struct pt_regs *regs)
  	if (ret)
  		arm64_notify_segfault(regs, address);
  	else
-		regs->pc += 4;
+		arm64_skip_trapped_instr(regs, AARCH64_INSN_SIZE);
  }
 
  static void ctr_read_handler(unsigned int esr, struct pt_regs *regs)
@@ -490,7 +501,7 @@ static void ctr_read_handler(unsigned int esr, struct pt_regs *regs)
 
  	pt_regs_write_reg(regs, rt, val);
 
-	regs->pc += 4;
+	arm64_skip_trapped_instr(regs, AARCH64_INSN_SIZE);
  }
 
  static void cntvct_read_handler(unsigned int esr, struct pt_regs *regs)
@@ -498,7 +509,7 @@ static void cntvct_read_handler(unsigned int esr, struct pt_regs *regs)
  	int rt = (esr & ESR_ELx_SYS64_ISS_RT_MASK) >> ESR_ELx_SYS64_ISS_RT_SHIFT;
 
  	pt_regs_write_reg(regs, rt, arch_counter_get_cntvct());
-	regs->pc += 4;
+	arm64_skip_trapped_instr(regs, AARCH64_INSN_SIZE);
  }
 
  static void cntfrq_read_handler(unsigned int esr, struct pt_regs *regs)
@@ -506,7 +517,7 @@ static void cntfrq_read_handler(unsigned int esr, struct pt_regs *regs)
  	int rt = (esr & ESR_ELx_SYS64_ISS_RT_MASK) >> ESR_ELx_SYS64_ISS_RT_SHIFT;
 
  	pt_regs_write_reg(regs, rt, arch_timer_get_rate());
-	regs->pc += 4;
+	arm64_skip_trapped_instr(regs, AARCH64_INSN_SIZE);
  }
 
  struct sys64_hook {

arch/arm64/kernel/traps.c static int bug_handler(struct pt_regs *regs, unsigned int esr) · line 777

Quoted 7 lines
arch/arm64/kernel/traps.c +1 -1
@@ -761,7 +772,7 @@ static int bug_handler(struct pt_regs *regs, unsigned int esr)
  	}
 
  	/* If thread survives, skip over the BUG instruction and continue: */
-	regs->pc += AARCH64_INSN_SIZE;	/* skip BRK and resume */
+	arm64_skip_trapped_instr(regs, AARCH64_INSN_SIZE);
  	return DBG_HOOK_HANDLED;
  }

It would be nice to find a better name but otherwise:

Quoted 2 lines

Thanks,

Signature · 1 line
Julien Thierry
#7 lore raw

PATCH 2/3 arm64: Fix single stepping in kernel traps

Quoted 36 lines · Julien Thierry
Quoted 26 lines · Alex Benn?e
Quoted 23 lines · Julien Thierry

Software Step exception is missing after stepping a trapped instruction.

Ensure SPSR.SS gets set to 0 after emulating/skipping a trapped instruction before doing ERET.

Cc Catalin Marinas <[email protected]>
Cc Will Deacon <[email protected]>
Cc Mark Rutland <[email protected]>
---
  arch/arm64/include/asm/traps.h       |  2 ++
  arch/arm64/kernel/armv8_deprecated.c |  8 ++++----
  arch/arm64/kernel/cpufeature.c       |  2 +-
  arch/arm64/kernel/traps.c            | 21 ++++++++++++++++-----
  4 files changed, 23 insertions(+), 10 deletions(-)
arch/arm64/include/asm/traps.h +0 -0
index d131501..dd7affb 100644
@@ -37,6 +37,8 @@ struct undef_hook {
 
Quoted 8 lines

arch/arm64/include/asm/traps.h struct undef_hook { · line 41

Quoted 4 lines
arch/arm64/include/asm/traps.h +2 -0
  void arm64_notify_segfault(struct pt_regs *regs, unsigned long addr);
 
+void arm64_skip_trapped_instr(struct pt_regs *regs, unsigned long size);
+

Personally I think this is a poor name as it implies there is no effect from the operation. I suspect arm64_update_regs is a little too generic through. Naming things as ever is hard....

Quoted 4 lines

What about arm64_step_trapped_instr? Don't know if it is much better but maybe less misleading than just "skip".

Or arm64_setup_next_instr?

Yes, I like arm64_setup_next_instr() better.

Quoted 38 lines
Quoted 27 lines
Quoted 27 lines
arch/arm64/include/asm/traps.h +0 -0
  static inline int __in_irqentry_text(unsigned long ptr)
  {
  	return ptr >= (unsigned long)&__irqentry_text_start &&
arch/arm64/kernel/armv8_deprecated.c +2 -2
index f0e6d71..1f38208 100644
@@ -431,7 +431,7 @@ static int swp_handler(struct pt_regs *regs, u32 instr)
  	pr_warn_ratelimited("\"%s\" (%ld) uses obsolete SWP{B} instruction at 0x%llx\n",
  			current->comm, (unsigned long)current->pid, regs->pc);
 
-	regs->pc += 4;
+	arm64_skip_trapped_instr(regs, 4);
  	return 0;
 
  fault:
@@ -512,7 +512,7 @@ static int cp15barrier_handler(struct pt_regs *regs, u32 instr)
  	pr_warn_ratelimited("\"%s\" (%ld) uses deprecated CP15 Barrier instruction at 0x%llx\n",
  			current->comm, (unsigned long)current->pid, regs->pc);
 
-	regs->pc += 4;
+	arm64_skip_trapped_instr(regs, 4);
  	return 0;
  }
 
Quoted 10 lines

arch/arm64/kernel/armv8_deprecated.c static int compat_setend_handler(struct pt_regs *regs, u32 big_endian) · line 591

Quoted 8 lines
arch/arm64/kernel/armv8_deprecated.c +1 -1
@@ -586,14 +586,14 @@ static int compat_setend_handler(struct pt_regs *regs, u32 big_endian)
  static int a32_setend_handler(struct pt_regs *regs, u32 instr)
  {
  	int rc = compat_setend_handler(regs, (instr >> 9) & 1);
-	regs->pc += 4;
+	arm64_skip_trapped_instr(regs, 4);
  	return rc;
  }

Why didn't we use AARCH64_INSN_SIZE for these here?

Quoted 2 lines

Because these are AARCH32 instructions, although it is the same size, I thought it would be misleading to use AARCH64 macro.

Ahh I was thrown by the fact we are in armv8_deprecated.c but of course AArch32 is part of armv8...

Quoted 14 lines
Quoted 13 lines

arch/arm64/kernel/armv8_deprecated.c static int compat_setend_handler(struct pt_regs *regs, u32 big_endian) · line 598

Quoted 10 lines
arch/arm64/kernel/armv8_deprecated.c +1 -1
  static int t16_setend_handler(struct pt_regs *regs, u32 instr)
  {
  	int rc = compat_setend_handler(regs, (instr >> 3) & 1);
-	regs->pc += 2;
+	arm64_skip_trapped_instr(regs, 2);
  	return rc;
  }

I guess we don't have an equivalent AARCH32_T16_INSN_SIZE?

Quoted 5 lines

Yes, I was a bit hesitant because thumb2 can have 16 and 32bits instructions, but I guess having "T16" in the macro name makes it clear it should be used only on 16bits instructions.

I'll add this define, and might as well add AARCH32_INSN_SIZE.

Maybe AARCH32_T32_INSN_SIZE and AARCH32_T16_INSN_SIZE? Then it is clear there are two encodings.

Quoted 84 lines
Quoted 84 lines
Quoted 74 lines
arch/arm64/kernel/cpufeature.c +1 -1
index cd52d36..2956d5a 100644
@@ -1287,7 +1287,7 @@ static int emulate_mrs(struct pt_regs *regs, u32 insn)
  	if (!rc) {
  		dst = aarch64_insn_decode_register(AARCH64_INSN_REGTYPE_RT, insn);
  		pt_regs_write_reg(regs, dst, val);
-		regs->pc += 4;
+		arm64_skip_trapped_instr(regs, AARCH64_INSN_SIZE);
  	}
 
  	return rc;
arch/arm64/kernel/traps.c +15 -4
index 5ea4b85..ed9d856 100644
@@ -293,6 +293,17 @@ void arm64_notify_die(const char *str, struct pt_regs *regs,
  	}
  }
 
+void arm64_skip_trapped_instr(struct pt_regs *regs, unsigned long size)
+{
+	regs->pc += size;
+
+	/*
+	 * If we were single stepping, we want to get the step exception after
+	 * we return from the skipped exception
+	 */
+	regs->pstate &= ~DBG_SPSR_SS;
+}
+
  static LIST_HEAD(undef_hook);
  static DEFINE_RAW_SPINLOCK(undef_lock);
 
@@ -480,7 +491,7 @@ static void user_cache_maint_handler(unsigned int esr, struct pt_regs *regs)
  	if (ret)
  		arm64_notify_segfault(regs, address);
  	else
-		regs->pc += 4;
+		arm64_skip_trapped_instr(regs, AARCH64_INSN_SIZE);
  }
 
  static void ctr_read_handler(unsigned int esr, struct pt_regs *regs)
@@ -490,7 +501,7 @@ static void ctr_read_handler(unsigned int esr, struct pt_regs *regs)
 
  	pt_regs_write_reg(regs, rt, val);
 
-	regs->pc += 4;
+	arm64_skip_trapped_instr(regs, AARCH64_INSN_SIZE);
  }
 
  static void cntvct_read_handler(unsigned int esr, struct pt_regs *regs)
@@ -498,7 +509,7 @@ static void cntvct_read_handler(unsigned int esr, struct pt_regs *regs)
  	int rt = (esr & ESR_ELx_SYS64_ISS_RT_MASK) >> ESR_ELx_SYS64_ISS_RT_SHIFT;
 
  	pt_regs_write_reg(regs, rt, arch_counter_get_cntvct());
-	regs->pc += 4;
+	arm64_skip_trapped_instr(regs, AARCH64_INSN_SIZE);
  }
 
  static void cntfrq_read_handler(unsigned int esr, struct pt_regs *regs)
@@ -506,7 +517,7 @@ static void cntfrq_read_handler(unsigned int esr, struct pt_regs *regs)
  	int rt = (esr & ESR_ELx_SYS64_ISS_RT_MASK) >> ESR_ELx_SYS64_ISS_RT_SHIFT;
 
  	pt_regs_write_reg(regs, rt, arch_timer_get_rate());
-	regs->pc += 4;
+	arm64_skip_trapped_instr(regs, AARCH64_INSN_SIZE);
  }
 
  struct sys64_hook {

arch/arm64/kernel/traps.c static int bug_handler(struct pt_regs *regs, unsigned int esr) · line 777

Quoted 7 lines
arch/arm64/kernel/traps.c +1 -1
@@ -761,7 +772,7 @@ static int bug_handler(struct pt_regs *regs, unsigned int esr)
  	}
 
  	/* If thread survives, skip over the BUG instruction and continue: */
-	regs->pc += AARCH64_INSN_SIZE;	/* skip BRK and resume */
+	arm64_skip_trapped_instr(regs, AARCH64_INSN_SIZE);
  	return DBG_HOOK_HANDLED;
  }

It would be nice to find a better name but otherwise:

Quoted 4 lines
Quoted 2 lines

Thanks,

--
Alex Benn?e

#8 lore raw

PATCH 3/3 arm64: kvm: Fix single step for guest skipped instructions

In reply to Julien Thierry

Software Step exception is missing after trapping instruction from the guest.

We need to set the PSR.SS to 0 for the guest vcpu before resuming guest execution.

Cc Christoffer Dall <[email protected]>
Cc Marc Zyngier <[email protected]>
Cc Alex Benn?e <[email protected]>
Cc Catalin Marinas <[email protected]>
Cc Will Deacon <[email protected]>
---
 arch/arm64/include/asm/kvm_asm.h     |  2 ++
 arch/arm64/include/asm/kvm_emulate.h |  2 ++
 arch/arm64/kvm/debug.c               | 17 ++++++++++++++++-
 arch/arm64/kvm/hyp/switch.c          | 10 ++++++++++
 4 files changed, 30 insertions(+), 1 deletion(-)
arch/arm64/include/asm/kvm_asm.h +2 -0
index 26a64d0..398bbaa 100644
@@ -32,6 +32,8 @@
 
 #define KVM_ARM64_DEBUG_DIRTY_SHIFT	0
 #define KVM_ARM64_DEBUG_DIRTY		(1 << KVM_ARM64_DEBUG_DIRTY_SHIFT)
+#define KVM_ARM64_DEBUG_INST_SKIP_SHIFT	1
+#define KVM_ARM64_DEBUG_INST_SKIP	(1 << KVM_ARM64_DEBUG_INST_SKIP_SHIFT)
 
 #define kvm_ksym_ref(sym)						\
 	({								\
arch/arm64/include/asm/kvm_emulate.h +2 -0
index e5df3fc..ee02dd2ee 100644
@@ -95,6 +95,8 @@ static inline void kvm_skip_instr(struct kvm_vcpu *vcpu, bool is_wide_instr)
 		kvm_skip_instr32(vcpu, is_wide_instr);
 	else
 		*vcpu_pc(vcpu) += 4;
+	/* Let debug engine know we skipped an instruction */
+	vcpu->arch.debug_flags |= KVM_ARM64_DEBUG_INST_SKIP;
 }
 
 static inline void vcpu_set_thumb(struct kvm_vcpu *vcpu)
arch/arm64/kvm/debug.c +16 -1
index dbadfaf..b5fcb96 100644
@@ -151,12 +151,27 @@ void kvm_arm_setup_debug(struct kvm_vcpu *vcpu)
 		 * debugging the system.
 		 */
 		if (vcpu->guest_debug & KVM_GUESTDBG_SINGLESTEP) {
-			*vcpu_cpsr(vcpu) |=  DBG_SPSR_SS;
+			/*
+			 * Taking a Software step exception, context being
+			 * stepped has PSTATE.SS == 0. In order to step the next
+			 * instruction, we need to reset this bit.
+			 * If we skipped an instruction while single stepping,
+			 * we want to get a software step exception for the
+			 * skipped instruction (i.e. as soon as we return to the
+			 * guest). This is obtained by returning to the guest
+			 * with PSTATE.SS cleared.
+			 */
+			if (!(vcpu->arch.debug_flags & KVM_ARM64_DEBUG_INST_SKIP))
+				*vcpu_cpsr(vcpu) |=  DBG_SPSR_SS;
+			else
+				*vcpu_cpsr(vcpu) &=  ~DBG_SPSR_SS;
 			vcpu_sys_reg(vcpu, MDSCR_EL1) |= DBG_MDSCR_SS;
 		} else {
 			vcpu_sys_reg(vcpu, MDSCR_EL1) &= ~DBG_MDSCR_SS;
 		}
 
+		vcpu->arch.debug_flags &= ~KVM_ARM64_DEBUG_INST_SKIP;
+
 		trace_kvm_arm_set_dreg32("SPSR_EL2", *vcpu_cpsr(vcpu));
 
 		/*
arch/arm64/kvm/hyp/switch.c +10 -0
index 945e79c..34fe215 100644
@@ -22,6 +22,7 @@
 #include <asm/kvm_emulate.h>
 #include <asm/kvm_hyp.h>
 #include <asm/fpsimd.h>
+#include <asm/debug-monitors.h>
 
 static bool __hyp_text __fpsimd_enabled_nvhe(void)
 {
@@ -276,6 +277,8 @@ static void __hyp_text __skip_instr(struct kvm_vcpu *vcpu)
 	}
 
 	write_sysreg_el2(*vcpu_pc(vcpu), elr);
+
+	write_sysreg_el2(read_sysreg_el2(spsr) & ~DBG_SPSR_SS, spsr);
 }
 
 int __hyp_text __kvm_vcpu_run(struct kvm_vcpu *vcpu)
@@ -343,6 +346,13 @@ int __hyp_text __kvm_vcpu_run(struct kvm_vcpu *vcpu)
 			if (ret == -1) {
 				/* Promote an illegal access to an SError */
 				__skip_instr(vcpu);
+
+				/*
+				 * We're not jumping back, let debug setup know
+				 * we skipped an instruction.
+				 */
+				vcpu->arch.debug_flags |= KVM_ARM64_DEBUG_INST_SKIP;
+
 				exit_code = ARM_EXCEPTION_EL1_SERROR;
 			}
 

--
1.9.1

#9 lore raw

PATCH 3/3 arm64: kvm: Fix single step for guest skipped instructions

Quoted 37 lines · Julien Thierry

Software Step exception is missing after trapping instruction from the guest.

We need to set the PSR.SS to 0 for the guest vcpu before resuming guest execution.

Cc Christoffer Dall <[email protected]>
Cc Marc Zyngier <[email protected]>
Cc Alex Benn?e <[email protected]>
Cc Catalin Marinas <[email protected]>
Cc Will Deacon <[email protected]>
---
 arch/arm64/include/asm/kvm_asm.h     |  2 ++
 arch/arm64/include/asm/kvm_emulate.h |  2 ++
 arch/arm64/kvm/debug.c               | 17 ++++++++++++++++-
 arch/arm64/kvm/hyp/switch.c          | 10 ++++++++++
 4 files changed, 30 insertions(+), 1 deletion(-)
arch/arm64/include/asm/kvm_asm.h +2 -0
index 26a64d0..398bbaa 100644
@@ -32,6 +32,8 @@
 
 #define KVM_ARM64_DEBUG_DIRTY_SHIFT	0
 #define KVM_ARM64_DEBUG_DIRTY		(1 << KVM_ARM64_DEBUG_DIRTY_SHIFT)
+#define KVM_ARM64_DEBUG_INST_SKIP_SHIFT	1
+#define KVM_ARM64_DEBUG_INST_SKIP	(1 << KVM_ARM64_DEBUG_INST_SKIP_SHIFT)
 
 #define kvm_ksym_ref(sym)						\
 	({								\
arch/arm64/include/asm/kvm_emulate.h +0 -0
index e5df3fc..ee02dd2ee 100644

arch/arm64/include/asm/kvm_emulate.h static inline void kvm_skip_instr(struct kvm_vcpu *vcpu, bool is_wide_instr) · line 100

Quoted 6 lines
arch/arm64/include/asm/kvm_emulate.h +2 -0
@@ -95,6 +95,8 @@ static inline void kvm_skip_instr(struct kvm_vcpu *vcpu, bool is_wide_instr)
 		kvm_skip_instr32(vcpu, is_wide_instr);
 	else
 		*vcpu_pc(vcpu) += 4;
+	/* Let debug engine know we skipped an instruction */
+	vcpu->arch.debug_flags |= KVM_ARM64_DEBUG_INST_SKIP;
 }

I can see the appeal of handling things here but I think for both trapped system instructions and mmio emulation everything flows back to handle_exit() where we could deal with it there and then.

Quoted 48 lines
arch/arm64/include/asm/kvm_emulate.h +0 -0
 static inline void vcpu_set_thumb(struct kvm_vcpu *vcpu)
arch/arm64/kvm/debug.c +16 -1
index dbadfaf..b5fcb96 100644
@@ -151,12 +151,27 @@ void kvm_arm_setup_debug(struct kvm_vcpu *vcpu)
 		 * debugging the system.
 		 */
 		if (vcpu->guest_debug & KVM_GUESTDBG_SINGLESTEP) {
-			*vcpu_cpsr(vcpu) |=  DBG_SPSR_SS;
+			/*
+			 * Taking a Software step exception, context being
+			 * stepped has PSTATE.SS == 0. In order to step the next
+			 * instruction, we need to reset this bit.
+			 * If we skipped an instruction while single stepping,
+			 * we want to get a software step exception for the
+			 * skipped instruction (i.e. as soon as we return to the
+			 * guest). This is obtained by returning to the guest
+			 * with PSTATE.SS cleared.
+			 */
+			if (!(vcpu->arch.debug_flags & KVM_ARM64_DEBUG_INST_SKIP))
+				*vcpu_cpsr(vcpu) |=  DBG_SPSR_SS;
+			else
+				*vcpu_cpsr(vcpu) &=  ~DBG_SPSR_SS;
 			vcpu_sys_reg(vcpu, MDSCR_EL1) |= DBG_MDSCR_SS;
 		} else {
 			vcpu_sys_reg(vcpu, MDSCR_EL1) &= ~DBG_MDSCR_SS;
 		}
 
+		vcpu->arch.debug_flags &= ~KVM_ARM64_DEBUG_INST_SKIP;
+
 		trace_kvm_arm_set_dreg32("SPSR_EL2", *vcpu_cpsr(vcpu));
 
 		/*
arch/arm64/kvm/hyp/switch.c +1 -0
index 945e79c..34fe215 100644
@@ -22,6 +22,7 @@
 #include <asm/kvm_emulate.h>
 #include <asm/kvm_hyp.h>
 #include <asm/fpsimd.h>
+#include <asm/debug-monitors.h>
 
 static bool __hyp_text __fpsimd_enabled_nvhe(void)
 {

arch/arm64/kvm/hyp/switch.c static void __hyp_text __skip_instr(struct kvm_vcpu *vcpu) · line 282

Quoted 6 lines
arch/arm64/kvm/hyp/switch.c +2 -0
@@ -276,6 +277,8 @@ static void __hyp_text __skip_instr(struct kvm_vcpu *vcpu)
 	}
 
 	write_sysreg_el2(*vcpu_pc(vcpu), elr);
+
+	write_sysreg_el2(read_sysreg_el2(spsr) & ~DBG_SPSR_SS, spsr);
 }

Hmm this function seems a bit magical - given it is manipulating PC/ELR. I guess it is only called for eret and other exception returns? I wonder if a rename to __skip_eret_instr would be in order for clarity?

arch/arm64/kvm/hyp/switch.c int __hyp_text __kvm_vcpu_run(struct kvm_vcpu *vcpu) · line 355

Quoted 13 lines
arch/arm64/kvm/hyp/switch.c +7 -0
 int __hyp_text __kvm_vcpu_run(struct kvm_vcpu *vcpu)
@@ -343,6 +346,13 @@ int __hyp_text __kvm_vcpu_run(struct kvm_vcpu *vcpu)
 			if (ret == -1) {
 				/* Promote an illegal access to an SError */
 				__skip_instr(vcpu);
+
+				/*
+				 * We're not jumping back, let debug setup know
+				 * we skipped an instruction.
+				 */
+				vcpu->arch.debug_flags |= KVM_ARM64_DEBUG_INST_SKIP;
+

I'm not so sure of this. We are exiting so it seems we are going to persist some debug state over an exit where an ioctl may change things before we re-enter.

arch/arm64/kvm/hyp/switch.c int __hyp_text __kvm_vcpu_run(struct kvm_vcpu *vcpu) · line 357

Quoted 2 lines
arch/arm64/kvm/hyp/switch.c +0 -0
 				exit_code = ARM_EXCEPTION_EL1_SERROR;
 			}

--
Alex Benn?e