]>
Commit | Line | Data |
---|---|---|
1c79356b A |
1 | /* |
2 | * Copyright (c) 2000 Apple Computer, Inc. All rights reserved. | |
3 | * | |
4 | * @APPLE_LICENSE_HEADER_START@ | |
5 | * | |
e5568f75 A |
6 | * The contents of this file constitute Original Code as defined in and |
7 | * are subject to the Apple Public Source License Version 1.1 (the | |
8 | * "License"). You may not use this file except in compliance with the | |
9 | * License. Please obtain a copy of the License at | |
10 | * http://www.apple.com/publicsource and read it before using this file. | |
1c79356b | 11 | * |
e5568f75 A |
12 | * This Original Code and all software distributed under the License are |
13 | * distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY KIND, EITHER | |
1c79356b A |
14 | * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES, |
15 | * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY, | |
e5568f75 A |
16 | * FITNESS FOR A PARTICULAR PURPOSE OR NON-INFRINGEMENT. Please see the |
17 | * License for the specific language governing rights and limitations | |
18 | * under the License. | |
1c79356b A |
19 | * |
20 | * @APPLE_LICENSE_HEADER_END@ | |
21 | */ | |
22 | /* | |
23 | * @OSF_COPYRIGHT@ | |
24 | */ | |
25 | /* | |
26 | * Mach Operating System | |
27 | * Copyright (c) 1991,1990 Carnegie Mellon University | |
28 | * All Rights Reserved. | |
29 | * | |
30 | * Permission to use, copy, modify and distribute this software and its | |
31 | * documentation is hereby granted, provided that both the copyright | |
32 | * notice and this permission notice appear in all copies of the | |
33 | * software, derivative works or modified versions, and any portions | |
34 | * thereof, and that both notices appear in supporting documentation. | |
35 | * | |
36 | * CARNEGIE MELLON ALLOWS FREE USE OF THIS SOFTWARE IN ITS "AS IS" | |
37 | * CONDITION. CARNEGIE MELLON DISCLAIMS ANY LIABILITY OF ANY KIND FOR | |
38 | * ANY DAMAGES WHATSOEVER RESULTING FROM THE USE OF THIS SOFTWARE. | |
39 | * | |
40 | * Carnegie Mellon requests users of this software to return to | |
41 | * | |
42 | * Software Distribution Coordinator or Software.Distribution@CS.CMU.EDU | |
43 | * School of Computer Science | |
44 | * Carnegie Mellon University | |
45 | * Pittsburgh PA 15213-3890 | |
46 | * | |
47 | * any improvements or extensions that they make and grant Carnegie Mellon | |
48 | * the rights to redistribute these changes. | |
49 | */ | |
50 | /* | |
51 | */ | |
52 | ||
53 | /* | |
54 | * Interface to new debugger. | |
55 | */ | |
56 | #include <cpus.h> | |
57 | #include <platforms.h> | |
58 | #include <time_stamp.h> | |
59 | #include <mach_mp_debug.h> | |
60 | #include <mach_ldebug.h> | |
61 | #include <kern/spl.h> | |
62 | #include <kern/cpu_number.h> | |
63 | #include <kern/kern_types.h> | |
64 | #include <kern/misc_protos.h> | |
65 | #include <vm/pmap.h> | |
66 | ||
67 | #include <i386/thread.h> | |
68 | #include <i386/db_machdep.h> | |
69 | #include <i386/seg.h> | |
70 | #include <i386/trap.h> | |
71 | #include <i386/setjmp.h> | |
72 | #include <i386/pmap.h> | |
73 | #include <i386/misc_protos.h> | |
74 | ||
75 | #include <mach/vm_param.h> | |
76 | #include <vm/vm_map.h> | |
77 | #include <kern/thread.h> | |
78 | #include <kern/task.h> | |
79 | ||
80 | #include <ddb/db_command.h> | |
81 | #include <ddb/db_task_thread.h> | |
82 | #include <ddb/db_run.h> | |
83 | #include <ddb/db_trap.h> | |
84 | #include <ddb/db_output.h> | |
85 | #include <ddb/db_access.h> | |
86 | #include <ddb/db_sym.h> | |
87 | #include <ddb/db_break.h> | |
88 | #include <ddb/db_watch.h> | |
89 | ||
90 | int db_active = 0; | |
91 | int db_pass_thru[NCPUS]; | |
92 | struct i386_saved_state *i386_last_saved_statep; | |
93 | struct i386_saved_state i386_nested_saved_state; | |
94 | unsigned i386_last_kdb_sp; | |
95 | ||
96 | vm_offset_t db_stacks[NCPUS]; | |
97 | ||
98 | extern thread_act_t db_default_act; | |
99 | ||
100 | #if MACH_MP_DEBUG | |
101 | extern int masked_state_cnt[]; | |
102 | #endif /* MACH_MP_DEBUG */ | |
103 | ||
104 | /* | |
105 | * Enter KDB through a keyboard trap. | |
106 | * We show the registers as of the keyboard interrupt | |
107 | * instead of those at its call to KDB. | |
108 | */ | |
109 | struct int_regs { | |
110 | int gs; | |
111 | int fs; | |
112 | int edi; | |
113 | int esi; | |
114 | int ebp; | |
115 | int ebx; | |
116 | struct i386_interrupt_state *is; | |
117 | }; | |
118 | ||
119 | extern char * trap_type[]; | |
120 | extern int TRAP_TYPES; | |
121 | ||
122 | /* Forward */ | |
123 | ||
124 | extern void kdbprinttrap( | |
125 | int type, | |
126 | int code, | |
127 | int *pc, | |
128 | int sp); | |
129 | extern void kdb_kentry( | |
130 | struct int_regs *int_regs); | |
131 | extern int db_user_to_kernel_address( | |
132 | task_t task, | |
133 | vm_offset_t addr, | |
134 | unsigned *kaddr, | |
135 | int flag); | |
136 | extern void db_write_bytes_user_space( | |
137 | vm_offset_t addr, | |
138 | int size, | |
139 | char *data, | |
140 | task_t task); | |
141 | extern int db_search_null( | |
142 | task_t task, | |
143 | unsigned *svaddr, | |
144 | unsigned evaddr, | |
145 | unsigned *skaddr, | |
146 | int flag); | |
147 | extern int kdb_enter(int); | |
148 | extern void kdb_leave(void); | |
149 | extern void lock_kdb(void); | |
150 | extern void unlock_kdb(void); | |
151 | ||
152 | /* | |
153 | * kdb_trap - field a TRACE or BPT trap | |
154 | */ | |
155 | ||
156 | ||
157 | extern jmp_buf_t *db_recover; | |
158 | spl_t saved_ipl[NCPUS]; /* just to know what IPL was before trap */ | |
159 | struct i386_saved_state *saved_state[NCPUS]; | |
160 | ||
161 | /* | |
162 | * Translate the state saved in a task state segment into an | |
163 | * exception frame. Since we "know" we always want the state | |
164 | * in a ktss, we hard-wire that in, rather than indexing the gdt | |
165 | * with tss_sel to derive a pointer to the desired tss. | |
166 | */ | |
167 | void | |
168 | db_tss_to_frame( | |
169 | int tss_sel, | |
170 | struct i386_saved_state *regs) | |
171 | { | |
172 | extern struct i386_tss ktss; | |
173 | int mycpu = cpu_number(); | |
174 | struct i386_tss *tss; | |
175 | ||
176 | #if NCPUS == 1 | |
177 | tss = &ktss; /* XXX */ | |
178 | #else /* NCPUS > 1 */ | |
179 | tss = mp_ktss[mycpu]; /* XXX */ | |
180 | #endif /* NCPUS > 1 */ | |
181 | ||
182 | /* | |
183 | * ddb will overwrite whatever's in esp, so put esp0 elsewhere, too. | |
184 | */ | |
185 | regs->esp = tss->esp0; | |
186 | regs->efl = tss->eflags; | |
187 | regs->eip = tss->eip; | |
188 | regs->trapno = tss->ss0; /* XXX */ | |
189 | regs->err = tss->esp0; /* XXX */ | |
190 | regs->eax = tss->eax; | |
191 | regs->ecx = tss->ecx; | |
192 | regs->edx = tss->edx; | |
193 | regs->ebx = tss->ebx; | |
194 | regs->uesp = tss->esp; | |
195 | regs->ebp = tss->ebp; | |
196 | regs->esi = tss->esi; | |
197 | regs->edi = tss->edi; | |
198 | regs->es = tss->es; | |
199 | regs->ss = tss->ss; | |
200 | regs->cs = tss->cs; | |
201 | regs->ds = tss->ds; | |
202 | regs->fs = tss->fs; | |
203 | regs->gs = tss->gs; | |
204 | } | |
205 | ||
206 | /* | |
207 | * Compose a call to the debugger from the saved state in regs. (No | |
208 | * reason not to do this in C.) | |
209 | */ | |
210 | boolean_t | |
211 | db_trap_from_asm( | |
212 | struct i386_saved_state *regs) | |
213 | { | |
214 | int code; | |
215 | int type; | |
216 | ||
217 | type = regs->trapno; | |
218 | code = regs->err; | |
219 | return (kdb_trap(type, code, regs)); | |
220 | } | |
221 | ||
222 | int | |
223 | kdb_trap( | |
224 | int type, | |
225 | int code, | |
226 | struct i386_saved_state *regs) | |
227 | { | |
228 | extern char etext; | |
229 | boolean_t trap_from_user; | |
230 | spl_t s = splhigh(); | |
231 | ||
232 | switch (type) { | |
233 | case T_DEBUG: /* single_step */ | |
234 | { | |
235 | extern int dr_addr[]; | |
236 | int addr; | |
237 | int status = dr6(); | |
238 | ||
239 | if (status & 0xf) { /* hmm hdw break */ | |
240 | addr = status & 0x8 ? dr_addr[3] : | |
241 | status & 0x4 ? dr_addr[2] : | |
242 | status & 0x2 ? dr_addr[1] : | |
243 | dr_addr[0]; | |
244 | regs->efl |= EFL_RF; | |
245 | db_single_step_cmd(addr, 0, 1, "p"); | |
246 | } | |
247 | } | |
248 | case T_INT3: /* breakpoint */ | |
249 | case T_WATCHPOINT: /* watchpoint */ | |
250 | case -1: /* keyboard interrupt */ | |
251 | break; | |
252 | ||
253 | default: | |
254 | if (db_recover) { | |
255 | i386_nested_saved_state = *regs; | |
256 | db_printf("Caught "); | |
257 | if (type < 0 || type > TRAP_TYPES) | |
258 | db_printf("type %d", type); | |
259 | else | |
260 | db_printf("%s", trap_type[type]); | |
261 | db_printf(" trap, code = %x, pc = %x\n", | |
262 | code, regs->eip); | |
263 | splx(s); | |
264 | db_error(""); | |
265 | /*NOTREACHED*/ | |
266 | } | |
267 | kdbprinttrap(type, code, (int *)®s->eip, regs->uesp); | |
268 | } | |
269 | ||
270 | #if NCPUS > 1 | |
271 | disable_preemption(); | |
272 | #endif /* NCPUS > 1 */ | |
273 | ||
274 | saved_ipl[cpu_number()] = s; | |
275 | saved_state[cpu_number()] = regs; | |
276 | ||
277 | i386_last_saved_statep = regs; | |
278 | i386_last_kdb_sp = (unsigned) &type; | |
279 | ||
280 | #if NCPUS > 1 | |
281 | if (!kdb_enter(regs->eip)) | |
282 | goto kdb_exit; | |
283 | #endif /* NCPUS > 1 */ | |
284 | ||
285 | /* Should switch to kdb's own stack here. */ | |
286 | ||
287 | if (!IS_USER_TRAP(regs, &etext)) { | |
288 | bzero((char *)&ddb_regs, sizeof (ddb_regs)); | |
289 | *(struct i386_saved_state_from_kernel *)&ddb_regs = | |
290 | *(struct i386_saved_state_from_kernel *)regs; | |
291 | trap_from_user = FALSE; | |
292 | } | |
293 | else { | |
294 | ddb_regs = *regs; | |
295 | trap_from_user = TRUE; | |
296 | } | |
297 | if (!trap_from_user) { | |
298 | /* | |
299 | * Kernel mode - esp and ss not saved | |
300 | */ | |
301 | ddb_regs.uesp = (int)®s->uesp; /* kernel stack pointer */ | |
302 | ddb_regs.ss = KERNEL_DS; | |
303 | } | |
304 | ||
305 | db_active++; | |
306 | db_task_trap(type, code, trap_from_user); | |
307 | db_active--; | |
308 | ||
309 | regs->eip = ddb_regs.eip; | |
310 | regs->efl = ddb_regs.efl; | |
311 | regs->eax = ddb_regs.eax; | |
312 | regs->ecx = ddb_regs.ecx; | |
313 | regs->edx = ddb_regs.edx; | |
314 | regs->ebx = ddb_regs.ebx; | |
315 | if (trap_from_user) { | |
316 | /* | |
317 | * user mode - saved esp and ss valid | |
318 | */ | |
319 | regs->uesp = ddb_regs.uesp; /* user stack pointer */ | |
320 | regs->ss = ddb_regs.ss & 0xffff; /* user stack segment */ | |
321 | } | |
322 | regs->ebp = ddb_regs.ebp; | |
323 | regs->esi = ddb_regs.esi; | |
324 | regs->edi = ddb_regs.edi; | |
325 | regs->es = ddb_regs.es & 0xffff; | |
326 | regs->cs = ddb_regs.cs & 0xffff; | |
327 | regs->ds = ddb_regs.ds & 0xffff; | |
328 | regs->fs = ddb_regs.fs & 0xffff; | |
329 | regs->gs = ddb_regs.gs & 0xffff; | |
330 | ||
331 | if ((type == T_INT3) && | |
332 | (db_get_task_value(regs->eip, | |
333 | BKPT_SIZE, | |
334 | FALSE, | |
335 | db_target_space(current_act(), | |
336 | trap_from_user)) | |
337 | == BKPT_INST)) | |
338 | regs->eip += BKPT_SIZE; | |
339 | ||
340 | #if NCPUS > 1 | |
341 | kdb_exit: | |
342 | kdb_leave(); | |
343 | #endif /* NCPUS > 1 */ | |
344 | ||
345 | saved_state[cpu_number()] = 0; | |
346 | ||
347 | #if MACH_MP_DEBUG | |
348 | masked_state_cnt[cpu_number()] = 0; | |
349 | #endif /* MACH_MP_DEBUG */ | |
350 | ||
351 | #if NCPUS > 1 | |
352 | enable_preemption(); | |
353 | #endif /* NCPUS > 1 */ | |
354 | ||
355 | splx(s); | |
356 | ||
357 | /* Allow continue to upper layers of exception handling if | |
358 | * trap was not a debugging trap. | |
359 | */ | |
360 | ||
361 | if (trap_from_user && type != T_DEBUG && type != T_INT3 | |
362 | && type != T_WATCHPOINT) | |
363 | return 0; | |
364 | else | |
365 | return (1); | |
366 | } | |
367 | ||
368 | /* | |
369 | * Enter KDB through a keyboard trap. | |
370 | * We show the registers as of the keyboard interrupt | |
371 | * instead of those at its call to KDB. | |
372 | */ | |
373 | ||
374 | spl_t kdb_oldspl; | |
375 | ||
376 | void | |
377 | kdb_kentry( | |
378 | struct int_regs *int_regs) | |
379 | { | |
380 | extern char etext; | |
381 | boolean_t trap_from_user; | |
382 | struct i386_interrupt_state *is = int_regs->is; | |
383 | struct i386_saved_state regs; | |
384 | spl_t s; | |
385 | ||
386 | s = splhigh(); | |
387 | kdb_oldspl = s; | |
388 | ||
389 | if (IS_USER_TRAP(is, &etext)) | |
390 | { | |
391 | regs.uesp = ((int *)(is+1))[0]; | |
392 | regs.ss = ((int *)(is+1))[1]; | |
393 | } | |
394 | else { | |
395 | regs.ss = KERNEL_DS; | |
396 | regs.uesp= (int)(is+1); | |
397 | } | |
398 | regs.efl = is->efl; | |
399 | regs.cs = is->cs; | |
400 | regs.eip = is->eip; | |
401 | regs.eax = is->eax; | |
402 | regs.ecx = is->ecx; | |
403 | regs.edx = is->edx; | |
404 | regs.ebx = int_regs->ebx; | |
405 | regs.ebp = int_regs->ebp; | |
406 | regs.esi = int_regs->esi; | |
407 | regs.edi = int_regs->edi; | |
408 | regs.ds = is->ds; | |
409 | regs.es = is->es; | |
410 | regs.fs = int_regs->fs; | |
411 | regs.gs = int_regs->gs; | |
412 | ||
413 | #if NCPUS > 1 | |
414 | disable_preemption(); | |
415 | #endif /* NCPUS > 1 */ | |
416 | ||
417 | saved_state[cpu_number()] = ®s; | |
418 | ||
419 | #if NCPUS > 1 | |
420 | if (!kdb_enter(regs.eip)) | |
421 | goto kdb_exit; | |
422 | #endif /* NCPUS > 1 */ | |
423 | ||
424 | bcopy((char *)®s, (char *)&ddb_regs, sizeof (ddb_regs)); | |
425 | trap_from_user = IS_USER_TRAP(&ddb_regs, &etext); | |
426 | ||
427 | db_active++; | |
428 | db_task_trap(-1, 0, trap_from_user); | |
429 | db_active--; | |
430 | ||
431 | if (trap_from_user) { | |
432 | ((int *)(is+1))[0] = ddb_regs.uesp; | |
433 | ((int *)(is+1))[1] = ddb_regs.ss & 0xffff; | |
434 | } | |
435 | is->efl = ddb_regs.efl; | |
436 | is->cs = ddb_regs.cs & 0xffff; | |
437 | is->eip = ddb_regs.eip; | |
438 | is->eax = ddb_regs.eax; | |
439 | is->ecx = ddb_regs.ecx; | |
440 | is->edx = ddb_regs.edx; | |
441 | int_regs->ebx = ddb_regs.ebx; | |
442 | int_regs->ebp = ddb_regs.ebp; | |
443 | int_regs->esi = ddb_regs.esi; | |
444 | int_regs->edi = ddb_regs.edi; | |
445 | is->ds = ddb_regs.ds & 0xffff; | |
446 | is->es = ddb_regs.es & 0xffff; | |
447 | int_regs->fs = ddb_regs.fs & 0xffff; | |
448 | int_regs->gs = ddb_regs.gs & 0xffff; | |
449 | ||
450 | #if NCPUS > 1 | |
451 | kdb_exit: | |
452 | kdb_leave(); | |
453 | #endif /* NCPUS > 1 */ | |
454 | saved_state[cpu_number()] = 0; | |
455 | ||
456 | #if NCPUS > 1 | |
457 | enable_preemption(); | |
458 | #endif /* NCPUS > 1 */ | |
459 | ||
460 | splx(s); | |
461 | } | |
462 | ||
463 | /* | |
464 | * Print trap reason. | |
465 | */ | |
466 | ||
467 | void | |
468 | kdbprinttrap( | |
469 | int type, | |
470 | int code, | |
471 | int *pc, | |
472 | int sp) | |
473 | { | |
474 | printf("kernel: "); | |
475 | if (type < 0 || type > TRAP_TYPES) | |
476 | db_printf("type %d", type); | |
477 | else | |
478 | db_printf("%s", trap_type[type]); | |
479 | db_printf(" trap, code=%x eip@%x = %x esp=%x\n", | |
480 | code, pc, *(int *)pc, sp); | |
481 | db_run_mode = STEP_CONTINUE; | |
482 | } | |
483 | ||
484 | int | |
485 | db_user_to_kernel_address( | |
486 | task_t task, | |
487 | vm_offset_t addr, | |
488 | unsigned *kaddr, | |
489 | int flag) | |
490 | { | |
491 | register pt_entry_t *ptp; | |
492 | ||
493 | ptp = pmap_pte(task->map->pmap, addr); | |
494 | if (ptp == PT_ENTRY_NULL || (*ptp & INTEL_PTE_VALID) == 0) { | |
495 | if (flag) { | |
496 | db_printf("\nno memory is assigned to address %08x\n", addr); | |
497 | db_error(0); | |
498 | /* NOTREACHED */ | |
499 | } | |
500 | return(-1); | |
501 | } | |
502 | *kaddr = (unsigned)ptetokv(*ptp) + (addr & (INTEL_PGBYTES-1)); | |
503 | return(0); | |
504 | } | |
505 | ||
506 | /* | |
507 | * Read bytes from kernel address space for debugger. | |
508 | */ | |
509 | ||
510 | void | |
511 | db_read_bytes( | |
512 | vm_offset_t addr, | |
513 | int size, | |
514 | char *data, | |
515 | task_t task) | |
516 | { | |
517 | register char *src; | |
518 | register int n; | |
519 | unsigned kern_addr; | |
520 | ||
521 | src = (char *)addr; | |
522 | if (task == kernel_task || task == TASK_NULL) { | |
523 | while (--size >= 0) { | |
524 | if (addr++ > VM_MAX_KERNEL_ADDRESS) { | |
525 | db_printf("\nbad address %x\n", addr); | |
526 | db_error(0); | |
527 | /* NOTREACHED */ | |
528 | } | |
529 | *data++ = *src++; | |
530 | } | |
531 | return; | |
532 | } | |
533 | while (size > 0) { | |
534 | if (db_user_to_kernel_address(task, addr, &kern_addr, 1) < 0) | |
535 | return; | |
536 | src = (char *)kern_addr; | |
537 | n = intel_trunc_page(addr+INTEL_PGBYTES) - addr; | |
538 | if (n > size) | |
539 | n = size; | |
540 | size -= n; | |
541 | addr += n; | |
542 | while (--n >= 0) | |
543 | *data++ = *src++; | |
544 | } | |
545 | } | |
546 | ||
547 | /* | |
548 | * Write bytes to kernel address space for debugger. | |
549 | */ | |
550 | ||
551 | void | |
552 | db_write_bytes( | |
553 | vm_offset_t addr, | |
554 | int size, | |
555 | char *data, | |
556 | task_t task) | |
557 | { | |
558 | register char *dst; | |
559 | ||
560 | register pt_entry_t *ptep0 = 0; | |
561 | pt_entry_t oldmap0 = 0; | |
562 | vm_offset_t addr1; | |
563 | register pt_entry_t *ptep1 = 0; | |
564 | pt_entry_t oldmap1 = 0; | |
565 | extern char etext; | |
566 | ||
567 | if (task && task != kernel_task) { | |
568 | db_write_bytes_user_space(addr, size, data, task); | |
569 | return; | |
570 | } | |
571 | ||
572 | ||
573 | if (addr >= VM_MIN_KERNEL_LOADED_ADDRESS) { | |
574 | db_write_bytes_user_space(addr, size, data, kernel_task); | |
575 | return; | |
576 | } | |
577 | ||
578 | if (addr >= VM_MIN_KERNEL_ADDRESS && | |
579 | addr <= (vm_offset_t)&etext) | |
580 | { | |
581 | ptep0 = pmap_pte(kernel_pmap, addr); | |
582 | oldmap0 = *ptep0; | |
583 | *ptep0 |= INTEL_PTE_WRITE; | |
584 | ||
585 | addr1 = i386_trunc_page(addr + size - 1); | |
586 | if (i386_trunc_page(addr) != addr1) { | |
587 | /* data crosses a page boundary */ | |
588 | ||
589 | ptep1 = pmap_pte(kernel_pmap, addr1); | |
590 | oldmap1 = *ptep1; | |
591 | *ptep1 |= INTEL_PTE_WRITE; | |
592 | } | |
593 | flush_tlb(); | |
594 | } | |
595 | ||
596 | dst = (char *)addr; | |
597 | ||
598 | while (--size >= 0) { | |
599 | if (addr++ > VM_MAX_KERNEL_ADDRESS) { | |
600 | db_printf("\nbad address %x\n", addr); | |
601 | db_error(0); | |
602 | /* NOTREACHED */ | |
603 | } | |
604 | *dst++ = *data++; | |
605 | } | |
606 | ||
607 | if (ptep0) { | |
608 | *ptep0 = oldmap0; | |
609 | if (ptep1) { | |
610 | *ptep1 = oldmap1; | |
611 | } | |
612 | flush_tlb(); | |
613 | } | |
614 | } | |
615 | ||
616 | void | |
617 | db_write_bytes_user_space( | |
618 | vm_offset_t addr, | |
619 | int size, | |
620 | char *data, | |
621 | task_t task) | |
622 | { | |
623 | register char *dst; | |
624 | register int n; | |
625 | unsigned kern_addr; | |
626 | ||
627 | while (size > 0) { | |
628 | if (db_user_to_kernel_address(task, addr, &kern_addr, 1) < 0) | |
629 | return; | |
630 | dst = (char *)kern_addr; | |
631 | n = intel_trunc_page(addr+INTEL_PGBYTES) - addr; | |
632 | if (n > size) | |
633 | n = size; | |
634 | size -= n; | |
635 | addr += n; | |
636 | while (--n >= 0) | |
637 | *dst++ = *data++; | |
638 | } | |
639 | } | |
640 | ||
641 | boolean_t | |
642 | db_check_access( | |
643 | vm_offset_t addr, | |
644 | int size, | |
645 | task_t task) | |
646 | { | |
647 | register n; | |
648 | unsigned kern_addr; | |
649 | ||
650 | if (task == kernel_task || task == TASK_NULL) { | |
651 | if (kernel_task == TASK_NULL) | |
652 | return(TRUE); | |
653 | task = kernel_task; | |
654 | } else if (task == TASK_NULL) { | |
655 | if (current_act() == THR_ACT_NULL) | |
656 | return(FALSE); | |
657 | task = current_act()->task; | |
658 | } | |
659 | while (size > 0) { | |
660 | if (db_user_to_kernel_address(task, addr, &kern_addr, 0) < 0) | |
661 | return(FALSE); | |
662 | n = intel_trunc_page(addr+INTEL_PGBYTES) - addr; | |
663 | if (n > size) | |
664 | n = size; | |
665 | size -= n; | |
666 | addr += n; | |
667 | } | |
668 | return(TRUE); | |
669 | } | |
670 | ||
671 | boolean_t | |
672 | db_phys_eq( | |
673 | task_t task1, | |
674 | vm_offset_t addr1, | |
675 | task_t task2, | |
676 | vm_offset_t addr2) | |
677 | { | |
678 | unsigned kern_addr1, kern_addr2; | |
679 | ||
680 | if ((addr1 & (INTEL_PGBYTES-1)) != (addr2 & (INTEL_PGBYTES-1))) | |
681 | return(FALSE); | |
682 | if (task1 == TASK_NULL) { | |
683 | if (current_act() == THR_ACT_NULL) | |
684 | return(FALSE); | |
685 | task1 = current_act()->task; | |
686 | } | |
687 | if (db_user_to_kernel_address(task1, addr1, &kern_addr1, 0) < 0 || | |
688 | db_user_to_kernel_address(task2, addr2, &kern_addr2, 0) < 0) | |
689 | return(FALSE); | |
690 | return(kern_addr1 == kern_addr2); | |
691 | } | |
692 | ||
693 | #define DB_USER_STACK_ADDR (VM_MIN_KERNEL_ADDRESS) | |
694 | #define DB_NAME_SEARCH_LIMIT (DB_USER_STACK_ADDR-(INTEL_PGBYTES*3)) | |
695 | ||
696 | int | |
697 | db_search_null( | |
698 | task_t task, | |
699 | unsigned *svaddr, | |
700 | unsigned evaddr, | |
701 | unsigned *skaddr, | |
702 | int flag) | |
703 | { | |
704 | register unsigned vaddr; | |
705 | register unsigned *kaddr; | |
706 | ||
707 | kaddr = (unsigned *)*skaddr; | |
708 | for (vaddr = *svaddr; vaddr > evaddr; vaddr -= sizeof(unsigned)) { | |
709 | if (vaddr % INTEL_PGBYTES == 0) { | |
710 | vaddr -= sizeof(unsigned); | |
711 | if (db_user_to_kernel_address(task, vaddr, skaddr, 0) < 0) | |
712 | return(-1); | |
713 | kaddr = (unsigned *)*skaddr; | |
714 | } else { | |
715 | vaddr -= sizeof(unsigned); | |
716 | kaddr--; | |
717 | } | |
718 | if ((*kaddr == 0) ^ (flag == 0)) { | |
719 | *svaddr = vaddr; | |
720 | *skaddr = (unsigned)kaddr; | |
721 | return(0); | |
722 | } | |
723 | } | |
724 | return(-1); | |
725 | } | |
726 | ||
727 | void | |
728 | db_task_name( | |
729 | task_t task) | |
730 | { | |
731 | register char *p; | |
732 | register n; | |
733 | unsigned vaddr, kaddr; | |
734 | ||
735 | vaddr = DB_USER_STACK_ADDR; | |
736 | kaddr = 0; | |
737 | ||
738 | /* | |
739 | * skip nulls at the end | |
740 | */ | |
741 | if (db_search_null(task, &vaddr, DB_NAME_SEARCH_LIMIT, &kaddr, 0) < 0) { | |
742 | db_printf(DB_NULL_TASK_NAME); | |
743 | return; | |
744 | } | |
745 | /* | |
746 | * search start of args | |
747 | */ | |
748 | if (db_search_null(task, &vaddr, DB_NAME_SEARCH_LIMIT, &kaddr, 1) < 0) { | |
749 | db_printf(DB_NULL_TASK_NAME); | |
750 | return; | |
751 | } | |
752 | ||
753 | n = DB_TASK_NAME_LEN-1; | |
754 | p = (char *)kaddr + sizeof(unsigned); | |
755 | for (vaddr += sizeof(int); vaddr < DB_USER_STACK_ADDR && n > 0; | |
756 | vaddr++, p++, n--) { | |
757 | if (vaddr % INTEL_PGBYTES == 0) { | |
758 | (void)db_user_to_kernel_address(task, vaddr, &kaddr, 0); | |
759 | p = (char*)kaddr; | |
760 | } | |
761 | db_printf("%c", (*p < ' ' || *p > '~')? ' ': *p); | |
762 | } | |
763 | while (n-- >= 0) /* compare with >= 0 for one more space */ | |
764 | db_printf(" "); | |
765 | } | |
766 | ||
767 | #if NCPUS == 1 | |
768 | ||
769 | void | |
770 | db_machdep_init(void) | |
771 | { | |
772 | db_stacks[0] = (vm_offset_t)(db_stack_store + | |
773 | INTSTACK_SIZE - sizeof (natural_t)); | |
774 | dbtss.esp0 = (int)(db_task_stack_store + | |
775 | INTSTACK_SIZE - sizeof (natural_t)); | |
776 | dbtss.esp = dbtss.esp0; | |
777 | dbtss.eip = (int)&db_task_start; | |
778 | } | |
779 | ||
780 | #else /* NCPUS > 1 */ | |
781 | ||
782 | /* | |
783 | * Code used to synchronize kdb among all cpus, one active at a time, switch | |
784 | * from on to another using kdb_on! #cpu or cpu #cpu | |
785 | */ | |
786 | ||
787 | decl_simple_lock_data(, kdb_lock) /* kdb lock */ | |
788 | ||
789 | #define db_simple_lock_init(l, e) hw_lock_init(&((l)->interlock)) | |
790 | #define db_simple_lock_try(l) hw_lock_try(&((l)->interlock)) | |
791 | #define db_simple_unlock(l) hw_lock_unlock(&((l)->interlock)) | |
792 | ||
793 | int kdb_cpu = -1; /* current cpu running kdb */ | |
794 | int kdb_debug = 0; | |
795 | int kdb_is_slave[NCPUS]; | |
796 | int kdb_active[NCPUS]; | |
797 | volatile unsigned int cpus_holding_bkpts; /* counter for number of cpus holding | |
798 | breakpoints (ie: cpus that did not | |
799 | insert back breakpoints) */ | |
800 | extern boolean_t db_breakpoints_inserted; | |
801 | ||
802 | void | |
803 | db_machdep_init(void) | |
804 | { | |
805 | int c; | |
806 | ||
807 | db_simple_lock_init(&kdb_lock, ETAP_MISC_KDB); | |
808 | for (c = 0; c < NCPUS; ++c) { | |
809 | db_stacks[c] = (vm_offset_t) (db_stack_store + | |
810 | (INTSTACK_SIZE * (c + 1)) - sizeof (natural_t)); | |
811 | if (c == master_cpu) { | |
812 | dbtss.esp0 = (int)(db_task_stack_store + | |
813 | (INTSTACK_SIZE * (c + 1)) - sizeof (natural_t)); | |
814 | dbtss.esp = dbtss.esp0; | |
815 | dbtss.eip = (int)&db_task_start; | |
816 | /* | |
817 | * The TSS for the debugging task on each slave CPU | |
818 | * is set up in mp_desc_init(). | |
819 | */ | |
820 | } | |
821 | } | |
822 | } | |
823 | ||
824 | /* | |
825 | * Called when entering kdb: | |
826 | * Takes kdb lock. If if we were called remotely (slave state) we just | |
827 | * wait for kdb_cpu to be equal to cpu_number(). Otherwise enter kdb if | |
828 | * not active on another cpu. | |
829 | * If db_pass_thru[cpu_number()] > 0, then kdb can't stop now. | |
830 | */ | |
831 | ||
832 | int | |
833 | kdb_enter(int pc) | |
834 | { | |
835 | int my_cpu; | |
836 | int retval; | |
837 | ||
838 | #if NCPUS > 1 | |
839 | disable_preemption(); | |
840 | #endif /* NCPUS > 1 */ | |
841 | ||
842 | my_cpu = cpu_number(); | |
843 | ||
844 | if (db_pass_thru[my_cpu]) { | |
845 | retval = 0; | |
846 | goto kdb_exit; | |
847 | } | |
848 | ||
849 | kdb_active[my_cpu]++; | |
850 | lock_kdb(); | |
851 | ||
852 | if (kdb_debug) | |
853 | db_printf("kdb_enter: cpu %d, is_slave %d, kdb_cpu %d, run mode %d pc %x (%x) holds %d\n", | |
854 | my_cpu, kdb_is_slave[my_cpu], kdb_cpu, | |
855 | db_run_mode, pc, *(int *)pc, cpus_holding_bkpts); | |
856 | if (db_breakpoints_inserted) | |
857 | cpus_holding_bkpts++; | |
858 | if (kdb_cpu == -1 && !kdb_is_slave[my_cpu]) { | |
859 | kdb_cpu = my_cpu; | |
860 | remote_kdb(); /* stop other cpus */ | |
861 | retval = 1; | |
862 | } else if (kdb_cpu == my_cpu) | |
863 | retval = 1; | |
864 | else | |
865 | retval = 0; | |
866 | ||
867 | kdb_exit: | |
868 | #if NCPUS > 1 | |
869 | enable_preemption(); | |
870 | #endif /* NCPUS > 1 */ | |
871 | ||
872 | return (retval); | |
873 | } | |
874 | ||
875 | void | |
876 | kdb_leave(void) | |
877 | { | |
878 | int my_cpu; | |
879 | boolean_t wait = FALSE; | |
880 | ||
881 | #if NCPUS > 1 | |
882 | disable_preemption(); | |
883 | #endif /* NCPUS > 1 */ | |
884 | ||
885 | my_cpu = cpu_number(); | |
886 | ||
887 | if (db_run_mode == STEP_CONTINUE) { | |
888 | wait = TRUE; | |
889 | kdb_cpu = -1; | |
890 | } | |
891 | if (db_breakpoints_inserted) | |
892 | cpus_holding_bkpts--; | |
893 | if (kdb_is_slave[my_cpu]) | |
894 | kdb_is_slave[my_cpu]--; | |
895 | if (kdb_debug) | |
896 | db_printf("kdb_leave: cpu %d, kdb_cpu %d, run_mode %d pc %x (%x) holds %d\n", | |
897 | my_cpu, kdb_cpu, db_run_mode, | |
898 | ddb_regs.eip, *(int *)ddb_regs.eip, | |
899 | cpus_holding_bkpts); | |
900 | clear_kdb_intr(); | |
901 | unlock_kdb(); | |
902 | kdb_active[my_cpu]--; | |
903 | ||
904 | #if NCPUS > 1 | |
905 | enable_preemption(); | |
906 | #endif /* NCPUS > 1 */ | |
907 | ||
908 | if (wait) { | |
909 | while(cpus_holding_bkpts); | |
910 | } | |
911 | } | |
912 | ||
913 | void | |
914 | lock_kdb(void) | |
915 | { | |
916 | int my_cpu; | |
917 | register i; | |
918 | extern void kdb_console(void); | |
919 | ||
920 | #if NCPUS > 1 | |
921 | disable_preemption(); | |
922 | #endif /* NCPUS > 1 */ | |
923 | ||
924 | my_cpu = cpu_number(); | |
925 | ||
926 | for(;;) { | |
927 | kdb_console(); | |
928 | if (kdb_cpu != -1 && kdb_cpu != my_cpu) { | |
929 | continue; | |
930 | } | |
931 | if (db_simple_lock_try(&kdb_lock)) { | |
932 | if (kdb_cpu == -1 || kdb_cpu == my_cpu) | |
933 | break; | |
934 | db_simple_unlock(&kdb_lock); | |
935 | } | |
936 | } | |
937 | ||
938 | #if NCPUS > 1 | |
939 | enable_preemption(); | |
940 | #endif /* NCPUS > 1 */ | |
941 | } | |
942 | ||
943 | #if TIME_STAMP | |
944 | extern unsigned old_time_stamp; | |
945 | #endif /* TIME_STAMP */ | |
946 | ||
947 | void | |
948 | unlock_kdb(void) | |
949 | { | |
950 | db_simple_unlock(&kdb_lock); | |
951 | #if TIME_STAMP | |
952 | old_time_stamp = 0; | |
953 | #endif /* TIME_STAMP */ | |
954 | } | |
955 | ||
956 | ||
957 | #ifdef __STDC__ | |
958 | #define KDB_SAVE(type, name) extern type name; type name##_save = name | |
959 | #define KDB_RESTORE(name) name = name##_save | |
960 | #else /* __STDC__ */ | |
961 | #define KDB_SAVE(type, name) extern type name; type name/**/_save = name | |
962 | #define KDB_RESTORE(name) name = name/**/_save | |
963 | #endif /* __STDC__ */ | |
964 | ||
965 | #define KDB_SAVE_CTXT() \ | |
966 | KDB_SAVE(int, db_run_mode); \ | |
967 | KDB_SAVE(boolean_t, db_sstep_print); \ | |
968 | KDB_SAVE(int, db_loop_count); \ | |
969 | KDB_SAVE(int, db_call_depth); \ | |
970 | KDB_SAVE(int, db_inst_count); \ | |
971 | KDB_SAVE(int, db_last_inst_count); \ | |
972 | KDB_SAVE(int, db_load_count); \ | |
973 | KDB_SAVE(int, db_store_count); \ | |
974 | KDB_SAVE(boolean_t, db_cmd_loop_done); \ | |
975 | KDB_SAVE(jmp_buf_t *, db_recover); \ | |
976 | KDB_SAVE(db_addr_t, db_dot); \ | |
977 | KDB_SAVE(db_addr_t, db_last_addr); \ | |
978 | KDB_SAVE(db_addr_t, db_prev); \ | |
979 | KDB_SAVE(db_addr_t, db_next); \ | |
980 | KDB_SAVE(db_regs_t, ddb_regs); | |
981 | ||
982 | #define KDB_RESTORE_CTXT() \ | |
983 | KDB_RESTORE(db_run_mode); \ | |
984 | KDB_RESTORE(db_sstep_print); \ | |
985 | KDB_RESTORE(db_loop_count); \ | |
986 | KDB_RESTORE(db_call_depth); \ | |
987 | KDB_RESTORE(db_inst_count); \ | |
988 | KDB_RESTORE(db_last_inst_count); \ | |
989 | KDB_RESTORE(db_load_count); \ | |
990 | KDB_RESTORE(db_store_count); \ | |
991 | KDB_RESTORE(db_cmd_loop_done); \ | |
992 | KDB_RESTORE(db_recover); \ | |
993 | KDB_RESTORE(db_dot); \ | |
994 | KDB_RESTORE(db_last_addr); \ | |
995 | KDB_RESTORE(db_prev); \ | |
996 | KDB_RESTORE(db_next); \ | |
997 | KDB_RESTORE(ddb_regs); | |
998 | ||
999 | /* | |
1000 | * switch to another cpu | |
1001 | */ | |
1002 | ||
1003 | void | |
1004 | kdb_on( | |
1005 | int cpu) | |
1006 | { | |
1007 | KDB_SAVE_CTXT(); | |
1008 | if (cpu < 0 || cpu >= NCPUS || !kdb_active[cpu]) | |
1009 | return; | |
1010 | db_set_breakpoints(); | |
1011 | db_set_watchpoints(); | |
1012 | kdb_cpu = cpu; | |
1013 | unlock_kdb(); | |
1014 | lock_kdb(); | |
1015 | db_clear_breakpoints(); | |
1016 | db_clear_watchpoints(); | |
1017 | KDB_RESTORE_CTXT(); | |
1018 | if (kdb_cpu == -1) {/* someone continued */ | |
1019 | kdb_cpu = cpu_number(); | |
1020 | db_continue_cmd(0, 0, 0, ""); | |
1021 | } | |
1022 | } | |
1023 | ||
1024 | #endif /* NCPUS > 1 */ | |
1025 | ||
1026 | void db_reboot( | |
1027 | db_expr_t addr, | |
1028 | boolean_t have_addr, | |
1029 | db_expr_t count, | |
1030 | char *modif) | |
1031 | { | |
1032 | boolean_t reboot = TRUE; | |
1033 | char *cp, c; | |
1034 | ||
1035 | cp = modif; | |
1036 | while ((c = *cp++) != 0) { | |
1037 | if (c == 'r') /* reboot */ | |
1038 | reboot = TRUE; | |
1039 | if (c == 'h') /* halt */ | |
1040 | reboot = FALSE; | |
1041 | } | |
1042 | halt_all_cpus(reboot); | |
1043 | } |