Subversion Repositories HelenOS

Rev

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