diff options
-rw-r--r-- | share/man/man4/ddb.4 | 81 | ||||
-rw-r--r-- | sys/amd64/amd64/trap.c | 38 | ||||
-rw-r--r-- | sys/amd64/include/db_machdep.h | 12 | ||||
-rw-r--r-- | sys/ddb/db_examine.c | 2 | ||||
-rw-r--r-- | sys/ddb/db_expr.c | 36 | ||||
-rw-r--r-- | sys/ddb/db_main.c | 5 | ||||
-rw-r--r-- | sys/ddb/db_run.c | 40 | ||||
-rw-r--r-- | sys/ddb/ddb.h | 3 | ||||
-rw-r--r-- | sys/i386/i386/db_disasm.c | 13 | ||||
-rw-r--r-- | sys/i386/i386/db_interface.c | 24 | ||||
-rw-r--r-- | sys/i386/i386/db_trace.c | 55 | ||||
-rw-r--r-- | sys/i386/i386/trap.c | 41 | ||||
-rw-r--r-- | sys/i386/i386/vm86.c | 8 | ||||
-rw-r--r-- | sys/i386/include/db_machdep.h | 19 | ||||
-rw-r--r-- | sys/x86/include/frame.h | 16 |
15 files changed, 274 insertions, 119 deletions
diff --git a/share/man/man4/ddb.4 b/share/man/man4/ddb.4 index 010ab00..6b072ba 100644 --- a/share/man/man4/ddb.4 +++ b/share/man/man4/ddb.4 @@ -146,25 +146,32 @@ to be the same as .Pp The general command syntax is: .Ar command Ns Op Li / Ns Ar modifier -.Ar address Ns Op Li , Ns Ar count +.Oo Ar addr Oc Ns Op Li , Ns Ar count .Pp A blank line repeats the previous command from the address .Va next with count 1 and no modifiers. Specifying -.Ar address +.Ar addr sets .Va dot to the address. Omitting -.Ar address +.Ar addr uses .Va dot . A missing .Ar count is taken to be 1 for printing commands or infinity for stack traces. +A +.Ar count +of -1 is equivalent to a missing +.Ar count . +Options that are supplied but not supported by the given +.Ar command +are usually ignored. .Pp The .Nm @@ -204,8 +211,14 @@ browse through the history buffer, and move the cursor within the current line. .Sh COMMANDS .Bl -tag -width indent -compact -.It Ic examine -.It Ic x +.It Xo +.Ic examine Ns Op Li / Ns Cm AISabcdghilmorsuxz ... +.Oo Ar addr Oc Ns Op Li , Ns Ar count +.Xc +.It Xo +.Ic x Ns Op Li / Ns Cm AISabcdghilmorsuxz ... +.Oo Ar addr Oc Ns Op Li , Ns Ar count +.Xc Display the addressed locations according to the formats in the modifier. Multiple modifier formats display multiple locations. If no format is specified, the last format specified for this command @@ -251,7 +264,9 @@ The location is also displayed in hex at the beginning of each line. display as an instruction .It Cm I display as an instruction with possible alternate formats depending on the -machine, but none of the supported architectures have an alternate format. +machine. +On i386, this selects the alternate format for the instruction decoding +(16 bits in a 32-bit code segment and vice versa). .It Cm S display a symbol name for the pointer stored at the address .El @@ -328,16 +343,17 @@ Set the named variable or register with the value of .Ar expr . Valid variable names are described below. .Pp -.It Ic break Ns Op Li / Ns Cm u -.It Ic b Ns Op Li / Ns Cm u +.It Ic break Ns Oo Li / Ns Cm u Oc Oo Ar addr Oc Ns Op Li , Ns Ar count +.It Ic b Ns Oo Li / Ns Cm u Oc Oo Ar addr Oc Ns Op Li , Ns Ar count Set a break point at .Ar addr . If .Ar count -is supplied, continues +is supplied, the +.Ic continue +command will not stop at this break point on the first .Ar count -\- 1 times before stopping at the -break point. +\- 1 times that it is hit. If the break point is set, a break point number is printed with .Ql # . @@ -361,21 +377,24 @@ user space break points may not work correctly. Setting a break point at the low-level code paths may also cause strange behavior. .Pp -.It Ic delete Ar addr -.It Ic d Ar addr +.It Ic delete Op Ar addr +.It Ic d Op Ar addr .It Ic delete Li # Ns Ar number -.It Ic d Li # Ns Ar number -Delete the break point. -The target break point can be specified by a +.It Ic d Li # Ns Ar number +Delete the specified break point. +The break point can be specified by a break point number with .Ql # , or by using the same .Ar addr specified in the original .Ic break -command. +command, or by omitting +.Ar addr +to get the default address of +.Va dot . .Pp -.It Ic watch Ar addr Ns Li , Ns Ar size +.It Ic watch Oo Ar addr Oc Ns Op Li , Ns Ar size Set a watchpoint for a region. Execution stops when an attempt to modify the region occurs. The @@ -389,7 +408,7 @@ Attempts to watch wired kernel memory may cause unrecoverable error in some systems such as i386. Watchpoints on user addresses work best. .Pp -.It Ic hwatch Ar addr Ns Li , Ns Ar size +.It Ic hwatch Oo Ar addr Oc Ns Op Li , Ns Ar size Set a hardware watchpoint for a region if supported by the architecture. Execution stops when an attempt to modify the region occurs. @@ -405,14 +424,14 @@ Use for setting watchpoints on kernel address locations only, and avoid its use on user mode address spaces. .Pp -.It Ic dhwatch Ar addr Ns Li , Ns Ar size +.It Ic dhwatch Oo Ar addr Oc Ns Op Li , Ns Ar size Delete specified hardware watchpoint. .Pp -.It Ic step Ns Op Li / Ns Cm p -.It Ic s Ns Op Li / Ns Cm p +.It Ic step Ns Oo Li / Ns Cm p Oc Ns Op Li , Ns Ar count +.It Ic s Ns Oo Li / Ns Cm p Oc Ns Op Li , Ns Ar count Single step .Ar count -times (the comma is a mandatory part of the syntax). +times. If the .Cm p modifier is specified, print each instruction at each step. @@ -458,22 +477,22 @@ Otherwise, only print when the matching return is hit. .Pp .It Xo .Ic trace Ns Op Li / Ns Cm u -.Op Ar pid | tid +.Op Ar pid | tid Ns .Op Li , Ns Ar count .Xc .It Xo .Ic t Ns Op Li / Ns Cm u -.Op Ar pid | tid +.Op Ar pid | tid Ns .Op Li , Ns Ar count .Xc .It Xo .Ic where Ns Op Li / Ns Cm u -.Op Ar pid | tid +.Op Ar pid | tid Ns .Op Li , Ns Ar count .Xc .It Xo .Ic bt Ns Op Li / Ns Cm u -.Op Ar pid | tid +.Op Ar pid | tid Ns .Op Li , Ns Ar count .Xc Stack trace. @@ -498,16 +517,11 @@ only if the machine dependent code supports it. .Ic search Ns Op Li / Ns Cm bhl .Ar addr .Ar value -.Op Ar mask +.Op Ar mask Ns .Op Li , Ns Ar count .Xc Search memory for .Ar value . -This command might fail in interesting -ways if it does not find the searched-for value. -This is because -.Nm -does not always recover from touching bad memory. The optional .Ar count argument limits the search. @@ -537,7 +551,6 @@ addresses for the process and not show other information. .Pp .It Ic show Cm all trace .It Ic alltrace -.Xc Show a stack trace for every thread in the system. .Pp .It Ic show Cm all ttys diff --git a/sys/amd64/amd64/trap.c b/sys/amd64/amd64/trap.c index 04c5dcc..8d2b399 100644 --- a/sys/amd64/amd64/trap.c +++ b/sys/amd64/amd64/trap.c @@ -176,7 +176,10 @@ trap(struct trapframe *frame) #endif struct thread *td = curthread; struct proc *p = td->td_proc; - int i = 0, ucode = 0, code; +#ifdef KDB + register_t dr6; +#endif + int i = 0, ucode = 0; u_int type; register_t addr = 0; ksiginfo_t ksi; @@ -236,7 +239,7 @@ trap(struct trapframe *frame) * interrupts disabled until they are accidentally * enabled later. */ - if (ISPL(frame->tf_cs) == SEL_UPL) + if (TRAPF_USERMODE(frame)) uprintf( "pid %ld (%s): trap %d with interrupts disabled\n", (long)curproc->p_pid, curthread->td_name, type); @@ -258,9 +261,7 @@ trap(struct trapframe *frame) } } - code = frame->tf_err; - - if (ISPL(frame->tf_cs) == SEL_UPL) { + if (TRAPF_USERMODE(frame)) { /* user trap */ td->td_pticks = 0; @@ -377,7 +378,7 @@ trap(struct trapframe *frame) #ifdef DEV_ISA case T_NMI: /* machine/parity/power fail/"kitchen sink" faults */ - if (isa_nmi(code) == 0) { + if (isa_nmi(frame->tf_err) == 0) { #ifdef KDB /* * NMI can be hooked up to a pushbutton @@ -540,8 +541,7 @@ trap(struct trapframe *frame) * Reset breakpoint bits because the * processor doesn't */ - /* XXX check upper bits here */ - load_dr6(rdr6() & 0xfffffff0); + load_dr6(rdr6() & ~0xf); goto out; } /* @@ -553,7 +553,10 @@ trap(struct trapframe *frame) * Otherwise, debugger traps "can't happen". */ #ifdef KDB - if (kdb_trap(type, 0, frame)) + /* XXX %dr6 is not quite reentrant. */ + dr6 = rdr6(); + load_dr6(dr6 & ~0x4000); + if (kdb_trap(type, dr6, frame)) goto out; #endif break; @@ -561,7 +564,7 @@ trap(struct trapframe *frame) #ifdef DEV_ISA case T_NMI: /* machine/parity/power fail/"kitchen sink" faults */ - if (isa_nmi(code) == 0) { + if (isa_nmi(frame->tf_err) == 0) { #ifdef KDB /* * NMI can be hooked up to a pushbutton @@ -773,7 +776,6 @@ trap_fatal(frame, eva) { int code, ss; u_int type; - long esp; struct soft_segment_descriptor softseg; char *msg; @@ -787,7 +789,7 @@ trap_fatal(frame, eva) else msg = "UNKNOWN"; printf("\n\nFatal trap %d: %s while in %s mode\n", type, msg, - ISPL(frame->tf_cs) == SEL_UPL ? "user" : "kernel"); + TRAPF_USERMODE(frame) ? "user" : "kernel"); #ifdef SMP /* two separate prints in case of a trap on an unmapped page */ printf("cpuid = %d; ", PCPU_GET(cpuid)); @@ -804,14 +806,8 @@ trap_fatal(frame, eva) } printf("instruction pointer = 0x%lx:0x%lx\n", frame->tf_cs & 0xffff, frame->tf_rip); - if (ISPL(frame->tf_cs) == SEL_UPL) { - ss = frame->tf_ss & 0xffff; - esp = frame->tf_rsp; - } else { - ss = GSEL(GDATA_SEL, SEL_KPL); - esp = (long)&frame->tf_rsp; - } - printf("stack pointer = 0x%x:0x%lx\n", ss, esp); + ss = frame->tf_ss & 0xffff; + printf("stack pointer = 0x%x:0x%lx\n", ss, frame->tf_rsp); printf("frame pointer = 0x%x:0x%lx\n", ss, frame->tf_rbp); printf("code segment = base 0x%lx, limit 0x%lx, type 0x%x\n", softseg.ssd_base, softseg.ssd_limit, softseg.ssd_type); @@ -934,7 +930,7 @@ amd64_syscall(struct thread *td, int traced) ksiginfo_t ksi; #ifdef DIAGNOSTIC - if (ISPL(td->td_frame->tf_cs) != SEL_UPL) { + if (!TRAPF_USERMODE(td->td_frame)) { panic("syscall"); /* NOT REACHED */ } diff --git a/sys/amd64/include/db_machdep.h b/sys/amd64/include/db_machdep.h index 29e385e..722a7b3 100644 --- a/sys/amd64/include/db_machdep.h +++ b/sys/amd64/include/db_machdep.h @@ -56,12 +56,16 @@ do { \ #define db_clear_single_step kdb_cpu_clear_singlestep #define db_set_single_step kdb_cpu_set_singlestep -#define IS_BREAKPOINT_TRAP(type, code) ((type) == T_BPTFLT) /* - * Watchpoints are not supported. The debug exception type is in %dr6 - * and not yet in the args to this macro. + * The debug exception type is copied from %dr6 to 'code' and used to + * disambiguate single step traps. Watchpoints have no special support. + * Our hardware breakpoints are not well integrated with ddb and are too + * different from watchpoints. ddb treats them as unknown traps with + * unknown addresses and doesn't turn them off while it is running. */ -#define IS_WATCHPOINT_TRAP(type, code) 0 +#define IS_BREAKPOINT_TRAP(type, code) ((type) == T_BPTFLT) +#define IS_SSTEP_TRAP(type, code) ((type) == T_TRCTRAP && (code) & 0x4000) +#define IS_WATCHPOINT_TRAP(type, code) 0 #define I_CALL 0xe8 #define I_CALLI 0xff diff --git a/sys/ddb/db_examine.c b/sys/ddb/db_examine.c index de2bbe4..a1e5a28 100644 --- a/sys/ddb/db_examine.c +++ b/sys/ddb/db_examine.c @@ -241,7 +241,7 @@ db_print_loc_and_inst(db_addr_t loc) db_printsym(loc, DB_STGY_PROC); if (db_search_symbol(loc, DB_STGY_PROC, &off) != C_DB_SYM_NULL) { db_printf(":\t"); - (void)db_disasm(loc, true); + (void)db_disasm(loc, false); } } diff --git a/sys/ddb/db_expr.c b/sys/ddb/db_expr.c index db17f36..c206a57 100644 --- a/sys/ddb/db_expr.c +++ b/sys/ddb/db_expr.c @@ -57,7 +57,8 @@ db_term(db_expr_t *valuep) if (!db_value_of_name(db_tok_string, valuep) && !db_value_of_name_pcpu(db_tok_string, valuep) && !db_value_of_name_vnet(db_tok_string, valuep)) { - db_error("Symbol not found\n"); + db_printf("Symbol '%s' not found\n", db_tok_string); + db_error(NULL); /*NOTREACHED*/ } return (true); @@ -89,12 +90,14 @@ db_term(db_expr_t *valuep) } if (t == tLPAREN) { if (!db_expression(valuep)) { - db_error("Syntax error\n"); + db_printf("Expression syntax error after '%c'\n", '('); + db_error(NULL); /*NOTREACHED*/ } t = db_read_token(); if (t != tRPAREN) { - db_error("Syntax error\n"); + db_printf("Expression syntax error -- expected '%c'\n", ')'); + db_error(NULL); /*NOTREACHED*/ } return (true); @@ -164,7 +167,9 @@ db_mult_expr(db_expr_t *valuep) while (t == tSTAR || t == tSLASH || t == tPCT || t == tHASH || t == tBIT_AND ) { if (!db_term(&rhs)) { - db_printf("Expression syntax error after '%c'\n", '!'); + db_printf("Expression syntax error after '%c'\n", + t == tSTAR ? '*' : t == tSLASH ? '/' : t == tPCT ? '%' : + t == tHASH ? '#' : '&'); db_error(NULL); /*NOTREACHED*/ } @@ -177,7 +182,7 @@ db_mult_expr(db_expr_t *valuep) break; default: if (rhs == 0) { - db_error("Divide by 0\n"); + db_error("Division by 0\n"); /*NOTREACHED*/ } if (t == tSLASH) @@ -199,7 +204,6 @@ db_add_expr(db_expr_t *valuep) { db_expr_t lhs, rhs; int t; - char c; if (!db_mult_expr(&lhs)) return (false); @@ -207,8 +211,8 @@ db_add_expr(db_expr_t *valuep) t = db_read_token(); while (t == tPLUS || t == tMINUS || t == tBIT_OR) { if (!db_mult_expr(&rhs)) { - c = db_tok_string[0]; - db_printf("Expression syntax error after '%c'\n", c); + db_printf("Expression syntax error after '%c'\n", + t == tPLUS ? '+' : t == tMINUS ? '-' : '|'); db_error(NULL); /*NOTREACHED*/ } @@ -243,11 +247,14 @@ db_shift_expr(db_expr_t *valuep) t = db_read_token(); while (t == tSHIFT_L || t == tSHIFT_R) { if (!db_add_expr(&rhs)) { - db_error("Syntax error\n"); + db_printf("Expression syntax error after '%s'\n", + t == tSHIFT_L ? "<<" : ">>"); + db_error(NULL); /*NOTREACHED*/ } if (rhs < 0) { - db_error("Negative shift amount\n"); + db_printf("Negative shift amount %jd\n", (intmax_t)rhs); + db_error(NULL); /*NOTREACHED*/ } if (t == tSHIFT_L) @@ -269,7 +276,6 @@ db_logical_relation_expr( { db_expr_t lhs, rhs; int t; - char op[3]; if (!db_shift_expr(&lhs)) return (false); @@ -277,11 +283,11 @@ db_logical_relation_expr( t = db_read_token(); while (t == tLOG_EQ || t == tLOG_NOT_EQ || t == tGREATER || t == tGREATER_EQ || t == tLESS || t == tLESS_EQ) { - op[0] = db_tok_string[0]; - op[1] = db_tok_string[1]; - op[2] = 0; if (!db_shift_expr(&rhs)) { - db_printf("Expression syntax error after \"%s\"\n", op); + db_printf("Expression syntax error after '%s'\n", + t == tLOG_EQ ? "==" : t == tLOG_NOT_EQ ? "!=" : + t == tGREATER ? ">" : t == tGREATER_EQ ? ">=" : + t == tLESS ? "<" : "<="); db_error(NULL); /*NOTREACHED*/ } diff --git a/sys/ddb/db_main.c b/sys/ddb/db_main.c index 3345bb6..da6a513 100644 --- a/sys/ddb/db_main.c +++ b/sys/ddb/db_main.c @@ -226,10 +226,7 @@ db_trap(int type, int code) if (cnunavailable()) return (0); - bkpt = IS_BREAKPOINT_TRAP(type, code); - watchpt = IS_WATCHPOINT_TRAP(type, code); - - if (db_stop_at_pc(&bkpt)) { + if (db_stop_at_pc(type, code, &bkpt, &watchpt)) { if (db_inst_count) { db_printf("After %d instructions (%d loads, %d stores),\n", db_inst_count, db_load_count, db_store_count); diff --git a/sys/ddb/db_run.c b/sys/ddb/db_run.c index 5250651..4d8d3c7 100644 --- a/sys/ddb/db_run.c +++ b/sys/ddb/db_run.c @@ -48,15 +48,15 @@ __FBSDID("$FreeBSD$"); #include <ddb/db_break.h> #include <ddb/db_access.h> -static int db_run_mode; -#define STEP_NONE 0 #define STEP_ONCE 1 #define STEP_RETURN 2 #define STEP_CALLT 3 #define STEP_CONTINUE 4 #define STEP_INVISIBLE 5 #define STEP_COUNT 6 +static int db_run_mode = STEP_CONTINUE; +static bool db_sstep_multiple; static bool db_sstep_print; static int db_loop_count; static int db_call_depth; @@ -90,13 +90,14 @@ db_pc_is_singlestep(db_addr_t pc) #endif bool -db_stop_at_pc(bool *is_breakpoint) +db_stop_at_pc(int type, int code, bool *is_breakpoint, bool *is_watchpoint) { db_addr_t pc; db_breakpoint_t bkpt; + *is_breakpoint = IS_BREAKPOINT_TRAP(type, code); + *is_watchpoint = IS_WATCHPOINT_TRAP(type, code); pc = PC_REGS(); - if (db_pc_is_singlestep(pc)) *is_breakpoint = false; @@ -125,13 +126,39 @@ db_stop_at_pc(bool *is_breakpoint) *is_breakpoint = true; return (true); /* stop here */ } + return (false); /* continue the countdown */ } else if (*is_breakpoint) { #ifdef BKPT_SKIP BKPT_SKIP; #endif } - *is_breakpoint = false; + *is_breakpoint = false; /* might be a breakpoint, but not ours */ + + /* + * If not stepping, then silently ignore single-step traps + * (except for clearing the single-step-flag above). + * + * If stepping, then abort if the trap type is unexpected. + * Breakpoints owned by us are expected and were handled above. + * Single-steps are expected and are handled below. All others + * are unexpected. + * + * Only do either of these if the MD layer claims to classify + * single-step traps unambiguously (by defining IS_SSTEP_TRAP). + * Otherwise, fall through to the bad historical behaviour + * given by turning unexpected traps into expected traps: if not + * stepping, then expect only breakpoints and stop, and if + * stepping, then expect only single-steps and step. + */ +#ifdef IS_SSTEP_TRAP + if (db_run_mode == STEP_CONTINUE && IS_SSTEP_TRAP(type, code)) + return (false); + if (db_run_mode != STEP_CONTINUE && !IS_SSTEP_TRAP(type, code)) { + printf("Stepping aborted\n"); + return (true); + } +#endif if (db_run_mode == STEP_INVISIBLE) { db_run_mode = STEP_CONTINUE; @@ -184,7 +211,6 @@ db_stop_at_pc(bool *is_breakpoint) return (false); /* continue */ } } - db_run_mode = STEP_NONE; return (true); } @@ -194,6 +220,7 @@ db_restart_at_pc(bool watchpt) db_addr_t pc = PC_REGS(); if ((db_run_mode == STEP_COUNT) || + ((db_run_mode == STEP_ONCE) && db_sstep_multiple) || (db_run_mode == STEP_RETURN) || (db_run_mode == STEP_CALLT)) { /* @@ -321,6 +348,7 @@ db_single_step_cmd(db_expr_t addr, bool have_addr, db_expr_t count, char *modif) db_run_mode = STEP_ONCE; db_loop_count = count; + db_sstep_multiple = (count != 1); db_sstep_print = print; db_inst_count = 0; db_load_count = 0; diff --git a/sys/ddb/ddb.h b/sys/ddb/ddb.h index a2adcdf..fcf772b 100644 --- a/sys/ddb/ddb.h +++ b/sys/ddb/ddb.h @@ -215,7 +215,8 @@ void db_restart_at_pc(bool watchpt); int db_set_variable(db_expr_t value); void db_set_watchpoints(void); void db_skip_to_eol(void); -bool db_stop_at_pc(bool *is_breakpoint); +bool db_stop_at_pc(int type, int code, bool *is_breakpoint, + bool *is_watchpoint); #define db_strcpy strcpy void db_trace_self(void); int db_trace_thread(struct thread *, int); diff --git a/sys/i386/i386/db_disasm.c b/sys/i386/i386/db_disasm.c index 2b398da..5728fe5 100644 --- a/sys/i386/i386/db_disasm.c +++ b/sys/i386/i386/db_disasm.c @@ -31,6 +31,7 @@ __FBSDID("$FreeBSD$"); * Instruction disassembler. */ #include <sys/param.h> +#include <sys/kdb.h> #include <ddb/ddb.h> #include <ddb/db_access.h> @@ -1168,9 +1169,17 @@ db_disasm(db_addr_t loc, bool altfmt) int len; struct i_addr address; + if (db_segsize(kdb_frame) == 16) + altfmt = !altfmt; get_value_inc(inst, loc, 1, FALSE); - short_addr = FALSE; - size = LONG; + if (altfmt) { + short_addr = TRUE; + size = WORD; + } + else { + short_addr = FALSE; + size = LONG; + } seg = NULL; /* diff --git a/sys/i386/i386/db_interface.c b/sys/i386/i386/db_interface.c index 7079e56..3c0ac81 100644 --- a/sys/i386/i386/db_interface.c +++ b/sys/i386/i386/db_interface.c @@ -135,6 +135,30 @@ db_write_bytes(vm_offset_t addr, size_t size, char *data) return (ret); } +int +db_segsize(struct trapframe *tfp) +{ + struct proc_ldt *plp; + struct segment_descriptor *sdp; + int sel; + + if (tfp == NULL) + return (32); + if (tfp->tf_eflags & PSL_VM) + return (16); + sel = tfp->tf_cs & 0xffff; + if (sel == GSEL(GCODE_SEL, SEL_KPL)) + return (32); + /* Rare cases follow. User mode cases are currently unreachable. */ + if (ISLDT(sel)) { + plp = curthread->td_proc->p_md.md_ldt; + sdp = (plp != NULL) ? &plp->ldt_sd : &ldt[0].sd; + } else { + sdp = &gdt[PCPU_GET(cpuid) * NGDT].sd; + } + return (sdp[IDXSEL(sel)].sd_def32 == 0 ? 16 : 32); +} + void db_show_mdpcpu(struct pcpu *pc) { diff --git a/sys/i386/i386/db_trace.c b/sys/i386/i386/db_trace.c index 3563579..4f5f413 100644 --- a/sys/i386/i386/db_trace.c +++ b/sys/i386/i386/db_trace.c @@ -34,6 +34,7 @@ __FBSDID("$FreeBSD$"); #include <sys/sysent.h> #include <machine/cpu.h> +#include <machine/frame.h> #include <machine/md_var.h> #include <machine/pcb.h> #include <machine/reg.h> @@ -81,8 +82,7 @@ struct db_variable *db_eregs = db_regs + nitems(db_regs); static __inline int get_esp(struct trapframe *tf) { - return ((ISPL(tf->tf_cs)) ? tf->tf_esp : - (db_expr_t)tf + (uintptr_t)DB_OFFSET(tf_esp)); + return (TF_HAS_STACKREGS(tf) ? tf->tf_esp : (intptr_t)&tf->tf_esp); } static int @@ -104,12 +104,32 @@ db_frame(struct db_variable *vp, db_expr_t *valuep, int op) static int db_frame_seg(struct db_variable *vp, db_expr_t *valuep, int op) { + struct trapframe_vm86 *tfp; + int off; uint16_t *reg; if (kdb_frame == NULL) return (0); - reg = (uint16_t *)((uintptr_t)kdb_frame + (db_expr_t)vp->valuep); + off = (intptr_t)vp->valuep; + if (kdb_frame->tf_eflags & PSL_VM) { + tfp = (void *)kdb_frame; + switch ((intptr_t)vp->valuep) { + case (intptr_t)DB_OFFSET(tf_cs): + reg = (uint16_t *)&tfp->tf_cs; + break; + case (intptr_t)DB_OFFSET(tf_ds): + reg = (uint16_t *)&tfp->tf_vm86_ds; + break; + case (intptr_t)DB_OFFSET(tf_es): + reg = (uint16_t *)&tfp->tf_vm86_es; + break; + case (intptr_t)DB_OFFSET(tf_fs): + reg = (uint16_t *)&tfp->tf_vm86_fs; + break; + } + } else + reg = (uint16_t *)((uintptr_t)kdb_frame + off); if (op == DB_VAR_GET) *valuep = *reg; else @@ -126,7 +146,7 @@ db_esp(struct db_variable *vp, db_expr_t *valuep, int op) if (op == DB_VAR_GET) *valuep = get_esp(kdb_frame); - else if (ISPL(kdb_frame->tf_cs)) + else if (TF_HAS_STACKREGS(kdb_frame)) kdb_frame->tf_esp = *valuep; return (1); } @@ -134,7 +154,16 @@ db_esp(struct db_variable *vp, db_expr_t *valuep, int op) static int db_gs(struct db_variable *vp, db_expr_t *valuep, int op) { + struct trapframe_vm86 *tfp; + if (kdb_frame != NULL && kdb_frame->tf_eflags & PSL_VM) { + tfp = (void *)kdb_frame; + if (op == DB_VAR_GET) + *valuep = tfp->tf_vm86_gs; + else + tfp->tf_vm86_gs = *valuep; + return (1); + } if (op == DB_VAR_GET) *valuep = rgs(); else @@ -150,8 +179,9 @@ db_ss(struct db_variable *vp, db_expr_t *valuep, int op) return (0); if (op == DB_VAR_GET) - *valuep = (ISPL(kdb_frame->tf_cs)) ? kdb_frame->tf_ss : rss(); - else if (ISPL(kdb_frame->tf_cs)) + *valuep = TF_HAS_STACKREGS(kdb_frame) ? kdb_frame->tf_ss : + rss(); + else if (TF_HAS_STACKREGS(kdb_frame)) kdb_frame->tf_ss = *valuep; return (1); } @@ -391,6 +421,17 @@ db_backtrace(struct thread *td, struct trapframe *tf, struct i386_frame *frame, int instr, narg; boolean_t first; + if (db_segsize(tf) == 16) { + db_printf( +"--- 16-bit%s, cs:eip = %#x:%#x, ss:esp = %#x:%#x, ebp = %#x, tf = %p ---\n", + (tf->tf_eflags & PSL_VM) ? " (vm86)" : "", + tf->tf_cs, tf->tf_eip, + TF_HAS_STACKREGS(tf) ? tf->tf_ss : rss(), + TF_HAS_STACKREGS(tf) ? tf->tf_esp : (intptr_t)&tf->tf_esp, + tf->tf_ebp, tf); + return (0); + } + /* * If an indirect call via an invalid pointer caused a trap, * %pc contains the invalid address while the return address @@ -408,7 +449,7 @@ db_backtrace(struct thread *td, struct trapframe *tf, struct i386_frame *frame, * Find where the trap frame actually ends. * It won't contain tf_esp or tf_ss unless crossing rings. */ - if (ISPL(kdb_frame->tf_cs)) + if (TF_HAS_STACKREGS(kdb_frame)) instr = (int)(kdb_frame + 1); else instr = (int)&kdb_frame->tf_esp; diff --git a/sys/i386/i386/trap.c b/sys/i386/i386/trap.c index c540a49..3decfd43 100644 --- a/sys/i386/i386/trap.c +++ b/sys/i386/i386/trap.c @@ -189,7 +189,10 @@ trap(struct trapframe *frame) #endif struct thread *td = curthread; struct proc *p = td->td_proc; - int i = 0, ucode = 0, code; +#ifdef KDB + register_t dr6; +#endif + int i = 0, ucode = 0; u_int type; register_t addr = 0; vm_offset_t eva; @@ -267,7 +270,8 @@ trap(struct trapframe *frame) * interrupts disabled until they are accidentally * enabled later. */ - if (ISPL(frame->tf_cs) == SEL_UPL || (frame->tf_eflags & PSL_VM)) + if (TRAPF_USERMODE(frame) && + (curpcb->pcb_flags & PCB_VM86CALL) == 0) uprintf( "pid %ld (%s): trap %d with interrupts disabled\n", (long)curproc->p_pid, curthread->td_name, type); @@ -291,7 +295,6 @@ trap(struct trapframe *frame) } } eva = 0; - code = frame->tf_err; if (type == T_PAGEFLT) { /* * For some Cyrix CPUs, %cr2 is clobbered by @@ -307,9 +310,7 @@ trap(struct trapframe *frame) enable_intr(); } - if ((ISPL(frame->tf_cs) == SEL_UPL) || - ((frame->tf_eflags & PSL_VM) && - !(curpcb->pcb_flags & PCB_VM86CALL))) { + if (TRAPF_USERMODE(frame) && (curpcb->pcb_flags & PCB_VM86CALL) == 0) { /* user trap */ td->td_pticks = 0; @@ -335,6 +336,7 @@ trap(struct trapframe *frame) goto out; } #endif +user_trctrap_out: frame->tf_eflags &= ~PSL_T; i = SIGTRAP; ucode = (type == T_TRCTRAP ? TRAP_TRACE : TRAP_BRKPT); @@ -360,6 +362,11 @@ trap(struct trapframe *frame) case T_STKFLT: /* stack fault */ if (frame->tf_eflags & PSL_VM) { i = vm86_emulate((struct vm86frame *)frame); + if (i == SIGTRAP) { + type = T_TRCTRAP; + load_dr6(rdr6() | 0x4000); + goto user_trctrap_out; + } if (i == 0) goto user; break; @@ -461,7 +468,7 @@ trap(struct trapframe *frame) goto userout; #else /* !POWERFAIL_NMI */ /* machine/parity/power fail/"kitchen sink" faults */ - if (isa_nmi(code) == 0) { + if (isa_nmi(frame->tf_err) == 0) { #ifdef KDB /* * NMI can be hooked up to a pushbutton @@ -566,6 +573,11 @@ trap(struct trapframe *frame) case T_STKFLT: /* stack fault */ if (frame->tf_eflags & PSL_VM) { i = vm86_emulate((struct vm86frame *)frame); + if (i == SIGTRAP) { + type = T_TRCTRAP; + load_dr6(rdr6() | 0x4000); + goto kernel_trctrap; + } if (i != 0) /* * returns to original process @@ -654,6 +666,7 @@ trap(struct trapframe *frame) break; case T_TRCTRAP: /* trace trap */ +kernel_trctrap: if (frame->tf_eip == (int)IDTVEC(lcall_syscall)) { /* * We've just entered system mode via the @@ -687,7 +700,7 @@ trap(struct trapframe *frame) * Reset breakpoint bits because the * processor doesn't */ - load_dr6(rdr6() & 0xfffffff0); + load_dr6(rdr6() & ~0xf); goto out; } /* @@ -699,7 +712,10 @@ trap(struct trapframe *frame) * Otherwise, debugger traps "can't happen". */ #ifdef KDB - if (kdb_trap(type, 0, frame)) + /* XXX %dr6 is not quite reentrant. */ + dr6 = rdr6(); + load_dr6(dr6 & ~0x4000); + if (kdb_trap(type, dr6, frame)) goto out; #endif break; @@ -715,7 +731,7 @@ trap(struct trapframe *frame) goto out; #else /* !POWERFAIL_NMI */ /* machine/parity/power fail/"kitchen sink" faults */ - if (isa_nmi(code) == 0) { + if (isa_nmi(frame->tf_err) == 0) { #ifdef KDB /* * NMI can be hooked up to a pushbutton @@ -953,7 +969,7 @@ trap_fatal(frame, eva) } printf("instruction pointer = 0x%x:0x%x\n", frame->tf_cs & 0xffff, frame->tf_eip); - if ((ISPL(frame->tf_cs) == SEL_UPL) || (frame->tf_eflags & PSL_VM)) { + if (TF_HAS_STACKREGS(frame)) { ss = frame->tf_ss & 0xffff; esp = frame->tf_esp; } else { @@ -1107,7 +1123,8 @@ syscall(struct trapframe *frame) ksiginfo_t ksi; #ifdef DIAGNOSTIC - if (ISPL(frame->tf_cs) != SEL_UPL) { + if (!(TRAPF_USERMODE(frame) && + (curpcb->pcb_flags & PCB_VM86CALL) == 0)) { panic("syscall"); /* NOT REACHED */ } diff --git a/sys/i386/i386/vm86.c b/sys/i386/i386/vm86.c index 93ff855..baa1912 100644 --- a/sys/i386/i386/vm86.c +++ b/sys/i386/i386/vm86.c @@ -171,7 +171,7 @@ vm86_emulate(vmf) PUSHL((vmf->vmf_eflags & PUSH_MASK) | PSL_IOPL, vmf); vmf->vmf_ip += inc_ip; - return (0); + return (retcode); case POPF: temp_flags = POPL(vmf) & POP_MASK; @@ -185,7 +185,7 @@ vm86_emulate(vmf) } else { vmf->vmf_eflags &= ~PSL_VIF; } - return (0); + return (retcode); } break; @@ -203,7 +203,7 @@ vm86_emulate(vmf) case INTn: break; - /* VME if trying to set PSL_TF, or PSL_I when VIP is set */ + /* VME if trying to set PSL_T, or PSL_I when VIP is set */ case POPF: temp_flags = POP(vmf) & POP_MASK; vmf->vmf_flags = (vmf->vmf_flags & ~POP_MASK) @@ -218,7 +218,7 @@ vm86_emulate(vmf) } return (retcode); - /* VME if trying to set PSL_TF, or PSL_I when VIP is set */ + /* VME if trying to set PSL_T, or PSL_I when VIP is set */ case IRET: vmf->vmf_ip = POP(vmf); vmf->vmf_cs = POP(vmf); diff --git a/sys/i386/include/db_machdep.h b/sys/i386/include/db_machdep.h index 27207a8..e8d1d8e 100644 --- a/sys/i386/include/db_machdep.h +++ b/sys/i386/include/db_machdep.h @@ -35,7 +35,10 @@ typedef vm_offset_t db_addr_t; /* address - unsigned */ typedef int db_expr_t; /* expression - signed */ -#define PC_REGS() ((db_addr_t)kdb_thrctx->pcb_eip) +#define PC_REGS() ((db_addr_t)(kdb_frame->tf_eflags & PSL_VM ? \ + (kdb_frame->tf_eip & 0xffff) + \ + ((kdb_frame->tf_cs & 0xffff) << 4) : \ + kdb_frame->tf_eip)) #define BKPT_INST 0xcc /* breakpoint instruction */ #define BKPT_SIZE (1) /* size of breakpoint inst */ @@ -56,12 +59,16 @@ do { \ #define db_clear_single_step kdb_cpu_clear_singlestep #define db_set_single_step kdb_cpu_set_singlestep -#define IS_BREAKPOINT_TRAP(type, code) ((type) == T_BPTFLT) /* - * Watchpoints are not supported. The debug exception type is in %dr6 - * and not yet in the args to this macro. + * The debug exception type is copied from %dr6 to 'code' and used to + * disambiguate single step traps. Watchpoints have no special support. + * Our hardware breakpoints are not well integrated with ddb and are too + * different from watchpoints. ddb treats them as unknown traps with + * unknown addresses and doesn't turn them off while it is running. */ -#define IS_WATCHPOINT_TRAP(type, code) 0 +#define IS_BREAKPOINT_TRAP(type, code) ((type) == T_BPTFLT) +#define IS_SSTEP_TRAP(type, code) ((type) == T_TRCTRAP && (code) & 0x4000) +#define IS_WATCHPOINT_TRAP(type, code) 0 #define I_CALL 0xe8 #define I_CALLI 0xff @@ -91,4 +98,6 @@ do { \ #define DB_SMALL_VALUE_MAX 0x7fffffff #define DB_SMALL_VALUE_MIN (-0x400001) +int db_segsize(struct trapframe *tfp); + #endif /* !_MACHINE_DB_MACHDEP_H_ */ diff --git a/sys/x86/include/frame.h b/sys/x86/include/frame.h index a32ef7c..7418736 100644 --- a/sys/x86/include/frame.h +++ b/sys/x86/include/frame.h @@ -64,7 +64,7 @@ struct trapframe { int tf_eip; int tf_cs; int tf_eflags; - /* below only when crossing rings (e.g. user to kernel) */ + /* below only when crossing rings (user to kernel) */ int tf_esp; int tf_ss; }; @@ -89,15 +89,24 @@ struct trapframe_vm86 { int tf_eip; int tf_cs; int tf_eflags; - /* below only when crossing rings (e.g. user to kernel) */ + /* below only when crossing rings (user (including vm86) to kernel) */ int tf_esp; int tf_ss; - /* below only when switching out of VM86 mode */ + /* below only when crossing from vm86 mode to kernel */ int tf_vm86_es; int tf_vm86_ds; int tf_vm86_fs; int tf_vm86_gs; }; + +/* + * This alias for the MI TRAPF_USERMODE() should be used when we don't + * care about user mode itself, but need to know if a frame has stack + * registers. The difference is only logical, but on i386 the logic + * for using TRAPF_USERMODE() is complicated by sometimes treating vm86 + * bioscall mode (which is a special ring 3 user mode) as kernel mode. + */ +#define TF_HAS_STACKREGS(tf) TRAPF_USERMODE(tf) #endif /* __i386__ */ #ifdef __amd64__ @@ -136,6 +145,7 @@ struct trapframe { register_t tf_rip; register_t tf_cs; register_t tf_rflags; + /* the amd64 frame always has the stack registers */ register_t tf_rsp; register_t tf_ss; }; |