state->prev_type = STACK_TYPE_UNKNOWN;
}
+/*
+ * stack_trace_translate_fp_fn() - Translates a non-kernel frame pointer to
+ * a kernel address.
+ *
+ * @fp: the frame pointer to be updated to its kernel address.
+ * @type: the stack type associated with frame pointer @fp
+ *
+ * Returns true and success and @fp is updated to the corresponding
+ * kernel virtual address; otherwise returns false.
+ */
+typedef bool (*stack_trace_translate_fp_fn)(unsigned long *fp,
+ enum stack_type type);
+
static inline int unwind_next_common(struct unwind_state *state,
- struct stack_info *info)
+ struct stack_info *info,
+ stack_trace_translate_fp_fn translate_fp)
{
+ unsigned long fp = state->fp, kern_fp = fp;
struct task_struct *tsk = state->task;
- unsigned long fp = state->fp;
if (fp & 0x7)
return -EINVAL;
if (test_bit(info->type, state->stacks_done))
return -EINVAL;
+ /*
+ * If fp is not from the current address space perform the necessary
+ * translation before dereferencing it to get the next fp.
+ */
+ if (translate_fp && !translate_fp(&kern_fp, info->type))
+ return -EINVAL;
+
/*
* As stacks grow downward, any valid record on the same stack must be
* at a strictly higher address than the prior record.
* Record this frame record's values and location. The prev_fp and
* prev_type are only meaningful to the next unwind_next() invocation.
*/
- state->fp = READ_ONCE(*(unsigned long *)(fp));
- state->pc = READ_ONCE(*(unsigned long *)(fp + 8));
+ state->fp = READ_ONCE(*(unsigned long *)(kern_fp));
+ state->pc = READ_ONCE(*(unsigned long *)(kern_fp + 8));
state->prev_fp = fp;
state->prev_type = info->type;