mirror of https://github.com/OpenIPC/firmware.git
163 lines
3.9 KiB
Diff
163 lines
3.9 KiB
Diff
diff -drupN a/arch/arm64/kernel/process.c b/arch/arm64/kernel/process.c
|
|
--- a/arch/arm64/kernel/process.c 2018-08-06 17:23:04.000000000 +0300
|
|
+++ b/arch/arm64/kernel/process.c 2022-06-12 05:28:14.000000000 +0300
|
|
@@ -45,6 +45,7 @@
|
|
#include <linux/personality.h>
|
|
#include <linux/notifier.h>
|
|
#include <trace/events/power.h>
|
|
+#include <linux/percpu.h>
|
|
|
|
#include <asm/alternative.h>
|
|
#include <asm/compat.h>
|
|
@@ -166,6 +167,70 @@ void machine_restart(char *cmd)
|
|
while (1);
|
|
}
|
|
|
|
+/*
|
|
+ * dump a block of kernel memory from around the given address
|
|
+ */
|
|
+static void show_data(unsigned long addr, int nbytes, const char *name)
|
|
+{
|
|
+ int i, j;
|
|
+ int nlines;
|
|
+ u32 *p;
|
|
+
|
|
+ /*
|
|
+ * don't attempt to dump non-kernel addresses or
|
|
+ * values that are probably just small negative numbers
|
|
+ */
|
|
+ if (addr < PAGE_OFFSET || addr > -256UL)
|
|
+ return;
|
|
+
|
|
+ printk("\n%s: %#lx:\n", name, addr);
|
|
+
|
|
+ /*
|
|
+ * round address down to a 32 bit boundary
|
|
+ * and always dump a multiple of 32 bytes
|
|
+ */
|
|
+ p = (u32 *)(addr & ~(sizeof(u32) - 1));
|
|
+ nbytes += (addr & (sizeof(u32) - 1));
|
|
+ nlines = (nbytes + 31) / 32;
|
|
+
|
|
+
|
|
+ for (i = 0; i < nlines; i++) {
|
|
+ /*
|
|
+ * just display low 16 bits of address to keep
|
|
+ * each line of the dump < 80 characters
|
|
+ */
|
|
+ printk("%04lx ", (unsigned long)p & 0xffff);
|
|
+ for (j = 0; j < 8; j++) {
|
|
+ u32 data;
|
|
+ if (probe_kernel_address(p, data)) {
|
|
+ pr_cont(" ********");
|
|
+ } else {
|
|
+ pr_cont(" %08x", data);
|
|
+ }
|
|
+ ++p;
|
|
+ }
|
|
+ pr_cont("\n");
|
|
+ }
|
|
+}
|
|
+
|
|
+static void show_extra_register_data(struct pt_regs *regs, int nbytes)
|
|
+{
|
|
+ mm_segment_t fs;
|
|
+ unsigned int i;
|
|
+
|
|
+ fs = get_fs();
|
|
+ set_fs(KERNEL_DS);
|
|
+ show_data(regs->pc - nbytes, nbytes * 2, "PC");
|
|
+ show_data(regs->regs[30] - nbytes, nbytes * 2, "LR");
|
|
+ show_data(regs->sp - nbytes, nbytes * 2, "SP");
|
|
+ for (i = 0; i < 30; i++) {
|
|
+ char name[4];
|
|
+ snprintf(name, sizeof(name), "X%u", i);
|
|
+ show_data(regs->regs[i] - nbytes, nbytes * 2, name);
|
|
+ }
|
|
+ set_fs(fs);
|
|
+}
|
|
+
|
|
void __show_regs(struct pt_regs *regs)
|
|
{
|
|
int i, top_reg;
|
|
@@ -201,6 +266,8 @@ void __show_regs(struct pt_regs *regs)
|
|
|
|
pr_cont("\n");
|
|
}
|
|
+ if (!user_mode(regs))
|
|
+ show_extra_register_data(regs, 128);
|
|
printk("\n");
|
|
}
|
|
|
|
@@ -331,6 +398,20 @@ void uao_thread_switch(struct task_struc
|
|
}
|
|
|
|
/*
|
|
+ * We store our current task in sp_el0, which is clobbered by userspace. Keep a
|
|
+ * shadow copy so that we can restore this upon entry from userspace.
|
|
+ *
|
|
+ * This is *only* for exception entry from EL0, and is not valid until we
|
|
+ * __switch_to() a user task.
|
|
+ */
|
|
+DEFINE_PER_CPU(struct task_struct *, __entry_task);
|
|
+
|
|
+static void entry_task_switch(struct task_struct *next)
|
|
+{
|
|
+ __this_cpu_write(__entry_task, next);
|
|
+}
|
|
+
|
|
+/*
|
|
* Thread switching.
|
|
*/
|
|
struct task_struct *__switch_to(struct task_struct *prev,
|
|
@@ -342,6 +423,7 @@ struct task_struct *__switch_to(struct t
|
|
tls_thread_switch(next);
|
|
hw_breakpoint_thread_switch(next);
|
|
contextidr_thread_switch(next);
|
|
+ entry_task_switch(next);
|
|
uao_thread_switch(next);
|
|
|
|
/*
|
|
@@ -359,27 +441,35 @@ struct task_struct *__switch_to(struct t
|
|
unsigned long get_wchan(struct task_struct *p)
|
|
{
|
|
struct stackframe frame;
|
|
- unsigned long stack_page;
|
|
+ unsigned long stack_page, ret = 0;
|
|
int count = 0;
|
|
if (!p || p == current || p->state == TASK_RUNNING)
|
|
return 0;
|
|
|
|
+ stack_page = (unsigned long)try_get_task_stack(p);
|
|
+ if (!stack_page)
|
|
+ return 0;
|
|
+
|
|
frame.fp = thread_saved_fp(p);
|
|
frame.sp = thread_saved_sp(p);
|
|
frame.pc = thread_saved_pc(p);
|
|
#ifdef CONFIG_FUNCTION_GRAPH_TRACER
|
|
frame.graph = p->curr_ret_stack;
|
|
#endif
|
|
- stack_page = (unsigned long)task_stack_page(p);
|
|
do {
|
|
if (frame.sp < stack_page ||
|
|
frame.sp >= stack_page + THREAD_SIZE ||
|
|
unwind_frame(p, &frame))
|
|
- return 0;
|
|
- if (!in_sched_functions(frame.pc))
|
|
- return frame.pc;
|
|
+ goto out;
|
|
+ if (!in_sched_functions(frame.pc)) {
|
|
+ ret = frame.pc;
|
|
+ goto out;
|
|
+ }
|
|
} while (count ++ < 16);
|
|
- return 0;
|
|
+
|
|
+out:
|
|
+ put_task_stack(p);
|
|
+ return ret;
|
|
}
|
|
|
|
unsigned long arch_align_stack(unsigned long sp)
|