1 From bb9bc4689b9c635714fbcd5d335bad9934a7ebfc Mon Sep 17 00:00:00 2001
2 From: Paul Burton <paul.burton@imgtec.com>
3 Date: Mon, 7 Nov 2016 15:07:06 +0000
4 Subject: MIPS: Calculate microMIPS ra properly when unwinding the stack
6 From: Paul Burton <paul.burton@imgtec.com>
8 commit bb9bc4689b9c635714fbcd5d335bad9934a7ebfc upstream.
10 get_frame_info() calculates the offset of the return address within a
11 stack frame simply by dividing a the bottom 16 bits of the instruction,
12 treated as a signed integer, by the size of a long. Whilst this works
13 for MIPS32 & MIPS64 ISAs where the sw or sd instructions are used, it's
14 incorrect for microMIPS where encodings differ. The result is that we
15 typically completely fail to unwind the stack on microMIPS.
17 Fix this by adjusting is_ra_save_ins() to calculate the return address
18 offset, and take into account the various different encodings there in
19 the same place as we consider whether an instruction is storing the
22 With this we are now able to unwind the stack for kernels targetting the
23 microMIPS ISA, for example we can produce:
26 [<80109e1f>] show_stack+0x63/0x7c
27 [<8011ea17>] __warn+0x9b/0xac
28 [<8011ea45>] warn_slowpath_fmt+0x1d/0x20
29 [<8013fe53>] register_console+0x43/0x314
30 [<8067c58d>] of_setup_earlycon+0x1dd/0x1ec
31 [<8067f63f>] early_init_dt_scan_chosen_stdout+0xe7/0xf8
32 [<8066c115>] do_early_param+0x75/0xac
33 [<801302f9>] parse_args+0x1dd/0x308
34 [<8066c459>] parse_early_options+0x25/0x28
35 [<8066c48b>] parse_early_param+0x2f/0x38
36 [<8066e8cf>] setup_arch+0x113/0x488
37 [<8066c4f3>] start_kernel+0x57/0x328
38 ---[ end trace 0000000000000000 ]---
40 Whereas previously we only produced:
43 [<80109e1f>] show_stack+0x63/0x7c
44 ---[ end trace 0000000000000000 ]---
46 Signed-off-by: Paul Burton <paul.burton@imgtec.com>
47 Fixes: 34c2f668d0f6 ("MIPS: microMIPS: Add unaligned access support.")
48 Cc: Leonid Yegoshin <leonid.yegoshin@imgtec.com>
49 Cc: linux-mips@linux-mips.org
50 Patchwork: https://patchwork.linux-mips.org/patch/14532/
51 Signed-off-by: Ralf Baechle <ralf@linux-mips.org>
52 Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
55 arch/mips/kernel/process.c | 85 +++++++++++++++++++++++++++++++++------------
56 1 file changed, 64 insertions(+), 21 deletions(-)
58 --- a/arch/mips/kernel/process.c
59 +++ b/arch/mips/kernel/process.c
60 @@ -195,7 +195,7 @@ struct mips_frame_info {
61 #define J_TARGET(pc,target) \
62 (((unsigned long)(pc) & 0xf0000000) | ((target) << 2))
64 -static inline int is_ra_save_ins(union mips_instruction *ip)
65 +static inline int is_ra_save_ins(union mips_instruction *ip, int *poff)
67 #ifdef CONFIG_CPU_MICROMIPS
69 @@ -208,25 +208,70 @@ static inline int is_ra_save_ins(union m
70 * microMIPS is way more fun...
72 if (mm_insn_16bit(ip->halfword[1])) {
73 - return (ip->mm16_r5_format.opcode == mm_swsp16_op &&
74 - ip->mm16_r5_format.rt == 31) ||
75 - (ip->mm16_m_format.opcode == mm_pool16c_op &&
76 - ip->mm16_m_format.func == mm_swm16_op);
79 - return (ip->mm_m_format.opcode == mm_pool32b_op &&
80 - ip->mm_m_format.rd > 9 &&
81 - ip->mm_m_format.base == 29 &&
82 - ip->mm_m_format.func == mm_swm32_func) ||
83 - (ip->i_format.opcode == mm_sw32_op &&
84 - ip->i_format.rs == 29 &&
85 - ip->i_format.rt == 31);
86 + switch (ip->mm16_r5_format.opcode) {
88 + if (ip->mm16_r5_format.rt != 31)
91 + *poff = ip->mm16_r5_format.simmediate;
92 + *poff = (*poff << 2) / sizeof(ulong);
96 + switch (ip->mm16_m_format.func) {
98 + *poff = ip->mm16_m_format.imm;
99 + *poff += 1 + ip->mm16_m_format.rlist;
100 + *poff = (*poff << 2) / sizeof(ulong);
112 + switch (ip->i_format.opcode) {
114 + if (ip->i_format.rs != 29)
116 + if (ip->i_format.rt != 31)
119 + *poff = ip->i_format.simmediate / sizeof(ulong);
122 + case mm_pool32b_op:
123 + switch (ip->mm_m_format.func) {
124 + case mm_swm32_func:
125 + if (ip->mm_m_format.rd < 0x10)
127 + if (ip->mm_m_format.base != 29)
130 + *poff = ip->mm_m_format.simmediate;
131 + *poff += (ip->mm_m_format.rd & 0xf) * sizeof(u32);
132 + *poff /= sizeof(ulong);
142 /* sw / sd $ra, offset($sp) */
143 - return (ip->i_format.opcode == sw_op || ip->i_format.opcode == sd_op) &&
144 - ip->i_format.rs == 29 &&
145 - ip->i_format.rt == 31;
146 + if ((ip->i_format.opcode == sw_op || ip->i_format.opcode == sd_op) &&
147 + ip->i_format.rs == 29 && ip->i_format.rt == 31) {
148 + *poff = ip->i_format.simmediate / sizeof(ulong);
156 @@ -349,11 +394,9 @@ static int get_frame_info(struct mips_fr
160 - if (info->pc_offset == -1 && is_ra_save_ins(&insn)) {
162 - ip->i_format.simmediate / sizeof(long);
163 + if (info->pc_offset == -1 &&
164 + is_ra_save_ins(&insn, &info->pc_offset))
168 if (info->frame_size && info->pc_offset >= 0) /* nested */