Subversion Repositories HelenOS

Rev

Rev 2894 | Rev 2897 | 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). It also locks t->debug_lock,
  56.  * making sure that t->debug_active is true - that the thread is
  57.  * in a valid debugging session.
  58.  *
  59.  * Returns EOK if all went well, or an error code otherwise.
  60.  * Interrupts must be already disabled when calling this function.
  61.  *
  62.  * Note: This function sports complicated locking.
  63.  */
  64. static int _thread_op_begin(thread_t *t)
  65. {
  66.     int rc;
  67.     task_id_t taskid;
  68.  
  69.     taskid = TASK->taskid;
  70.  
  71.     /* Must lock threads_lock to ensure continued existence of the thread */
  72.     spinlock_lock(&threads_lock);
  73.  
  74.     if (!thread_exists(t)) {
  75.         spinlock_unlock(&threads_lock);
  76.         return ENOENT;
  77.     }
  78.  
  79.     spinlock_lock(&t->debug_lock);
  80.     spinlock_lock(&t->lock);
  81.    
  82.     /* Now verify that it's the current task */
  83.     if (t->task != TASK) {
  84.         /* No such thread belonging to callee */
  85.         rc = ENOENT;
  86.         goto error_exit;
  87.     }
  88.  
  89.     /* Verify that 't' is a userspace thread */
  90.     if ((t->flags & THREAD_FLAG_USPACE) == 0) {
  91.         /* It's not, deny its existence */
  92.         rc = ENOENT;
  93.         goto error_exit;
  94.     }
  95.  
  96.     if ((t->debug_active != true) || (t->debug_stop != true)) {
  97.         /* Not in debugging session or already has GO */
  98.         rc = ENOENT;
  99.         goto error_exit;
  100.     }
  101.  
  102.     spinlock_unlock(&threads_lock);
  103.     spinlock_unlock(&t->lock);
  104.  
  105.     /* Only t->debug_lock left */
  106.  
  107.     return EOK; /* All went well */
  108.  
  109.  
  110.     /* Executed when a check on the thread fails */
  111. error_exit:
  112.     spinlock_unlock(&t->lock);
  113.     spinlock_unlock(&t->debug_lock);
  114.     spinlock_unlock(&threads_lock);
  115.  
  116.     /* No locks left here */
  117.     return rc;  /* Some errors occured */
  118. }
  119.  
  120.  
  121. static void _thread_op_end(thread_t *t)
  122. {
  123.     spinlock_unlock(&t->debug_lock);
  124. }
  125.  
  126. /**
  127.  * \return 0 (ok, but not done yet), 1 (done) or negative error code.
  128.  */
  129. int udebug_begin(call_t *call)
  130. {
  131.     ipl_t ipl;
  132.     int reply;
  133.  
  134.     thread_t *t;
  135.     link_t *cur;
  136.  
  137.     klog_printf("udebug_begin()");
  138.  
  139.     ipl = interrupts_disable();
  140.     klog_printf("debugging task %llu", TASK->taskid);
  141.  
  142.     spinlock_lock(&TASK->lock);
  143.  
  144.     if (TASK->dt_state != UDEBUG_TS_INACTIVE) {
  145.         spinlock_unlock(&TASK->lock);
  146.         interrupts_restore(ipl);
  147.         klog_printf("udebug_begin(): busy error");
  148.  
  149.         return EBUSY;
  150.     }
  151.  
  152.     TASK->dt_state = UDEBUG_TS_BEGINNING;
  153.     TASK->debug_begin_call = call;
  154.     TASK->debugger = call->sender;
  155.  
  156.     if (TASK->not_stoppable_count == 0) {
  157.         TASK->dt_state = UDEBUG_TS_ACTIVE;
  158.         TASK->debug_begin_call = NULL;
  159.         reply = 1; /* immediate reply */
  160.     } else {
  161.         reply = 0; /* no reply */
  162.     }
  163.    
  164.     /* Set debug_active on all of the task's userspace threads */
  165.  
  166.     for (cur = TASK->th_head.next; cur != &TASK->th_head; cur = cur->next) {
  167.         t = list_get_instance(cur, thread_t, th_link);
  168.  
  169.         spinlock_lock(&t->debug_lock);
  170.         if ((t->flags & THREAD_FLAG_USPACE) != 0)
  171.             t->debug_active = true;
  172.         spinlock_unlock(&t->debug_lock);
  173.     }
  174.  
  175.     spinlock_unlock(&TASK->lock);
  176.     interrupts_restore(ipl);
  177.  
  178.     klog_printf("udebug_begin() done (%s)",
  179.         reply ? "reply" : "stoppability wait");
  180.  
  181.     return reply;
  182. }
  183.  
  184. int udebug_end(void)
  185. {
  186.     ipl_t ipl;
  187.     int rc;
  188.  
  189.     klog_printf("udebug_end()");
  190.  
  191.     ipl = interrupts_disable();
  192.     spinlock_lock(&TASK->lock);
  193.  
  194.     rc = udebug_task_cleanup(TASK);
  195.  
  196.     klog_printf("task %llu", TASK->taskid);
  197.  
  198.     spinlock_unlock(&TASK->lock);
  199.     interrupts_restore(ipl);
  200.  
  201.     if (rc < 0) return EINVAL;
  202.  
  203.     return 0;
  204. }
  205.  
  206. int udebug_go(thread_t *t, call_t *call)
  207. {
  208.     ipl_t ipl;
  209.     int rc;
  210.  
  211.     klog_printf("udebug_go()");
  212.  
  213.     ipl = interrupts_disable();
  214.  
  215.     /* On success, this will lock t->debug_lock */
  216.     rc = _thread_op_begin(t);
  217.     if (rc != EOK) {
  218.         interrupts_restore(ipl);
  219.         return rc;
  220.     }
  221.  
  222.     t->debug_go_call = call;
  223.     t->debug_stop = false;
  224.     t->cur_event = 0;   /* none */
  225.  
  226.     /*
  227.      * Neither t's lock nor threads_lock may be held during wakeup
  228.      */
  229.     waitq_wakeup(&t->go_wq, WAKEUP_FIRST);
  230.  
  231.     _thread_op_end(t);
  232.     interrupts_restore(ipl);
  233.  
  234.     return 0;
  235. }
  236.  
  237.  
  238. int udebug_thread_read(void **buffer, size_t *n)
  239. {
  240.     thread_t *t;
  241.     link_t *cur;
  242.     unative_t tid;
  243.     unsigned num_threads, copied_ids;
  244.     ipl_t ipl;
  245.     unative_t *id_buffer;
  246.     int flags;
  247.  
  248.     klog_printf("udebug_thread_read()");
  249.  
  250.     ipl = interrupts_disable();
  251.     spinlock_lock(&TASK->lock);
  252.  
  253.     /* Verify task state */
  254.     if (TASK->dt_state != UDEBUG_TS_ACTIVE) {
  255.         spinlock_unlock(&TASK->lock);
  256.         interrupts_restore(ipl);
  257.  
  258.         return EINVAL;
  259.     }
  260.  
  261.     /* Count the threads first */
  262.  
  263.     num_threads = 0;
  264.     for (cur = TASK->th_head.next; cur != &TASK->th_head; cur = cur->next) {
  265.         /* Count all threads, to be on the safe side */
  266.         ++num_threads;
  267.     }
  268.  
  269.     /* Allocate a buffer and copy down the threads' ids */
  270.     //FIXME!!! must not malloc when locks are held
  271.     id_buffer = malloc(num_threads * sizeof(unative_t), 0);
  272.     if (!id_buffer) {
  273.         spinlock_unlock(&TASK->lock);
  274.         interrupts_restore(ipl);
  275.  
  276.         return ENOMEM;
  277.     }
  278.  
  279.     copied_ids = 0;
  280.     for (cur = TASK->th_head.next; cur != &TASK->th_head; cur = cur->next) {
  281.         t = list_get_instance(cur, thread_t, th_link);
  282.  
  283.         spinlock_lock(&t->lock);
  284.         flags = t->flags;
  285.         spinlock_unlock(&t->lock);
  286.  
  287.         /* Not interested in kernel threads */
  288.         if ((flags & THREAD_FLAG_USPACE) != 0) {
  289.             /* Using thread struct pointer for identification */
  290.             tid = (unative_t) t;
  291.             id_buffer[copied_ids++] = tid;
  292.         }
  293.     }
  294.  
  295.     spinlock_unlock(&TASK->lock);
  296.     interrupts_restore(ipl);
  297.  
  298.     *buffer = id_buffer;
  299.     *n = copied_ids * sizeof(unative_t);
  300.  
  301.     return 0;
  302. }
  303.  
  304. int udebug_args_read(thread_t *t, void **buffer)
  305. {
  306.     int rc;
  307.     ipl_t ipl;
  308.     unative_t *arg_buffer;
  309.  
  310.     klog_printf("udebug_args_read()");
  311.  
  312.     /* Prepare a buffer to hold the arguments */
  313.     arg_buffer = malloc(6 * sizeof(unative_t), 0);
  314.     if (!arg_buffer) return ENOMEM;
  315.  
  316.     ipl = interrupts_disable();
  317.  
  318.     /* On success, this will lock t->debug_lock */
  319.     rc = _thread_op_begin(t);
  320.     if (rc != EOK) {
  321.         interrupts_restore(ipl);
  322.         return rc;
  323.     }
  324.  
  325.     /* Additionally we need to verify that we are inside a syscall */
  326.     if (t->cur_event != UDEBUG_EVENT_SYSCALL) {
  327.         _thread_op_end(t);
  328.         interrupts_restore(ipl);
  329.  
  330.         return EINVAL;
  331.     }
  332.  
  333.     /* Copy to a local buffer before releasing the lock */
  334.     memcpy(arg_buffer, t->syscall_args, 6 * sizeof(unative_t));
  335.  
  336.     _thread_op_end(t);
  337.     interrupts_restore(ipl);
  338.  
  339.     *buffer = arg_buffer;
  340.     return 0;
  341. }
  342.  
  343. int udebug_regs_read(thread_t *t, void **buffer, size_t *n)
  344. {
  345.     istate_t *state;
  346.     void *regs_buffer;
  347.     int rc;
  348.     ipl_t ipl;
  349.  
  350.     klog_printf("udebug_regs_read()");
  351.  
  352.     /* Prepare a buffer to hold the registers */
  353.     regs_buffer = malloc(sizeof(istate_t), 0);
  354.     if (!regs_buffer) return ENOMEM;
  355.  
  356.     ipl = interrupts_disable();
  357.  
  358.     /* On success, this will lock t->debug_lock */
  359.     rc = _thread_op_begin(t);
  360.     if (rc != EOK) {
  361.         interrupts_restore(ipl);
  362.         return rc;
  363.     }
  364.  
  365.     state = t->uspace_state;
  366.     if (state == NULL) {
  367.         _thread_op_end(t);
  368.         interrupts_restore(ipl);
  369.         klog_printf("udebug_regs_read() - istate not available");
  370.         return EBUSY;
  371.     }
  372.  
  373.     /* Copy to the allocated buffer */
  374.     memcpy(regs_buffer, state, sizeof(istate_t));
  375.  
  376.     _thread_op_end(t);
  377.     interrupts_restore(ipl);
  378.  
  379.     *buffer = regs_buffer;
  380.     *n = sizeof(istate_t);
  381.  
  382.     return 0;
  383. }
  384.  
  385. int udebug_regs_write(thread_t *t, void *buffer)
  386. {
  387.     int rc;
  388.     istate_t *state;
  389.     ipl_t ipl;
  390.  
  391.     klog_printf("udebug_regs_write()");
  392.  
  393.     /* Try to change the thread's uspace_state */
  394.  
  395.     ipl = interrupts_disable();
  396.  
  397.     /* On success, this will lock t->debug_lock */
  398.     rc = _thread_op_begin(t);
  399.     if (rc != EOK) {
  400.         interrupts_restore(ipl);
  401.         return rc;
  402.     }
  403.  
  404.     state = t->uspace_state;
  405.     if (state == NULL) {
  406.         _thread_op_end(t);
  407.         interrupts_restore(ipl);
  408.         klog_printf("udebug_regs_write() - istate not available");
  409.  
  410.         return EBUSY;
  411.     }
  412.  
  413.     memcpy(t->uspace_state, buffer, sizeof(t->uspace_state));
  414.  
  415.     _thread_op_end(t);
  416.     interrupts_restore(ipl);
  417.  
  418.     return 0;
  419. }
  420.  
  421.  
  422. int udebug_mem_read(unative_t uspace_addr, size_t n, void **buffer)
  423. {
  424.     void *data_buffer;
  425.     int rc;
  426.  
  427.     klog_printf("udebug_mem_read()");
  428.  
  429.     data_buffer = malloc(n, 0);
  430.     if (!data_buffer) return ENOMEM;
  431.  
  432.     klog_printf("udebug_mem_read: src=%u, size=%u", uspace_addr, n);
  433.  
  434.     /* NOTE: this is not strictly from a syscall... but that shouldn't
  435.      * be a problem */
  436.     rc = copy_from_uspace(data_buffer, (void *)uspace_addr, n);
  437.     if (rc) return rc;
  438.  
  439.     *buffer = data_buffer;
  440.     return 0;
  441. }
  442.  
  443. int udebug_mem_write(unative_t uspace_addr, void *data, size_t n)
  444. {
  445.     int rc;
  446.     udebug_task_state_t dts;
  447.  
  448.     klog_printf("udebug_mem_write()");
  449.  
  450.     /* Verify task state */
  451.     spinlock_lock(&TASK->lock);
  452.     dts = TASK->dt_state;
  453.     spinlock_unlock(&TASK->lock);
  454.  
  455.     if (dts != UDEBUG_TS_ACTIVE)
  456.         return EBUSY;
  457.    
  458.     klog_printf("dst=%u, size=%u", uspace_addr, n);
  459.  
  460.     /* NOTE: this is not strictly from a syscall... but that shouldn't
  461.      * be a problem */
  462.     rc = copy_to_uspace((void *)uspace_addr, data, n);
  463.     if (rc) return rc;
  464.  
  465.     return 0;
  466. }
  467.  
  468. /** @}
  469.  */
  470.