Subversion Repositories HelenOS

Rev

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