Back to home page

LXR

 
 

    


File indexing completed on 2025-05-11 08:24:15

0001 /*
0002  * Copyright (c) 2016-2019 Chris Johns <chrisj@rtems.org>.
0003  * All rights reserved.
0004  *
0005  * Redistribution and use in source and binary forms, with or without
0006  * modification, are permitted provided that the following conditions
0007  * are met:
0008  * 1. Redistributions of source code must retain the above copyright
0009  *    notice, this list of conditions and the following disclaimer.
0010  * 2. Redistributions in binary form must reproduce the above copyright
0011  *    notice, this list of conditions and the following disclaimer in the
0012  *    documentation and/or other materials provided with the distribution.
0013  *
0014  * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
0015  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
0016  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
0017  * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
0018  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
0019  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
0020  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
0021  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
0022  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
0023  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
0024  * SUCH DAMAGE.
0025  */
0026 
0027 #define TARGET_DEBUG 0
0028 
0029 #ifdef HAVE_CONFIG_H
0030 #include "config.h"
0031 #endif
0032 
0033 #include <errno.h>
0034 #include <inttypes.h>
0035 #include <stdlib.h>
0036 
0037 #include <rtems.h>
0038 #include <rtems/score/threadimpl.h>
0039 
0040 #include "rtems-debugger-target.h"
0041 #include "rtems-debugger-threads.h"
0042 
0043 /**
0044  * Exception local stack frame data to synchronise with the debugger
0045  * server's events loop processor.
0046  */
0047 typedef struct {
0048   rtems_chain_node     node;
0049   rtems_id             id;
0050   CPU_Exception_frame* frame;
0051   rtems_rx_cond        cond;
0052 } rtems_debugger_exception;
0053 
0054 #if TARGET_DEBUG
0055 #include <rtems/bspIo.h>
0056 static void target_printk(const char* format, ...) RTEMS_PRINTFLIKE(1, 2);
0057 static void
0058 target_printk(const char* format, ...)
0059 {
0060   rtems_interrupt_lock_context lock_context;
0061   va_list ap;
0062   va_start(ap, format);
0063   rtems_debugger_printk_lock(&lock_context);
0064   vprintk(format, ap);
0065   rtems_debugger_printk_unlock(&lock_context);
0066   va_end(ap);
0067 }
0068 #else
0069 #define target_printk(_fmt, ...)
0070 #endif
0071 
0072 int
0073 rtems_debugger_target_create(void)
0074 {
0075   if (rtems_debugger->target == NULL) {
0076     rtems_debugger_target* target;
0077     int                    r;
0078 
0079     target = calloc(1, sizeof(rtems_debugger_target));
0080     if (target == NULL) {
0081       errno = ENOMEM;
0082       return -1;
0083     }
0084 
0085     r = rtems_debugger_target_configure(target);
0086     if (r < 0) {
0087       free(target);
0088       return -1;
0089     }
0090 
0091     if (target->breakpoint_size > RTEMS_DEBUGGER_TARGET_SWBREAK_MAX_SIZE) {
0092       free(target);
0093       rtems_debugger_printf("error: rtems-db: target: breakpoint size too big\n");
0094       return -1;
0095     }
0096 
0097     r = rtems_debugger_block_create(&target->swbreaks,
0098                                     RTEMS_DEBUGGER_TARGET_SWBREAK_NUM,
0099                                     sizeof(rtems_debugger_target_swbreak));
0100     if (r < 0) {
0101       free(target);
0102       return -1;
0103     }
0104 
0105     rtems_debugger->target = target;
0106   }
0107 
0108   return 0;
0109 }
0110 
0111 int
0112 rtems_debugger_target_destroy(void)
0113 {
0114   if (rtems_debugger->target != NULL) {
0115     rtems_debugger_target* target = rtems_debugger->target;
0116     rtems_debugger_target_swbreak_remove();
0117     rtems_debugger_target_disable();
0118     rtems_debugger_block_destroy(&target->swbreaks);
0119     free(target);
0120     rtems_debugger->target = NULL;
0121   }
0122   return 0;
0123 }
0124 
0125 uint32_t
0126 rtems_debugger_target_capabilities(void)
0127 {
0128   if (rtems_debugger->target != NULL)
0129 
0130     return rtems_debugger->target->capabilities;
0131   return 0;
0132 }
0133 
0134 size_t
0135 rtems_debugger_target_reg_num(void)
0136 {
0137   rtems_debugger_target* target = rtems_debugger->target;
0138   if (target != NULL)
0139     return target->reg_num;
0140   return 0;
0141 }
0142 
0143 size_t
0144 rtems_debugger_target_reg_size(size_t reg)
0145 {
0146   rtems_debugger_target* target = rtems_debugger->target;
0147   if (target != NULL && reg < target->reg_num)
0148     return target->reg_offset[reg + 1] - target->reg_offset[reg];
0149   return 0;
0150 }
0151 
0152 size_t
0153 rtems_debugger_target_reg_offset(size_t reg)
0154 {
0155   rtems_debugger_target* target = rtems_debugger->target;
0156   if (target != NULL && reg < target->reg_num)
0157     return target->reg_offset[reg];
0158   return 0;
0159 }
0160 
0161 size_t
0162 rtems_debugger_target_reg_table_size(void)
0163 {
0164   rtems_debugger_target* target = rtems_debugger->target;
0165   if (target != NULL)
0166     return target->reg_offset[target->reg_num];
0167   return 0;
0168 }
0169 
0170 bool
0171 rtems_debugger_target_swbreak_is_configured( uintptr_t addr )
0172 {
0173   size_t                         i;
0174   rtems_debugger_target_swbreak *swbreaks;
0175   rtems_debugger_target         *target = rtems_debugger->target;
0176 
0177   if ( target == NULL ) {
0178     return false;
0179   }
0180 
0181   swbreaks = target->swbreaks.block;
0182 
0183   if ( swbreaks == NULL ) {
0184     return false;
0185   }
0186 
0187   for ( i = 0; i < target->swbreaks.level; ++i ) {
0188     if ( (uintptr_t) swbreaks[ i ].address == addr ) {
0189       return true;
0190     }
0191   }
0192 
0193   return false;
0194 }
0195 
0196 int
0197 rtems_debugger_target_swbreak_control(bool insert, uintptr_t addr, DB_UINT kind)
0198 {
0199   rtems_debugger_target*         target = rtems_debugger->target;
0200   rtems_debugger_target_swbreak* swbreaks;
0201   size_t                         swbreak_size;
0202   uint8_t*                       loc = (void*) addr;
0203   size_t                         i;
0204   int                            r;
0205 
0206   if (target == NULL || target->swbreaks.block == NULL ||
0207       kind != target->breakpoint_size) {
0208     errno = EIO;
0209     return -1;
0210   }
0211 
0212   swbreaks = target->swbreaks.block;
0213   swbreak_size =
0214     sizeof(rtems_debugger_target_swbreak) + target->breakpoint_size;
0215 
0216   for (i = 0; i < target->swbreaks.level; ++i) {
0217     if (loc == swbreaks[i].address) {
0218       size_t remaining;
0219       if (!insert) {
0220         if (target->code_writer != NULL) {
0221           r = target->code_writer(loc,
0222                                   &swbreaks[i].contents[0],
0223                                   target->breakpoint_size);
0224           if (r < 0) {
0225             return r;
0226           }
0227         } else {
0228           if (target->breakpoint_size > 4) {
0229             memcpy(loc, &swbreaks[i].contents[0], target->breakpoint_size);
0230           } else {
0231             switch (target->breakpoint_size) {
0232             case 4:
0233               loc[3] = swbreaks[i].contents[3];
0234             case 3:
0235               loc[2] = swbreaks[i].contents[2];
0236             case 2:
0237               loc[1] = swbreaks[i].contents[1];
0238             case 1:
0239               loc[0] = swbreaks[i].contents[0];
0240               break;
0241             }
0242           }
0243           rtems_debugger_target_cache_sync(&swbreaks[i]);
0244         }
0245         --target->swbreaks.level;
0246         remaining = (target->swbreaks.level - i) * swbreak_size;
0247         memmove(&swbreaks[i], &swbreaks[i + 1], remaining);
0248       }
0249       return 0;
0250     }
0251   }
0252 
0253   if (!insert)
0254     return 0;
0255 
0256   r = rtems_debugger_block_resize(&target->swbreaks);
0257   if (r < 0)
0258     return -1;
0259 
0260   swbreaks = target->swbreaks.block;
0261 
0262   swbreaks[target->swbreaks.level].address = loc;
0263   if (target->breakpoint_size > 4)
0264     memcpy(&swbreaks[target->swbreaks.level].contents[0],
0265            loc,
0266            target->breakpoint_size);
0267   else {
0268     uint8_t* contents = &swbreaks[target->swbreaks.level].contents[0];
0269     switch (target->breakpoint_size) {
0270     case 4:
0271       contents[3] = loc[3];
0272     case 3:
0273       contents[2] = loc[2];
0274     case 2:
0275       contents[1] = loc[1];
0276     case 1:
0277       contents[0] = loc[0];
0278       break;
0279     }
0280   }
0281   ++target->swbreaks.level;
0282 
0283   return 0;
0284 }
0285 
0286 int
0287 rtems_debugger_target_swbreak_insert(void)
0288 {
0289   rtems_debugger_target* target = rtems_debugger->target;
0290   int                    r = -1;
0291   if (target != NULL && target->swbreaks.block != NULL) {
0292     rtems_debugger_target_swbreak* swbreaks = target->swbreaks.block;
0293     size_t                         i;
0294     r = 0;
0295     for (i = 0; i < target->swbreaks.level; ++i) {
0296       uint8_t* loc = swbreaks[i].address;
0297       if (rtems_debugger_verbose())
0298         rtems_debugger_printf("rtems-db:  bp:  in: %p\n", swbreaks[i].address);
0299       if (target->code_writer != NULL) {
0300         r = target->code_writer(loc,
0301                                 &target->breakpoint[0],
0302                                 target->breakpoint_size);
0303         if (r < 0) {
0304           return r;
0305         }
0306       } else {
0307         if (target->breakpoint_size > 4) {
0308           memcpy(loc, &target->breakpoint[0], target->breakpoint_size);
0309         } else {
0310           if (rtems_debugger_verbose())
0311             rtems_debugger_printf("rtems-db:  bp:  in: %p %p %d %d %d\n",
0312                                   loc, &target->breakpoint[0],
0313                                   (int) target->breakpoint_size,
0314                                   (int) i, (int) target->swbreaks.level);
0315           switch (target->breakpoint_size) {
0316           case 4:
0317             loc[3] = target->breakpoint[3];
0318           case 3:
0319             loc[2] = target->breakpoint[2];
0320           case 2:
0321             loc[1] = target->breakpoint[1];
0322           case 1:
0323             loc[0] = target->breakpoint[0];
0324             break;
0325           }
0326         }
0327         r = rtems_debugger_target_cache_sync(&swbreaks[i]);
0328       }
0329     }
0330   }
0331   return r;
0332 }
0333 
0334 int
0335 rtems_debugger_target_swbreak_remove(void)
0336 {
0337   rtems_debugger_target* target = rtems_debugger->target;
0338   int                    r = -1;
0339   if (target != NULL && target->swbreaks.block != NULL) {
0340     rtems_debugger_target*         target = rtems_debugger->target;
0341     rtems_debugger_target_swbreak* swbreaks = target->swbreaks.block;
0342     size_t                         i;
0343     r = 0;
0344     for (i = 0; i < target->swbreaks.level; ++i) {
0345       uint8_t* loc = swbreaks[i].address;
0346       uint8_t* contents = &swbreaks[i].contents[0];
0347       if (rtems_debugger_verbose())
0348         rtems_debugger_printf("rtems-db:  bp: out: %p\n", swbreaks[i].address);
0349       if (target->code_writer != NULL) {
0350         r = target->code_writer(loc, contents, target->breakpoint_size);
0351         if (r < 0) {
0352           return r;
0353         }
0354       } else {
0355         if (target->breakpoint_size > 4) {
0356           memcpy(loc, contents, target->breakpoint_size);
0357         } else {
0358           switch (target->breakpoint_size) {
0359           case 4:
0360             loc[3] = contents[3];
0361           case 3:
0362             loc[2] = contents[2];
0363           case 2:
0364             loc[1] = contents[1];
0365           case 1:
0366             loc[0] = contents[0];
0367             break;
0368           }
0369         }
0370         r = rtems_debugger_target_cache_sync(&swbreaks[i]);
0371       }
0372     }
0373   }
0374   return r;
0375 }
0376 
0377 static rtems_debugger_target_exc_action
0378 rtems_debugger_soft_step_and_continue(CPU_Exception_frame* frame)
0379 {
0380   uintptr_t              break_address;
0381   rtems_debugger_target *target = rtems_debugger->target;
0382   Thread_Control        *thread = _Thread_Get_executing();
0383   const rtems_id         tid = thread->Object.id;
0384   rtems_debugger_thread  fake_debugger_thread;
0385 
0386   /*
0387    * If this was a hwbreak, cascade. If this is a swbreak replace the contents
0388    * of the instruction, step then return the swbreak's contents.
0389    */
0390   if ((target->capabilities & RTEMS_DEBUGGER_TARGET_CAP_SWBREAK) == 0) {
0391     target_printk("rtems-db: exception in an interrupt, cascading\n");
0392     rtems_debugger_unlock();
0393     return rtems_debugger_target_exc_cascade;
0394   }
0395 
0396   break_address = rtems_debugger_target_frame_pc( frame );
0397   if ( rtems_debugger_target_swbreak_is_configured( break_address ) == false ) {
0398     target_printk("rtems-db: exception in an interrupt, cascading\n");
0399     rtems_debugger_unlock();
0400     return rtems_debugger_target_exc_cascade;
0401   }
0402 
0403   /*
0404    * Remove the current breakpoint
0405    */
0406   rtems_debugger_target_swbreak_control(
0407     false,
0408     break_address,
0409     target->breakpoint_size
0410   );
0411 
0412   /*
0413    * Save the thread ID and break address to recover after stepping
0414    */
0415   target->step_tid = tid;
0416   target->step_bp_address = break_address;
0417 
0418   /*
0419    * Populate the fake rtems_debugger_thread
0420    */
0421   fake_debugger_thread.flags |= RTEMS_DEBUGGER_THREAD_FLAG_STEP;
0422   fake_debugger_thread.frame = frame;
0423   target_printk("rtems-db: stepping to the next instruction\n");
0424   rtems_debugger_target_thread_stepping(&fake_debugger_thread);
0425 
0426   /*
0427    * rtems_debugger_unlock() not called until the step is resolved
0428    */
0429   return rtems_debugger_target_exc_step;
0430 }
0431 
0432 rtems_debugger_target_exc_action
0433 rtems_debugger_target_exception(CPU_Exception_frame* frame)
0434 {
0435   rtems_debugger_target *target = rtems_debugger->target;
0436   Thread_Control*        thread = _Thread_Get_executing();
0437   const rtems_id         tid = thread->Object.id;
0438 
0439   /*
0440    * Resolve outstanding step+continue
0441    */
0442   if (target->step_bp_address != 0 && target->step_tid == tid) {
0443     rtems_debugger_target_swbreak_control(
0444       true,
0445       target->step_bp_address,
0446       rtems_debugger->target->breakpoint_size
0447     );
0448     target->step_bp_address = 0;
0449     target->step_tid = 0;
0450 
0451     /*
0452      * Release the debugger lock now that the step+continue is complete
0453      */
0454     target_printk("rtems-db: resuming after step\n");
0455     rtems_debugger_unlock();
0456     return rtems_debugger_target_exc_consumed;
0457   }
0458 
0459   rtems_debugger_lock();
0460 
0461   if (!rtems_interrupt_is_in_progress()) {
0462     rtems_debugger_threads*              threads = rtems_debugger->threads;
0463     rtems_id*                            excludes;
0464     uintptr_t                            pc;
0465     const rtems_debugger_thread_stepper* stepper;
0466     rtems_debugger_exception             target_exception;
0467     size_t                               i;
0468 
0469     target_printk("[} tid:%08" PRIx32 ": thread:%08" PRIxPTR
0470                   " frame:%08" PRIxPTR "\n",
0471                   tid, (intptr_t) thread, (intptr_t) frame);
0472 
0473     /*
0474      * If the thread is in the debugger recover. If the access is from gdb
0475      * continue else shutdown and let the user know.
0476      */
0477     if (tid == rtems_debugger->server_task ||
0478         tid == rtems_debugger->events_task) {
0479       bool memory_access = rtems_debugger_target_is_memory_access();
0480       rtems_debugger_unlock();
0481       /*
0482        * Has GDB has asked us to write to an address?
0483        */
0484       if (memory_access) {
0485         target_printk("[} server fault: memory access\n");
0486         longjmp(rtems_debugger->target->access_return, -1);
0487       }
0488       rtems_debugger_printf("rtems-db: server exception (report)\n");
0489       rtems_debugger_target_exception_print(frame);
0490       rtems_debugger_server_crash();
0491       return rtems_debugger_target_exc_cascade;
0492     }
0493 
0494     /*
0495      * See if the thread is excluded.
0496      */
0497     excludes = rtems_debugger_thread_excludes(threads);
0498     for (i = 0; i < threads->excludes.level; ++i) {
0499       if (tid == excludes[i]) {
0500         /*
0501          * We do nothing with this condition and cascade the exception.
0502          *
0503          * @todo: if this is a hwbreak carry on, if this is a swbreak replace
0504          *        the contents of the instruction, step then return the
0505          *        swbreak's contents.
0506          */
0507         target_printk("[} tid:%08" PRIx32 ": excluded\n", tid);
0508         rtems_debugger_unlock();
0509         return rtems_debugger_target_exc_cascade;
0510       }
0511     }
0512 
0513     /*
0514      * See if the thread is inside the stepping a range.
0515      */
0516     pc = rtems_debugger_target_frame_pc(frame);
0517     stepper = rtems_debugger_thread_is_stepping(tid, pc);
0518     if (stepper != NULL) {
0519       stepper->thread->frame = frame;
0520       rtems_debugger_target_thread_stepping(stepper->thread);
0521       target_printk("[} tid:%08" PRIx32 ": stepping\n", tid);
0522       rtems_debugger_unlock();
0523       return rtems_debugger_target_exc_step;
0524     }
0525 
0526     target_printk("[} tid:%08" PRIx32 ": suspending\n", tid);
0527 
0528     /*
0529      * Initialise the target exception data and queue ready for the debugger
0530      * server's event processor to handle.
0531      */
0532     rtems_chain_initialize_node(&target_exception.node);
0533     target_exception.frame = frame;
0534     target_exception.id = tid;
0535     _Condition_Initialize(&target_exception.cond);
0536 
0537     rtems_chain_append_unprotected(&rtems_debugger->exception_threads,
0538                                    &target_exception.node);
0539 
0540     /*
0541      * Signal the debug server's thread.
0542      */
0543     rtems_debugger_server_events_signal();
0544 
0545     /*
0546      * Block on the exception thread's condition variable unlocking the
0547      * debugger's mutex and letting the server's thread run.
0548      */
0549     _Condition_Wait_recursive(&target_exception.cond, &rtems_debugger->lock);
0550 
0551     /*
0552      * Unlock the debugger's lock now the exception is resuming.
0553      */
0554     rtems_debugger_unlock();
0555 
0556     target_printk("[} tid:%08" PRIx32 ": resuming\n", tid);
0557 
0558     return rtems_debugger_target_exc_consumed;
0559   }
0560 
0561   target_printk("[} tid:%08" PRIx32 ": exception in interrupt context\n", tid);
0562 
0563   /*
0564    * Soft_step_and_continue releases the debugger lock
0565    */
0566   return rtems_debugger_soft_step_and_continue(frame);
0567 }
0568 
0569 void
0570 rtems_debugger_target_exception_thread(rtems_debugger_thread* thread)
0571 {
0572   rtems_chain_node* node;
0573   thread->frame = NULL;
0574   thread->flags &= ~RTEMS_DEBUGGER_THREAD_FLAG_EXCEPTION;
0575   for (node = rtems_chain_first(&rtems_debugger->exception_threads);
0576        !rtems_chain_is_tail(&rtems_debugger->exception_threads, node);
0577        node = rtems_chain_next(node)) {
0578     rtems_debugger_exception* target_exception = (rtems_debugger_exception*) node;
0579     if (target_exception->id == thread->id) {
0580       thread->frame = target_exception->frame;
0581       thread->flags |= RTEMS_DEBUGGER_THREAD_FLAG_EXCEPTION;
0582     }
0583   }
0584 }
0585 
0586 void
0587 rtems_debugger_target_exception_thread_resume(rtems_debugger_thread* thread)
0588 {
0589   rtems_chain_node* node;
0590   for (node = rtems_chain_first(&rtems_debugger->exception_threads);
0591        !rtems_chain_is_tail(&rtems_debugger->exception_threads, node);
0592        node = rtems_chain_next(node)) {
0593     rtems_debugger_exception* target_exception = (rtems_debugger_exception*) node;
0594     if (target_exception->id == thread->id) {
0595       rtems_chain_extract(node);
0596       thread->frame = NULL;
0597       thread->flags &= ~RTEMS_DEBUGGER_THREAD_FLAG_EXCEPTION;
0598       _Condition_Signal(&target_exception->cond);
0599       break;
0600     }
0601   }
0602 }
0603 
0604 int
0605 rtems_debugger_target_start_memory_access(void)
0606 {
0607   rtems_debugger_target* target = rtems_debugger->target;
0608   target->memory_access = true;
0609   return setjmp(target->access_return);
0610 }
0611 
0612 void
0613 rtems_debugger_target_end_memory_access(void)
0614 {
0615   rtems_debugger->target->memory_access = false;
0616 }
0617 
0618 bool
0619 rtems_debugger_target_is_memory_access(void)
0620 {
0621   return rtems_debugger->target->memory_access;
0622 }