/* * Copyright (c) 2000-2005 Apple Computer, Inc. All rights reserved. * * @APPLE_OSREFERENCE_LICENSE_HEADER_START@ * * This file contains Original Code and/or Modifications of Original Code * as defined in and that are subject to the Apple Public Source License * Version 2.0 (the 'License'). You may not use this file except in * compliance with the License. The rights granted to you under the License * may not be used to create, or enable the creation or redistribution of, * unlawful or unlicensed copies of an Apple operating system, or to * circumvent, violate, or enable the circumvention or violation of, any * terms of an Apple operating system software license agreement. * * Please obtain a copy of the License at * http://www.opensource.apple.com/apsl/ and read it before using this file. * * The Original Code and all software distributed under the License are * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES, * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY, * FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT. * Please see the License for the specific language governing rights and * limitations under the License. * * @APPLE_OSREFERENCE_LICENSE_HEADER_END@ */ /* * @OSF_COPYRIGHT@ */ /* * Mach Operating System * Copyright (c) 1991,1990 Carnegie Mellon University * All Rights Reserved. * * Permission to use, copy, modify and distribute this software and its * documentation is hereby granted, provided that both the copyright * notice and this permission notice appear in all copies of the * software, derivative works or modified versions, and any portions * thereof, and that both notices appear in supporting documentation. * * CARNEGIE MELLON ALLOWS FREE USE OF THIS SOFTWARE IN ITS "AS IS" * CONDITION. CARNEGIE MELLON DISCLAIMS ANY LIABILITY OF ANY KIND FOR * ANY DAMAGES WHATSOEVER RESULTING FROM THE USE OF THIS SOFTWARE. * * Carnegie Mellon requests users of this software to return to * * Software Distribution Coordinator or Software.Distribution@CS.CMU.EDU * School of Computer Science * Carnegie Mellon University * Pittsburgh PA 15213-3890 * * any improvements or extensions that they make and grant Carnegie Mellon * the rights to redistribute these changes. */ /* */ /* * Interface to new debugger. */ #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include int db_active = 0; x86_saved_state32_t *i386_last_saved_statep; x86_saved_state32_t i386_nested_saved_state; unsigned i386_last_kdb_sp; extern thread_t db_default_act; extern pt_entry_t *DMAP1; extern caddr_t DADDR1; #if MACH_MP_DEBUG extern int masked_state_cnt[]; #endif /* MACH_MP_DEBUG */ /* * Enter KDB through a keyboard trap. * We show the registers as of the keyboard interrupt * instead of those at its call to KDB. */ struct int_regs { int gs; int fs; int edi; int esi; int ebp; int ebx; x86_saved_state32_t *is; }; extern char * trap_type[]; extern int TRAP_TYPES; /* Forward */ extern void kdbprinttrap( int type, int code, int *pc, int sp); extern void kdb_kentry( struct int_regs *int_regs); extern int db_user_to_kernel_address( task_t task, vm_offset_t addr, unsigned *kaddr, int flag); extern void db_write_bytes_user_space( vm_offset_t addr, int size, char *data, task_t task); extern int db_search_null( task_t task, unsigned *svaddr, unsigned evaddr, unsigned *skaddr, int flag); extern int kdb_enter(int); extern void kdb_leave(void); extern void lock_kdb(void); extern void unlock_kdb(void); /* * kdb_trap - field a TRACE or BPT trap */ extern jmp_buf_t *db_recover; /* * Translate the state saved in a task state segment into an * exception frame. Since we "know" we always want the state * in a ktss, we hard-wire that in, rather than indexing the gdt * with tss_sel to derive a pointer to the desired tss. */ /* * Code used to synchronize kdb among all cpus, one active at a time, switch * from one to another using cpu #cpu */ decl_simple_lock_data(, kdb_lock) /* kdb lock */ #define db_simple_lock_init(l, e) hw_lock_init(&((l)->interlock)) #define db_simple_lock_try(l) hw_lock_try(&((l)->interlock)) #define db_simple_unlock(l) hw_lock_unlock(&((l)->interlock)) int kdb_cpu = -1; /* current cpu running kdb */ int kdb_debug = 1; volatile unsigned int cpus_holding_bkpts; /* counter for number of cpus * holding breakpoints */ extern boolean_t db_breakpoints_inserted; void db_tss_to_frame( int tss_sel, x86_saved_state32_t *regs) { extern struct i386_tss ktss; int mycpu = cpu_number(); struct i386_tss *tss; tss = cpu_datap(mycpu)->cpu_desc_index.cdi_ktss; /* XXX */ /* * ddb will overwrite whatever's in esp, so put esp0 elsewhere, too. */ regs->cr2 = tss->esp0; regs->efl = tss->eflags; regs->eip = tss->eip; regs->trapno = tss->ss0; /* XXX */ regs->err = tss->esp0; /* XXX */ regs->eax = tss->eax; regs->ecx = tss->ecx; regs->edx = tss->edx; regs->ebx = tss->ebx; regs->uesp = tss->esp; regs->ebp = tss->ebp; regs->esi = tss->esi; regs->edi = tss->edi; regs->es = tss->es; regs->ss = tss->ss; regs->cs = tss->cs; regs->ds = tss->ds; regs->fs = tss->fs; regs->gs = tss->gs; } /* * Compose a call to the debugger from the saved state in regs. (No * reason not to do this in C.) */ boolean_t db_trap_from_asm( x86_saved_state32_t *regs) { int code; int type; type = regs->trapno; code = regs->err; return (kdb_trap(type, code, regs)); } int kdb_trap( int type, int code, x86_saved_state32_t *regs) { extern char etext; boolean_t trap_from_user; spl_t s; int previous_console_device; s = splhigh(); previous_console_device = switch_to_serial_console(); db_printf("kdb_trap(): type %d, code %d, regs->eip 0x%x\n", type, code, regs->eip); switch (type) { case T_DEBUG: /* single_step */ { extern int dr_addr[]; int addr; uint32_t status; __asm__ volatile ("movl %%dr6, %0" : "=r" (status)); if (status & 0xf) { /* hmm hdw break */ addr = status & 0x8 ? dr_addr[3] : status & 0x4 ? dr_addr[2] : status & 0x2 ? dr_addr[1] : dr_addr[0]; regs->efl |= EFL_RF; db_single_step_cmd(addr, 0, 1, "p"); } } case T_INT3: /* breakpoint */ case T_WATCHPOINT: /* watchpoint */ case -1: /* keyboard interrupt */ break; default: if (db_recover) { i386_nested_saved_state = *regs; db_printf("Caught "); if (type < 0 || type > TRAP_TYPES) db_printf("type %d", type); else db_printf("%s", trap_type[type]); db_printf(" trap, code = %x, pc = %x\n", code, regs->eip); splx(s); db_error(""); /*NOTREACHED*/ } kdbprinttrap(type, code, (int *)®s->eip, regs->uesp); } disable_preemption(); current_cpu_datap()->cpu_kdb_saved_ipl = s; current_cpu_datap()->cpu_kdb_saved_state = regs; i386_last_saved_statep = regs; i386_last_kdb_sp = (unsigned) &type; if (!kdb_enter(regs->eip)) goto kdb_exit; /* Should switch to kdb's own stack here. */ if (!IS_USER_TRAP(regs, &etext)) { bzero((char *)&ddb_regs, sizeof (ddb_regs)); *(struct x86_saved_state32_from_kernel *)&ddb_regs = *(struct x86_saved_state32_from_kernel *)regs; trap_from_user = FALSE; } else { ddb_regs = *regs; trap_from_user = TRUE; } if (!trap_from_user) { /* * Kernel mode - esp and ss not saved */ ddb_regs.uesp = (int)®s->uesp; /* kernel stack pointer */ ddb_regs.ss = KERNEL_DS; } db_active++; db_task_trap(type, code, trap_from_user); db_active--; regs->eip = ddb_regs.eip; regs->efl = ddb_regs.efl; regs->eax = ddb_regs.eax; regs->ecx = ddb_regs.ecx; regs->edx = ddb_regs.edx; regs->ebx = ddb_regs.ebx; if (trap_from_user) { /* * user mode - saved esp and ss valid */ regs->uesp = ddb_regs.uesp; /* user stack pointer */ regs->ss = ddb_regs.ss & 0xffff; /* user stack segment */ } regs->ebp = ddb_regs.ebp; regs->esi = ddb_regs.esi; regs->edi = ddb_regs.edi; regs->es = ddb_regs.es & 0xffff; regs->cs = ddb_regs.cs & 0xffff; regs->ds = ddb_regs.ds & 0xffff; regs->fs = ddb_regs.fs & 0xffff; regs->gs = ddb_regs.gs & 0xffff; if ((type == T_INT3) && (db_get_task_value(regs->eip, BKPT_SIZE, FALSE, db_target_space(current_thread(), trap_from_user)) == BKPT_INST)) regs->eip += BKPT_SIZE; switch_to_old_console(previous_console_device); kdb_exit: kdb_leave(); current_cpu_datap()->cpu_kdb_saved_state = 0; enable_preemption(); splx(s); /* Allow continue to upper layers of exception handling if * trap was not a debugging trap. */ if (trap_from_user && type != T_DEBUG && type != T_INT3 && type != T_WATCHPOINT) return 0; else return (1); } /* * Enter KDB through a keyboard trap. * We show the registers as of the keyboard interrupt * instead of those at its call to KDB. */ spl_t kdb_oldspl; void kdb_kentry( struct int_regs *int_regs) { extern char etext; boolean_t trap_from_user; x86_saved_state32_t *is = int_regs->is; x86_saved_state32_t regs; spl_t s; s = splhigh(); kdb_oldspl = s; if (IS_USER_TRAP(is, &etext)) { regs.uesp = ((int *)(is+1))[0]; regs.ss = ((int *)(is+1))[1]; } else { regs.ss = KERNEL_DS; regs.uesp= (int)(is+1); } regs.efl = is->efl; regs.cs = is->cs; regs.eip = is->eip; regs.eax = is->eax; regs.ecx = is->ecx; regs.edx = is->edx; regs.ebx = int_regs->ebx; regs.ebp = int_regs->ebp; regs.esi = int_regs->esi; regs.edi = int_regs->edi; regs.ds = is->ds; regs.es = is->es; regs.fs = int_regs->fs; regs.gs = int_regs->gs; disable_preemption(); current_cpu_datap()->cpu_kdb_saved_state = ®s; if (!kdb_enter(regs.eip)) goto kdb_exit; bcopy((char *)®s, (char *)&ddb_regs, sizeof (ddb_regs)); trap_from_user = IS_USER_TRAP(&ddb_regs, &etext); db_active++; db_task_trap(-1, 0, trap_from_user); db_active--; if (trap_from_user) { ((int *)(is+1))[0] = ddb_regs.uesp; ((int *)(is+1))[1] = ddb_regs.ss & 0xffff; } is->efl = ddb_regs.efl; is->cs = ddb_regs.cs & 0xffff; is->eip = ddb_regs.eip; is->eax = ddb_regs.eax; is->ecx = ddb_regs.ecx; is->edx = ddb_regs.edx; int_regs->ebx = ddb_regs.ebx; int_regs->ebp = ddb_regs.ebp; int_regs->esi = ddb_regs.esi; int_regs->edi = ddb_regs.edi; is->ds = ddb_regs.ds & 0xffff; is->es = ddb_regs.es & 0xffff; int_regs->fs = ddb_regs.fs & 0xffff; int_regs->gs = ddb_regs.gs & 0xffff; kdb_exit: kdb_leave(); current_cpu_datap()->cpu_kdb_saved_state = 0; enable_preemption(); splx(s); } /* * Print trap reason. */ void kdbprinttrap( int type, int code, int *pc, int sp) { printf("kernel: "); if (type < 0 || type > TRAP_TYPES) db_printf("type %d", type); else db_printf("%s", trap_type[type]); db_printf(" trap, code=%x eip@%x = %x esp=%x\n", code, pc, *(int *)pc, sp); db_run_mode = STEP_CONTINUE; } int db_user_to_kernel_address( task_t task, vm_offset_t addr, unsigned *kaddr, int flag) { register pt_entry_t *ptp; vm_offset_t src; /* * must not pre-empted while using the pte pointer passed * back since it's been mapped through a per-cpu window */ mp_disable_preemption(); ptp = pmap_pte(task->map->pmap, (vm_map_offset_t)addr); if (ptp == PT_ENTRY_NULL || (*ptp & INTEL_PTE_VALID) == 0) { if (flag) { db_printf("\nno memory is assigned to address %08x\n", addr); db_error(0); /* NOTREACHED */ } mp_enable_preemption(); return(-1); } src = (vm_offset_t)pte_to_pa(*ptp); mp_enable_preemption(); *(int *) DMAP1 = INTEL_PTE_VALID | INTEL_PTE_RW | (src & PG_FRAME) | INTEL_PTE_REF | INTEL_PTE_MOD; #if defined(I386_CPU) if (cpu_class == CPUCLASS_386) { invltlb(); } else #endif { invlpg((u_int)DADDR1); } *kaddr = (unsigned)DADDR1 + (addr & PAGE_MASK); return(0); } /* * Read bytes from kernel address space for debugger. */ void db_read_bytes( vm_offset_t addr, int size, char *data, task_t task) { register char *src; register int n; unsigned kern_addr; src = (char *)addr; if (task == kernel_task || task == TASK_NULL) { while (--size >= 0) { if (addr++ > VM_MAX_KERNEL_ADDRESS) { db_printf("\nbad address %x\n", addr); db_error(0); /* NOTREACHED */ } *data++ = *src++; } return; } while (size > 0) { if (db_user_to_kernel_address(task, addr, &kern_addr, 1) < 0) return; src = (char *)kern_addr; n = intel_trunc_page(addr+INTEL_PGBYTES) - addr; if (n > size) n = size; size -= n; addr += n; while (--n >= 0) *data++ = *src++; } } /* * Write bytes to kernel address space for debugger. */ void db_write_bytes( vm_offset_t addr, int size, char *data, task_t task) { register char *dst; register pt_entry_t *ptep0 = 0; pt_entry_t oldmap0 = 0; vm_offset_t addr1; register pt_entry_t *ptep1 = 0; pt_entry_t oldmap1 = 0; extern char etext; if (task && task != kernel_task) { db_write_bytes_user_space(addr, size, data, task); return; } if (addr >= VM_MIN_KERNEL_LOADED_ADDRESS) { db_write_bytes_user_space(addr, size, data, kernel_task); return; } if (addr >= VM_MIN_KERNEL_ADDRESS && addr <= (vm_offset_t)&etext) { ptep0 = pmap_pte(kernel_pmap, (vm_map_offset_t)addr); oldmap0 = *ptep0; *ptep0 |= INTEL_PTE_WRITE; addr1 = i386_trunc_page(addr + size - 1); if (i386_trunc_page(addr) != addr1) { /* data crosses a page boundary */ ptep1 = pmap_pte(kernel_pmap, (vm_map_offset_t)addr1); oldmap1 = *ptep1; *ptep1 |= INTEL_PTE_WRITE; } flush_tlb(); } dst = (char *)addr; while (--size >= 0) { if (addr++ > VM_MAX_KERNEL_ADDRESS) { db_printf("\nbad address %x\n", addr); db_error(0); /* NOTREACHED */ } *dst++ = *data++; } if (ptep0) { *ptep0 = oldmap0; if (ptep1) { *ptep1 = oldmap1; } flush_tlb(); } } void db_write_bytes_user_space( vm_offset_t addr, int size, char *data, task_t task) { register char *dst; register int n; unsigned kern_addr; while (size > 0) { if (db_user_to_kernel_address(task, addr, &kern_addr, 1) < 0) return; dst = (char *)kern_addr; n = intel_trunc_page(addr+INTEL_PGBYTES) - addr; if (n > size) n = size; size -= n; addr += n; while (--n >= 0) *dst++ = *data++; } } boolean_t db_check_access( vm_offset_t addr, int size, task_t task) { register n; unsigned kern_addr; if (task == kernel_task || task == TASK_NULL) { if (kernel_task == TASK_NULL) return(TRUE); task = kernel_task; } else if (task == TASK_NULL) { if (current_thread() == THREAD_NULL) return(FALSE); task = current_thread()->task; } while (size > 0) { if (db_user_to_kernel_address(task, addr, &kern_addr, 0) < 0) return(FALSE); n = intel_trunc_page(addr+INTEL_PGBYTES) - addr; if (n > size) n = size; size -= n; addr += n; } return(TRUE); } boolean_t db_phys_eq( task_t task1, vm_offset_t addr1, task_t task2, vm_offset_t addr2) { unsigned kern_addr1, kern_addr2; if ((addr1 & (INTEL_PGBYTES-1)) != (addr2 & (INTEL_PGBYTES-1))) return(FALSE); if (task1 == TASK_NULL) { if (current_thread() == THREAD_NULL) return(FALSE); task1 = current_thread()->task; } if (db_user_to_kernel_address(task1, addr1, &kern_addr1, 0) < 0 || db_user_to_kernel_address(task2, addr2, &kern_addr2, 0) < 0) return(FALSE); return(kern_addr1 == kern_addr2); } #define DB_USER_STACK_ADDR (VM_MIN_KERNEL_ADDRESS) #define DB_NAME_SEARCH_LIMIT (DB_USER_STACK_ADDR-(INTEL_PGBYTES*3)) int db_search_null( task_t task, unsigned *svaddr, unsigned evaddr, unsigned *skaddr, int flag) { register unsigned vaddr; register unsigned *kaddr; kaddr = (unsigned *)*skaddr; for (vaddr = *svaddr; vaddr > evaddr; vaddr -= sizeof(unsigned)) { if (vaddr % INTEL_PGBYTES == 0) { vaddr -= sizeof(unsigned); if (db_user_to_kernel_address(task, vaddr, skaddr, 0) < 0) return(-1); kaddr = (unsigned *)*skaddr; } else { vaddr -= sizeof(unsigned); kaddr--; } if ((*kaddr == 0) ^ (flag == 0)) { *svaddr = vaddr; *skaddr = (unsigned)kaddr; return(0); } } return(-1); } void db_task_name( task_t task) { register char *p; register n; unsigned vaddr, kaddr; vaddr = DB_USER_STACK_ADDR; kaddr = 0; /* * skip nulls at the end */ if (db_search_null(task, &vaddr, DB_NAME_SEARCH_LIMIT, &kaddr, 0) < 0) { db_printf(DB_NULL_TASK_NAME); return; } /* * search start of args */ if (db_search_null(task, &vaddr, DB_NAME_SEARCH_LIMIT, &kaddr, 1) < 0) { db_printf(DB_NULL_TASK_NAME); return; } n = DB_TASK_NAME_LEN-1; p = (char *)kaddr + sizeof(unsigned); for (vaddr += sizeof(int); vaddr < DB_USER_STACK_ADDR && n > 0; vaddr++, p++, n--) { if (vaddr % INTEL_PGBYTES == 0) { (void)db_user_to_kernel_address(task, vaddr, &kaddr, 0); p = (char*)kaddr; } db_printf("%c", (*p < ' ' || *p > '~')? ' ': *p); } while (n-- >= 0) /* compare with >= 0 for one more space */ db_printf(" "); } void db_machdep_init(void) { int c; db_simple_lock_init(&kdb_lock, 0); for (c = 0; c < real_ncpus; ++c) { if (c == master_cpu) { master_dbtss.esp0 = (int)(db_task_stack_store + (INTSTACK_SIZE * (c + 1)) - sizeof (natural_t)); master_dbtss.esp = master_dbtss.esp0; master_dbtss.eip = (int)&db_task_start; /* * The TSS for the debugging task on each slave CPU * is set up in cpu_desc_init(). */ } } } /* * Called when entering kdb: * Takes kdb lock. If if we were called remotely (slave state) we just * wait for kdb_cpu to be equal to cpu_number(). Otherwise enter kdb if * not active on another cpu. * If db_pass_thru[cpu_number()] > 0, then kdb can't stop now. */ int kdb_enter(int pc) { int my_cpu; int retval; disable_preemption(); my_cpu = cpu_number(); if (current_cpu_datap()->cpu_db_pass_thru) { retval = 0; goto kdb_exit; } current_cpu_datap()->cpu_kdb_active++; lock_kdb(); db_printf("kdb_enter(): cpu_number %d, kdb_cpu %d\n", my_cpu, kdb_cpu); if (db_breakpoints_inserted) cpus_holding_bkpts++; if (kdb_cpu == -1 && !current_cpu_datap()->cpu_kdb_is_slave) { kdb_cpu = my_cpu; db_printf("Signaling other processors..\n"); remote_kdb(); /* stop other cpus */ retval = 1; } else if (kdb_cpu == my_cpu) retval = 1; else retval = 0; kdb_exit: enable_preemption(); return (retval); } void kdb_leave(void) { int my_cpu; boolean_t wait = FALSE; disable_preemption(); my_cpu = cpu_number(); if (db_run_mode == STEP_CONTINUE) { wait = TRUE; kdb_cpu = -1; } if (db_breakpoints_inserted) cpus_holding_bkpts--; if (current_cpu_datap()->cpu_kdb_is_slave) current_cpu_datap()->cpu_kdb_is_slave--; if (kdb_debug) db_printf("kdb_leave: cpu %d, kdb_cpu %d, run_mode %d pc %x (%x) holds %d\n", my_cpu, kdb_cpu, db_run_mode, ddb_regs.eip, *(int *)ddb_regs.eip, cpus_holding_bkpts); clear_kdb_intr(); unlock_kdb(); current_cpu_datap()->cpu_kdb_active--; mp_kdb_exit(); enable_preemption(); if (wait) { while(cpus_holding_bkpts); } } void lock_kdb(void) { int my_cpu; register i; disable_preemption(); my_cpu = cpu_number(); for(;;) { if (kdb_cpu != -1 && kdb_cpu != my_cpu) { continue; } if (db_simple_lock_try(&kdb_lock)) { if (kdb_cpu == -1 || kdb_cpu == my_cpu) break; db_simple_unlock(&kdb_lock); } } enable_preemption(); } #if TIME_STAMP extern unsigned old_time_stamp; #endif /* TIME_STAMP */ void unlock_kdb(void) { db_simple_unlock(&kdb_lock); #if TIME_STAMP old_time_stamp = 0; #endif /* TIME_STAMP */ } #ifdef __STDC__ #define KDB_SAVE(type, name) extern type name; type name##_save = name #define KDB_RESTORE(name) name = name##_save #else /* __STDC__ */ #define KDB_SAVE(type, name) extern type name; type name/**/_save = name #define KDB_RESTORE(name) name = name/**/_save #endif /* __STDC__ */ #define KDB_SAVE_CTXT() \ KDB_SAVE(int, db_run_mode); \ KDB_SAVE(boolean_t, db_sstep_print); \ KDB_SAVE(int, db_loop_count); \ KDB_SAVE(int, db_call_depth); \ KDB_SAVE(int, db_inst_count); \ KDB_SAVE(int, db_last_inst_count); \ KDB_SAVE(int, db_load_count); \ KDB_SAVE(int, db_store_count); \ KDB_SAVE(boolean_t, db_cmd_loop_done); \ KDB_SAVE(jmp_buf_t *, db_recover); \ KDB_SAVE(db_addr_t, db_dot); \ KDB_SAVE(db_addr_t, db_last_addr); \ KDB_SAVE(db_addr_t, db_prev); \ KDB_SAVE(db_addr_t, db_next); \ KDB_SAVE(db_regs_t, ddb_regs); #define KDB_RESTORE_CTXT() \ KDB_RESTORE(db_run_mode); \ KDB_RESTORE(db_sstep_print); \ KDB_RESTORE(db_loop_count); \ KDB_RESTORE(db_call_depth); \ KDB_RESTORE(db_inst_count); \ KDB_RESTORE(db_last_inst_count); \ KDB_RESTORE(db_load_count); \ KDB_RESTORE(db_store_count); \ KDB_RESTORE(db_cmd_loop_done); \ KDB_RESTORE(db_recover); \ KDB_RESTORE(db_dot); \ KDB_RESTORE(db_last_addr); \ KDB_RESTORE(db_prev); \ KDB_RESTORE(db_next); \ KDB_RESTORE(ddb_regs); /* * switch to another cpu */ void kdb_on( int cpu) { KDB_SAVE_CTXT(); if (cpu < 0 || cpu >= real_ncpus || !cpu_datap(cpu)->cpu_kdb_active) return; db_set_breakpoints(); db_set_watchpoints(); kdb_cpu = cpu; unlock_kdb(); lock_kdb(); db_clear_breakpoints(); db_clear_watchpoints(); KDB_RESTORE_CTXT(); if (kdb_cpu == -1) {/* someone continued */ kdb_cpu = cpu_number(); db_continue_cmd(0, 0, 0, ""); } } /* * system reboot */ extern void kdp_reboot(void); void db_reboot( db_expr_t addr, boolean_t have_addr, db_expr_t count, char *modif) { kdp_reboot(); }