Subversion Repositories HelenOS

Rev

Rev 2898 | Rev 2900 | Go to most recent revision | Blame | Compare with Previous | Last modification | View Log | Download | RSS feed

  1. /*
  2.  * Copyright (c) 2008 Jiri Svoboda
  3.  * All rights reserved.
  4.  *
  5.  * Redistribution and use in source and binary forms, with or without
  6.  * modification, are permitted provided that the following conditions
  7.  * are met:
  8.  *
  9.  * - Redistributions of source code must retain the above copyright
  10.  *   notice, this list of conditions and the following disclaimer.
  11.  * - Redistributions in binary form must reproduce the above copyright
  12.  *   notice, this list of conditions and the following disclaimer in the
  13.  *   documentation and/or other materials provided with the distribution.
  14.  * - The name of the author may not be used to endorse or promote products
  15.  *   derived from this software without specific prior written permission.
  16.  *
  17.  * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
  18.  * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
  19.  * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
  20.  * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
  21.  * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
  22.  * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
  23.  * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
  24.  * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
  25.  * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
  26.  * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
  27.  */
  28.  
  29. /** @addtogroup generic
  30.  * @{
  31.  */
  32.  
  33. /**
  34.  * @file
  35.  * @brief   Udebug operations.
  36.  */
  37.  
  38. #include <console/klog.h>
  39. #include <proc/task.h>
  40. #include <proc/thread.h>
  41. #include <arch.h>
  42. #include <errno.h>
  43. #include <syscall/copy.h>
  44. #include <ipc/ipc.h>
  45. #include <udebug/udebug.h>
  46. #include <udebug/udebug_ops.h>
  47.  
  48. /**
  49.  * Prepare a thread for a debugging operation.
  50.  *
  51.  * Simply put, return thread t with t->debug_lock held,
  52.  * but only if it verifies all conditions.
  53.  *
  54.  * Specifically, verifies that thread t exists, is a userspace thread,
  55.  * and belongs to the current task (TASK). Verifies, that the thread
  56.  * has (or hasn't) go according to having_go (typically false).
  57.  * It also locks t->debug_lock, making sure that t->debug_active is true
  58.  * - that the thread is in a valid debugging session.
  59.  *
  60.  * Returns EOK if all went well, or an error code otherwise.
  61.  * Interrupts must be already disabled when calling this function.
  62.  *
  63.  * Note: This function sports complicated locking.
  64.  */
  65. static int _thread_op_begin(thread_t *t, bool having_go)
  66. {
  67.     int rc;
  68.     task_id_t taskid;
  69.  
  70.     taskid = TASK->taskid;
  71.  
  72.     /* Must lock threads_lock to ensure continued existence of the thread */
  73.     spinlock_lock(&threads_lock);
  74.  
  75.     if (!thread_exists(t)) {
  76.         spinlock_unlock(&threads_lock);
  77.         return ENOENT;
  78.     }
  79.  
  80.     spinlock_lock(&t->debug_lock);
  81.     spinlock_lock(&t->lock);
  82.    
  83.     /* Now verify that it's the current task */
  84.     if (t->task != TASK) {
  85.         /* No such thread belonging to callee */
  86.         rc = ENOENT;
  87.         goto error_exit;
  88.     }
  89.  
  90.     /* Verify that 't' is a userspace thread */
  91.     if ((t->flags & THREAD_FLAG_USPACE) == 0) {
  92.         /* It's not, deny its existence */
  93.         rc = ENOENT;
  94.         goto error_exit;
  95.     }
  96.  
  97.     if ((t->debug_active != true) || (!t->debug_stop != having_go)) {
  98.         /* Not in debugging session or undesired GO state */
  99.         rc = EINVAL;
  100.         goto error_exit;
  101.     }
  102.  
  103.     spinlock_unlock(&threads_lock);
  104.     spinlock_unlock(&t->lock);
  105.  
  106.     /* Only t->debug_lock left */
  107.  
  108.     return EOK; /* All went well */
  109.  
  110.  
  111.     /* Executed when a check on the thread fails */
  112. error_exit:
  113.     spinlock_unlock(&t->lock);
  114.     spinlock_unlock(&t->debug_lock);
  115.     spinlock_unlock(&threads_lock);
  116.  
  117.     /* No locks left here */
  118.     return rc;  /* Some errors occured */
  119. }
  120.  
  121.  
  122. static void _thread_op_end(thread_t *t)
  123. {
  124.     spinlock_unlock(&t->debug_lock);
  125. }
  126.  
  127. /**
  128.  * \return 0 (ok, but not done yet), 1 (done) or negative error code.
  129.  */
  130. int udebug_begin(call_t *call)
  131. {
  132.     ipl_t ipl;
  133.     int reply;
  134.  
  135.     thread_t *t;
  136.     link_t *cur;
  137.  
  138.     klog_printf("udebug_begin()");
  139.  
  140.     ipl = interrupts_disable();
  141.     klog_printf("debugging task %llu", TASK->taskid);
  142.  
  143.     spinlock_lock(&TASK->lock);
  144.  
  145.     if (TASK->dt_state != UDEBUG_TS_INACTIVE) {
  146.         spinlock_unlock(&TASK->lock);
  147.         interrupts_restore(ipl);
  148.         klog_printf("udebug_begin(): busy error");
  149.  
  150.         return EBUSY;
  151.     }
  152.  
  153.     TASK->dt_state = UDEBUG_TS_BEGINNING;
  154.     TASK->debug_begin_call = call;
  155.     TASK->debugger = call->sender;
  156.  
  157.     if (TASK->not_stoppable_count == 0) {
  158.         TASK->dt_state = UDEBUG_TS_ACTIVE;
  159.         TASK->debug_begin_call = NULL;
  160.         reply = 1; /* immediate reply */
  161.     } else {
  162.         reply = 0; /* no reply */
  163.     }
  164.    
  165.     /* Set debug_active on all of the task's userspace threads */
  166.  
  167.     for (cur = TASK->th_head.next; cur != &TASK->th_head; cur = cur->next) {
  168.         t = list_get_instance(cur, thread_t, th_link);
  169.  
  170.         spinlock_lock(&t->debug_lock);
  171.         if ((t->flags & THREAD_FLAG_USPACE) != 0)
  172.             t->debug_active = true;
  173.         spinlock_unlock(&t->debug_lock);
  174.     }
  175.  
  176.     spinlock_unlock(&TASK->lock);
  177.     interrupts_restore(ipl);
  178.  
  179.     klog_printf("udebug_begin() done (%s)",
  180.         reply ? "reply" : "stoppability wait");
  181.  
  182.     return reply;
  183. }
  184.  
  185. int udebug_end(void)
  186. {
  187.     ipl_t ipl;
  188.     int rc;
  189.  
  190.     klog_printf("udebug_end()");
  191.  
  192.     ipl = interrupts_disable();
  193.     spinlock_lock(&TASK->lock);
  194.  
  195.     rc = udebug_task_cleanup(TASK);
  196.  
  197.     klog_printf("task %llu", TASK->taskid);
  198.  
  199.     spinlock_unlock(&TASK->lock);
  200.     interrupts_restore(ipl);
  201.  
  202.     if (rc < 0) return EINVAL;
  203.  
  204.     return 0;
  205. }
  206.  
  207. int udebug_set_evmask(udebug_evmask_t mask)
  208. {
  209.     ipl_t ipl;
  210.  
  211.     klog_printf("udebug_set_mask()");
  212.  
  213.     ipl = interrupts_disable();
  214.     klog_printf("debugging task %llu", TASK->taskid);
  215.  
  216.     spinlock_lock(&TASK->lock);
  217.  
  218.     if (TASK->dt_state != UDEBUG_TS_ACTIVE) {
  219.         spinlock_unlock(&TASK->lock);
  220.         interrupts_restore(ipl);
  221.         klog_printf("udebug_set_mask(): not active debuging session");
  222.  
  223.         return EINVAL;
  224.     }
  225.  
  226.     TASK->debug_evmask = mask;
  227.  
  228.     spinlock_unlock(&TASK->lock);
  229.     interrupts_restore(ipl);
  230.  
  231.     return 0;
  232. }
  233.  
  234.  
  235. int udebug_go(thread_t *t, call_t *call)
  236. {
  237.     ipl_t ipl;
  238.     int rc;
  239.  
  240.     klog_printf("udebug_go()");
  241.  
  242.     ipl = interrupts_disable();
  243.  
  244.     /* On success, this will lock t->debug_lock */
  245.     rc = _thread_op_begin(t, false);
  246.     if (rc != EOK) {
  247.         interrupts_restore(ipl);
  248.         return rc;
  249.     }
  250.  
  251.     t->debug_go_call = call;
  252.     t->debug_stop = false;
  253.     t->cur_event = 0;   /* none */
  254.  
  255.     /*
  256.      * Neither t's lock nor threads_lock may be held during wakeup
  257.      */
  258.     waitq_wakeup(&t->go_wq, WAKEUP_FIRST);
  259.  
  260.     _thread_op_end(t);
  261.     interrupts_restore(ipl);
  262.  
  263.     return 0;
  264. }
  265.  
  266. int udebug_stop(thread_t *t, call_t *call)
  267. {
  268.     ipl_t ipl;
  269.     int rc;
  270.  
  271.     klog_printf("udebug_stop()");
  272.  
  273.     ipl = interrupts_disable();
  274.  
  275.     /*
  276.      * On success, this will lock t->debug_lock. Note that this makes sure
  277.      * the thread is not stopped.
  278.      */
  279.     rc = _thread_op_begin(t, true);
  280.     if (rc != EOK) {
  281.         interrupts_restore(ipl);
  282.         return rc;
  283.     }
  284.  
  285.     /* Take GO away from the thread */
  286.     t->debug_stop = true;
  287.  
  288.     if (!t->debug_stoppable) {
  289.         /* Answer will be sent when the thread becomes stoppable */
  290.         _thread_op_end(t);
  291.         interrupts_restore(ipl);
  292.         return 0;
  293.     }
  294.  
  295.     /*
  296.      * Answer GO call
  297.      */
  298.     klog_printf("udebug_stop - answering go call");
  299.  
  300.     /* Make sure nobody takes this call away from us */
  301.     call = t->debug_go_call;
  302.     t->debug_go_call = NULL;
  303.  
  304.     IPC_SET_RETVAL(call->data, 0);
  305.     IPC_SET_ARG1(call->data, UDEBUG_EVENT_STOP);
  306.     klog_printf("udebug_stop/ipc_answer");
  307.  
  308.     THREAD->cur_event = UDEBUG_EVENT_STOP;
  309.     _thread_op_end(t);
  310.  
  311.     spinlock_lock(&TASK->lock);
  312.     ipc_answer(&TASK->answerbox, call);
  313.     spinlock_unlock(&TASK->lock);
  314.  
  315.     interrupts_restore(ipl);
  316.     klog_printf("udebog_stop/done");
  317.     return 0;
  318. }
  319.  
  320. int udebug_thread_read(void **buffer, size_t buf_size, size_t *n)
  321. {
  322.     thread_t *t;
  323.     link_t *cur;
  324.     unative_t tid;
  325.     unsigned copied_ids;
  326.     ipl_t ipl;
  327.     unative_t *id_buffer;
  328.     int flags;
  329.     size_t max_ids;
  330.  
  331.     klog_printf("udebug_thread_read()");
  332.  
  333.     /* Allocate a buffer to hold thread IDs */
  334.     id_buffer = malloc(buf_size, 0);
  335.     if (!id_buffer) return ENOMEM;
  336.  
  337.     ipl = interrupts_disable();
  338.     spinlock_lock(&TASK->lock);
  339.  
  340.     /* Verify task state */
  341.     if (TASK->dt_state != UDEBUG_TS_ACTIVE) {
  342.         spinlock_unlock(&TASK->lock);
  343.         interrupts_restore(ipl);
  344.  
  345.         return EINVAL;
  346.     }
  347.  
  348.     /* Copy down the thread IDs */
  349.  
  350.     max_ids = buf_size / sizeof(unative_t);
  351.     copied_ids = 0;
  352.  
  353.     for (cur = TASK->th_head.next; cur != &TASK->th_head; cur = cur->next) {
  354.         /* Do not write past end of buffer */
  355.         if (copied_ids >= max_ids) break;
  356.  
  357.         t = list_get_instance(cur, thread_t, th_link);
  358.  
  359.         spinlock_lock(&t->lock);
  360.         flags = t->flags;
  361.         spinlock_unlock(&t->lock);
  362.  
  363.         /* Not interested in kernel threads */
  364.         if ((flags & THREAD_FLAG_USPACE) != 0) {
  365.             /* Using thread struct pointer as identification hash */
  366.             tid = (unative_t) t;
  367.             id_buffer[copied_ids++] = tid;
  368.         }
  369.     }
  370.  
  371.     spinlock_unlock(&TASK->lock);
  372.     interrupts_restore(ipl);
  373.  
  374.     *buffer = id_buffer;
  375.     *n = copied_ids * sizeof(unative_t);
  376.  
  377.     return 0;
  378. }
  379.  
  380. int udebug_args_read(thread_t *t, void **buffer)
  381. {
  382.     int rc;
  383.     ipl_t ipl;
  384.     unative_t *arg_buffer;
  385.  
  386.     klog_printf("udebug_args_read()");
  387.  
  388.     /* Prepare a buffer to hold the arguments */
  389.     arg_buffer = malloc(6 * sizeof(unative_t), 0);
  390.     if (!arg_buffer) return ENOMEM;
  391.  
  392.     ipl = interrupts_disable();
  393.  
  394.     /* On success, this will lock t->debug_lock */
  395.     rc = _thread_op_begin(t, false);
  396.     if (rc != EOK) {
  397.         interrupts_restore(ipl);
  398.         return rc;
  399.     }
  400.  
  401.     /* Additionally we need to verify that we are inside a syscall */
  402.     if (t->cur_event != UDEBUG_EVENT_SYSCALL) {
  403.         _thread_op_end(t);
  404.         interrupts_restore(ipl);
  405.  
  406.         return EINVAL;
  407.     }
  408.  
  409.     /* Copy to a local buffer before releasing the lock */
  410.     memcpy(arg_buffer, t->syscall_args, 6 * sizeof(unative_t));
  411.  
  412.     _thread_op_end(t);
  413.     interrupts_restore(ipl);
  414.  
  415.     *buffer = arg_buffer;
  416.     return 0;
  417. }
  418.  
  419. int udebug_regs_read(thread_t *t, void **buffer, size_t *n)
  420. {
  421.     istate_t *state;
  422.     void *regs_buffer;
  423.     int rc;
  424.     ipl_t ipl;
  425.  
  426.     klog_printf("udebug_regs_read()");
  427.  
  428.     /* Prepare a buffer to hold the registers */
  429.     regs_buffer = malloc(sizeof(istate_t), 0);
  430.     if (!regs_buffer) return ENOMEM;
  431.  
  432.     ipl = interrupts_disable();
  433.  
  434.     /* On success, this will lock t->debug_lock */
  435.     rc = _thread_op_begin(t, false);
  436.     if (rc != EOK) {
  437.         interrupts_restore(ipl);
  438.         return rc;
  439.     }
  440.  
  441.     state = t->uspace_state;
  442.     if (state == NULL) {
  443.         _thread_op_end(t);
  444.         interrupts_restore(ipl);
  445.         klog_printf("udebug_regs_read() - istate not available");
  446.         return EBUSY;
  447.     }
  448.  
  449.     /* Copy to the allocated buffer */
  450.     memcpy(regs_buffer, state, sizeof(istate_t));
  451.  
  452.     _thread_op_end(t);
  453.     interrupts_restore(ipl);
  454.  
  455.     *buffer = regs_buffer;
  456.     *n = sizeof(istate_t);
  457.  
  458.     return 0;
  459. }
  460.  
  461. int udebug_regs_write(thread_t *t, void *buffer)
  462. {
  463.     int rc;
  464.     istate_t *state;
  465.     ipl_t ipl;
  466.  
  467.     klog_printf("udebug_regs_write()");
  468.  
  469.     /* Try to change the thread's uspace_state */
  470.  
  471.     ipl = interrupts_disable();
  472.  
  473.     /* On success, this will lock t->debug_lock */
  474.     rc = _thread_op_begin(t, false);
  475.     if (rc != EOK) {
  476.         interrupts_restore(ipl);
  477.         return rc;
  478.     }
  479.  
  480.     state = t->uspace_state;
  481.     if (state == NULL) {
  482.         _thread_op_end(t);
  483.         interrupts_restore(ipl);
  484.         klog_printf("udebug_regs_write() - istate not available");
  485.  
  486.         return EBUSY;
  487.     }
  488.  
  489.     memcpy(t->uspace_state, buffer, sizeof(t->uspace_state));
  490.  
  491.     _thread_op_end(t);
  492.     interrupts_restore(ipl);
  493.  
  494.     return 0;
  495. }
  496.  
  497.  
  498. int udebug_mem_read(unative_t uspace_addr, size_t n, void **buffer)
  499. {
  500.     void *data_buffer;
  501.     int rc;
  502.  
  503.     klog_printf("udebug_mem_read()");
  504.  
  505.     data_buffer = malloc(n, 0);
  506.     if (!data_buffer) return ENOMEM;
  507.  
  508.     klog_printf("udebug_mem_read: src=%u, size=%u", uspace_addr, n);
  509.  
  510.     /* NOTE: this is not strictly from a syscall... but that shouldn't
  511.      * be a problem */
  512.     rc = copy_from_uspace(data_buffer, (void *)uspace_addr, n);
  513.     if (rc) return rc;
  514.  
  515.     *buffer = data_buffer;
  516.     return 0;
  517. }
  518.  
  519. int udebug_mem_write(unative_t uspace_addr, void *data, size_t n)
  520. {
  521.     int rc;
  522.     udebug_task_state_t dts;
  523.  
  524.     klog_printf("udebug_mem_write()");
  525.  
  526.     /* Verify task state */
  527.     spinlock_lock(&TASK->lock);
  528.     dts = TASK->dt_state;
  529.     spinlock_unlock(&TASK->lock);
  530.  
  531.     if (dts != UDEBUG_TS_ACTIVE)
  532.         return EBUSY;
  533.    
  534.     klog_printf("dst=%u, size=%u", uspace_addr, n);
  535.  
  536.     /* NOTE: this is not strictly from a syscall... but that shouldn't
  537.      * be a problem */
  538.     rc = copy_to_uspace((void *)uspace_addr, data, n);
  539.     if (rc) return rc;
  540.  
  541.     return 0;
  542. }
  543.  
  544. /** @}
  545.  */
  546.