Subversion Repositories HelenOS

Rev

Rev 2833 | Rev 2835 | Go to most recent revision | Blame | Compare with Previous | Last modification | View Log | Download | RSS feed

  1. /** @addtogroup generic
  2.  * @{
  3.  */
  4.  
  5. /**
  6.  * @file
  7.  * @brief   Tdebug.
  8.  */
  9.  
  10. #include <console/klog.h>
  11. #include <proc/task.h>
  12. #include <proc/thread.h>
  13. #include <arch.h>
  14. #include <errno.h>
  15. #include <ipc/ipc.h>
  16. #include <syscall/copy.h>
  17. #include <udebug/udebug.h>
  18. #include <udebug/udebug_ipc.h>
  19.  
  20. /**
  21.  * Get and lock a phone's callee task.
  22.  *
  23.  * This will return a pointer to the task to which the phone
  24.  * is connected. It will lock the task, making sure it exists.
  25.  * (TODO: make sure the udebug-cleanup of the task hasn't
  26.  * started yet)
  27.  */
  28. static task_t *get_lock_callee_task(phone_t *phone)
  29. {
  30.     answerbox_t *box;
  31.     task_t *ta;
  32.     task_id_t taskid;
  33.     ipl_t ipl;
  34.  
  35.     ipl = interrupts_disable();
  36.     spinlock_lock(&phone->lock);
  37.     if (phone->state != IPC_PHONE_CONNECTED) {
  38.         spinlock_unlock(&phone->lock);
  39.         interrupts_restore(ipl);
  40.         return NULL;
  41.     }
  42.  
  43.     box = phone->callee;
  44.    
  45.     spinlock_lock(&box->lock);
  46.     ta = box->task;
  47.     taskid = ta->taskid;
  48.     spinlock_unlock(&box->lock);
  49.     spinlock_unlock(&phone->lock);
  50.  
  51.     /* Locking decoupled using taskid */
  52.    
  53.     spinlock_lock(&tasks_lock);
  54.     ta = task_find_by_id(taskid);
  55.     if (ta == NULL) {
  56.         spinlock_unlock(&tasks_lock);
  57.         interrupts_restore(ipl);
  58.         return NULL;
  59.     }
  60.  
  61.     spinlock_lock(&ta->lock);
  62.     spinlock_unlock(&tasks_lock);
  63.     interrupts_restore(ipl);
  64.  
  65.     return ta;
  66. }
  67.  
  68. static int udebug_rp_begin(call_t *call, phone_t *phone)
  69. {
  70.     task_t *ta;
  71.     ipl_t ipl;
  72.     int rc;
  73.  
  74.     thread_t *t;
  75.     link_t *cur;
  76.  
  77.     klog_printf("debug_begin()");
  78.  
  79.     ipl = interrupts_disable();
  80.     ta = get_lock_callee_task(phone);
  81.     klog_printf("debugging task %llu", ta->taskid);
  82.  
  83.     if (ta->dt_state != UDEBUG_TS_INACTIVE) {
  84.         spinlock_unlock(&ta->lock);
  85.         interrupts_restore(ipl);
  86.         klog_printf("debug_begin(): busy error");
  87.         return EBUSY;
  88.     }
  89.  
  90.     ta->dt_state = UDEBUG_TS_BEGINNING;
  91.     ta->debug_begin_call = call;
  92.  
  93.     if (ta->not_stoppable_count == 0) {
  94.         ta->dt_state = UDEBUG_TS_ACTIVE;
  95.         ta->debug_begin_call = NULL;
  96.         rc = 1; /* actually we need backsend with 0 retval */
  97.     } else {
  98.         rc = 0; /* no backsend */
  99.     }
  100.    
  101.     /* Set debug_active on all of the task's userspace threads */
  102.  
  103.     for (cur = ta->th_head.next; cur != &ta->th_head; cur = cur->next) {
  104.         t = list_get_instance(cur, thread_t, th_link);
  105.  
  106.         spinlock_lock(&t->lock);
  107.         if ((t->flags & THREAD_FLAG_USPACE) != 0)
  108.             t->debug_active = true;
  109.         spinlock_unlock(&t->lock);
  110.     }
  111.  
  112.     spinlock_unlock(&ta->lock);
  113.     interrupts_restore(ipl);
  114.  
  115.     klog_printf("debug_begin() done (%s)",
  116.         rc ? "backsend" : "stoppability wait");
  117.  
  118.     return rc;
  119. }
  120.  
  121. static int udebug_rp_end(call_t *call, phone_t *phone)
  122. {
  123.     task_t *ta;
  124.     ipl_t ipl;
  125.  
  126.     thread_t *t;
  127.     link_t *cur;
  128.  
  129.     klog_printf("udebug_rp_end()");
  130.  
  131.     ipl = interrupts_disable();
  132.     ta = get_lock_callee_task(phone);
  133.     klog_printf("task %llu", ta->taskid);
  134.  
  135.     //TODO: UDEBUG_TS_BEGINNING
  136.     if (ta->dt_state != UDEBUG_TS_ACTIVE) {
  137.         spinlock_unlock(&ta->lock);
  138.         interrupts_restore(ipl);
  139.         klog_printf("udebug_rp_begin(): task not in TS_BEGINNING");
  140.         return EINVAL;
  141.     }
  142.  
  143.     /* Clear debug_active from all of the task's userspace threads */
  144.  
  145.     for (cur = ta->th_head.next; cur != &ta->th_head; cur = cur->next) {
  146.         t = list_get_instance(cur, thread_t, th_link);
  147.  
  148.         spinlock_lock(&t->lock);
  149.         if ((t->flags & THREAD_FLAG_USPACE) != 0)
  150.             t->debug_active = true;
  151.         spinlock_unlock(&t->lock);
  152.     }
  153.  
  154.     /* Now respond to unanswered GO calls and reset thread states */
  155.     for (cur = ta->th_head.next; cur != &ta->th_head; cur = cur->next) {
  156.         t = list_get_instance(cur, thread_t, th_link);
  157.  
  158.         spinlock_lock(&t->lock);
  159.  
  160.         /* Still has go? */
  161.         if ((t->flags & THREAD_FLAG_USPACE) != 0 &&
  162.             t->debug_stop == false) {
  163.             /*
  164.              * Yes, so clear go. As debug_active == false,
  165.              * this doesn't affect anything.
  166.              */
  167.             t->debug_stop = true;  
  168.  
  169.             /* Answer GO call */
  170.             ipc_answer(&ta->answerbox, t->debug_go_call);
  171.         } else {
  172.             /*
  173.              * Debug_stop is already at initial value.
  174.              * Yet this means the thread needs waking up.
  175.              */
  176.             waitq_wakeup(&t->go_wq, WAKEUP_FIRST);
  177.         }
  178.         spinlock_unlock(&t->lock);
  179.     }
  180.  
  181.     ta->dt_state = UDEBUG_TS_INACTIVE;
  182.  
  183.     spinlock_unlock(&ta->lock);
  184.     interrupts_restore(ipl);
  185.  
  186.     klog_printf("udebug_rp_end() done\n");
  187.  
  188.     return 1;
  189. }
  190.  
  191.  
  192. static int udebug_rp_go(call_t *call, phone_t *phone)
  193. {
  194.     thread_t *t;
  195.     task_t *ta;
  196.     ipl_t ipl;
  197.  
  198.     klog_printf("debug_go()");
  199.     ta = get_lock_callee_task(phone);
  200.     spinlock_unlock(&ta->lock);
  201.     // TODO: don't lock ta
  202.  
  203.     t = (thread_t *) IPC_GET_ARG2(call->data);
  204.  
  205.     ipl = interrupts_disable();
  206.     spinlock_lock(&threads_lock);
  207.  
  208.     /* Verify that 't' exists and belongs to task 'ta' */
  209.     if (!thread_exists(t) || (t->task != ta)) {
  210.         spinlock_unlock(&threads_lock);
  211.         interrupts_restore(ipl);
  212.         return ENOENT;
  213.     }
  214.  
  215.     if ((t->debug_active != true) || (t->debug_stop != true)) {
  216.         /* Not in debugging session or already has GO */
  217.         spinlock_unlock(&threads_lock);
  218.         interrupts_restore(ipl);       
  219.         return EBUSY;
  220.     }
  221.  
  222.     t->debug_go_call = call;
  223.     t->debug_stop = false;
  224.     waitq_wakeup(&t->go_wq, WAKEUP_FIRST);
  225.  
  226.     spinlock_unlock(&threads_lock);
  227.     interrupts_restore(ipl);
  228.  
  229.     return 0; /* no backsend */
  230. }
  231.  
  232. static int udebug_rp_args_read(call_t *call, phone_t *phone)
  233. {
  234.     thread_t *t;
  235.     task_t *ta;
  236.     void *uspace_buffer;
  237.     int rc;
  238.     ipl_t ipl;
  239.     unative_t buffer[6];
  240.  
  241.     klog_printf("debug_args_read()");
  242.  
  243.     ta = get_lock_callee_task(phone);
  244.     klog_printf("task %llu", ta->taskid);
  245.     spinlock_unlock(&ta->lock);
  246.  
  247.     t = (thread_t *) IPC_GET_ARG2(call->data);
  248.  
  249.     ipl = interrupts_disable();
  250.     spinlock_lock(&threads_lock);
  251.  
  252.     /* Verify that 't' exists and belongs to task 'ta' */
  253.     if (!thread_exists(t) || (t->task != ta)) {
  254.         spinlock_unlock(&threads_lock);
  255.         interrupts_restore(ipl);
  256.         return ENOENT;
  257.     }
  258.  
  259.     //FIXME: additionally we need to verify that we are inside a syscall
  260.     if ((t->debug_active != true) || (t->debug_stop != true)) {
  261.         /* Not in debugging session or has GO */
  262.         spinlock_unlock(&threads_lock);
  263.         interrupts_restore(ipl);       
  264.         return EBUSY;
  265.     }
  266.  
  267.     /* Copy to a local buffer before releasing the lock */
  268.     memcpy(buffer, t->syscall_args, 6 * sizeof(unative_t));
  269.  
  270.     spinlock_unlock(&threads_lock);
  271.     interrupts_restore(ipl);
  272.  
  273.     /* Now copy to userspace */
  274.  
  275.     uspace_buffer = (void *)IPC_GET_ARG3(call->data);
  276.  
  277.     rc = copy_to_uspace(uspace_buffer, buffer, 6 * sizeof(unative_t));
  278.     if (rc != 0) {
  279.         spinlock_unlock(&ta->lock);
  280.         klog_printf("debug_args_read() - copy failed");
  281.         return rc;
  282.     }
  283.  
  284.     klog_printf("debug_args_read() done");
  285.     return 1; /* actually need becksend with retval 0 */
  286. }
  287.  
  288. static int udebug_rp_regs_read(call_t *call, phone_t *phone)
  289. {
  290.     thread_t *t;
  291.     task_t *ta;
  292.     void *uspace_buffer;
  293.     unative_t to_copy;
  294.     int rc;
  295.     istate_t *state;
  296.     istate_t state_copy;
  297.     ipl_t ipl;
  298.  
  299.     klog_printf("debug_regs_read()");
  300.  
  301.     ta = get_lock_callee_task(phone);
  302.     spinlock_unlock(&ta->lock);
  303.     //FIXME: don't lock ta
  304.  
  305.     ipl = interrupts_disable();
  306.     spinlock_lock(&threads_lock);
  307.  
  308.     t = (thread_t *) IPC_GET_ARG2(call->data);
  309.  
  310.     /* Verify that 't' exists and belongs to task 'ta' */
  311.     if (!thread_exists(t) || (t->task != ta)) {
  312.         spinlock_unlock(&threads_lock);
  313.         interrupts_restore(ipl);
  314.         return ENOENT;
  315.     }
  316.  
  317.     if ((t->debug_active != true) || (t->debug_stop != true)) {
  318.         /* Not in debugging session or has GO */
  319.         spinlock_unlock(&threads_lock);
  320.         interrupts_restore(ipl);       
  321.         return EBUSY;
  322.     }
  323.  
  324.     state = t->uspace_state;
  325.     if (state == NULL) {
  326.         spinlock_unlock(&threads_lock);
  327.         interrupts_restore(ipl);
  328.         klog_printf("debug_regs_read() - istate not available");
  329.         return EBUSY;
  330.     }
  331.  
  332.     /* Copy to a local buffer so that we can release the lock */
  333.     memcpy(&state_copy, state, sizeof(state_copy));
  334.     spinlock_unlock(&threads_lock);
  335.     interrupts_restore(ipl);
  336.  
  337.     uspace_buffer = (void *)IPC_GET_ARG3(call->data);
  338.     to_copy = IPC_GET_ARG4(call->data);
  339.     if (to_copy > sizeof(istate_t)) to_copy = sizeof(istate_t);
  340.  
  341.     rc = copy_to_uspace(uspace_buffer, &state_copy, to_copy);
  342.     if (rc != 0) {
  343.         spinlock_unlock(&ta->lock);
  344.         klog_printf("debug_regs_read() - copy failed");
  345.         return rc;
  346.     }
  347.  
  348.     IPC_SET_ARG1(call->data, to_copy);
  349.     IPC_SET_ARG2(call->data, sizeof(istate_t));
  350.  
  351.     klog_printf("debug_regs_read() done");
  352.     return 1; /* actually need becksend with retval 0 */
  353. }
  354.  
  355. static int udebug_rp_regs_write(call_t *call, phone_t *phone)
  356. {
  357.     thread_t *t;
  358.     task_t *ta;
  359.     void *uspace_data;
  360.     unative_t to_copy;
  361.     int rc;
  362.     istate_t *state;
  363.     istate_t data_copy;
  364.     ipl_t ipl;
  365.  
  366.     klog_printf("debug_regs_write()");
  367.  
  368.     /* First copy to a local buffer */
  369.  
  370.     uspace_data = (void *)IPC_GET_ARG3(call->data);
  371.     to_copy = IPC_GET_ARG4(call->data);
  372.     if (to_copy > sizeof(istate_t)) to_copy = sizeof(istate_t);
  373.  
  374.     rc = copy_from_uspace(&data_copy, uspace_data, to_copy);
  375.     if (rc != 0) {
  376.         klog_printf("debug_regs_write() - copy failed");
  377.         return rc;
  378.     }
  379.  
  380.     ta = get_lock_callee_task(phone);
  381.     spinlock_unlock(&ta->lock);
  382.     //FIXME: don't lock ta
  383.  
  384.     /* Now try to change the thread's uspace_state */
  385.  
  386.     ipl = interrupts_disable();
  387.     spinlock_lock(&threads_lock);
  388.  
  389.     t = (thread_t *) IPC_GET_ARG2(call->data);
  390.  
  391.     /* Verify that 't' exists and belongs to task 'ta' */
  392.     if (!thread_exists(t) || (t->task != ta)) {
  393.         spinlock_unlock(&threads_lock);
  394.         interrupts_restore(ipl);
  395.         return ENOENT;
  396.     }
  397.  
  398.     if ((t->debug_active != true) || (t->debug_stop != true)) {
  399.         /* Not in debugging session or has GO */
  400.         spinlock_unlock(&threads_lock);
  401.         interrupts_restore(ipl);       
  402.         return EBUSY;
  403.     }
  404.  
  405.     state = t->uspace_state;
  406.     if (state == NULL) {
  407.         spinlock_unlock(&threads_lock);
  408.         interrupts_restore(ipl);
  409.         klog_printf("debug_regs_write() - istate not available");
  410.         return EBUSY;
  411.     }
  412.  
  413.     memcpy(t->uspace_state, &data_copy, sizeof(t->uspace_state));
  414.  
  415.     spinlock_unlock(&threads_lock);
  416.     interrupts_restore(ipl);
  417.  
  418.     /* Set answer values */
  419.  
  420.     IPC_SET_ARG1(call->data, to_copy);
  421.     IPC_SET_ARG2(call->data, sizeof(istate_t));
  422.  
  423.     klog_printf("debug_regs_write() done");
  424.     return 1; /* actually need becksend with retval 0 */
  425. }
  426.  
  427. static int udebug_rp_thread_read(call_t *call, phone_t *phone)
  428. {
  429.     thread_t *t;
  430.     link_t *cur;
  431.     task_t *ta;
  432.     unative_t *uspace_buffer;
  433.     unative_t to_copy;
  434.     int rc;
  435.     unsigned total_bytes;
  436.     unsigned buf_size;
  437.     unative_t tid;
  438.     unsigned num_threads, copied_ids;
  439.     ipl_t ipl;
  440.     unative_t *buffer;
  441.     int flags;
  442.  
  443.     klog_printf("debug_thread_read()");
  444.  
  445.     ipl = interrupts_disable();
  446.     ta = get_lock_callee_task(phone);
  447.  
  448.     /* Verify task state */
  449.     if (ta->dt_state != UDEBUG_TS_ACTIVE) {
  450.         spinlock_unlock(&ta->lock);
  451.         interrupts_restore(ipl);
  452.         return EBUSY;
  453.     }
  454.  
  455.     /* Count the threads first */
  456.  
  457.     num_threads = 0;
  458.     for (cur = ta->th_head.next; cur != &ta->th_head; cur = cur->next) {
  459.         /* Count all threads, to be on the safe side */
  460.         ++num_threads;
  461.     }
  462.  
  463.     /* Allocate a buffer and copy down the threads' ids */
  464.     buffer = malloc(num_threads * sizeof(unative_t), 0); // ???
  465.  
  466.     copied_ids = 0;
  467.     for (cur = ta->th_head.next; cur != &ta->th_head; cur = cur->next) {
  468.         t = list_get_instance(cur, thread_t, th_link);
  469.  
  470.         spinlock_lock(&t->lock);
  471.         flags = t->flags;
  472.         spinlock_unlock(&t->lock);
  473.  
  474.         /* Not interested in kernel threads */
  475.         if ((flags & THREAD_FLAG_USPACE) != 0) {
  476.             /* Using thread struct pointer for identification */
  477.             tid = (unative_t) t;
  478.             buffer[copied_ids++] = tid;
  479.         }
  480.     }
  481.  
  482.     spinlock_unlock(&ta->lock);
  483.     interrupts_restore(ipl);
  484.  
  485.     /* Now copy to userspace */
  486.  
  487.     uspace_buffer = (void *)IPC_GET_ARG2(call->data);
  488.     buf_size = IPC_GET_ARG3(call->data);
  489.  
  490.     total_bytes = copied_ids * sizeof(unative_t);
  491.  
  492.     if (buf_size > total_bytes)
  493.         to_copy = total_bytes;
  494.     else
  495.         to_copy = buf_size;
  496.  
  497.     rc = copy_to_uspace(uspace_buffer, buffer, to_copy);
  498.     free(buffer);
  499.  
  500.     if (rc != 0) {
  501.         klog_printf("debug_thread_read() - copy failed");
  502.         return rc;
  503.     }
  504.  
  505.     IPC_SET_ARG1(call->data, to_copy);
  506.     IPC_SET_ARG2(call->data, total_bytes);
  507.  
  508.     klog_printf("debug_thread_read() done");
  509.     return 1; /* actually need becksend with retval 0 */
  510. }
  511.  
  512. static int udebug_rp_mem_write(call_t *call, phone_t *phone)
  513. {
  514.     void *uspace_data;
  515.     unative_t to_copy;
  516.     int rc;
  517.     void *buffer;
  518.  
  519.     klog_printf("udebug_rp_mem_write()");
  520.  
  521.     uspace_data = (void *)IPC_GET_ARG2(call->data);
  522.     to_copy = IPC_GET_ARG4(call->data);
  523.  
  524.     buffer = malloc(to_copy, 0); // ???
  525.  
  526.     rc = copy_from_uspace(buffer, uspace_data, to_copy);
  527.     if (rc != 0) {
  528.         klog_printf(" - copy failed");
  529.         return rc;
  530.     }
  531.  
  532.     call->buffer = buffer;
  533.  
  534.     klog_printf(" - done");
  535.     return 1; /* actually need becksend with retval 0 */
  536. }
  537.  
  538.  
  539. int udebug_request_preprocess(call_t *call, phone_t *phone)
  540. {
  541.     int rc;
  542.  
  543.     switch (IPC_GET_ARG1(call->data)) {
  544.     case UDEBUG_M_BEGIN:
  545.         rc = udebug_rp_begin(call, phone);
  546.         return rc;
  547.     case UDEBUG_M_END:
  548.         rc = udebug_rp_end(call, phone);
  549.         return rc;
  550.     case UDEBUG_M_GO:
  551.         rc = udebug_rp_go(call, phone);
  552.         return rc;
  553.     case UDEBUG_M_ARGS_READ:
  554.         rc = udebug_rp_args_read(call, phone);
  555.         return rc;
  556.     case UDEBUG_M_REGS_READ:
  557.         rc = udebug_rp_regs_read(call, phone);
  558.         return rc;
  559.     case UDEBUG_M_REGS_WRITE:
  560.         rc = udebug_rp_regs_write(call, phone);
  561.         return rc;
  562.     case UDEBUG_M_THREAD_READ:
  563.         rc = udebug_rp_thread_read(call, phone);
  564.         return rc;
  565.     case UDEBUG_M_MEM_WRITE:
  566.         rc = udebug_rp_mem_write(call, phone);
  567.         return rc;
  568.     default:
  569.         break;
  570.     }
  571.  
  572.     return 0;
  573. }
  574.  
  575. static void udebug_receive_mem_read(call_t *call)
  576. {
  577.     unative_t uspace_dst;
  578.     void *uspace_ptr;
  579.     unsigned size;
  580.     void *buffer;
  581.     int rc;
  582.  
  583.     klog_printf("debug_mem_read()");
  584.     uspace_dst = IPC_GET_ARG2(call->data);
  585.     uspace_ptr = (void *)IPC_GET_ARG3(call->data);
  586.     size = IPC_GET_ARG4(call->data);
  587.  
  588.     buffer = malloc(size, 0); // ???
  589.     klog_printf("debug_mem_read: src=%u, size=%u", uspace_ptr, size);
  590.  
  591.     /* NOTE: this is not strictly from a syscall... but that shouldn't
  592.      * be a problem */
  593.     rc = copy_from_uspace(buffer, uspace_ptr, size);
  594.     if (rc) {
  595.         IPC_SET_RETVAL(call->data, rc);
  596.         return;
  597.     }
  598.  
  599.     klog_printf("first word: %u", *((unative_t *)buffer));
  600.  
  601.     IPC_SET_RETVAL(call->data, 0);
  602.     /* Hack: ARG1=dest, ARG2=size as in IPC_M_DATA_READ so that
  603.        same code in process_answer() can be used
  604.        (no way to distinguish method in answer) */
  605.     IPC_SET_ARG1(call->data, uspace_dst);
  606.     IPC_SET_ARG2(call->data, size);
  607.     call->buffer = buffer;
  608.  
  609.     ipc_answer(&TASK->kernel_box, call);
  610. }
  611.  
  612. static void udebug_receive_mem_write(call_t *call)
  613. {
  614.     void *uspace_dst;
  615.     unsigned size;
  616.     void *buffer;
  617.     int rc;
  618.     udebug_task_state_t dts;
  619.  
  620.     klog_printf("udebug_receive_mem_write()");
  621.  
  622.     /* Verify task state */
  623.     spinlock_lock(&TASK->lock);
  624.     dts = TASK->dt_state;
  625.     spinlock_unlock(&TASK->lock);
  626.  
  627.     if (dts != UDEBUG_TS_ACTIVE) {
  628.         IPC_SET_RETVAL(call->data, EBUSY);
  629.         ipc_answer(&TASK->kernel_box, call);
  630.         return;
  631.     }
  632.    
  633.     uspace_dst = (void *)IPC_GET_ARG3(call->data);
  634.     size = IPC_GET_ARG4(call->data);
  635.  
  636.     buffer = call->buffer;
  637.     klog_printf("dst=%u, size=%u", uspace_dst, size);
  638.  
  639.     /* NOTE: this is not strictly from a syscall... but that shouldn't
  640.      * be a problem */
  641.     rc = copy_to_uspace(uspace_dst, buffer, size);
  642.     if (rc) {
  643.         IPC_SET_RETVAL(call->data, rc);
  644.         ipc_answer(&TASK->kernel_box, call);
  645.         return;
  646.     }
  647.  
  648.     IPC_SET_RETVAL(call->data, 0);
  649.  
  650.     free(call->buffer);
  651.     call->buffer = NULL;
  652.  
  653.     ipc_answer(&TASK->kernel_box, call);
  654. }
  655.  
  656.  
  657. /**
  658.  * Handle a debug call received on the kernel answerbox.
  659.  *
  660.  * This is called by the kbox servicing thread.
  661.  */
  662. void udebug_call_receive(call_t *call)
  663. {
  664.     int debug_method;
  665.  
  666.     debug_method = IPC_GET_ARG1(call->data);
  667.  
  668.     switch (debug_method) {
  669.     case UDEBUG_M_MEM_READ:
  670.         udebug_receive_mem_read(call);
  671.         break;
  672.     case UDEBUG_M_MEM_WRITE:
  673.         udebug_receive_mem_write(call);
  674.         break;
  675.     }
  676. }
  677.  
  678. /** @}
  679.  */
  680.