Commit c38cb25b authored by James Hogan's avatar James Hogan Committed by Ben Hutchings

MIPS: KGDB: Use kernel context for sleeping threads

commit 162b270c upstream.

KGDB is a kernel debug stub and it can't be used to debug userland as it
can only safely access kernel memory.

On MIPS however KGDB has always got the register state of sleeping
processes from the userland register context at the beginning of the
kernel stack. This is meaningless for kernel threads (which never enter
userland), and for user threads it prevents the user seeing what it is
doing while in the kernel:

(gdb) info threads
  Id   Target Id         Frame
  ...
  3    Thread 2 (kthreadd) 0x0000000000000000 in ?? ()
  2    Thread 1 (init)   0x000000007705c4b4 in ?? ()
  1    Thread -2 (shadowCPU0) 0xffffffff8012524c in arch_kgdb_breakpoint () at arch/mips/kernel/kgdb.c:201

Get the register state instead from the (partial) kernel register
context stored in the task's thread_struct for resume() to restore. All
threads now correctly appear to be in context_switch():

(gdb) info threads
  Id   Target Id         Frame
  ...
  3    Thread 2 (kthreadd) context_switch (rq=<optimized out>, cookie=..., next=<optimized out>, prev=0x0) at kernel/sched/core.c:2903
  2    Thread 1 (init)   context_switch (rq=<optimized out>, cookie=..., next=<optimized out>, prev=0x0) at kernel/sched/core.c:2903
  1    Thread -2 (shadowCPU0) 0xffffffff8012524c in arch_kgdb_breakpoint () at arch/mips/kernel/kgdb.c:201

Call clobbered registers which aren't saved and exception registers
(BadVAddr & Cause) which can't be easily determined without stack
unwinding are reported as 0. The PC is taken from the return address,
such that the state presented matches that found immediately after
returning from resume().

Fixes: 88547001 ("[MIPS] kgdb: add arch support for the kernel's kgdb core")
Signed-off-by: default avatarJames Hogan <james.hogan@imgtec.com>
Cc: Jason Wessel <jason.wessel@windriver.com>
Cc: linux-mips@linux-mips.org
Patchwork: https://patchwork.linux-mips.org/patch/15829/Signed-off-by: default avatarRalf Baechle <ralf@linux-mips.org>
Signed-off-by: default avatarBen Hutchings <ben@decadent.org.uk>
parent df1c5dfd
...@@ -236,9 +236,6 @@ static int compute_signal(int tt) ...@@ -236,9 +236,6 @@ static int compute_signal(int tt)
void sleeping_thread_to_gdb_regs(unsigned long *gdb_regs, struct task_struct *p) void sleeping_thread_to_gdb_regs(unsigned long *gdb_regs, struct task_struct *p)
{ {
int reg; int reg;
struct thread_info *ti = task_thread_info(p);
unsigned long ksp = (unsigned long)ti + THREAD_SIZE - 32;
struct pt_regs *regs = (struct pt_regs *)ksp - 1;
#if (KGDB_GDB_REG_SIZE == 32) #if (KGDB_GDB_REG_SIZE == 32)
u32 *ptr = (u32 *)gdb_regs; u32 *ptr = (u32 *)gdb_regs;
#else #else
...@@ -246,25 +243,46 @@ void sleeping_thread_to_gdb_regs(unsigned long *gdb_regs, struct task_struct *p) ...@@ -246,25 +243,46 @@ void sleeping_thread_to_gdb_regs(unsigned long *gdb_regs, struct task_struct *p)
#endif #endif
for (reg = 0; reg < 16; reg++) for (reg = 0; reg < 16; reg++)
*(ptr++) = regs->regs[reg]; *(ptr++) = 0;
/* S0 - S7 */ /* S0 - S7 */
for (reg = 16; reg < 24; reg++) *(ptr++) = p->thread.reg16;
*(ptr++) = regs->regs[reg]; *(ptr++) = p->thread.reg17;
*(ptr++) = p->thread.reg18;
*(ptr++) = p->thread.reg19;
*(ptr++) = p->thread.reg20;
*(ptr++) = p->thread.reg21;
*(ptr++) = p->thread.reg22;
*(ptr++) = p->thread.reg23;
for (reg = 24; reg < 28; reg++) for (reg = 24; reg < 28; reg++)
*(ptr++) = 0; *(ptr++) = 0;
/* GP, SP, FP, RA */ /* GP, SP, FP, RA */
for (reg = 28; reg < 32; reg++) *(ptr++) = (long)p;
*(ptr++) = regs->regs[reg]; *(ptr++) = p->thread.reg29;
*(ptr++) = p->thread.reg30;
*(ptr++) = regs->cp0_status; *(ptr++) = p->thread.reg31;
*(ptr++) = regs->lo;
*(ptr++) = regs->hi; *(ptr++) = p->thread.cp0_status;
*(ptr++) = regs->cp0_badvaddr;
*(ptr++) = regs->cp0_cause; /* lo, hi */
*(ptr++) = regs->cp0_epc; *(ptr++) = 0;
*(ptr++) = 0;
/*
* BadVAddr, Cause
* Ideally these would come from the last exception frame up the stack
* but that requires unwinding, otherwise we can't know much for sure.
*/
*(ptr++) = 0;
*(ptr++) = 0;
/*
* PC
* use return address (RA), i.e. the moment after return from resume()
*/
*(ptr++) = p->thread.reg31;
} }
void kgdb_arch_set_pc(struct pt_regs *regs, unsigned long pc) void kgdb_arch_set_pc(struct pt_regs *regs, unsigned long pc)
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment