/* * Mach Operating System * Copyright (c) 1993,1992 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. */ #if MACH_KDB #if NCPUS > 1 #include #include #include #include #include #include #include #include #include #include /* * Routines to interlock access to the kernel debugger on * multiprocessors. */ decl_simple_lock_data(,db_lock) /* lock to enter debugger */ volatile int db_cpu = -1; /* CPU currently in debugger */ /* -1 if none */ int db_active[NCPUS] = { 0 }; /* count recursive entries into debugger */ int db_slave[NCPUS] = { 0 }; /* nonzero if cpu interrupted by another cpu in debugger */ boolean_t db_enter_debug = FALSE; /* * Called when entering kernel debugger. * Takes db lock. If we were called remotely (slave state) we just * wait for db_cpu to be equal to cpu_number(). Otherwise enter debugger * if not active on another cpu */ boolean_t db_enter(void) { int mycpu = cpu_number(); /* * Count recursive entries to debugger. */ db_active[mycpu]++; /* * Wait for other CPUS to leave debugger. */ lock_db(); if (db_enter_debug) db_printf( "db_enter: cpu %d[%d], master %d, db_cpu %d, run mode %d\n", mycpu, db_slave[mycpu], master_cpu, db_cpu, db_run_mode); /* * If no CPU in debugger, and I am not being stopped, * enter the debugger. */ if (db_cpu == -1 && !db_slave[mycpu]) { remote_db(); /* stop other cpus */ db_cpu = mycpu; return TRUE; } /* * If I am already in the debugger (recursive entry * or returning from single step), enter debugger. */ else if (db_cpu == mycpu) return TRUE; /* * Otherwise, cannot enter debugger. */ else return FALSE; } /* * Leave debugger. */ void db_leave(void) { int mycpu = cpu_number(); /* * If continuing, give up debugger */ if (db_run_mode == STEP_CONTINUE) db_cpu = -1; /* * If I am a slave, drop my slave count. */ if (db_slave[mycpu]) db_slave[mycpu]--; if (db_enter_debug) db_printf("db_leave: cpu %d[%d], db_cpu %d, run_mode %d\n", mycpu, db_slave[mycpu], db_cpu, db_run_mode); /* * Unlock debugger. */ unlock_db(); /* * Drop recursive entry count. */ db_active[mycpu]--; } /* * invoke kernel debugger on slave processors */ void remote_db(void) { int my_cpu = cpu_number(); int i; for (i = 0; i < NCPUS; i++) { if (i != my_cpu && machine_slot[i].is_cpu && machine_slot[i].running) { cpu_interrupt_to_db(i); } } } /* * Save and restore DB global registers. * * DB_SAVE_CTXT must be at the start of a block, and * DB_RESTORE_CTXT must be in the same block. */ #ifdef __STDC__ #define DB_SAVE(type, name) extern type name; type name##_save = name #define DB_RESTORE(name) name = name##_save #else /* __STDC__ */ #define DB_SAVE(type, name) extern type name; type name/**/_save = name #define DB_RESTORE(name) name = name/**/_save #endif /* __STDC__ */ #define DB_SAVE_CTXT() \ DB_SAVE(int, db_run_mode); \ DB_SAVE(boolean_t, db_sstep_print); \ DB_SAVE(int, db_loop_count); \ DB_SAVE(int, db_call_depth); \ DB_SAVE(int, db_inst_count); \ DB_SAVE(int, db_last_inst_count); \ DB_SAVE(int, db_load_count); \ DB_SAVE(int, db_store_count); \ DB_SAVE(boolean_t, db_cmd_loop_done); \ DB_SAVE(jmp_buf_t *, db_recover); \ DB_SAVE(db_addr_t, db_dot); \ DB_SAVE(db_addr_t, db_last_addr); \ DB_SAVE(db_addr_t, db_prev); \ DB_SAVE(db_addr_t, db_next); \ SAVE_DDB_REGS #define DB_RESTORE_CTXT() \ DB_RESTORE(db_run_mode); \ DB_RESTORE(db_sstep_print); \ DB_RESTORE(db_loop_count); \ DB_RESTORE(db_call_depth); \ DB_RESTORE(db_inst_count); \ DB_RESTORE(db_last_inst_count); \ DB_RESTORE(db_load_count); \ DB_RESTORE(db_store_count); \ DB_RESTORE(db_cmd_loop_done); \ DB_RESTORE(db_recover); \ DB_RESTORE(db_dot); \ DB_RESTORE(db_last_addr); \ DB_RESTORE(db_prev); \ DB_RESTORE(db_next); \ RESTORE_DDB_REGS /* * switch to another cpu */ void db_on(int cpu) { /* * Save ddb global variables */ DB_SAVE_CTXT(); /* * Don`t do if bad CPU number. * CPU must also be spinning in db_entry. */ if (cpu < 0 || cpu >= NCPUS || !db_active[cpu]) return; /* * Give debugger to that CPU */ db_cpu = cpu; unlock_db(); /* * Wait for it to come back again */ lock_db(); /* * Restore ddb globals */ DB_RESTORE_CTXT(); if (db_cpu == -1) /* someone continued */ db_continue_cmd(0, 0, 0, ""); } /* * Called by interprocessor interrupt when one CPU is * in kernel debugger and wants to stop other CPUs */ void remote_db_enter(void) { db_slave[cpu_number()]++; kdb_kintr(); } /* * Acquire kernel debugger. * Conditional code for forwarding characters from slave to console * if console on master only. */ /* * As long as db_cpu is not -1 or cpu_number(), we know that debugger * is active on another cpu. */ void lock_db(void) { int my_cpu = cpu_number(); for (;;) { #if CONSOLE_ON_MASTER if (my_cpu == master_cpu) { db_console(); } #endif /* CONSOLE_ON_MASTER */ if (db_cpu != -1 && db_cpu != my_cpu) continue; #if CONSOLE_ON_MASTER if (my_cpu == master_cpu) { if (!simple_lock_try(&db_lock)) continue; } else { simple_lock(&db_lock); } #else /* CONSOLE_ON_MASTER */ simple_lock(&db_lock); #endif /* CONSOLE_ON_MASTER */ if (db_cpu == -1 || db_cpu == my_cpu) break; simple_unlock(&db_lock); } } void unlock_db(void) { simple_unlock(&db_lock); } #if CONSOLE_ON_MASTER void db_console(void) { if (i_bit(CBUS_PUT_CHAR, my_word)) { volatile u_char c = cbus_ochar; i_bit_clear(CBUS_PUT_CHAR, my_word); cnputc(c); } else if (i_bit(CBUS_GET_CHAR, my_word)) { if (cbus_wait_char) cbus_ichar = cngetc(); else cbus_ichar = cnmaygetc(); i_bit_clear(CBUS_GET_CHAR, my_word); #ifndef notdef } else if (!cnmaygetc()) { #else /* notdef */ } else if (com_is_char() && !com_getc(TRUE)) { #endif /* notdef */ simple_unlock(&db_lock); db_cpu = my_cpu; } } #endif /* CONSOLE_ON_MASTER */ #endif /* NCPUS > 1 */ #endif /* MACH_KDB */