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

5
Last change on this file since a0d4e99 was a0d4e99, checked in by Chris Johns <chrisj@…>, on 11/25/16 at 04:13:36

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

  • Property mode set to 100644
File size: 11.2 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 0
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 * Frame signature.
44 */
45#define TARGET_FRAME_MAGIC_NUM (2)
46#define TARGET_FRAME_MAGIC 0xdeadbeef, 0xb2107016
47static const uint32_t
48  frame_magic[TARGET_FRAME_MAGIC_NUM] = { TARGET_FRAME_MAGIC };
49
50#if TARGET_DEBUG
51#include <rtems/bspIo.h>
52static void target_printk(const char* format, ...) RTEMS_PRINTFLIKE(1, 2);
53static void
54target_printk(const char* format, ...)
55{
56  va_list ap;
57  va_start(ap, format);
58  vprintk(format, ap);
59  va_end(ap);
60}
61#else
62#define target_printk(_fmt, ...)
63#endif
64
65int
66rtems_debugger_target_create(void)
67{
68  if (rtems_debugger->target == NULL) {
69    rtems_debugger_target* target;
70    int                    r;
71
72    target = calloc(1, sizeof(rtems_debugger_target));
73    if (target == NULL) {
74      errno = ENOMEM;
75      return -1;
76    }
77
78    r = rtems_debugger_target_configure(target);
79    if (r < 0) {
80      free(target);
81      return -1;
82    }
83
84    if (target->breakpoint_size > RTEMS_DEBUGGER_TARGET_SWBREAK_MAX_SIZE) {
85      free(target);
86      rtems_debugger_printf("error: rtems-db: target: breakpoint size too big\n");
87      return -1;
88    }
89
90    r = rtems_debugger_block_create(&target->swbreaks,
91                                    RTEMS_DEBUGGER_TARGET_SWBREAK_NUM,
92                                    sizeof(rtems_debugger_target_swbreak));
93    if (r < 0) {
94      free(target);
95      return -1;
96    }
97
98    rtems_debugger->target = target;
99  }
100
101  return 0;
102}
103
104int
105rtems_debugger_target_destroy(void)
106{
107  if (rtems_debugger->target != NULL) {
108    rtems_debugger_target* target = rtems_debugger->target;
109    rtems_debugger_target_swbreak_remove();
110    rtems_debugger_target_disable();
111    rtems_debugger_block_destroy(&target->swbreaks);
112    free(target);
113    rtems_debugger->target = NULL;
114  }
115  return 0;
116}
117
118uint32_t
119rtems_debugger_target_capabilities(void)
120{
121  if (rtems_debugger->target != NULL)
122
123    return rtems_debugger->target->capabilities;
124  return 0;
125}
126
127size_t
128rtems_debugger_target_reg_num(void)
129{
130  if (rtems_debugger->target != NULL)
131    return rtems_debugger->target->reg_num;
132  return 0;
133}
134
135size_t
136rtems_debugger_target_reg_size(void)
137{
138  if (rtems_debugger->target != NULL)
139    return rtems_debugger->target->reg_num * rtems_debugger->target->reg_size;
140  return 0;
141}
142
143int
144rtems_debugger_target_swbreak_control(bool insert, DB_UINT addr, DB_UINT kind)
145{
146  rtems_debugger_target*         target = rtems_debugger->target;
147  rtems_debugger_target_swbreak* swbreaks = target->swbreaks.block;
148  size_t                         swbreak_size;
149  uint8_t*                       loc = (void*) addr;
150  size_t                         i;
151  int                            r;
152
153  if (target == NULL || swbreaks == NULL || kind != target->breakpoint_size) {
154    errno = EIO;
155    return -1;
156  }
157
158  swbreak_size =
159    sizeof(rtems_debugger_target_swbreak) + target->breakpoint_size;
160
161  for (i = 0; i < target->swbreaks.level; ++i) {
162    if (loc == swbreaks[i].address) {
163      size_t remaining;
164      if (!insert) {
165        --target->swbreaks.level;
166        remaining = (target->swbreaks.level - i) * swbreak_size;
167        memmove(&swbreaks[i], &swbreaks[i + 1], remaining);
168      }
169      return 0;
170    }
171  }
172
173  if (!insert)
174    return 0;
175
176  r = rtems_debugger_block_resize(&target->swbreaks);
177  if (r < 0)
178    return -1;
179
180  swbreaks = target->swbreaks.block;
181
182  swbreaks[target->swbreaks.level].address = loc;
183  if (target->breakpoint_size > 4)
184    memcpy(&swbreaks[target->swbreaks.level].contents[0],
185           loc,
186           target->breakpoint_size);
187  else {
188    uint8_t* contents = &swbreaks[target->swbreaks.level].contents[0];
189    switch (target->breakpoint_size) {
190    case 4:
191      contents[3] = loc[3];
192    case 3:
193      contents[2] = loc[2];
194    case 2:
195      contents[1] = loc[1];
196    case 1:
197      contents[0] = loc[0];
198      break;
199    }
200  }
201  ++target->swbreaks.level;
202
203  return 0;
204}
205
206int
207rtems_debugger_target_swbreak_insert(void)
208{
209  rtems_debugger_target* target = rtems_debugger->target;
210  int                    r = -1;
211  if (target != NULL && target->swbreaks.block != NULL) {
212    rtems_debugger_target_swbreak* swbreaks = target->swbreaks.block;
213    size_t                         i;
214    r = 0;
215    for (i = 0; i < target->swbreaks.level; ++i) {
216      uint8_t* loc = swbreaks[i].address;
217      if (rtems_debugger_verbose())
218        rtems_debugger_printf("rtems-db:  bp:  in: %p\n", swbreaks[i].address);
219      if (target->breakpoint_size > 4)
220        memcpy(loc, &target->breakpoint[0], target->breakpoint_size);
221      else {
222        switch (target->breakpoint_size) {
223        case 4:
224          loc[3] = target->breakpoint[3];
225        case 3:
226          loc[2] = target->breakpoint[2];
227        case 2:
228          loc[1] = target->breakpoint[1];
229        case 1:
230          loc[0] = target->breakpoint[0];
231          break;
232        }
233      }
234      r = rtems_debugger_target_cache_sync(&swbreaks[i]);
235    }
236  }
237  return r;
238}
239
240int
241rtems_debugger_target_swbreak_remove(void)
242{
243  rtems_debugger_target* target = rtems_debugger->target;
244  int                    r = -1;
245  if (target != NULL && target->swbreaks.block != NULL) {
246    rtems_debugger_target*         target = rtems_debugger->target;
247    rtems_debugger_target_swbreak* swbreaks = target->swbreaks.block;
248    size_t                         i;
249    r = 0;
250    for (i = 0; i < target->swbreaks.level; ++i) {
251      uint8_t* loc = swbreaks[i].address;
252      uint8_t* contents = &swbreaks[i].contents[0];
253      if (rtems_debugger_verbose())
254        rtems_debugger_printf("rtems-db:  bp: out: %p\n", swbreaks[i].address);
255      if (target->breakpoint_size > 4)
256        memcpy(loc, contents, target->breakpoint_size);
257      else {
258        switch (target->breakpoint_size) {
259        case 4:
260          loc[3] = contents[3];
261        case 3:
262          loc[2] = contents[2];
263        case 2:
264          loc[1] = contents[1];
265        case 1:
266          loc[0] = contents[0];
267          break;
268        }
269      }
270      r = rtems_debugger_target_cache_sync(&swbreaks[i]);
271    }
272  }
273  return r;
274}
275
276rtems_debugger_target_exc_action
277rtems_debugger_target_exception(CPU_Exception_frame* frame)
278{
279  volatile const uint32_t magic[3] = {
280    (uint32_t) frame, TARGET_FRAME_MAGIC
281  };
282
283  (void) magic;
284
285  if (!rtems_interrupt_is_in_progress()) {
286    rtems_debugger_threads*              threads = rtems_debugger->threads;
287    Thread_Control*                      thread = _Thread_Executing;
288    rtems_id*                            excludes;
289    const rtems_id                       tid = thread->Object.id;
290    DB_UINT                              pc;
291    const rtems_debugger_thread_stepper* stepper;
292    size_t                               i;
293
294    target_printk("[} tid:%08" PRIx32 ": thread:%08" PRIxPTR
295                  " frame:%08" PRIxPTR "\n",
296                  tid, (intptr_t) thread, (intptr_t) frame);
297
298    /*
299     * If the thread is the debugger recover.
300     */
301    if (tid == rtems_debugger->server_task) {
302      if (rtems_debugger->target->memory_access) {
303        target_printk("[} server access fault\n");
304        rtems_debugger->target->memory_access = true;
305        longjmp(rtems_debugger->target->access_return, -1);
306      }
307      target_printk("[} server exception\n");
308      return rtems_debugger_target_exc_cascade;
309    }
310
311    /*
312     * See if the thread is excluded.
313     */
314    excludes = rtems_debugger_thread_excludes(threads);
315    for (i = 0; i < threads->excludes.level; ++i) {
316      if (tid == excludes[i]) {
317        /*
318         * We do nothing with this condition and cascade the exception.
319         *
320         * @todo: if this is a hwbreak carry on, if this is a swbreak replace
321         *        the contents of the instruction, step then return the
322         *        swbreak's contents.
323         */
324        target_printk("[} tid:%08lx: excluded\n", tid);
325        return rtems_debugger_target_exc_cascade;
326      }
327    }
328
329    /*
330     * See if the thread is inside the stepping a range.
331     */
332    pc = rtems_debugger_target_frame_pc(frame);
333    stepper = rtems_debugger_thread_is_stepping(tid, pc);
334    if (stepper != NULL) {
335      stepper->thread->frame = frame;
336      rtems_debugger_target_thread_stepping(stepper->thread);
337      target_printk("[} tid:%08lx: stepping\n", tid);
338      return rtems_debugger_target_exc_step;
339    }
340
341    target_printk("[} tid:%08lx: suspending\n", tid);
342
343    /*
344     * Tag the thread as being debugged, wake the debug server's event thread,
345     * then suspend this thread.
346     */
347    _Thread_Set_state(thread, STATES_DEBUGGER);
348    rtems_debugger_server_events_wake();
349    rtems_task_suspend(tid);
350
351    target_printk("[} tid:%08lx: resuming\n", tid);
352
353    return rtems_debugger_target_exc_consumed;
354  }
355
356  target_printk("[} cascade, in interrupt\n");
357
358  return rtems_debugger_target_exc_cascade;
359}
360
361int
362rtems_debugger_target_set_exception_frame(rtems_debugger_thread* thread)
363{
364  int r = 0;
365  thread->frame = NULL;
366  thread->flags &= ~RTEMS_DEBUGGER_THREAD_FLAG_DEBUGGING;
367  if ((thread->tcb->current_state & STATES_DEBUGGER) != 0) {
368    CPU_Exception_frame* frame = NULL;
369    DB_UINT*             sp;
370    int                  i;
371    sp = (DB_UINT*) rtems_debugger_target_tcb_sp(thread);
372    for (i = 0; i < 128; ++i) {
373      if (sp[i] == frame_magic[0] && sp[i + 1] == frame_magic[1]) {
374        frame = (CPU_Exception_frame*) sp[i + 2];
375        break;
376      }
377    }
378    _Thread_Clear_state(thread->tcb, STATES_DEBUGGER);
379    thread->frame = frame;
380    if (frame != NULL)
381      thread->flags |= RTEMS_DEBUGGER_THREAD_FLAG_DEBUGGING;
382    else
383      r = -1;
384  }
385  return r;
386}
387
388int
389rtems_debugger_target_start_memory_access(void)
390{
391  rtems_debugger_target* target = rtems_debugger->target;
392  target->memory_access = true;
393  return setjmp(target->access_return);
394}
395
396void
397rtems_debugger_target_end_memory_access(void)
398{
399  rtems_debugger->target->memory_access = false;
400}
Note: See TracBrowser for help on using the repository browser.