Subversion Repositories HelenOS

Rev

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