source: rtems/cpukit/libdebugger/rtems-debugger-i386.c @ b2353ed9

Last change on this file since b2353ed9 was b2353ed9, checked in by Chris Johns <chrisj@…>, on Jul 16, 2017 at 11:53:11 PM

libdebugger: Fixes to debugging, ARM support, locking, and gcc-7.1 warnings.

  • Add printk support to aid multi-core debugging.
  • Add lock trace to aid lock debugging.
  • Fixes to gcc-7.1 warnings.
  • Fixes from ticket #2879.
  • Add verbose command controls.
  • Change using the RTEMS sys/lock.h API to manage exception threads.
  • ARM hardware breakpoint fixes. Support for SMP stepping is not implemented, this requires use of the context id register.

Closes #2879.

  • Property mode set to 100644
File size: 11.3 KB
Line 
1/*
2 * Copyright (c) 2016 Chris Johns <chrisj@rtems.org>.
3 * All rights reserved.
4 *
5 * Redistribution and use in source and binary forms, with or without
6 * modification, are permitted provided that the following conditions
7 * are met:
8 * 1. Redistributions of source code must retain the above copyright
9 *    notice, this list of conditions and the following disclaimer.
10 * 2. Redistributions in binary form must reproduce the above copyright
11 *    notice, this list of conditions and the following disclaimer in the
12 *    documentation and/or other materials provided with the distribution.
13 *
14 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
15 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
16 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
17 * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
18 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
19 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
20 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
21 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
22 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
23 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
24 * SUCH DAMAGE.
25 */
26
27#define TARGET_DEBUG 1
28
29#ifdef HAVE_CONFIG_H
30#include "config.h"
31#endif
32
33#include <errno.h>
34#include <inttypes.h>
35#include <stdlib.h>
36
37#include <rtems.h>
38#include <rtems/score/threadimpl.h>
39
40#include "rtems-debugger-target.h"
41#include "rtems-debugger-threads.h"
42
43/*
44 * Hardware breakpoints. Limited by hardware
45 */
46#define RTEMS_DEBUGGER_HWBREAK_NUM 4
47
48/*
49 * Number of registers.
50 */
51#define RTEMS_DEBUGGER_NUMREGS 16
52
53/*
54 * Number of bytes per register.
55 */
56#define RTEMS_DEBUGGER_REGBYTES 4
57
58/*
59 * Number of bytes of registers.
60 */
61#define RTEMS_DEBUGGER_NUMREGBYTES \
62  (RTEMS_DEBUGGER_NUMREGS * RTEMS_DEBUGGER_REGBYTES)
63
64/*
65 * Debugger registers layout.
66 */
67#define REG_EAX    0
68#define REG_ECX    1
69#define REG_EDX    2
70#define REG_EBX    3
71#define REG_ESP    4
72#define REG_EBP    5
73#define REG_ESI    6
74#define REG_EDI    7
75#define REG_PC     8
76#define REG_EIP    REG_PC
77#define REG_PS     9
78#define REG_EFLAGS REG_PS
79#define REG_CS     10
80#define REG_SS     11
81#define REG_DS     12
82#define REG_ES     13
83#define REG_FS     14
84#define REG_GS     15
85
86/**
87 * The int 3 opcode.
88 */
89#define TARGET_BKPT 0xcc
90
91static const uint8_t breakpoint[1] = { TARGET_BKPT };
92
93/*
94 * Get a copy of a register.
95 */
96#define GET_REG(_r, _v) asm volatile("pushl %%" #_r "; popl %0" : "=rm" (_v))
97
98/*
99 * Get a copy of a segment register.
100 */
101#define GET_SEG_REG(_r, _v) \
102  do {                      \
103    int _i;                 \
104    GET_REG(_r, _i);        \
105    _v = _i & 0xffff;       \
106  } while (0)
107
108/**
109 * Target lock.
110 */
111RTEMS_INTERRUPT_LOCK_DEFINE(static, target_lock, "target_lock")
112
113/**
114 * The orginal exception handler.
115 */
116static void (*orig_currentExcHandler)(CPU_Exception_frame* frame);
117
118#if TARGET_DEBUG
119#include <rtems/bspIo.h>
120static void target_printk(const char* format, ...) RTEMS_PRINTFLIKE(1, 2);
121static void
122target_printk(const char* format, ...)
123{
124  va_list ap;
125  va_start(ap, format);
126  vprintk(format, ap);
127  va_end(ap);
128}
129#else
130#define target_printk(_fmt, ...)
131#endif
132
133#if TODO
134/**
135 * Target description.
136 */
137static const char* const target_xml =
138"<?xml version=\"1.0\">                        \
139 <!DOCTYPE target SYSTEM \"gdb-target.dtd\">   \
140 <target version=\"1.0\">                      \
141  <architecture>i386</architecture>            \
142  <xi:include href=\"32bit-core.xml\"/>        \
143  <xi:include href=\"32bit-sse.xml\"/>         \
144</target>";
145#endif
146
147int
148rtems_debugger_target_configure(rtems_debugger_target* target)
149{
150  target->capabilities = (RTEMS_DEBUGGER_TARGET_CAP_SWBREAK);
151  target->reg_num = RTEMS_DEBUGGER_NUMREGS;
152  target->reg_size = sizeof(uint32_t);
153  target->breakpoint = &breakpoint[0];
154  target->breakpoint_size = sizeof(breakpoint);
155  return 0;
156}
157
158static void
159target_exception(CPU_Exception_frame* frame)
160{
161  target_printk("[} frame = %08lx sig=%d (%lx)\n",
162                (uint32_t) frame,
163                rtems_debugger_target_exception_to_signal(frame),
164                frame->idtIndex);
165  target_printk("[} EAX = %" PRIx32 " EBX = %" PRIx32 \
166                " ECX = %" PRIx32 " EDX = %" PRIx32 "\n",
167                frame->eax, frame->ebx, frame->ecx, frame->edx);
168  target_printk("[} ESI = %" PRIx32 " EDI = %" PRIx32           \
169                " EBP = %" PRIx32 " ESP = %" PRIx32 "\n",
170                frame->esi, frame->edi, frame->ebp, frame->esp0);
171  target_printk("[} EIP = %" PRIx32"\n", frame->eip);
172
173  frame->eflags &= ~EFLAGS_TRAP;
174
175  switch (rtems_debugger_target_exception(frame)) {
176  case rtems_debugger_target_exc_consumed:
177  default:
178    break;
179  case rtems_debugger_target_exc_step:
180    frame->eflags |= EFLAGS_TRAP;
181    break;
182  case rtems_debugger_target_exc_cascade:
183    orig_currentExcHandler(frame);
184    break;
185  }
186}
187
188int
189rtems_debugger_target_enable(void)
190{
191  rtems_interrupt_lock_context lock_context;
192  rtems_interrupt_lock_acquire(&target_lock, &lock_context);
193  if (orig_currentExcHandler == NULL) {
194    orig_currentExcHandler = _currentExcHandler;
195    _currentExcHandler = target_exception;
196  }
197  rtems_interrupt_lock_release(&target_lock, &lock_context);
198  return 0;
199}
200
201int
202rtems_debugger_target_disable(void)
203{
204  rtems_interrupt_lock_context lock_context;
205  rtems_interrupt_lock_acquire(&target_lock, &lock_context);
206  if (orig_currentExcHandler != NULL)
207    _currentExcHandler = orig_currentExcHandler;
208  rtems_interrupt_lock_release(&target_lock, &lock_context);
209  return 0;
210}
211
212int
213rtems_debugger_target_read_regs(rtems_debugger_thread* thread)
214{
215  if (!rtems_debugger_thread_flag(thread,
216                                  RTEMS_DEBUGGER_THREAD_FLAG_REG_VALID)) {
217    uint32_t* regs = &thread->registers[0];
218    size_t    i;
219
220    for (i = 0; i < rtems_debugger_target_reg_num(); ++i)
221      regs[i] = 0xdeaddead;
222
223    if (thread->frame) {
224      CPU_Exception_frame* frame = thread->frame;
225      regs[REG_EAX]    = frame->eax;
226      regs[REG_ECX]    = frame->ecx;
227      regs[REG_EDX]    = frame->edx;
228      regs[REG_EBX]    = frame->ebx;
229      regs[REG_ESP]    = frame->esp0;
230      regs[REG_EBP]    = frame->ebp;
231      regs[REG_ESI]    = frame->esi;
232      regs[REG_EDI]    = frame->edi;
233      regs[REG_EIP]    = frame->eip;
234      regs[REG_EFLAGS] = frame->eflags;
235      regs[REG_CS]     = frame->cs;
236
237      /*
238       * Get the signal from the frame.
239       */
240      thread->signal = rtems_debugger_target_exception_to_signal(frame);
241    }
242    else {
243      regs[REG_EBX]    = thread->tcb->Registers.ebx;
244      regs[REG_ESI]    = thread->tcb->Registers.esi;
245      regs[REG_EDI]    = thread->tcb->Registers.edi;
246      regs[REG_EFLAGS] = thread->tcb->Registers.eflags;
247      regs[REG_ESP]    = (intptr_t) thread->tcb->Registers.esp;
248      regs[REG_EBP]    = (intptr_t) thread->tcb->Registers.ebp;
249      regs[REG_EIP]    = *((DB_UINT*) thread->tcb->Registers.esp);
250      regs[REG_EAX]    = (intptr_t) thread;
251
252      GET_SEG_REG(CS, regs[REG_CS]);
253
254      /*
255       * Blocked threads have no signal.
256       */
257      thread->signal = 0;
258    }
259
260    GET_SEG_REG(SS, regs[REG_SS]);
261    GET_SEG_REG(DS, regs[REG_DS]);
262    GET_SEG_REG(ES, regs[REG_ES]);
263    GET_SEG_REG(FS, regs[REG_FS]);
264    GET_SEG_REG(GS, regs[REG_GS]);
265
266    thread->flags |= RTEMS_DEBUGGER_THREAD_FLAG_REG_VALID;
267    thread->flags &= ~RTEMS_DEBUGGER_THREAD_FLAG_REG_DIRTY;
268  }
269
270  return 0;
271}
272
273int
274rtems_debugger_target_write_regs(rtems_debugger_thread* thread)
275{
276  if (rtems_debugger_thread_flag(thread,
277                                 RTEMS_DEBUGGER_THREAD_FLAG_REG_DIRTY)) {
278    uint32_t* regs = &thread->registers[0];
279
280    /*
281     * Only write to debugger controlled threads. Do not touch the registers
282     * for threads blocked in the context switcher.
283     */
284    if (rtems_debugger_thread_flag(thread,
285                                   RTEMS_DEBUGGER_THREAD_FLAG_DEBUGGING)) {
286      CPU_Exception_frame* frame = thread->frame;
287      frame->eax    = regs[REG_EAX];
288      frame->ecx    = regs[REG_ECX];
289      frame->edx    = regs[REG_EDX];
290      frame->ebx    = regs[REG_EBX];
291      frame->esp0   = regs[REG_ESP];
292      frame->ebp    = regs[REG_EBP];
293      frame->esi    = regs[REG_ESI];
294      frame->edi    = regs[REG_EDI];
295      frame->eip    = regs[REG_EIP];
296      frame->eflags = regs[REG_EFLAGS];
297      frame->cs     = regs[REG_CS];
298    }
299    thread->flags &= ~RTEMS_DEBUGGER_THREAD_FLAG_REG_DIRTY;
300  }
301  return 0;
302}
303
304DB_UINT
305rtems_debugger_target_reg_pc(rtems_debugger_thread* thread)
306{
307  int r;
308  r = rtems_debugger_target_read_regs(thread);
309  if (r >= 0) {
310    uint32_t* regs = &thread->registers[0];
311    return regs[REG_EIP];
312  }
313  return 0;
314}
315
316DB_UINT
317rtems_debugger_target_frame_pc(CPU_Exception_frame* frame)
318{
319  return (DB_UINT) frame->eip;
320}
321
322DB_UINT
323rtems_debugger_target_reg_sp(rtems_debugger_thread* thread)
324{
325  int r;
326  r = rtems_debugger_target_read_regs(thread);
327  if (r >= 0) {
328    uint32_t* regs = &thread->registers[0];
329    return regs[REG_ESP];
330  }
331  return 0;
332}
333
334DB_UINT
335rtems_debugger_target_tcb_sp(rtems_debugger_thread* thread)
336{
337  return (DB_UINT) thread->tcb->Registers.esp;
338}
339
340int
341rtems_debugger_target_thread_stepping(rtems_debugger_thread* thread)
342{
343  if (rtems_debugger_thread_flag(thread,
344                                 (RTEMS_DEBUGGER_THREAD_FLAG_STEP |
345                                  RTEMS_DEBUGGER_THREAD_FLAG_STEPPING))) {
346    CPU_Exception_frame* frame = thread->frame;
347    /*
348     * Single step instructions with interrupts masked to avoid stepping into
349     * an interrupt handler.
350     */
351    if ((frame->eflags & EFLAGS_INTR_ENABLE) == 0)
352      thread->flags |= RTEMS_DEBUGGER_THREAD_FLAG_INTS_DISABLED;
353    else
354      frame->eflags &= ~EFLAGS_INTR_ENABLE;
355    frame->eflags |= EFLAGS_TRAP;
356  }
357  return 0;
358}
359
360int
361rtems_debugger_target_exception_to_signal(CPU_Exception_frame* frame)
362{
363  int sig = RTEMS_DEBUGGER_SIGNAL_HUP;
364  switch (frame->idtIndex) {
365  case 1:  /* debug exception */
366  case 3:  /* breakpoint int3 */
367    sig = RTEMS_DEBUGGER_SIGNAL_TRAP;
368    break;
369  case 4:  /* int overflow    */
370  case 5:  /* out-of-bounds   */
371    sig = RTEMS_DEBUGGER_SIGNAL_URG;
372    break;
373  case 6:  /* invalid opcode  */
374    sig = RTEMS_DEBUGGER_SIGNAL_ILL;
375    break;
376  case 8:  /* double fault    */
377  case 16: /* fp error        */
378    sig = RTEMS_DEBUGGER_SIGNAL_EMT;
379    break;
380  case 0:  /* divide by zero  */
381  case 7:  /* FPU not avail.  */
382    sig = RTEMS_DEBUGGER_SIGNAL_FPE;
383    break;
384  case 9:  /* i387 seg overr. */
385  case 10: /* Invalid TSS     */
386  case 11: /* seg. not pres.  */
387  case 12: /* stack except.   */
388  case 13: /* general prot.   */
389  case 14: /* page fault      */
390  case 17: /* alignment check */
391    sig = RTEMS_DEBUGGER_SIGNAL_SEGV;
392    break;
393  case 2:  /* NMI             */
394  case 18: /* machine check   */
395    sig = RTEMS_DEBUGGER_SIGNAL_BUS;
396    break;
397  default:
398    break;
399  }
400  return sig;
401}
402
403int
404rtems_debugger_target_hwbreak_control(rtems_debugger_target_watchpoint wp,
405                                      bool                             insert,
406                                      DB_UINT                          addr,
407                                      DB_UINT                          kind)
408{
409  /*
410   * To do.
411   */
412  return 0;
413}
414
415int
416rtems_debugger_target_cache_sync(rtems_debugger_target_swbreak* swbreak)
417{
418  /*
419   * Nothing to do on an i386.
420   */
421  return 0;
422}
Note: See TracBrowser for help on using the repository browser.