~ [ source navigation ] ~ [ diff markup ] ~ [ identifier search ] ~

TOMOYO Linux Cross Reference
Linux/arch/arc/kernel/kgdb.c

Version: ~ [ linux-5.18 ] ~ [ linux-5.17.9 ] ~ [ linux-5.16.20 ] ~ [ linux-5.15.41 ] ~ [ linux-5.14.21 ] ~ [ linux-5.13.19 ] ~ [ linux-5.12.19 ] ~ [ linux-5.11.22 ] ~ [ linux-5.10.117 ] ~ [ linux-5.9.16 ] ~ [ linux-5.8.18 ] ~ [ linux-5.7.19 ] ~ [ linux-5.6.19 ] ~ [ linux-5.5.19 ] ~ [ linux-5.4.195 ] ~ [ linux-5.3.18 ] ~ [ linux-5.2.21 ] ~ [ linux-5.1.21 ] ~ [ linux-5.0.21 ] ~ [ linux-4.20.17 ] ~ [ linux-4.19.244 ] ~ [ linux-4.18.20 ] ~ [ linux-4.17.19 ] ~ [ linux-4.16.18 ] ~ [ linux-4.15.18 ] ~ [ linux-4.14.280 ] ~ [ linux-4.13.16 ] ~ [ linux-4.12.14 ] ~ [ linux-4.11.12 ] ~ [ linux-4.10.17 ] ~ [ linux-4.9.315 ] ~ [ linux-4.8.17 ] ~ [ linux-4.7.10 ] ~ [ linux-4.6.7 ] ~ [ linux-4.5.7 ] ~ [ linux-4.4.302 ] ~ [ linux-4.3.6 ] ~ [ linux-4.2.8 ] ~ [ linux-4.1.52 ] ~ [ linux-4.0.9 ] ~ [ linux-3.10.108 ] ~ [ linux-2.6.32.71 ] ~ [ linux-2.6.0 ] ~ [ linux-2.4.37.11 ] ~ [ unix-v6-master ] ~ [ ccs-tools-1.8.9 ] ~ [ policy-sample ] ~
Architecture: ~ [ i386 ] ~ [ alpha ] ~ [ m68k ] ~ [ mips ] ~ [ ppc ] ~ [ sparc ] ~ [ sparc64 ] ~

  1 /*
  2  * kgdb support for ARC
  3  *
  4  * Copyright (C) 2012 Synopsys, Inc. (www.synopsys.com)
  5  *
  6  * This program is free software; you can redistribute it and/or modify
  7  * it under the terms of the GNU General Public License version 2 as
  8  * published by the Free Software Foundation.
  9  */
 10 
 11 #include <linux/kgdb.h>
 12 #include <linux/sched.h>
 13 #include <asm/disasm.h>
 14 #include <asm/cacheflush.h>
 15 
 16 static void to_gdb_regs(unsigned long *gdb_regs, struct pt_regs *kernel_regs,
 17                         struct callee_regs *cregs)
 18 {
 19         int regno;
 20 
 21         for (regno = 0; regno <= 26; regno++)
 22                 gdb_regs[_R0 + regno] = get_reg(regno, kernel_regs, cregs);
 23 
 24         for (regno = 27; regno < GDB_MAX_REGS; regno++)
 25                 gdb_regs[regno] = 0;
 26 
 27         gdb_regs[_FP]           = kernel_regs->fp;
 28         gdb_regs[__SP]          = kernel_regs->sp;
 29         gdb_regs[_BLINK]        = kernel_regs->blink;
 30         gdb_regs[_RET]          = kernel_regs->ret;
 31         gdb_regs[_STATUS32]     = kernel_regs->status32;
 32         gdb_regs[_LP_COUNT]     = kernel_regs->lp_count;
 33         gdb_regs[_LP_END]       = kernel_regs->lp_end;
 34         gdb_regs[_LP_START]     = kernel_regs->lp_start;
 35         gdb_regs[_BTA]          = kernel_regs->bta;
 36         gdb_regs[_STOP_PC]      = kernel_regs->ret;
 37 }
 38 
 39 static void from_gdb_regs(unsigned long *gdb_regs, struct pt_regs *kernel_regs,
 40                         struct callee_regs *cregs)
 41 {
 42         int regno;
 43 
 44         for (regno = 0; regno <= 26; regno++)
 45                 set_reg(regno, gdb_regs[regno + _R0], kernel_regs, cregs);
 46 
 47         kernel_regs->fp         = gdb_regs[_FP];
 48         kernel_regs->sp         = gdb_regs[__SP];
 49         kernel_regs->blink      = gdb_regs[_BLINK];
 50         kernel_regs->ret        = gdb_regs[_RET];
 51         kernel_regs->status32   = gdb_regs[_STATUS32];
 52         kernel_regs->lp_count   = gdb_regs[_LP_COUNT];
 53         kernel_regs->lp_end     = gdb_regs[_LP_END];
 54         kernel_regs->lp_start   = gdb_regs[_LP_START];
 55         kernel_regs->bta        = gdb_regs[_BTA];
 56 }
 57 
 58 
 59 void pt_regs_to_gdb_regs(unsigned long *gdb_regs, struct pt_regs *kernel_regs)
 60 {
 61         to_gdb_regs(gdb_regs, kernel_regs, (struct callee_regs *)
 62                 current->thread.callee_reg);
 63 }
 64 
 65 void gdb_regs_to_pt_regs(unsigned long *gdb_regs, struct pt_regs *kernel_regs)
 66 {
 67         from_gdb_regs(gdb_regs, kernel_regs, (struct callee_regs *)
 68                 current->thread.callee_reg);
 69 }
 70 
 71 void sleeping_thread_to_gdb_regs(unsigned long *gdb_regs,
 72                                  struct task_struct *task)
 73 {
 74         if (task)
 75                 to_gdb_regs(gdb_regs, task_pt_regs(task),
 76                         (struct callee_regs *) task->thread.callee_reg);
 77 }
 78 
 79 struct single_step_data_t {
 80         uint16_t opcode[2];
 81         unsigned long address[2];
 82         int is_branch;
 83         int armed;
 84 } single_step_data;
 85 
 86 static void undo_single_step(struct pt_regs *regs)
 87 {
 88         if (single_step_data.armed) {
 89                 int i;
 90 
 91                 for (i = 0; i < (single_step_data.is_branch ? 2 : 1); i++) {
 92                         memcpy((void *) single_step_data.address[i],
 93                                 &single_step_data.opcode[i],
 94                                 BREAK_INSTR_SIZE);
 95 
 96                         flush_icache_range(single_step_data.address[i],
 97                                 single_step_data.address[i] +
 98                                 BREAK_INSTR_SIZE);
 99                 }
100                 single_step_data.armed = 0;
101         }
102 }
103 
104 static void place_trap(unsigned long address, void *save)
105 {
106         memcpy(save, (void *) address, BREAK_INSTR_SIZE);
107         memcpy((void *) address, &arch_kgdb_ops.gdb_bpt_instr,
108                 BREAK_INSTR_SIZE);
109         flush_icache_range(address, address + BREAK_INSTR_SIZE);
110 }
111 
112 static void do_single_step(struct pt_regs *regs)
113 {
114         single_step_data.is_branch = disasm_next_pc((unsigned long)
115                 regs->ret, regs, (struct callee_regs *)
116                 current->thread.callee_reg,
117                 &single_step_data.address[0],
118                 &single_step_data.address[1]);
119 
120         place_trap(single_step_data.address[0], &single_step_data.opcode[0]);
121 
122         if (single_step_data.is_branch) {
123                 place_trap(single_step_data.address[1],
124                         &single_step_data.opcode[1]);
125         }
126 
127         single_step_data.armed++;
128 }
129 
130 int kgdb_arch_handle_exception(int e_vector, int signo, int err_code,
131                                char *remcomInBuffer, char *remcomOutBuffer,
132                                struct pt_regs *regs)
133 {
134         unsigned long addr;
135         char *ptr;
136 
137         undo_single_step(regs);
138 
139         switch (remcomInBuffer[0]) {
140         case 's':
141         case 'c':
142                 ptr = &remcomInBuffer[1];
143                 if (kgdb_hex2long(&ptr, &addr))
144                         regs->ret = addr;
145 
146         case 'D':
147         case 'k':
148                 atomic_set(&kgdb_cpu_doing_single_step, -1);
149 
150                 if (remcomInBuffer[0] == 's') {
151                         do_single_step(regs);
152                         atomic_set(&kgdb_cpu_doing_single_step,
153                                    smp_processor_id());
154                 }
155 
156                 return 0;
157         }
158         return -1;
159 }
160 
161 int kgdb_arch_init(void)
162 {
163         single_step_data.armed = 0;
164         return 0;
165 }
166 
167 void kgdb_trap(struct pt_regs *regs)
168 {
169         /* trap_s 3 is used for breakpoints that overwrite existing
170          * instructions, while trap_s 4 is used for compiled breakpoints.
171          *
172          * with trap_s 3 breakpoints the original instruction needs to be
173          * restored and continuation needs to start at the location of the
174          * breakpoint.
175          *
176          * with trap_s 4 (compiled) breakpoints, continuation needs to
177          * start after the breakpoint.
178          */
179         if (regs->ecr_param == 3)
180                 instruction_pointer(regs) -= BREAK_INSTR_SIZE;
181 
182         kgdb_handle_exception(1, SIGTRAP, 0, regs);
183 }
184 
185 void kgdb_arch_exit(void)
186 {
187 }
188 
189 void kgdb_arch_set_pc(struct pt_regs *regs, unsigned long ip)
190 {
191         instruction_pointer(regs) = ip;
192 }
193 
194 static void kgdb_call_nmi_hook(void *ignored)
195 {
196         kgdb_nmicallback(raw_smp_processor_id(), NULL);
197 }
198 
199 void kgdb_roundup_cpus(unsigned long flags)
200 {
201         local_irq_enable();
202         smp_call_function(kgdb_call_nmi_hook, NULL, 0);
203         local_irq_disable();
204 }
205 
206 struct kgdb_arch arch_kgdb_ops = {
207         /* breakpoint instruction: TRAP_S 0x3 */
208 #ifdef CONFIG_CPU_BIG_ENDIAN
209         .gdb_bpt_instr          = {0x78, 0x7e},
210 #else
211         .gdb_bpt_instr          = {0x7e, 0x78},
212 #endif
213 };
214 

~ [ source navigation ] ~ [ diff markup ] ~ [ identifier search ] ~

kernel.org | git.kernel.org | LWN.net | Project Home | Wiki (Japanese) | Wiki (English) | SVN repository | Mail admin

Linux® is a registered trademark of Linus Torvalds in the United States and other countries.
TOMOYO® is a registered trademark of NTT DATA CORPORATION.

osdn.jp