source: mainline/kernel/generic/src/udebug/udebug_ops.c@ e39d7b8

Last change on this file since e39d7b8 was e39d7b8, checked in by Jiří Zárevúcky <zarevucky.jiri@…>, 6 years ago

Change type of uspace pointers in kernel from pointer type to numeric type

From kernel's perspective, userspace addresses are not valid pointers,
and can only be used in calls to copy_to/from_uspace().
Therefore, we change the type of those arguments and variables to
uspace_addr_t which is an alias for sysarg_t.

This allows the compiler to catch accidental direct accesses to
userspace addresses.

Additionally, to avoid losing the type information in code,
a macro uspace_ptr(type) is used that translates to uspace_addr_t.
I makes no functional difference, but allows keeping the type information
in code in case we implement some sort of static checking for it in the future.

  • Property mode set to 100644
File size: 15.9 KB
Line 
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 kernel_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 <stdbool.h>
48#include <str.h>
49#include <syscall/copy.h>
50#include <ipc/ipc.h>
51#include <udebug/udebug.h>
52#include <udebug/udebug_ops.h>
53#include <mem.h>
54#include <stdlib.h>
55
56/** Prepare a thread for a debugging operation.
57 *
58 * Simply put, return thread t with t->udebug.lock held,
59 * but only if it verifies all conditions.
60 *
61 * Specifically, verifies that thread t exists, is a userspace thread,
62 * and belongs to the current task (TASK). Verifies, that the thread
63 * is (or is not) go according to being_go (typically false).
64 * It also locks t->udebug.lock, making sure that t->udebug.active
65 * is true - that the thread is in a valid debugging session.
66 *
67 * With this verified and the t->udebug.lock mutex held, it is ensured
68 * that the thread cannot leave the debugging session, let alone cease
69 * to exist.
70 *
71 * In this function, holding the TASK->udebug.lock mutex prevents the
72 * thread from leaving the debugging session, while relaxing from
73 * the t->lock spinlock to the t->udebug.lock mutex.
74 *
75 * @param thread Pointer, need not at all be valid.
76 * @param being_go Required thread state.
77 *
78 * Returns EOK if all went well, or an error code otherwise.
79 *
80 */
81static errno_t _thread_op_begin(thread_t *thread, bool being_go)
82{
83 mutex_lock(&TASK->udebug.lock);
84
85 /* thread_exists() must be called with threads_lock held */
86 irq_spinlock_lock(&threads_lock, true);
87
88 if (!thread_exists(thread)) {
89 irq_spinlock_unlock(&threads_lock, true);
90 mutex_unlock(&TASK->udebug.lock);
91 return ENOENT;
92 }
93
94 /* thread->lock is enough to ensure the thread's existence */
95 irq_spinlock_exchange(&threads_lock, &thread->lock);
96
97 /* Verify that 'thread' is a userspace thread. */
98 if (!thread->uspace) {
99 /* It's not, deny its existence */
100 irq_spinlock_unlock(&thread->lock, true);
101 mutex_unlock(&TASK->udebug.lock);
102 return ENOENT;
103 }
104
105 /* Verify debugging state. */
106 if (thread->udebug.active != true) {
107 /* Not in debugging session or undesired GO state */
108 irq_spinlock_unlock(&thread->lock, true);
109 mutex_unlock(&TASK->udebug.lock);
110 return ENOENT;
111 }
112
113 /*
114 * Since the thread has active == true, TASK->udebug.lock
115 * is enough to ensure its existence and that active remains
116 * true.
117 *
118 */
119 irq_spinlock_unlock(&thread->lock, true);
120
121 /* Only mutex TASK->udebug.lock left. */
122
123 /* Now verify that the thread belongs to the current task. */
124 if (thread->task != TASK) {
125 /* No such thread belonging this task*/
126 mutex_unlock(&TASK->udebug.lock);
127 return ENOENT;
128 }
129
130 /*
131 * Now we need to grab the thread's debug lock for synchronization
132 * of the threads stoppability/stop state.
133 *
134 */
135 mutex_lock(&thread->udebug.lock);
136
137 /* The big task mutex is no longer needed. */
138 mutex_unlock(&TASK->udebug.lock);
139
140 if (thread->udebug.go != being_go) {
141 /* Not in debugging session or undesired GO state. */
142 mutex_unlock(&thread->udebug.lock);
143 return EINVAL;
144 }
145
146 /* Only thread->udebug.lock left. */
147
148 return EOK; /* All went well. */
149}
150
151/** End debugging operation on a thread. */
152static void _thread_op_end(thread_t *thread)
153{
154 mutex_unlock(&thread->udebug.lock);
155}
156
157/** Begin debugging the current task.
158 *
159 * Initiates a debugging session for the current task (and its threads).
160 * When the debugging session has started a reply should be sent to the
161 * UDEBUG_BEGIN call. This may happen immediately in this function if
162 * all the threads in this task are stoppable at the moment and in this
163 * case the function sets @a *active to @c true.
164 *
165 * Otherwise the function sets @a *active to false and the resonse should
166 * be sent as soon as all the threads become stoppable (i.e. they can be
167 * considered stopped).
168 *
169 * @param call The BEGIN call we are servicing.
170 * @param active Place to store @c true iff we went directly to active state,
171 * @c false if we only went to beginning state
172 *
173 * @return EOK on success, EBUSY if the task is already has an active
174 * debugging session.
175 */
176errno_t udebug_begin(call_t *call, bool *active)
177{
178 LOG("Debugging task %" PRIu64, TASK->taskid);
179
180 mutex_lock(&TASK->udebug.lock);
181
182 if (TASK->udebug.dt_state != UDEBUG_TS_INACTIVE) {
183 mutex_unlock(&TASK->udebug.lock);
184 return EBUSY;
185 }
186
187 TASK->udebug.dt_state = UDEBUG_TS_BEGINNING;
188 TASK->udebug.begin_call = call;
189 TASK->udebug.debugger = call->sender;
190
191 if (TASK->udebug.not_stoppable_count == 0) {
192 TASK->udebug.dt_state = UDEBUG_TS_ACTIVE;
193 TASK->udebug.begin_call = NULL;
194 *active = true; /* directly to active state */
195 } else
196 *active = false; /* only in beginning state */
197
198 /* Set udebug.active on all of the task's userspace threads. */
199
200 list_foreach(TASK->threads, th_link, thread_t, thread) {
201 mutex_lock(&thread->udebug.lock);
202 if (thread->uspace) {
203 thread->udebug.active = true;
204 mutex_unlock(&thread->udebug.lock);
205 condvar_broadcast(&thread->udebug.active_cv);
206 } else
207 mutex_unlock(&thread->udebug.lock);
208 }
209
210 mutex_unlock(&TASK->udebug.lock);
211 return EOK;
212}
213
214/** Finish debugging the current task.
215 *
216 * Closes the debugging session for the current task.
217 *
218 * @return Zero on success or an error code.
219 *
220 */
221errno_t udebug_end(void)
222{
223 LOG("Task %" PRIu64, TASK->taskid);
224
225 mutex_lock(&TASK->udebug.lock);
226 errno_t rc = udebug_task_cleanup(TASK);
227 mutex_unlock(&TASK->udebug.lock);
228
229 return rc;
230}
231
232/** Set the event mask.
233 *
234 * Sets the event mask that determines which events are enabled.
235 *
236 * @param mask Or combination of events that should be enabled.
237 *
238 * @return Zero on success or an error code.
239 *
240 */
241errno_t udebug_set_evmask(udebug_evmask_t mask)
242{
243 LOG("mask = 0x%x", mask);
244
245 mutex_lock(&TASK->udebug.lock);
246
247 if (TASK->udebug.dt_state != UDEBUG_TS_ACTIVE) {
248 mutex_unlock(&TASK->udebug.lock);
249 return EINVAL;
250 }
251
252 TASK->udebug.evmask = mask;
253 mutex_unlock(&TASK->udebug.lock);
254
255 return EOK;
256}
257
258/** Give thread GO.
259 *
260 * Upon recieving a go message, the thread is given GO. Being GO
261 * means the thread is allowed to execute userspace code (until
262 * a debugging event or STOP occurs, at which point the thread loses GO.
263 *
264 * @param thread The thread to operate on (unlocked and need not be valid).
265 * @param call The GO call that we are servicing.
266 *
267 */
268errno_t udebug_go(thread_t *thread, call_t *call)
269{
270 /* On success, this will lock thread->udebug.lock. */
271 errno_t rc = _thread_op_begin(thread, false);
272 if (rc != EOK)
273 return rc;
274
275 thread->udebug.go_call = call;
276 thread->udebug.go = true;
277 thread->udebug.cur_event = 0; /* none */
278
279 /*
280 * Neither thread's lock nor threads_lock may be held during wakeup.
281 *
282 */
283 waitq_wakeup(&thread->udebug.go_wq, WAKEUP_FIRST);
284
285 _thread_op_end(thread);
286
287 return EOK;
288}
289
290/** Stop a thread (i.e. take its GO away)
291 *
292 * Generates a STOP event as soon as the thread becomes stoppable (i.e.
293 * can be considered stopped).
294 *
295 * @param thread The thread to operate on (unlocked and need not be valid).
296 * @param call The GO call that we are servicing.
297 *
298 */
299errno_t udebug_stop(thread_t *thread, call_t *call)
300{
301 LOG("udebug_stop()");
302
303 /*
304 * On success, this will lock thread->udebug.lock. Note that this
305 * makes sure the thread is not stopped.
306 *
307 */
308 errno_t rc = _thread_op_begin(thread, true);
309 if (rc != EOK)
310 return rc;
311
312 /* Take GO away from the thread. */
313 thread->udebug.go = false;
314
315 if (thread->udebug.stoppable != true) {
316 /* Answer will be sent when the thread becomes stoppable. */
317 _thread_op_end(thread);
318 return EOK;
319 }
320
321 /*
322 * Answer GO call.
323 *
324 */
325
326 /* Make sure nobody takes this call away from us. */
327 call = thread->udebug.go_call;
328 thread->udebug.go_call = NULL;
329
330 ipc_set_retval(&call->data, 0);
331 ipc_set_arg1(&call->data, UDEBUG_EVENT_STOP);
332
333 THREAD->udebug.cur_event = UDEBUG_EVENT_STOP;
334
335 _thread_op_end(thread);
336
337 mutex_lock(&TASK->udebug.lock);
338 ipc_answer(&TASK->answerbox, call);
339 mutex_unlock(&TASK->udebug.lock);
340
341 return EOK;
342}
343
344/** Read the list of userspace threads in the current task.
345 *
346 * The list takes the form of a sequence of thread hashes (i.e. the pointers
347 * to thread structures). A buffer of size @a buf_size is allocated and
348 * a pointer to it written to @a buffer. The sequence of hashes is written
349 * into this buffer.
350 *
351 * If the sequence is longer than @a buf_size bytes, only as much hashes
352 * as can fit are copied. The number of bytes copied is stored in @a stored.
353 * The total number of thread bytes that could have been saved had there been
354 * enough space is stored in @a needed.
355 *
356 * The rationale for having @a buf_size is that this function is only
357 * used for servicing the THREAD_READ message, which always specifies
358 * a maximum size for the userspace buffer.
359 *
360 * @param buffer The buffer for storing thread hashes.
361 * @param buf_size Buffer size in bytes.
362 * @param stored The actual number of bytes copied will be stored here.
363 * @param needed Total number of hashes that could have been saved.
364 *
365 */
366errno_t udebug_thread_read(void **buffer, size_t buf_size, size_t *stored,
367 size_t *needed)
368{
369 LOG("udebug_thread_read()");
370
371 /* Allocate a buffer to hold thread IDs */
372 sysarg_t *id_buffer = malloc(buf_size + 1);
373 if (!id_buffer)
374 return ENOMEM;
375
376 mutex_lock(&TASK->udebug.lock);
377
378 /* Verify task state */
379 if (TASK->udebug.dt_state != UDEBUG_TS_ACTIVE) {
380 mutex_unlock(&TASK->udebug.lock);
381 free(id_buffer);
382 return EINVAL;
383 }
384
385 irq_spinlock_lock(&TASK->lock, true);
386
387 /* Copy down the thread IDs */
388
389 size_t max_ids = buf_size / sizeof(sysarg_t);
390 size_t copied_ids = 0;
391 size_t extra_ids = 0;
392
393 /* FIXME: make sure the thread isn't past debug shutdown... */
394 list_foreach(TASK->threads, th_link, thread_t, thread) {
395 irq_spinlock_lock(&thread->lock, false);
396 bool uspace = thread->uspace;
397 irq_spinlock_unlock(&thread->lock, false);
398
399 /* Not interested in kernel threads. */
400 if (!uspace)
401 continue;
402
403 if (copied_ids < max_ids) {
404 /* Using thread struct pointer as identification hash */
405 id_buffer[copied_ids++] = (sysarg_t) thread;
406 } else
407 extra_ids++;
408 }
409
410 irq_spinlock_unlock(&TASK->lock, true);
411
412 mutex_unlock(&TASK->udebug.lock);
413
414 *buffer = id_buffer;
415 *stored = copied_ids * sizeof(sysarg_t);
416 *needed = (copied_ids + extra_ids) * sizeof(sysarg_t);
417
418 return EOK;
419}
420
421/** Read task name.
422 *
423 * Returns task name as non-terminated string in a newly allocated buffer.
424 * Also returns the size of the data.
425 *
426 * @param data Place to store pointer to newly allocated block.
427 * @param data_size Place to store size of the data.
428 *
429 * @return EOK.
430 *
431 */
432errno_t udebug_name_read(char **data, size_t *data_size)
433{
434 size_t name_size = str_size(TASK->name) + 1;
435
436 *data = malloc(name_size);
437 if (!*data)
438 return ENOMEM;
439 *data_size = name_size;
440
441 memcpy(*data, TASK->name, name_size);
442
443 return EOK;
444}
445
446/** Read the arguments of a system call.
447 *
448 * The arguments of the system call being being executed are copied
449 * to an allocated buffer and a pointer to it is written to @a buffer.
450 * The size of the buffer is exactly such that it can hold the maximum number
451 * of system-call arguments.
452 *
453 * Unless the thread is currently blocked in a SYSCALL_B or SYSCALL_E event,
454 * this function will fail with an EINVAL error code.
455 *
456 * @param thread Thread where call arguments are to be read.
457 * @param buffer Place to store pointer to new buffer.
458 *
459 * @return EOK on success, ENOENT if @a t is invalid, EINVAL
460 * if thread state is not valid for this operation.
461 *
462 */
463errno_t udebug_args_read(thread_t *thread, void **buffer)
464{
465 /* On success, this will lock t->udebug.lock. */
466 errno_t rc = _thread_op_begin(thread, false);
467 if (rc != EOK)
468 return rc;
469
470 /* Additionally we need to verify that we are inside a syscall. */
471 if ((thread->udebug.cur_event != UDEBUG_EVENT_SYSCALL_B) &&
472 (thread->udebug.cur_event != UDEBUG_EVENT_SYSCALL_E)) {
473 _thread_op_end(thread);
474 return EINVAL;
475 }
476
477 /* Prepare a buffer to hold the arguments. */
478 sysarg_t *arg_buffer = malloc(6 * sizeof(sysarg_t));
479 if (!arg_buffer) {
480 _thread_op_end(thread);
481 return ENOMEM;
482 }
483
484 /* Copy to a local buffer before releasing the lock. */
485 memcpy(arg_buffer, thread->udebug.syscall_args, 6 * sizeof(sysarg_t));
486
487 _thread_op_end(thread);
488
489 *buffer = arg_buffer;
490 return EOK;
491}
492
493/** Read the register state of the thread.
494 *
495 * The contents of the thread's istate structure are copied to a newly
496 * allocated buffer and a pointer to it is written to @a buffer. The size of
497 * the buffer will be sizeof(istate_t).
498 *
499 * Currently register state cannot be read if the thread is inside a system
500 * call (as opposed to an exception). This is an implementation limit.
501 *
502 * @param thread Thread whose state is to be read.
503 * @param buffer Place to store pointer to new buffer.
504 *
505 * @return EOK on success, ENOENT if @a t is invalid, EINVAL
506 * if thread is not in valid state, EBUSY if istate
507 * is not available.
508 *
509 */
510errno_t udebug_regs_read(thread_t *thread, void **buffer)
511{
512 /* On success, this will lock t->udebug.lock */
513 errno_t rc = _thread_op_begin(thread, false);
514 if (rc != EOK)
515 return rc;
516
517 istate_t *state = thread->udebug.uspace_state;
518 if (state == NULL) {
519 _thread_op_end(thread);
520 return EBUSY;
521 }
522
523 /* Prepare a buffer to hold the data. */
524 istate_t *state_buf = malloc(sizeof(istate_t));
525 if (!state_buf) {
526 _thread_op_end(thread);
527 return ENOMEM;
528 }
529
530 /* Copy to the allocated buffer */
531 memcpy(state_buf, state, sizeof(istate_t));
532
533 _thread_op_end(thread);
534
535 *buffer = (void *) state_buf;
536 return EOK;
537}
538
539/** Read the memory of the debugged task.
540 *
541 * Reads @a n bytes from the address space of the debugged task, starting
542 * from @a uspace_addr. The bytes are copied into an allocated buffer
543 * and a pointer to it is written into @a buffer.
544 *
545 * @param uspace_addr Address from where to start reading.
546 * @param n Number of bytes to read.
547 * @param buffer For storing a pointer to the allocated buffer.
548 *
549 */
550errno_t udebug_mem_read(uspace_addr_t uspace_addr, size_t n, void **buffer)
551{
552 /* Verify task state */
553 mutex_lock(&TASK->udebug.lock);
554
555 if (TASK->udebug.dt_state != UDEBUG_TS_ACTIVE) {
556 mutex_unlock(&TASK->udebug.lock);
557 return EBUSY;
558 }
559
560 void *data_buffer = malloc(n);
561 if (!data_buffer) {
562 mutex_unlock(&TASK->udebug.lock);
563 return ENOMEM;
564 }
565
566 /*
567 * NOTE: this is not strictly from a syscall... but that shouldn't
568 * be a problem
569 *
570 */
571 errno_t rc = copy_from_uspace(data_buffer, uspace_addr, n);
572 mutex_unlock(&TASK->udebug.lock);
573
574 if (rc != EOK)
575 return rc;
576
577 *buffer = data_buffer;
578 return EOK;
579}
580
581/** @}
582 */
Note: See TracBrowser for help on using the repository browser.