Subversion Repositories HelenOS

Rev

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