Project

General

Profile

Actions

Bug #831

closed

vkernel can not access debug registers

Added by nthery about 17 years ago. Updated almost 17 years ago.

Status:
Closed
Priority:
Normal
Assignee:
Category:
-
Target version:
-
Start date:
Due date:
% Done:

0%

Estimated time:

Description

I mistakenly pressed 'h' to get help in the vkernel ddb and got a bus error.

ddb interprets 'h' as 'hwatch' (set hardware watchpoint). This calls
db_md_set_watchpoint() which in turn calls fill_dbregs(). The latter
tries to access debug registers which triggers a GPF as the vkernel
runs in user mode.

Using 'show registers' also results in a GPF for the same reason.

This patch removes debug register code from the vkernel.

Index: dfly/src/sys/platform/vkernel/i386/db_trace.c ===================================================================
--- dfly.orig/src/sys/platform/vkernel/i386/db_trace.c 2007-10-27
09:50:50.108742000 0200
++ dfly/src/sys/platform/vkernel/i386/db_trace.c 2007-10-27
09:51:38.000000000 +0200
@ -50,14 +50,7 @
#include <ddb/db_sym.h>
#include <ddb/db_variables.h>

-db_varfcn_t db_dr0;
-db_varfcn_t db_dr1;
-db_varfcn_t db_dr2;
-db_varfcn_t db_dr3;
-db_varfcn_t db_dr4;
-db_varfcn_t db_dr5;
-db_varfcn_t db_dr6;
-db_varfcn_t db_dr7;
+static int db_dr(struct db_variable *vp, db_expr_t *valuep, int op);

/*
 * Machine register set.
@ -79,14 +72,14 @ { "edi", &ddb_regs.tf_edi, FCN_NULL }, { "eip", &ddb_regs.tf_eip, FCN_NULL }, { "efl", &ddb_regs.tf_eflags, FCN_NULL },
- { "dr0", NULL, db_dr0 },
- { "dr1", NULL, db_dr1 },
- { "dr2", NULL, db_dr2 },
- { "dr3", NULL, db_dr3 },
- { "dr4", NULL, db_dr4 },
- { "dr5", NULL, db_dr5 },
- { "dr6", NULL, db_dr6 },
- { "dr7", NULL, db_dr7 },
+ { "dr0", NULL, db_dr },
+ { "dr1", NULL, db_dr },
+ { "dr2", NULL, db_dr },
+ { "dr3", NULL, db_dr },
+ { "dr4", NULL, db_dr },
+ { "dr5", NULL, db_dr },
+ { "dr6", NULL, db_dr },
+ { "dr7", NULL, db_dr },
};
struct db_variable *db_eregs = db_regs + sizeof(db_regs)/sizeof(db_regs[0]);

@ -112,10 +105,6 @
static void dl_symbol_values(int callpc, const char **name);

static char *watchtype_str(int type);
-static int ki386_set_watch(int watchnum, unsigned int watchaddr,
int size, int access, struct dbreg * d);
-static int ki386_clr_watch(int watchnum, struct dbreg * d);
int db_md_set_watchpoint(db_expr_t addr, db_expr_t size);
int db_md_clr_watchpoint(db_expr_t addr, db_expr_t size);
void db_md_list_watchpoints(void);
@ -405,201 +394,30 @
db_stack_trace_cmd(ebp, 1, -1, NULL);
}

#define DB_DRX_FUNC(reg) \
-int \
-db_ ## reg (struct db_variable *vp, db_expr_t *valuep, int op) \
{ \
- if (op == DB_VAR_GET) \
- *valuep = r ## reg (); \
- else \
- load_ ## reg (*valuep); \
- \
- return(0); \
}

DB_DRX_FUNC(dr0)
-DB_DRX_FUNC(dr1)
-DB_DRX_FUNC(dr2)
-DB_DRX_FUNC(dr3)
-DB_DRX_FUNC(dr4)
-DB_DRX_FUNC(dr5)
-DB_DRX_FUNC(dr6)
-DB_DRX_FUNC(dr7)

static int
ki386_set_watch(int watchnum, unsigned int watchaddr, int size, int access,
struct dbreg *d)
+db_dr(struct db_variable *vp, db_expr_t *valuep, int op) {
- int i;
- unsigned int mask;
-
- if (watchnum == 1) {
for (i = 0, mask = 0x3; i < 4; i++, mask <<= 2)
- if ((d->dr7 & mask) == 0)
- break;
- if (i < 4)
- watchnum = i;
- else
- return(1);
}
-
- switch (access) {
- case DBREG_DR7_EXEC:
- size = 1; /* size must be 1 for an execution breakpoint /
- /
fall through /
- case DBREG_DR7_WRONLY:
- case DBREG_DR7_RDWR:
- break;
- default:
- return(1);
}

/

- * we can watch a 1, 2, or 4 byte sized location
- /
- switch (size) {
- case 1:
- mask = 0x00;
- break;
- case 2:
- mask = 0x01 << 2;
- break;
- case 4:
- mask = 0x03 << 2;
- break;
- default:
- return(1);
}

mask |= access;

/
clear the bits we are about to affect /
- d->dr7 &= ~((0x3 << (watchnum * 2)) | (0x0f << (watchnum * 4 + 16)));

/
set drN register to the address, N=watchnum /
- DBREG_DRX(d, watchnum) = watchaddr;

/
enable the watchpoint */
- d->dr7 |= (0x2 << (watchnum * 2)) | (mask << (watchnum * 4 + 16));

return(watchnum);
+ if (op == DB_VAR_GET)
+ *valuep = 0;
+ return(-1);
}


-int
-ki386_clr_watch(int watchnum, struct dbreg *d)
{
- if (watchnum < 0 || watchnum >= 4)
- return(1);

- d->dr7 &= ~((0x3 << (watchnum * 2)) | (0x0f << (watchnum * 4 + 16)));
- DBREG_DRX(d, watchnum) = 0;
-
- return(0);
}

-
int
db_md_set_watchpoint(db_expr_t addr, db_expr_t size) {
- int avail, wsize;
- int i;
- struct dbreg d;
-
- fill_dbregs(NULL, &d);
-
- avail = 0;
- for(i=0; i < 4; i++) {
- if ((d.dr7 & (3 << (i * 2))) == 0)
- avail++;
- }
-
- if (avail * 4 < size)
- return(1);

- for (i=0; i < 4 && (size != 0); i++) {
- if ((d.dr7 & (3 << (i * 2))) == 0) {
- if (size > 4)
- wsize = 4;
- else
- wsize = size;
- if (wsize == 3)
- wsize++;
- ki386_set_watch(i, addr, wsize, DBREG_DR7_WRONLY, &d);
- addr = wsize;
- size = wsize;
}
- }

set_dbregs(NULL, &d);

return(0);
return(-1);
} ===================================================================
--- dfly.orig/src/sys/platform/vkernel/i386/cpu_regs.c 2007-10-27
09:50:50.109229000 0200
++ dfly/src/sys/platform/vkernel/i386/cpu_regs.c 2007-10-27
09:51:38.000000000 +0200
@ -1056,115 +1056,13 @
int
fill_dbregs(struct lwp *lp, struct dbreg *dbregs) {
- if (lp == NULL) {
- dbregs->dr0 = rdr0();
- dbregs->dr1 = rdr1();
- dbregs->dr2 = rdr2();
- dbregs->dr3 = rdr3();
- dbregs->dr4 = rdr4();
- dbregs->dr5 = rdr5();
- dbregs->dr6 = rdr6();
- dbregs->dr7 = rdr7();
- } else {
- struct pcb *pcb;

pcb = lp->lwp_thread->td_pcb;
- dbregs->dr0 = pcb->pcb_dr0;
- dbregs->dr1 = pcb->pcb_dr1;
- dbregs->dr2 = pcb->pcb_dr2;
- dbregs->dr3 = pcb->pcb_dr3;
- dbregs->dr4 = 0;
- dbregs->dr5 = 0;
- dbregs->dr6 = pcb->pcb_dr6;
- dbregs->dr7 = pcb->pcb_dr7;
- }
- return (0);
+ return (ENOSYS);
}

int
set_dbregs(struct lwp *lp, struct dbreg *dbregs) {
- if (lp == NULL) {
- load_dr0(dbregs->dr0);
- load_dr1(dbregs->dr1);
- load_dr2(dbregs->dr2);
- load_dr3(dbregs->dr3);
- load_dr4(dbregs->dr4);
- load_dr5(dbregs->dr5);
- load_dr6(dbregs->dr6);
- load_dr7(dbregs->dr7);
- } else {
- struct pcb pcb;
- struct ucred *ucred;
- int i;
- uint32_t mask1, mask2;

/

- * Don't let an illegal value for dr7 get set. Specifically,
- * check for undefined settings. Setting these bit patterns
- * result in undefined behaviour and can lead to an unexpected
- * TRCTRAP.
- /
- for (i = 0, mask1 = 0x3<&lt;16, mask2 = 0x2<&lt;16; i < 8;
- i++, mask1 <<= 2, mask2 <<= 2)
- if ((dbregs->dr7 & mask1) == mask2)
- return (EINVAL);
-
- pcb = lp->lwp_thread->td_pcb;
- ucred = lp->lwp_proc->p_ucred;

/

- * Don't let a process set a breakpoint that is not within the
- * process's address space. If a process could do this, it
- * could halt the system by setting a breakpoint in the kernel
- * (if ddb was enabled). Thus, we need to check to make sure
- * that no breakpoints are being enabled for addresses outside
- * process's address space, unless, perhaps, we were called by
- * uid 0.
-
- * XXX - what about when the watched area of the user's
- * address space is written into from within the kernel
- * ... wouldn't that still cause a breakpoint to be generated
- * from within kernel mode?
- */

if (suser_cred(ucred, 0) != 0) {
- if (dbregs->dr7 & 0x3) {
- /
dr0 is enabled /
- if (dbregs->dr0 >= VM_MAX_USER_ADDRESS)
- return (EINVAL);
- }

if (dbregs->dr7 & (0x3<&lt;2)) {
- /
dr1 is enabled /
- if (dbregs->dr1 >= VM_MAX_USER_ADDRESS)
- return (EINVAL);
- }

if (dbregs->dr7 & (0x3<&lt;4)) {
- /
dr2 is enabled /
- if (dbregs->dr2 >= VM_MAX_USER_ADDRESS)
- return (EINVAL);
- }

if (dbregs->dr7 & (0x3<&lt;6)) {
- /
dr3 is enabled */
- if (dbregs->dr3 >= VM_MAX_USER_ADDRESS)
- return (EINVAL);
- }
- }

pcb->pcb_dr0 = dbregs->dr0;
- pcb->pcb_dr1 = dbregs->dr1;
- pcb->pcb_dr2 = dbregs->dr2;
- pcb->pcb_dr3 = dbregs->dr3;
- pcb->pcb_dr6 = dbregs->dr6;
- pcb->pcb_dr7 = dbregs->dr7;

pcb->pcb_flags |= PCB_DBREGS;
- }

return (0);
+ return (ENOSYS);
}
#if 0
Actions

Also available in: Atom PDF