cregit-Linux how code gets into the kernel

Release 4.14 arch/mn10300/kernel/kgdb.c

/* kgdb support for MN10300
 *
 * Copyright (C) 2010 Red Hat, Inc. All Rights Reserved.
 * Written by David Howells (dhowells@redhat.com)
 *
 * This program is free software; you can redistribute it and/or
 * modify it under the terms of the GNU General Public Licence
 * as published by the Free Software Foundation; either version
 * 2 of the Licence, or (at your option) any later version.
 */

#include <linux/slab.h>
#include <linux/ptrace.h>
#include <linux/kgdb.h>
#include <linux/uaccess.h>
#include <unit/leds.h>
#include <unit/serial.h>
#include <asm/debugger.h>
#include <asm/serial-regs.h>
#include "internal.h"

/*
 * Software single-stepping breakpoint save (used by __switch_to())
 */

static struct thread_info *kgdb_sstep_thread;

u8 *kgdb_sstep_bp_addr[2];

u8 kgdb_sstep_bp[2];

/*
 * Copy kernel exception frame registers to the GDB register file
 */

void pt_regs_to_gdb_regs(unsigned long *gdb_regs, struct pt_regs *regs) { unsigned long ssp = (unsigned long) (regs + 1); gdb_regs[GDB_FR_D0] = regs->d0; gdb_regs[GDB_FR_D1] = regs->d1; gdb_regs[GDB_FR_D2] = regs->d2; gdb_regs[GDB_FR_D3] = regs->d3; gdb_regs[GDB_FR_A0] = regs->a0; gdb_regs[GDB_FR_A1] = regs->a1; gdb_regs[GDB_FR_A2] = regs->a2; gdb_regs[GDB_FR_A3] = regs->a3; gdb_regs[GDB_FR_SP] = (regs->epsw & EPSW_nSL) ? regs->sp : ssp; gdb_regs[GDB_FR_PC] = regs->pc; gdb_regs[GDB_FR_MDR] = regs->mdr; gdb_regs[GDB_FR_EPSW] = regs->epsw; gdb_regs[GDB_FR_LIR] = regs->lir; gdb_regs[GDB_FR_LAR] = regs->lar; gdb_regs[GDB_FR_MDRQ] = regs->mdrq; gdb_regs[GDB_FR_E0] = regs->e0; gdb_regs[GDB_FR_E1] = regs->e1; gdb_regs[GDB_FR_E2] = regs->e2; gdb_regs[GDB_FR_E3] = regs->e3; gdb_regs[GDB_FR_E4] = regs->e4; gdb_regs[GDB_FR_E5] = regs->e5; gdb_regs[GDB_FR_E6] = regs->e6; gdb_regs[GDB_FR_E7] = regs->e7; gdb_regs[GDB_FR_SSP] = ssp; gdb_regs[GDB_FR_MSP] = 0; gdb_regs[GDB_FR_USP] = regs->sp; gdb_regs[GDB_FR_MCRH] = regs->mcrh; gdb_regs[GDB_FR_MCRL] = regs->mcrl; gdb_regs[GDB_FR_MCVF] = regs->mcvf; gdb_regs[GDB_FR_DUMMY0] = 0; gdb_regs[GDB_FR_DUMMY1] = 0; gdb_regs[GDB_FR_FS0] = 0; }

Contributors

PersonTokensPropCommitsCommitProp
David Howells317100.00%1100.00%
Total317100.00%1100.00%

/* * Extracts kernel SP/PC values understandable by gdb from the values * saved by switch_to(). */
void sleeping_thread_to_gdb_regs(unsigned long *gdb_regs, struct task_struct *p) { gdb_regs[GDB_FR_SSP] = p->thread.sp; gdb_regs[GDB_FR_PC] = p->thread.pc; gdb_regs[GDB_FR_A3] = p->thread.a3; gdb_regs[GDB_FR_USP] = p->thread.usp; gdb_regs[GDB_FR_FPCR] = p->thread.fpu_state.fpcr; }

Contributors

PersonTokensPropCommitsCommitProp
David Howells72100.00%1100.00%
Total72100.00%1100.00%

/* * Fill kernel exception frame registers from the GDB register file */
void gdb_regs_to_pt_regs(unsigned long *gdb_regs, struct pt_regs *regs) { regs->d0 = gdb_regs[GDB_FR_D0]; regs->d1 = gdb_regs[GDB_FR_D1]; regs->d2 = gdb_regs[GDB_FR_D2]; regs->d3 = gdb_regs[GDB_FR_D3]; regs->a0 = gdb_regs[GDB_FR_A0]; regs->a1 = gdb_regs[GDB_FR_A1]; regs->a2 = gdb_regs[GDB_FR_A2]; regs->a3 = gdb_regs[GDB_FR_A3]; regs->sp = gdb_regs[GDB_FR_SP]; regs->pc = gdb_regs[GDB_FR_PC]; regs->mdr = gdb_regs[GDB_FR_MDR]; regs->epsw = gdb_regs[GDB_FR_EPSW]; regs->lir = gdb_regs[GDB_FR_LIR]; regs->lar = gdb_regs[GDB_FR_LAR]; regs->mdrq = gdb_regs[GDB_FR_MDRQ]; regs->e0 = gdb_regs[GDB_FR_E0]; regs->e1 = gdb_regs[GDB_FR_E1]; regs->e2 = gdb_regs[GDB_FR_E2]; regs->e3 = gdb_regs[GDB_FR_E3]; regs->e4 = gdb_regs[GDB_FR_E4]; regs->e5 = gdb_regs[GDB_FR_E5]; regs->e6 = gdb_regs[GDB_FR_E6]; regs->e7 = gdb_regs[GDB_FR_E7]; regs->sp = gdb_regs[GDB_FR_SSP]; /* gdb_regs[GDB_FR_MSP]; */ // regs->usp = gdb_regs[GDB_FR_USP]; regs->mcrh = gdb_regs[GDB_FR_MCRH]; regs->mcrl = gdb_regs[GDB_FR_MCRL]; regs->mcvf = gdb_regs[GDB_FR_MCVF]; /* gdb_regs[GDB_FR_DUMMY0]; */ /* gdb_regs[GDB_FR_DUMMY1]; */ // regs->fpcr = gdb_regs[GDB_FR_FPCR]; // regs->fs0 = gdb_regs[GDB_FR_FS0]; }

Contributors

PersonTokensPropCommitsCommitProp
David Howells264100.00%1100.00%
Total264100.00%1100.00%

struct kgdb_arch arch_kgdb_ops = { .gdb_bpt_instr = { 0xff }, .flags = KGDB_HW_BREAKPOINT, }; static const unsigned char mn10300_kgdb_insn_sizes[256] = { /* 1 2 3 4 5 6 7 8 9 a b c d e f */ 1, 3, 3, 3, 1, 3, 3, 3, 1, 3, 3, 3, 1, 3, 3, 3, /* 0 */ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, /* 1 */ 2, 2, 2, 2, 3, 3, 3, 3, 2, 2, 2, 2, 3, 3, 3, 3, /* 2 */ 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 1, 1, 1, 1, /* 3 */ 1, 1, 2, 2, 1, 1, 2, 2, 1, 1, 2, 2, 1, 1, 2, 2, /* 4 */ 1, 1, 1, 1, 1, 1, 1, 1, 2, 2, 2, 2, 2, 2, 2, 2, /* 5 */ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, /* 6 */ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, /* 7 */ 2, 1, 1, 1, 1, 2, 1, 1, 1, 1, 2, 1, 1, 1, 1, 2, /* 8 */ 2, 1, 1, 1, 1, 2, 1, 1, 1, 1, 2, 1, 1, 1, 1, 2, /* 9 */ 2, 1, 1, 1, 1, 2, 1, 1, 1, 1, 2, 1, 1, 1, 1, 2, /* a */ 2, 1, 1, 1, 1, 2, 1, 1, 1, 1, 2, 1, 1, 1, 1, 2, /* b */ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 2, 2, /* c */ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, /* d */ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, /* e */ 0, 2, 2, 2, 2, 2, 2, 4, 0, 3, 0, 4, 0, 6, 7, 1 /* f */ }; /* * Attempt to emulate single stepping by means of breakpoint instructions. * Although there is a single-step trace flag in EPSW, its use is not * sufficiently documented and is only intended for use with the JTAG debugger. */
static int kgdb_arch_do_singlestep(struct pt_regs *regs) { unsigned long arg; unsigned size; u8 *pc = (u8 *)regs->pc, *sp = (u8 *)(regs + 1), cur; u8 *x = NULL, *y = NULL; int ret; ret = probe_kernel_read(&cur, pc, 1); if (ret < 0) return ret; size = mn10300_kgdb_insn_sizes[cur]; if (size > 0) { x = pc + size; goto set_x; } switch (cur) { /* Bxx (d8,PC) */ case 0xc0 ... 0xca: ret = probe_kernel_read(&arg, pc + 1, 1); if (ret < 0) return ret; x = pc + 2; if (arg >= 0 && arg <= 2) goto set_x; y = pc + (s8)arg; goto set_x_and_y; /* LXX (d8,PC) */ case 0xd0 ... 0xda: x = pc + 1; if (regs->pc == regs->lar) goto set_x; y = (u8 *)regs->lar; goto set_x_and_y; /* SETLB - loads the next four bytes into the LIR register * (which mustn't include a breakpoint instruction) */ case 0xdb: x = pc + 5; goto set_x; /* JMP (d16,PC) or CALL (d16,PC) */ case 0xcc: case 0xcd: ret = probe_kernel_read(&arg, pc + 1, 2); if (ret < 0) return ret; x = pc + (s16)arg; goto set_x; /* JMP (d32,PC) or CALL (d32,PC) */ case 0xdc: case 0xdd: ret = probe_kernel_read(&arg, pc + 1, 4); if (ret < 0) return ret; x = pc + (s32)arg; goto set_x; /* RETF */ case 0xde: x = (u8 *)regs->mdr; goto set_x; /* RET */ case 0xdf: ret = probe_kernel_read(&arg, pc + 2, 1); if (ret < 0) return ret; ret = probe_kernel_read(&x, sp + (s8)arg, 4); if (ret < 0) return ret; goto set_x; case 0xf0: ret = probe_kernel_read(&cur, pc + 1, 1); if (ret < 0) return ret; if (cur >= 0xf0 && cur <= 0xf7) { /* JMP (An) / CALLS (An) */ switch (cur & 3) { case 0: x = (u8 *)regs->a0; break; case 1: x = (u8 *)regs->a1; break; case 2: x = (u8 *)regs->a2; break; case 3: x = (u8 *)regs->a3; break; } goto set_x; } else if (cur == 0xfc) { /* RETS */ ret = probe_kernel_read(&x, sp, 4); if (ret < 0) return ret; goto set_x; } else if (cur == 0xfd) { /* RTI */ ret = probe_kernel_read(&x, sp + 4, 4); if (ret < 0) return ret; goto set_x; } else { x = pc + 2; goto set_x; } break; /* potential 3-byte conditional branches */ case 0xf8: ret = probe_kernel_read(&cur, pc + 1, 1); if (ret < 0) return ret; x = pc + 3; if (cur >= 0xe8 && cur <= 0xeb) { ret = probe_kernel_read(&arg, pc + 2, 1); if (ret < 0) return ret; if (arg >= 0 && arg <= 3) goto set_x; y = pc + (s8)arg; goto set_x_and_y; } goto set_x; case 0xfa: ret = probe_kernel_read(&cur, pc + 1, 1); if (ret < 0) return ret; if (cur == 0xff) { /* CALLS (d16,PC) */ ret = probe_kernel_read(&arg, pc + 2, 2); if (ret < 0) return ret; x = pc + (s16)arg; goto set_x; } x = pc + 4; goto set_x; case 0xfc: ret = probe_kernel_read(&cur, pc + 1, 1); if (ret < 0) return ret; if (cur == 0xff) { /* CALLS (d32,PC) */ ret = probe_kernel_read(&arg, pc + 2, 4); if (ret < 0) return ret; x = pc + (s32)arg; goto set_x; } x = pc + 6; goto set_x; } return 0; set_x: kgdb_sstep_bp_addr[0] = x; kgdb_sstep_bp_addr[1] = NULL; ret = probe_kernel_read(&kgdb_sstep_bp[0], x, 1); if (ret < 0) return ret; ret = probe_kernel_write(x, &arch_kgdb_ops.gdb_bpt_instr, 1); if (ret < 0) return ret; kgdb_sstep_thread = current_thread_info(); debugger_local_cache_flushinv_one(x); return ret; set_x_and_y: kgdb_sstep_bp_addr[0] = x; kgdb_sstep_bp_addr[1] = y; ret = probe_kernel_read(&kgdb_sstep_bp[0], x, 1); if (ret < 0) return ret; ret = probe_kernel_read(&kgdb_sstep_bp[1], y, 1); if (ret < 0) return ret; ret = probe_kernel_write(x, &arch_kgdb_ops.gdb_bpt_instr, 1); if (ret < 0) return ret; ret = probe_kernel_write(y, &arch_kgdb_ops.gdb_bpt_instr, 1); if (ret < 0) { probe_kernel_write(kgdb_sstep_bp_addr[0], &kgdb_sstep_bp[0], 1); } else { kgdb_sstep_thread = current_thread_info(); } debugger_local_cache_flushinv_one(x); debugger_local_cache_flushinv_one(y); return ret; }

Contributors

PersonTokensPropCommitsCommitProp
David Howells1046100.00%1100.00%
Total1046100.00%1100.00%

/* * Remove emplaced single-step breakpoints, returning true if we hit one of * them. */
static bool kgdb_arch_undo_singlestep(struct pt_regs *regs) { bool hit = false; u8 *x = kgdb_sstep_bp_addr[0], *y = kgdb_sstep_bp_addr[1]; u8 opcode; if (kgdb_sstep_thread == current_thread_info()) { if (x) { if (x == (u8 *)regs->pc) hit = true; if (probe_kernel_read(&opcode, x, 1) < 0 || opcode != 0xff) BUG(); probe_kernel_write(x, &kgdb_sstep_bp[0], 1); debugger_local_cache_flushinv_one(x); } if (y) { if (y == (u8 *)regs->pc) hit = true; if (probe_kernel_read(&opcode, y, 1) < 0 || opcode != 0xff) BUG(); probe_kernel_write(y, &kgdb_sstep_bp[1], 1); debugger_local_cache_flushinv_one(y); } } kgdb_sstep_bp_addr[0] = NULL; kgdb_sstep_bp_addr[1] = NULL; kgdb_sstep_thread = NULL; return hit; }

Contributors

PersonTokensPropCommitsCommitProp
David Howells188100.00%1100.00%
Total188100.00%1100.00%

/* * Catch a single-step-pending thread being deleted and make sure the global * single-step state is cleared. At this point the breakpoints should have * been removed by __switch_to(). */
void arch_release_thread_stack(unsigned long *stack) { struct thread_info *ti = (void *)stack; if (kgdb_sstep_thread == ti) { kgdb_sstep_thread = NULL; /* However, we may now be running in degraded mode, with most * of the CPUs disabled until such a time as KGDB is reentered, * so force immediate reentry */ kgdb_breakpoint(); } }

Contributors

PersonTokensPropCommitsCommitProp
David Howells2362.16%150.00%
Linus Torvalds1437.84%150.00%
Total37100.00%2100.00%

/* * Handle unknown packets and [CcsDk] packets * - at this point breakpoints have been installed */
int kgdb_arch_handle_exception(int vector, int signo, int err_code, char *remcom_in_buffer, char *remcom_out_buffer, struct pt_regs *regs) { long addr; char *ptr; switch (remcom_in_buffer[0]) { case 'c': case 's': /* try to read optional parameter, pc unchanged if no parm */ ptr = &remcom_in_buffer[1]; if (kgdb_hex2long(&ptr, &addr)) regs->pc = addr; case 'D': case 'k': atomic_set(&kgdb_cpu_doing_single_step, -1); if (remcom_in_buffer[0] == 's') { kgdb_arch_do_singlestep(regs); kgdb_single_step = 1; atomic_set(&kgdb_cpu_doing_single_step, raw_smp_processor_id()); } return 0; } return -1; /* this means that we do not want to exit from the handler */ }

Contributors

PersonTokensPropCommitsCommitProp
David Howells127100.00%1100.00%
Total127100.00%1100.00%

/* * Handle event interception * - returns 0 if the exception should be skipped, -ERROR otherwise. */
int debugger_intercept(enum exception_code excep, int signo, int si_code, struct pt_regs *regs) { int ret; if (kgdb_arch_undo_singlestep(regs)) { excep = EXCEP_TRAP; signo = SIGTRAP; si_code = TRAP_TRACE; } ret = kgdb_handle_exception(excep, signo, si_code, regs); debugger_local_cache_flushinv(); return ret; }

Contributors

PersonTokensPropCommitsCommitProp
David Howells63100.00%2100.00%
Total63100.00%2100.00%

/* * Determine if we've hit a debugger special breakpoint */
int at_debugger_breakpoint(struct pt_regs *regs) { return regs->pc == (unsigned long)&__arch_kgdb_breakpoint; }

Contributors

PersonTokensPropCommitsCommitProp
David Howells22100.00%1100.00%
Total22100.00%1100.00%

/* * Initialise kgdb */
int kgdb_arch_init(void) { return 0; }

Contributors

PersonTokensPropCommitsCommitProp
David Howells10100.00%1100.00%
Total10100.00%1100.00%

/* * Do something, perhaps, but don't know what. */
void kgdb_arch_exit(void) { }

Contributors

PersonTokensPropCommitsCommitProp
David Howells6100.00%1100.00%
Total6100.00%1100.00%

#ifdef CONFIG_SMP
void debugger_nmi_interrupt(struct pt_regs *regs, enum exception_code code) { kgdb_nmicallback(arch_smp_processor_id(), regs); debugger_local_cache_flushinv(); }

Contributors

PersonTokensPropCommitsCommitProp
David Howells25100.00%1100.00%
Total25100.00%1100.00%


void kgdb_roundup_cpus(unsigned long flags) { smp_jump_to_debugger(); }

Contributors

PersonTokensPropCommitsCommitProp
David Howells12100.00%1100.00%
Total12100.00%1100.00%

#endif

Overall Contributors

PersonTokensPropCommitsCommitProp
David Howells279799.50%266.67%
Linus Torvalds140.50%133.33%
Total2811100.00%3100.00%
Information contained on this website is for historical information purposes only and does not indicate or represent copyright ownership.
Created with cregit.