Subversion Repositories HelenOS

Rev

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