Subversion Repositories HelenOS

Rev

Rev 4605 | 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.  * Udebug operations on tasks and threads are implemented here. The
  38.  * functions defined here are called from the udebug_ipc module
  39.  * when servicing udebug IPC messages.
  40.  */
  41.  
  42. #include <debug.h>
  43. #include <proc/task.h>
  44. #include <proc/thread.h>
  45. #include <arch.h>
  46. #include <errno.h>
  47. #include <print.h>
  48. #include <syscall/copy.h>
  49. #include <ipc/ipc.h>
  50. #include <udebug/udebug.h>
  51. #include <udebug/udebug_ops.h>
  52.  
  53. /**
  54.  * Prepare a thread for a debugging operation.
  55.  *
  56.  * Simply put, return thread t with t->udebug.lock held,
  57.  * but only if it verifies all conditions.
  58.  *
  59.  * Specifically, verifies that thread t exists, is a userspace thread,
  60.  * and belongs to the current task (TASK). Verifies, that the thread
  61.  * is (or is not) go according to being_go (typically false).
  62.  * It also locks t->udebug.lock, making sure that t->udebug.active
  63.  * is true - that the thread is in a valid debugging session.
  64.  *
  65.  * With this verified and the t->udebug.lock mutex held, it is ensured
  66.  * that the thread cannot leave the debugging session, let alone cease
  67.  * to exist.
  68.  *
  69.  * In this function, holding the TASK->udebug.lock mutex prevents the
  70.  * thread from leaving the debugging session, while relaxing from
  71.  * the t->lock spinlock to the t->udebug.lock mutex.
  72.  *
  73.  * @param t     Pointer, need not at all be valid.
  74.  * @param being_go  Required thread state.
  75.  *
  76.  * Returns EOK if all went well, or an error code otherwise.
  77.  */
  78. static int _thread_op_begin(thread_t *t, bool being_go)
  79. {
  80.     task_id_t taskid;
  81.     ipl_t ipl;
  82.  
  83.     taskid = TASK->taskid;
  84.  
  85.     mutex_lock(&TASK->udebug.lock);
  86.  
  87.     /* thread_exists() must be called with threads_lock held */
  88.     ipl = interrupts_disable();
  89.     spinlock_lock(&threads_lock);
  90.  
  91.     if (!thread_exists(t)) {
  92.         spinlock_unlock(&threads_lock);
  93.         interrupts_restore(ipl);
  94.         mutex_unlock(&TASK->udebug.lock);
  95.         return ENOENT;
  96.     }
  97.  
  98.     /* t->lock is enough to ensure the thread's existence */
  99.     spinlock_lock(&t->lock);
  100.     spinlock_unlock(&threads_lock);
  101.  
  102.     /* Verify that 't' is a userspace thread. */
  103.     if ((t->flags & THREAD_FLAG_USPACE) == 0) {
  104.         /* It's not, deny its existence */
  105.         spinlock_unlock(&t->lock);
  106.         interrupts_restore(ipl);
  107.         mutex_unlock(&TASK->udebug.lock);
  108.         return ENOENT;
  109.     }
  110.  
  111.     /* Verify debugging state. */
  112.     if (t->udebug.active != true) {
  113.         /* Not in debugging session or undesired GO state */
  114.         spinlock_unlock(&t->lock);
  115.         interrupts_restore(ipl);
  116.         mutex_unlock(&TASK->udebug.lock);
  117.         return ENOENT;
  118.     }
  119.  
  120.     /*
  121.      * Since the thread has active == true, TASK->udebug.lock
  122.      * is enough to ensure its existence and that active remains
  123.      * true.
  124.      */
  125.     spinlock_unlock(&t->lock);
  126.     interrupts_restore(ipl);
  127.  
  128.     /* Only mutex TASK->udebug.lock left. */
  129.    
  130.     /* Now verify that the thread belongs to the current task. */
  131.     if (t->task != TASK) {
  132.         /* No such thread belonging this task*/
  133.         mutex_unlock(&TASK->udebug.lock);
  134.         return ENOENT;
  135.     }
  136.  
  137.     /*
  138.      * Now we need to grab the thread's debug lock for synchronization
  139.      * of the threads stoppability/stop state.
  140.      */
  141.     mutex_lock(&t->udebug.lock);
  142.  
  143.     /* The big task mutex is no longer needed. */
  144.     mutex_unlock(&TASK->udebug.lock);
  145.  
  146.     if (t->udebug.go != being_go) {
  147.         /* Not in debugging session or undesired GO state. */
  148.         mutex_unlock(&t->udebug.lock);
  149.         return EINVAL;
  150.     }
  151.  
  152.     /* Only t->udebug.lock left. */
  153.  
  154.     return EOK; /* All went well. */
  155. }
  156.  
  157. /** End debugging operation on a thread. */
  158. static void _thread_op_end(thread_t *t)
  159. {
  160.     mutex_unlock(&t->udebug.lock);
  161. }
  162.  
  163. /** Begin debugging the current task.
  164.  *
  165.  * Initiates a debugging session for the current task (and its threads).
  166.  * When the debugging session has started a reply will be sent to the
  167.  * UDEBUG_BEGIN call. This may happen immediately in this function if
  168.  * all the threads in this task are stoppable at the moment and in this
  169.  * case the function returns 1.
  170.  *
  171.  * Otherwise the function returns 0 and the reply will be sent as soon as
  172.  * all the threads become stoppable (i.e. they can be considered stopped).
  173.  *
  174.  * @param call  The BEGIN call we are servicing.
  175.  * @return  0 (OK, but not done yet), 1 (done) or negative error code.
  176.  */
  177. int udebug_begin(call_t *call)
  178. {
  179.     int reply;
  180.  
  181.     thread_t *t;
  182.     link_t *cur;
  183.  
  184.     LOG("Debugging task %llu", TASK->taskid);
  185.     mutex_lock(&TASK->udebug.lock);
  186.  
  187.     if (TASK->udebug.dt_state != UDEBUG_TS_INACTIVE) {
  188.         mutex_unlock(&TASK->udebug.lock);
  189.         return EBUSY;
  190.     }
  191.  
  192.     TASK->udebug.dt_state = UDEBUG_TS_BEGINNING;
  193.     TASK->udebug.begin_call = call;
  194.     TASK->udebug.debugger = call->sender;
  195.  
  196.     if (TASK->udebug.not_stoppable_count == 0) {
  197.         TASK->udebug.dt_state = UDEBUG_TS_ACTIVE;
  198.         TASK->udebug.begin_call = NULL;
  199.         reply = 1; /* immediate reply */
  200.     } else {
  201.         reply = 0; /* no reply */
  202.     }
  203.    
  204.     /* Set udebug.active on all of the task's userspace threads. */
  205.  
  206.     for (cur = TASK->th_head.next; cur != &TASK->th_head; cur = cur->next) {
  207.         t = list_get_instance(cur, thread_t, th_link);
  208.  
  209.         mutex_lock(&t->udebug.lock);
  210.         if ((t->flags & THREAD_FLAG_USPACE) != 0)
  211.             t->udebug.active = true;
  212.         mutex_unlock(&t->udebug.lock);
  213.     }
  214.  
  215.     mutex_unlock(&TASK->udebug.lock);
  216.     return reply;
  217. }
  218.  
  219. /** Finish debugging the current task.
  220.  *
  221.  * Closes the debugging session for the current task.
  222.  * @return Zero on success or negative error code.
  223.  */
  224. int udebug_end(void)
  225. {
  226.     int rc;
  227.  
  228.     LOG("Task %" PRIu64, TASK->taskid);
  229.  
  230.     mutex_lock(&TASK->udebug.lock);
  231.     rc = udebug_task_cleanup(TASK);
  232.     mutex_unlock(&TASK->udebug.lock);
  233.  
  234.     return rc;
  235. }
  236.  
  237. /** Set the event mask.
  238.  *
  239.  * Sets the event mask that determines which events are enabled.
  240.  *
  241.  * @param mask  Or combination of events that should be enabled.
  242.  * @return  Zero on success or negative error code.
  243.  */
  244. int udebug_set_evmask(udebug_evmask_t mask)
  245. {
  246.     LOG("mask = 0x%x", mask);
  247.  
  248.     mutex_lock(&TASK->udebug.lock);
  249.  
  250.     if (TASK->udebug.dt_state != UDEBUG_TS_ACTIVE) {
  251.         mutex_unlock(&TASK->udebug.lock);
  252.         return EINVAL;
  253.     }
  254.  
  255.     TASK->udebug.evmask = mask;
  256.     mutex_unlock(&TASK->udebug.lock);
  257.  
  258.     return 0;
  259. }
  260.  
  261. /** Give thread GO.
  262.  *
  263.  * Upon recieving a go message, the thread is given GO. Being GO
  264.  * means the thread is allowed to execute userspace code (until
  265.  * a debugging event or STOP occurs, at which point the thread loses GO.
  266.  *
  267.  * @param t The thread to operate on (unlocked and need not be valid).
  268.  * @param call  The GO call that we are servicing.
  269.  */
  270. int udebug_go(thread_t *t, call_t *call)
  271. {
  272.     int rc;
  273.  
  274.     /* On success, this will lock t->udebug.lock. */
  275.     rc = _thread_op_begin(t, false);
  276.     if (rc != EOK) {
  277.         return rc;
  278.     }
  279.  
  280.     t->udebug.go_call = call;
  281.     t->udebug.go = true;
  282.     t->udebug.cur_event = 0;    /* none */
  283.  
  284.     /*
  285.      * Neither t's lock nor threads_lock may be held during wakeup.
  286.      */
  287.     waitq_wakeup(&t->udebug.go_wq, WAKEUP_FIRST);
  288.  
  289.     _thread_op_end(t);
  290.  
  291.     return 0;
  292. }
  293.  
  294. /** Stop a thread (i.e. take its GO away)
  295.  *
  296.  * Generates a STOP event as soon as the thread becomes stoppable (i.e.
  297.  * can be considered stopped).
  298.  *
  299.  * @param t The thread to operate on (unlocked and need not be valid).
  300.  * @param call  The GO call that we are servicing.
  301.  */
  302. int udebug_stop(thread_t *t, call_t *call)
  303. {
  304.     int rc;
  305.  
  306.     LOG("udebug_stop()");
  307.  
  308.     /*
  309.      * On success, this will lock t->udebug.lock. Note that this makes sure
  310.      * the thread is not stopped.
  311.      */
  312.     rc = _thread_op_begin(t, true);
  313.     if (rc != EOK) {
  314.         return rc;
  315.     }
  316.  
  317.     /* Take GO away from the thread. */
  318.     t->udebug.go = false;
  319.  
  320.     if (t->udebug.stoppable != true) {
  321.         /* Answer will be sent when the thread becomes stoppable. */
  322.         _thread_op_end(t);
  323.         return 0;
  324.     }
  325.  
  326.     /*
  327.      * Answer GO call.
  328.      */
  329.  
  330.     /* Make sure nobody takes this call away from us. */
  331.     call = t->udebug.go_call;
  332.     t->udebug.go_call = NULL;
  333.  
  334.     IPC_SET_RETVAL(call->data, 0);
  335.     IPC_SET_ARG1(call->data, UDEBUG_EVENT_STOP);
  336.  
  337.     THREAD->udebug.cur_event = UDEBUG_EVENT_STOP;
  338.  
  339.     _thread_op_end(t);
  340.  
  341.     mutex_lock(&TASK->udebug.lock);
  342.     ipc_answer(&TASK->answerbox, call);
  343.     mutex_unlock(&TASK->udebug.lock);
  344.  
  345.     return 0;
  346. }
  347.  
  348. /** Read the list of userspace threads in the current task.
  349.  *
  350.  * The list takes the form of a sequence of thread hashes (i.e. the pointers
  351.  * to thread structures). A buffer of size @a buf_size is allocated and
  352.  * a pointer to it written to @a buffer. The sequence of hashes is written
  353.  * into this buffer.
  354.  *
  355.  * If the sequence is longer than @a buf_size bytes, only as much hashes
  356.  * as can fit are copied. The number of thread hashes copied is stored
  357.  * in @a n.
  358.  *
  359.  * The rationale for having @a buf_size is that this function is only
  360.  * used for servicing the THREAD_READ message, which always specifies
  361.  * a maximum size for the userspace buffer.
  362.  *
  363.  * @param buffer    The buffer for storing thread hashes.
  364.  * @param buf_size  Buffer size in bytes.
  365.  * @param n     The actual number of hashes copied will be stored here.
  366.  */
  367. int udebug_thread_read(void **buffer, size_t buf_size, size_t *n)
  368. {
  369.     thread_t *t;
  370.     link_t *cur;
  371.     unative_t tid;
  372.     unsigned copied_ids;
  373.     ipl_t ipl;
  374.     unative_t *id_buffer;
  375.     int flags;
  376.     size_t max_ids;
  377.  
  378.     LOG("udebug_thread_read()");
  379.  
  380.     /* Allocate a buffer to hold thread IDs */
  381.     id_buffer = malloc(buf_size, 0);
  382.  
  383.     mutex_lock(&TASK->udebug.lock);
  384.  
  385.     /* Verify task state */
  386.     if (TASK->udebug.dt_state != UDEBUG_TS_ACTIVE) {
  387.         mutex_unlock(&TASK->udebug.lock);
  388.         return EINVAL;
  389.     }
  390.  
  391.     ipl = interrupts_disable();
  392.     spinlock_lock(&TASK->lock);
  393.     /* Copy down the thread IDs */
  394.  
  395.     max_ids = buf_size / sizeof(unative_t);
  396.     copied_ids = 0;
  397.  
  398.     /* FIXME: make sure the thread isn't past debug shutdown... */
  399.     for (cur = TASK->th_head.next; cur != &TASK->th_head; cur = cur->next) {
  400.         /* Do not write past end of buffer */
  401.         if (copied_ids >= max_ids) break;
  402.  
  403.         t = list_get_instance(cur, thread_t, th_link);
  404.  
  405.         spinlock_lock(&t->lock);
  406.         flags = t->flags;
  407.         spinlock_unlock(&t->lock);
  408.  
  409.         /* Not interested in kernel threads. */
  410.         if ((flags & THREAD_FLAG_USPACE) != 0) {
  411.             /* Using thread struct pointer as identification hash */
  412.             tid = (unative_t) t;
  413.             id_buffer[copied_ids++] = tid;
  414.         }
  415.     }
  416.  
  417.     spinlock_unlock(&TASK->lock);
  418.     interrupts_restore(ipl);
  419.  
  420.     mutex_unlock(&TASK->udebug.lock);
  421.  
  422.     *buffer = id_buffer;
  423.     *n = copied_ids * sizeof(unative_t);
  424.  
  425.     return 0;
  426. }
  427.  
  428. /** Read the arguments of a system call.
  429.  *
  430.  * The arguments of the system call being being executed are copied
  431.  * to an allocated buffer and a pointer to it is written to @a buffer.
  432.  * The size of the buffer is exactly such that it can hold the maximum number
  433.  * of system-call arguments.
  434.  *
  435.  * Unless the thread is currently blocked in a SYSCALL_B or SYSCALL_E event,
  436.  * this function will fail with an EINVAL error code.
  437.  *
  438.  * @param buffer    The buffer for storing thread hashes.
  439.  */
  440. int udebug_args_read(thread_t *t, void **buffer)
  441. {
  442.     int rc;
  443.     unative_t *arg_buffer;
  444.  
  445.     /* Prepare a buffer to hold the arguments. */
  446.     arg_buffer = malloc(6 * sizeof(unative_t), 0);
  447.  
  448.     /* On success, this will lock t->udebug.lock. */
  449.     rc = _thread_op_begin(t, false);
  450.     if (rc != EOK) {
  451.         return rc;
  452.     }
  453.  
  454.     /* Additionally we need to verify that we are inside a syscall. */
  455.     if (t->udebug.cur_event != UDEBUG_EVENT_SYSCALL_B &&
  456.         t->udebug.cur_event != UDEBUG_EVENT_SYSCALL_E) {
  457.         _thread_op_end(t);
  458.         return EINVAL;
  459.     }
  460.  
  461.     /* Copy to a local buffer before releasing the lock. */
  462.     memcpy(arg_buffer, t->udebug.syscall_args, 6 * sizeof(unative_t));
  463.  
  464.     _thread_op_end(t);
  465.  
  466.     *buffer = arg_buffer;
  467.     return 0;
  468. }
  469.  
  470. /** Read the memory of the debugged task.
  471.  *
  472.  * Reads @a n bytes from the address space of the debugged task, starting
  473.  * from @a uspace_addr. The bytes are copied into an allocated buffer
  474.  * and a pointer to it is written into @a buffer.
  475.  *
  476.  * @param uspace_addr   Address from where to start reading.
  477.  * @param n     Number of bytes to read.
  478.  * @param buffer    For storing a pointer to the allocated buffer.
  479.  */
  480. int udebug_mem_read(unative_t uspace_addr, size_t n, void **buffer)
  481. {
  482.     void *data_buffer;
  483.     int rc;
  484.  
  485.     /* Verify task state */
  486.     mutex_lock(&TASK->udebug.lock);
  487.  
  488.     if (TASK->udebug.dt_state != UDEBUG_TS_ACTIVE) {
  489.         mutex_unlock(&TASK->udebug.lock);
  490.         return EBUSY;
  491.     }
  492.  
  493.     data_buffer = malloc(n, 0);
  494.  
  495.     /* NOTE: this is not strictly from a syscall... but that shouldn't
  496.      * be a problem */
  497.     rc = copy_from_uspace(data_buffer, (void *)uspace_addr, n);
  498.     mutex_unlock(&TASK->udebug.lock);
  499.  
  500.     if (rc != 0) return rc;
  501.  
  502.     *buffer = data_buffer;
  503.     return 0;
  504. }
  505.  
  506. int udebug_thread_get_thread_struct(thread_t *t, void **buffer)
  507. {
  508.     ipl_t ipl = interrupts_disable();
  509.  
  510.     void *data_buffer = (void *)malloc(sizeof(thread_t), 0);
  511.  
  512.     memcpy(data_buffer, (void *)t, sizeof(thread_t));
  513.  
  514.     *buffer = data_buffer;
  515.  
  516.     interrupts_restore(ipl);
  517.  
  518.     return (0);
  519. }
  520.  
  521. int udebug_task_get_memory_areas(void **buffer, size_t buf_size, size_t *n)
  522. {
  523.     link_t *cur;
  524.     ipl_t ipl;
  525.     unative_t *areas_buffer;
  526.     size_t max_index;
  527.  
  528.     as_print(TASK->as);
  529.    
  530.     areas_buffer = malloc(buf_size, 0);
  531.  
  532.     mutex_lock(&TASK->udebug.lock);
  533.  
  534.     /* Verify task state */
  535.     if (TASK->udebug.dt_state != UDEBUG_TS_ACTIVE) {
  536.         mutex_unlock(&TASK->udebug.lock);
  537.         return EINVAL;
  538.     }
  539.  
  540.     ipl = interrupts_disable();
  541.     spinlock_lock(&TASK->lock);
  542.  
  543.     max_index = buf_size / sizeof(unative_t);
  544.     as_t *as = TASK->as;
  545.    
  546.     mutex_lock(&as->lock);
  547.    
  548.     /* print out info about address space areas */
  549.     unsigned int index = 0;
  550.     for (cur = as->as_area_btree.leaf_head.next;
  551.         cur != &as->as_area_btree.leaf_head; cur = cur->next) {
  552.         btree_node_t *node;
  553.        
  554.         node = list_get_instance(cur, btree_node_t, leaf_link);
  555.        
  556.         unsigned int i;
  557.         for (i = 0; i < node->keys; i++) {
  558.             if (index >= max_index)
  559.                 break;
  560.  
  561.             as_area_t *area = node->value[i];
  562.        
  563.             mutex_lock(&area->lock);
  564.             areas_buffer[index++] = area->base;
  565.             areas_buffer[index++] = area->base + FRAMES2SIZE(area->pages);
  566.             mutex_unlock(&area->lock);
  567.         }
  568.     }
  569.    
  570.     mutex_unlock(&as->lock);
  571.  
  572.     spinlock_unlock(&TASK->lock);
  573.     interrupts_restore(ipl);
  574.  
  575.     mutex_unlock(&TASK->udebug.lock);
  576.  
  577.     *buffer = areas_buffer;
  578.     *n = (index) * sizeof(unative_t);
  579.  
  580.     return 0;
  581.  
  582. }
  583.  
  584. int udebug_copy_kstack(void *kstack, void **buffer, size_t n)
  585. {
  586.     ipl_t ipl = interrupts_disable();
  587.  
  588.     void *data_buffer = malloc(n, 0);
  589.  
  590.     memcpy(data_buffer, (void *)kstack, n);
  591.  
  592.     *buffer = data_buffer;
  593.  
  594.     interrupts_restore(ipl);
  595.  
  596.     return 0;
  597. }
  598.  
  599. int udebug_restore_thread_struct(void *buffer, thread_t *t_old)
  600. {
  601.     ipl_t ipl = interrupts_disable();
  602.  
  603.     thread_t *t_new = (thread_t *)buffer;
  604.  
  605.     t_old->thread_code = t_new->thread_code;
  606.  
  607.     printf("old sp: %p, new sp: %p\n", t_old->saved_context.sp, t_new->saved_context.sp);
  608.     printf("old kstack: %p, new kstack: %p\n", t_old->kstack, t_new->kstack);
  609.  
  610.     t_old->saved_context = t_new->saved_context;
  611.     t_old->saved_context.sp = (uintptr_t)t_old->kstack + ((uintptr_t)t_new->saved_context.sp - (uintptr_t)t_new->kstack);
  612.  
  613.     t_old->sleep_timeout_context = t_new->sleep_timeout_context;
  614.     t_old->sleep_timeout = t_new->sleep_timeout;
  615.     t_old->timeout_pending = t_new->timeout_pending;
  616.    
  617.     t_old->in_copy_from_uspace = t_new->in_copy_from_uspace;
  618.     t_old->in_copy_to_uspace = t_new->in_copy_to_uspace;
  619.  
  620.     t_old->interrupted = t_new->interrupted;
  621.  
  622.     t_old->call_me = t_new->call_me;
  623.     t_old->call_me_with = t_new->call_me_with;
  624.  
  625.     t_old->udebug.go_call = t_new->udebug.go_call;
  626.  
  627.     interrupts_restore(ipl);
  628.    
  629.     return (0);
  630. }
  631.  
  632. int udebug_mem_write(void *buffer, void *start, size_t n)
  633. {
  634.     ipl_t ipl = interrupts_disable();
  635.  
  636.     if (((unsigned) start & 0x80000000) == 0)
  637.         copy_to_uspace(start, buffer, n);
  638.  
  639.     interrupts_restore(ipl);
  640.  
  641.     return (0);
  642. }
  643.  
  644. int udebug_restore_kstack(void *buffer, size_t size, thread_t *t)
  645. {
  646.     ipl_t ipl = interrupts_disable();
  647.  
  648.     memcpy(t->kstack + sizeof(the_t), buffer + sizeof(the_t), size - sizeof(the_t));
  649.  
  650.     interrupts_restore(ipl);
  651.  
  652.     return (0);
  653. }
  654.  
  655.  
  656. /** @}
  657.  */
  658.