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

Last change on this file since a0d4e99 was a0d4e99, checked in by Chris Johns <chrisj@…>, on Nov 25, 2016 at 4:13:36 AM

cpukit: Add libdebugger, a remote debugger agent for GDB.

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