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

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

Version: ~ [ linux-6.11.5 ] ~ [ linux-6.10.14 ] ~ [ linux-6.9.12 ] ~ [ linux-6.8.12 ] ~ [ linux-6.7.12 ] ~ [ linux-6.6.58 ] ~ [ linux-6.5.13 ] ~ [ linux-6.4.16 ] ~ [ linux-6.3.13 ] ~ [ linux-6.2.16 ] ~ [ linux-6.1.114 ] ~ [ linux-6.0.19 ] ~ [ linux-5.19.17 ] ~ [ linux-5.18.19 ] ~ [ linux-5.17.15 ] ~ [ linux-5.16.20 ] ~ [ linux-5.15.169 ] ~ [ linux-5.14.21 ] ~ [ linux-5.13.19 ] ~ [ linux-5.12.19 ] ~ [ linux-5.11.22 ] ~ [ linux-5.10.228 ] ~ [ linux-5.9.16 ] ~ [ linux-5.8.18 ] ~ [ linux-5.7.19 ] ~ [ linux-5.6.19 ] ~ [ linux-5.5.19 ] ~ [ linux-5.4.284 ] ~ [ linux-5.3.18 ] ~ [ linux-5.2.21 ] ~ [ linux-5.1.21 ] ~ [ linux-5.0.21 ] ~ [ linux-4.20.17 ] ~ [ linux-4.19.322 ] ~ [ linux-4.18.20 ] ~ [ linux-4.17.19 ] ~ [ linux-4.16.18 ] ~ [ linux-4.15.18 ] ~ [ linux-4.14.336 ] ~ [ linux-4.13.16 ] ~ [ linux-4.12.14 ] ~ [ linux-4.11.12 ] ~ [ linux-4.10.17 ] ~ [ linux-4.9.337 ] ~ [ linux-4.4.302 ] ~ [ 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 // SPDX-License-Identifier: GPL-2.0-only
  2 /*
  3  * AArch64 KGDB support
  4  *
  5  * Based on arch/arm/kernel/kgdb.c
  6  *
  7  * Copyright (C) 2013 Cavium Inc.
  8  * Author: Vijaya Kumar K <vijaya.kumar@caviumnetworks.com>
  9  */
 10 
 11 #include <linux/bug.h>
 12 #include <linux/irq.h>
 13 #include <linux/kdebug.h>
 14 #include <linux/kgdb.h>
 15 #include <linux/kprobes.h>
 16 #include <linux/sched/task_stack.h>
 17 
 18 #include <asm/debug-monitors.h>
 19 #include <asm/insn.h>
 20 #include <asm/patching.h>
 21 #include <asm/traps.h>
 22 
 23 struct dbg_reg_def_t dbg_reg_def[DBG_MAX_REG_NUM] = {
 24         { "x0", 8, offsetof(struct pt_regs, regs[0])},
 25         { "x1", 8, offsetof(struct pt_regs, regs[1])},
 26         { "x2", 8, offsetof(struct pt_regs, regs[2])},
 27         { "x3", 8, offsetof(struct pt_regs, regs[3])},
 28         { "x4", 8, offsetof(struct pt_regs, regs[4])},
 29         { "x5", 8, offsetof(struct pt_regs, regs[5])},
 30         { "x6", 8, offsetof(struct pt_regs, regs[6])},
 31         { "x7", 8, offsetof(struct pt_regs, regs[7])},
 32         { "x8", 8, offsetof(struct pt_regs, regs[8])},
 33         { "x9", 8, offsetof(struct pt_regs, regs[9])},
 34         { "x10", 8, offsetof(struct pt_regs, regs[10])},
 35         { "x11", 8, offsetof(struct pt_regs, regs[11])},
 36         { "x12", 8, offsetof(struct pt_regs, regs[12])},
 37         { "x13", 8, offsetof(struct pt_regs, regs[13])},
 38         { "x14", 8, offsetof(struct pt_regs, regs[14])},
 39         { "x15", 8, offsetof(struct pt_regs, regs[15])},
 40         { "x16", 8, offsetof(struct pt_regs, regs[16])},
 41         { "x17", 8, offsetof(struct pt_regs, regs[17])},
 42         { "x18", 8, offsetof(struct pt_regs, regs[18])},
 43         { "x19", 8, offsetof(struct pt_regs, regs[19])},
 44         { "x20", 8, offsetof(struct pt_regs, regs[20])},
 45         { "x21", 8, offsetof(struct pt_regs, regs[21])},
 46         { "x22", 8, offsetof(struct pt_regs, regs[22])},
 47         { "x23", 8, offsetof(struct pt_regs, regs[23])},
 48         { "x24", 8, offsetof(struct pt_regs, regs[24])},
 49         { "x25", 8, offsetof(struct pt_regs, regs[25])},
 50         { "x26", 8, offsetof(struct pt_regs, regs[26])},
 51         { "x27", 8, offsetof(struct pt_regs, regs[27])},
 52         { "x28", 8, offsetof(struct pt_regs, regs[28])},
 53         { "x29", 8, offsetof(struct pt_regs, regs[29])},
 54         { "x30", 8, offsetof(struct pt_regs, regs[30])},
 55         { "sp", 8, offsetof(struct pt_regs, sp)},
 56         { "pc", 8, offsetof(struct pt_regs, pc)},
 57         /*
 58          * struct pt_regs thinks PSTATE is 64-bits wide but gdb remote
 59          * protocol disagrees. Therefore we must extract only the lower
 60          * 32-bits. Look for the big comment in asm/kgdb.h for more
 61          * detail.
 62          */
 63         { "pstate", 4, offsetof(struct pt_regs, pstate)
 64 #ifdef CONFIG_CPU_BIG_ENDIAN
 65                                                         + 4
 66 #endif
 67         },
 68         { "v0", 16, -1 },
 69         { "v1", 16, -1 },
 70         { "v2", 16, -1 },
 71         { "v3", 16, -1 },
 72         { "v4", 16, -1 },
 73         { "v5", 16, -1 },
 74         { "v6", 16, -1 },
 75         { "v7", 16, -1 },
 76         { "v8", 16, -1 },
 77         { "v9", 16, -1 },
 78         { "v10", 16, -1 },
 79         { "v11", 16, -1 },
 80         { "v12", 16, -1 },
 81         { "v13", 16, -1 },
 82         { "v14", 16, -1 },
 83         { "v15", 16, -1 },
 84         { "v16", 16, -1 },
 85         { "v17", 16, -1 },
 86         { "v18", 16, -1 },
 87         { "v19", 16, -1 },
 88         { "v20", 16, -1 },
 89         { "v21", 16, -1 },
 90         { "v22", 16, -1 },
 91         { "v23", 16, -1 },
 92         { "v24", 16, -1 },
 93         { "v25", 16, -1 },
 94         { "v26", 16, -1 },
 95         { "v27", 16, -1 },
 96         { "v28", 16, -1 },
 97         { "v29", 16, -1 },
 98         { "v30", 16, -1 },
 99         { "v31", 16, -1 },
100         { "fpsr", 4, -1 },
101         { "fpcr", 4, -1 },
102 };
103 
104 char *dbg_get_reg(int regno, void *mem, struct pt_regs *regs)
105 {
106         if (regno >= DBG_MAX_REG_NUM || regno < 0)
107                 return NULL;
108 
109         if (dbg_reg_def[regno].offset != -1)
110                 memcpy(mem, (void *)regs + dbg_reg_def[regno].offset,
111                        dbg_reg_def[regno].size);
112         else
113                 memset(mem, 0, dbg_reg_def[regno].size);
114         return dbg_reg_def[regno].name;
115 }
116 
117 int dbg_set_reg(int regno, void *mem, struct pt_regs *regs)
118 {
119         if (regno >= DBG_MAX_REG_NUM || regno < 0)
120                 return -EINVAL;
121 
122         if (dbg_reg_def[regno].offset != -1)
123                 memcpy((void *)regs + dbg_reg_def[regno].offset, mem,
124                        dbg_reg_def[regno].size);
125         return 0;
126 }
127 
128 void
129 sleeping_thread_to_gdb_regs(unsigned long *gdb_regs, struct task_struct *task)
130 {
131         struct cpu_context *cpu_context = &task->thread.cpu_context;
132 
133         /* Initialize to zero */
134         memset((char *)gdb_regs, 0, NUMREGBYTES);
135 
136         gdb_regs[19] = cpu_context->x19;
137         gdb_regs[20] = cpu_context->x20;
138         gdb_regs[21] = cpu_context->x21;
139         gdb_regs[22] = cpu_context->x22;
140         gdb_regs[23] = cpu_context->x23;
141         gdb_regs[24] = cpu_context->x24;
142         gdb_regs[25] = cpu_context->x25;
143         gdb_regs[26] = cpu_context->x26;
144         gdb_regs[27] = cpu_context->x27;
145         gdb_regs[28] = cpu_context->x28;
146         gdb_regs[29] = cpu_context->fp;
147 
148         gdb_regs[31] = cpu_context->sp;
149         gdb_regs[32] = cpu_context->pc;
150 }
151 
152 void kgdb_arch_set_pc(struct pt_regs *regs, unsigned long pc)
153 {
154         regs->pc = pc;
155 }
156 
157 static int compiled_break;
158 
159 static void kgdb_arch_update_addr(struct pt_regs *regs,
160                                 char *remcom_in_buffer)
161 {
162         unsigned long addr;
163         char *ptr;
164 
165         ptr = &remcom_in_buffer[1];
166         if (kgdb_hex2long(&ptr, &addr))
167                 kgdb_arch_set_pc(regs, addr);
168         else if (compiled_break == 1)
169                 kgdb_arch_set_pc(regs, regs->pc + 4);
170 
171         compiled_break = 0;
172 }
173 
174 int kgdb_arch_handle_exception(int exception_vector, int signo,
175                                int err_code, char *remcom_in_buffer,
176                                char *remcom_out_buffer,
177                                struct pt_regs *linux_regs)
178 {
179         int err;
180 
181         switch (remcom_in_buffer[0]) {
182         case 'D':
183         case 'k':
184                 /*
185                  * Packet D (Detach), k (kill). No special handling
186                  * is required here. Handle same as c packet.
187                  */
188         case 'c':
189                 /*
190                  * Packet c (Continue) to continue executing.
191                  * Set pc to required address.
192                  * Try to read optional parameter and set pc.
193                  * If this was a compiled breakpoint, we need to move
194                  * to the next instruction else we will just breakpoint
195                  * over and over again.
196                  */
197                 kgdb_arch_update_addr(linux_regs, remcom_in_buffer);
198                 atomic_set(&kgdb_cpu_doing_single_step, -1);
199                 kgdb_single_step =  0;
200 
201                 /*
202                  * Received continue command, disable single step
203                  */
204                 if (kernel_active_single_step())
205                         kernel_disable_single_step();
206 
207                 err = 0;
208                 break;
209         case 's':
210                 /*
211                  * Update step address value with address passed
212                  * with step packet.
213                  * On debug exception return PC is copied to ELR
214                  * So just update PC.
215                  * If no step address is passed, resume from the address
216                  * pointed by PC. Do not update PC
217                  */
218                 kgdb_arch_update_addr(linux_regs, remcom_in_buffer);
219                 atomic_set(&kgdb_cpu_doing_single_step, raw_smp_processor_id());
220                 kgdb_single_step =  1;
221 
222                 /*
223                  * Enable single step handling
224                  */
225                 if (!kernel_active_single_step())
226                         kernel_enable_single_step(linux_regs);
227                 else
228                         kernel_rewind_single_step(linux_regs);
229                 err = 0;
230                 break;
231         default:
232                 err = -1;
233         }
234         return err;
235 }
236 
237 static int kgdb_brk_fn(struct pt_regs *regs, unsigned long esr)
238 {
239         kgdb_handle_exception(1, SIGTRAP, 0, regs);
240         return DBG_HOOK_HANDLED;
241 }
242 NOKPROBE_SYMBOL(kgdb_brk_fn)
243 
244 static int kgdb_compiled_brk_fn(struct pt_regs *regs, unsigned long esr)
245 {
246         compiled_break = 1;
247         kgdb_handle_exception(1, SIGTRAP, 0, regs);
248 
249         return DBG_HOOK_HANDLED;
250 }
251 NOKPROBE_SYMBOL(kgdb_compiled_brk_fn);
252 
253 static int kgdb_step_brk_fn(struct pt_regs *regs, unsigned long esr)
254 {
255         if (!kgdb_single_step)
256                 return DBG_HOOK_ERROR;
257 
258         kgdb_handle_exception(0, SIGTRAP, 0, regs);
259         return DBG_HOOK_HANDLED;
260 }
261 NOKPROBE_SYMBOL(kgdb_step_brk_fn);
262 
263 static struct break_hook kgdb_brkpt_hook = {
264         .fn             = kgdb_brk_fn,
265         .imm            = KGDB_DYN_DBG_BRK_IMM,
266 };
267 
268 static struct break_hook kgdb_compiled_brkpt_hook = {
269         .fn             = kgdb_compiled_brk_fn,
270         .imm            = KGDB_COMPILED_DBG_BRK_IMM,
271 };
272 
273 static struct step_hook kgdb_step_hook = {
274         .fn             = kgdb_step_brk_fn
275 };
276 
277 static int __kgdb_notify(struct die_args *args, unsigned long cmd)
278 {
279         struct pt_regs *regs = args->regs;
280 
281         if (kgdb_handle_exception(1, args->signr, cmd, regs))
282                 return NOTIFY_DONE;
283         return NOTIFY_STOP;
284 }
285 
286 static int
287 kgdb_notify(struct notifier_block *self, unsigned long cmd, void *ptr)
288 {
289         unsigned long flags;
290         int ret;
291 
292         local_irq_save(flags);
293         ret = __kgdb_notify(ptr, cmd);
294         local_irq_restore(flags);
295 
296         return ret;
297 }
298 
299 static struct notifier_block kgdb_notifier = {
300         .notifier_call  = kgdb_notify,
301         /*
302          * Want to be lowest priority
303          */
304         .priority       = -INT_MAX,
305 };
306 
307 /*
308  * kgdb_arch_init - Perform any architecture specific initialization.
309  * This function will handle the initialization of any architecture
310  * specific callbacks.
311  */
312 int kgdb_arch_init(void)
313 {
314         int ret = register_die_notifier(&kgdb_notifier);
315 
316         if (ret != 0)
317                 return ret;
318 
319         register_kernel_break_hook(&kgdb_brkpt_hook);
320         register_kernel_break_hook(&kgdb_compiled_brkpt_hook);
321         register_kernel_step_hook(&kgdb_step_hook);
322         return 0;
323 }
324 
325 /*
326  * kgdb_arch_exit - Perform any architecture specific uninitalization.
327  * This function will handle the uninitalization of any architecture
328  * specific callbacks, for dynamic registration and unregistration.
329  */
330 void kgdb_arch_exit(void)
331 {
332         unregister_kernel_break_hook(&kgdb_brkpt_hook);
333         unregister_kernel_break_hook(&kgdb_compiled_brkpt_hook);
334         unregister_kernel_step_hook(&kgdb_step_hook);
335         unregister_die_notifier(&kgdb_notifier);
336 }
337 
338 const struct kgdb_arch arch_kgdb_ops;
339 
340 int kgdb_arch_set_breakpoint(struct kgdb_bkpt *bpt)
341 {
342         int err;
343 
344         BUILD_BUG_ON(AARCH64_INSN_SIZE != BREAK_INSTR_SIZE);
345 
346         err = aarch64_insn_read((void *)bpt->bpt_addr, (u32 *)bpt->saved_instr);
347         if (err)
348                 return err;
349 
350         return aarch64_insn_write((void *)bpt->bpt_addr,
351                         (u32)AARCH64_BREAK_KGDB_DYN_DBG);
352 }
353 
354 int kgdb_arch_remove_breakpoint(struct kgdb_bkpt *bpt)
355 {
356         return aarch64_insn_write((void *)bpt->bpt_addr,
357                         *(u32 *)bpt->saved_instr);
358 }
359 

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

kernel.org | git.kernel.org | LWN.net | Project Home | 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.

sflogo.php