summary refs log tree commit diff
path: root/arch/arm64/kvm
diff options
context:
space:
mode:
authorMark Rutland <mark.rutland@arm.com>2022-09-01 14:06:46 +0100
committerCatalin Marinas <catalin.marinas@arm.com>2022-09-09 12:30:08 +0100
commit4b5e694e25ca2cbb5c97694a7d2a473f35099829 (patch)
tree752ec5eca9f0081fb7067152bf6ae74b1b7de9fd /arch/arm64/kvm
parent8df137300d1964c3810991aa2fe17a105348b647 (diff)
downloadlinux-4b5e694e25ca2cbb5c97694a7d2a473f35099829.tar.gz
arm64: stacktrace: track hyp stacks in unwinder's address space
Currently unwind_next_frame_record() has an optional callback to convert
the address space of the FP. This is necessary for the NVHE unwinder,
which tracks the stacks in the hyp VA space, but accesses the frame
records in the kernel VA space.

This is a bit unfortunate since it clutters unwind_next_frame_record(),
which will get in the way of future rework.

Instead, this patch changes the NVHE unwinder to track the stacks in the
kernel's VA space and translate to FP prior to calling
unwind_next_frame_record(). This removes the need for the translate_fp()
callback, as all unwinders consistently track stacks in the native
address space of the unwinder.

At the same time, this patch consolidates the generation of the stack
addresses behind the stackinfo_get_*() helpers.

Signed-off-by: Mark Rutland <mark.rutland@arm.com>
Reviewed-by: Kalesh Singh <kaleshsingh@google.com>
Reviewed-by: Madhavan T. Venkataraman <madvenka@linux.microsoft.com>
Reviewed-by: Mark Brown <broonie@kernel.org>
Cc: Fuad Tabba <tabba@google.com>
Cc: Marc Zyngier <maz@kernel.org>
Cc: Will Deacon <will@kernel.org>
Link: https://lore.kernel.org/r/20220901130646.1316937-10-mark.rutland@arm.com
Signed-off-by: Catalin Marinas <catalin.marinas@arm.com>
Diffstat (limited to 'arch/arm64/kvm')
-rw-r--r--arch/arm64/kvm/hyp/nvhe/stacktrace.c2
-rw-r--r--arch/arm64/kvm/stacktrace.c62
2 files changed, 41 insertions, 23 deletions
diff --git a/arch/arm64/kvm/hyp/nvhe/stacktrace.c b/arch/arm64/kvm/hyp/nvhe/stacktrace.c
index 08e1325ead73..ed6b58b19cfa 100644
--- a/arch/arm64/kvm/hyp/nvhe/stacktrace.c
+++ b/arch/arm64/kvm/hyp/nvhe/stacktrace.c
@@ -64,7 +64,7 @@ static struct stack_info stackinfo_get_hyp(void)
 
 static int unwind_next(struct unwind_state *state)
 {
-	return unwind_next_frame_record(state, NULL);
+	return unwind_next_frame_record(state);
 }
 
 static void notrace unwind(struct unwind_state *state,
diff --git a/arch/arm64/kvm/stacktrace.c b/arch/arm64/kvm/stacktrace.c
index 0b4703945780..3ace5b75813b 100644
--- a/arch/arm64/kvm/stacktrace.c
+++ b/arch/arm64/kvm/stacktrace.c
@@ -34,6 +34,17 @@ static struct stack_info stackinfo_get_overflow(void)
 	};
 }
 
+static struct stack_info stackinfo_get_overflow_kern_va(void)
+{
+	unsigned long low = (unsigned long)this_cpu_ptr_nvhe_sym(overflow_stack);
+	unsigned long high = low + OVERFLOW_STACK_SIZE;
+
+	return (struct stack_info) {
+		.low = low,
+		.high = high,
+	};
+}
+
 static struct stack_info stackinfo_get_hyp(void)
 {
 	struct kvm_nvhe_stacktrace_info *stacktrace_info
@@ -47,6 +58,17 @@ static struct stack_info stackinfo_get_hyp(void)
 	};
 }
 
+static struct stack_info stackinfo_get_hyp_kern_va(void)
+{
+	unsigned long low = (unsigned long)*this_cpu_ptr(&kvm_arm_hyp_stack_page);
+	unsigned long high = low + PAGE_SIZE;
+
+	return (struct stack_info) {
+		.low = low,
+		.high = high,
+	};
+}
+
 /*
  * kvm_nvhe_stack_kern_va - Convert KVM nVHE HYP stack addresses to a kernel VAs
  *
@@ -62,33 +84,22 @@ static struct stack_info stackinfo_get_hyp(void)
  */
 static bool kvm_nvhe_stack_kern_va(unsigned long *addr, unsigned long size)
 {
-	struct kvm_nvhe_stacktrace_info *stacktrace_info;
-	unsigned long hyp_base, kern_base, hyp_offset;
-	struct stack_info stack;
+	struct stack_info stack_hyp, stack_kern;
 
-	stacktrace_info = this_cpu_ptr_nvhe_sym(kvm_stacktrace_info);
-
-	stack = stackinfo_get_hyp();
-	if (stackinfo_on_stack(&stack, *addr, size)) {
-		kern_base = (unsigned long)*this_cpu_ptr(&kvm_arm_hyp_stack_page);
-		hyp_base = (unsigned long)stacktrace_info->stack_base;
+	stack_hyp = stackinfo_get_hyp();
+	stack_kern = stackinfo_get_hyp_kern_va();
+	if (stackinfo_on_stack(&stack_hyp, *addr, size))
 		goto found;
-	}
 
-	stack = stackinfo_get_overflow();
-	if (stackinfo_on_stack(&stack, *addr, size)) {
-		kern_base = (unsigned long)this_cpu_ptr_nvhe_sym(overflow_stack);
-		hyp_base = (unsigned long)stacktrace_info->overflow_stack_base;
+	stack_hyp = stackinfo_get_overflow();
+	stack_kern = stackinfo_get_overflow_kern_va();
+	if (stackinfo_on_stack(&stack_hyp, *addr, size))
 		goto found;
-	}
 
 	return false;
 
 found:
-	hyp_offset = *addr - hyp_base;
-
-	*addr = kern_base + hyp_offset;
-
+	*addr = *addr - stack_hyp.low + stack_kern.low;
 	return true;
 }
 
@@ -102,7 +113,14 @@ static bool kvm_nvhe_stack_kern_record_va(unsigned long *addr)
 
 static int unwind_next(struct unwind_state *state)
 {
-	return unwind_next_frame_record(state, kvm_nvhe_stack_kern_record_va);
+	/*
+	 * The FP is in the hypervisor VA space. Convert it to the kernel VA
+	 * space so it can be unwound by the regular unwind functions.
+	 */
+	if (!kvm_nvhe_stack_kern_record_va(&state->fp))
+		return -EINVAL;
+
+	return unwind_next_frame_record(state);
 }
 
 static void unwind(struct unwind_state *state,
@@ -161,8 +179,8 @@ static void hyp_dump_backtrace(unsigned long hyp_offset)
 {
 	struct kvm_nvhe_stacktrace_info *stacktrace_info;
 	struct stack_info stacks[] = {
-		stackinfo_get_overflow(),
-		stackinfo_get_hyp(),
+		stackinfo_get_overflow_kern_va(),
+		stackinfo_get_hyp_kern_va(),
 	};
 	struct unwind_state state = {
 		.stacks = stacks,