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