aboutsummaryrefslogtreecommitdiffstats
path: root/tools/debugger/pdb/linux-2.6-module/debug.c
diff options
context:
space:
mode:
Diffstat (limited to 'tools/debugger/pdb/linux-2.6-module/debug.c')
-rw-r--r--tools/debugger/pdb/linux-2.6-module/debug.c535
1 files changed, 484 insertions, 51 deletions
diff --git a/tools/debugger/pdb/linux-2.6-module/debug.c b/tools/debugger/pdb/linux-2.6-module/debug.c
index 983e02dccc..ae1064ebad 100644
--- a/tools/debugger/pdb/linux-2.6-module/debug.c
+++ b/tools/debugger/pdb/linux-2.6-module/debug.c
@@ -9,33 +9,143 @@
#include <asm-i386/kdebug.h>
#include <asm-xen/asm-i386/processor.h>
#include <asm-xen/asm-i386/ptrace.h>
+#include <asm-xen/asm-i386/tlbflush.h>
#include <asm-xen/xen-public/xen.h>
#include "pdb_module.h"
#include "pdb_debug.h"
-#define BWC_DEBUG 1
-#define BWC_INT3 3
+
+static int pdb_debug_fn (struct pt_regs *regs, long error_code,
+ unsigned int condition);
+static int pdb_int3_fn (struct pt_regs *regs, long error_code);
+static int pdb_page_fault_fn (struct pt_regs *regs, long error_code,
+ unsigned int condition);
+
+/***********************************************************************/
+
typedef struct bwcpoint /* break/watch/catch point */
{
struct list_head list;
memory_t address;
- u32 domain;
- u32 process;
- u8 old_value; /* old value for software bkpt */
+ int length;
+
u8 type; /* BWC_??? */
+ u8 mode; /* for BWC_PAGE, the current protection mode */
+ u32 process;
+ u8 error; /* error occured when enabling: don't disable. */
+
+ /* original values */
+ u8 orig_bkpt; /* single byte breakpoint */
+ pte_t orig_pte;
+
+ struct list_head watchpt_read_list; /* read watchpoints on this page */
+ struct list_head watchpt_write_list; /* write */
+ struct list_head watchpt_access_list; /* access */
+ struct list_head watchpt_disabled_list; /* disabled */
+
+ struct bwcpoint *parent; /* watchpoint: bwc_watch (the page) */
+ struct bwcpoint *watchpoint; /* bwc_watch_step: original watchpoint */
} bwcpoint_t, *bwcpoint_p;
-static bwcpoint_t bwcpoint_list;
+static struct list_head bwcpoint_list = LIST_HEAD_INIT(bwcpoint_list);
+
+#define _pdb_bwcpoint_alloc(_var) \
+{ \
+ if ( (_var = kmalloc(sizeof(bwcpoint_t), GFP_KERNEL)) == NULL ) \
+ printk("error: unable to allocate memory %d\n", __LINE__); \
+ else { \
+ memset(_var, 0, sizeof(bwcpoint_t)); \
+ INIT_LIST_HEAD(&_var->watchpt_read_list); \
+ INIT_LIST_HEAD(&_var->watchpt_write_list); \
+ INIT_LIST_HEAD(&_var->watchpt_access_list); \
+ INIT_LIST_HEAD(&_var->watchpt_disabled_list); \
+ } \
+}
+
+/***********************************************************************/
+
+static void _pdb_bwc_print_list (struct list_head *, char *, int);
+
+static void
+_pdb_bwc_print (bwcpoint_p bwc, char *label, int level)
+{
+ printk("%s%03d 0x%08lx:0x%02x %c\n", label, bwc->type,
+ bwc->address, bwc->length, bwc->error ? 'e' : '-');
+
+ if ( !list_empty(&bwc->watchpt_read_list) )
+ _pdb_bwc_print_list(&bwc->watchpt_read_list, "r", level);
+ if ( !list_empty(&bwc->watchpt_write_list) )
+ _pdb_bwc_print_list(&bwc->watchpt_write_list, "w", level);
+ if ( !list_empty(&bwc->watchpt_access_list) )
+ _pdb_bwc_print_list(&bwc->watchpt_access_list, "a", level);
+ if ( !list_empty(&bwc->watchpt_disabled_list) )
+ _pdb_bwc_print_list(&bwc->watchpt_disabled_list, "d", level);
+}
+
+static void
+_pdb_bwc_print_list (struct list_head *bwc_list, char *label, int level)
+{
+ struct list_head *ptr;
+ int counter = 0;
+
+ list_for_each(ptr, bwc_list)
+ {
+ bwcpoint_p bwc = list_entry(ptr, bwcpoint_t, list);
+ printk(" %s[%02d]%s ", level > 0 ? " " : "", counter++,
+ level > 0 ? "" : " ");
+ _pdb_bwc_print(bwc, label, level+1);
+ }
+
+ if (counter == 0)
+ {
+ printk(" empty list\n");
+ }
+}
void
-pdb_initialize_bwcpoint (void)
+pdb_bwc_print_list (void)
+{
+ _pdb_bwc_print_list(&bwcpoint_list, " ", 0);
+}
+
+bwcpoint_p
+pdb_search_watchpoint (u32 process, memory_t address)
{
- memset((void *) &bwcpoint_list, 0, sizeof(bwcpoint_t));
- INIT_LIST_HEAD(&bwcpoint_list.list);
+ bwcpoint_p bwc_watch = (bwcpoint_p) 0;
+ bwcpoint_p bwc_entry = (bwcpoint_p) 0;
+ struct list_head *ptr;
- return;
+ list_for_each(ptr, &bwcpoint_list) /* find bwc page entry */
+ {
+ bwc_watch = list_entry(ptr, bwcpoint_t, list);
+ if (bwc_watch->address == (address & PAGE_MASK)) break;
+ }
+
+ if ( !bwc_watch )
+ {
+ return (bwcpoint_p) 0;
+ }
+
+#define __pdb_search_watchpoint_list(__list) \
+ list_for_each(ptr, (__list)) \
+ { \
+ bwc_entry = list_entry(ptr, bwcpoint_t, list); \
+ if ( bwc_entry->process == process && \
+ bwc_entry->address <= address && \
+ bwc_entry->address + bwc_entry->length > address ) \
+ return bwc_entry; \
+ }
+
+ __pdb_search_watchpoint_list(&bwc_watch->watchpt_read_list);
+ __pdb_search_watchpoint_list(&bwc_watch->watchpt_write_list);
+ __pdb_search_watchpoint_list(&bwc_watch->watchpt_access_list);
+
+#undef __pdb_search_watchpoint_list
+
+ return (bwcpoint_p) 0;
}
+/*************************************************************/
int
pdb_suspend (struct task_struct *target)
@@ -137,6 +247,35 @@ _pdb_set_register (struct task_struct *target, int reg, unsigned long val)
}
int
+pdb_read_register (struct task_struct *target, pdb_op_rd_reg_p op)
+{
+ int rc = 0;
+
+ switch (op->reg)
+ {
+ case 0: op->value = _pdb_get_register(target, LINUX_EAX); break;
+ case 1: op->value = _pdb_get_register(target, LINUX_ECX); break;
+ case 2: op->value = _pdb_get_register(target, LINUX_EDX); break;
+ case 3: op->value = _pdb_get_register(target, LINUX_EBX); break;
+ case 4: op->value = _pdb_get_register(target, LINUX_ESP); break;
+ case 5: op->value = _pdb_get_register(target, LINUX_EBP); break;
+ case 6: op->value = _pdb_get_register(target, LINUX_ESI); break;
+ case 7: op->value = _pdb_get_register(target, LINUX_EDI); break;
+ case 8: op->value = _pdb_get_register(target, LINUX_EIP); break;
+ case 9: op->value = _pdb_get_register(target, LINUX_EFL); break;
+
+ case 10: op->value = _pdb_get_register(target, LINUX_CS); break;
+ case 11: op->value = _pdb_get_register(target, LINUX_SS); break;
+ case 12: op->value = _pdb_get_register(target, LINUX_DS); break;
+ case 13: op->value = _pdb_get_register(target, LINUX_ES); break;
+ case 14: op->value = _pdb_get_register(target, LINUX_FS); break;
+ case 15: op->value = _pdb_get_register(target, LINUX_GS); break;
+ }
+
+ return rc;
+}
+
+int
pdb_read_registers (struct task_struct *target, pdb_op_rd_regs_p op)
{
int rc = 0;
@@ -209,18 +348,14 @@ pdb_step (struct task_struct *target)
eflags |= X86_EFLAGS_TF;
_pdb_set_register(target, LINUX_EFL, eflags);
- bkpt = kmalloc(sizeof(bwcpoint_t), GFP_KERNEL);
- if ( bkpt == NULL )
- {
- printk("error: unable to allocation memory\n");
- return -1;
- }
+ _pdb_bwcpoint_alloc(bkpt);
+ if ( bkpt == NULL ) return -1;
bkpt->process = target->pid;
bkpt->address = 0;
bkpt->type = BWC_DEBUG;
- list_add(&bkpt->list, &bwcpoint_list.list);
+ list_add_tail(&bkpt->list, &bwcpoint_list);
wake_up_process(target);
@@ -237,31 +372,27 @@ pdb_insert_memory_breakpoint (struct task_struct *target,
printk("insert breakpoint %d:%lx len: %d\n", target->pid, address, length);
- bkpt = kmalloc(sizeof(bwcpoint_t), GFP_KERNEL);
- if ( bkpt == NULL )
- {
- printk("error: unable to allocation memory\n");
- return -1;
- }
-
if ( length != 1 )
{
printk("error: breakpoint length should be 1\n");
- kfree(bkpt);
return -1;
}
+ _pdb_bwcpoint_alloc(bkpt);
+ if ( bkpt == NULL ) return -1;
+
bkpt->process = target->pid;
bkpt->address = address;
bkpt->type = BWC_INT3;
- pdb_access_memory(target, address, &bkpt->old_value, 1, 0);
- pdb_access_memory(target, address, &breakpoint_opcode, 1, 1);
+ pdb_access_memory(target, address, &bkpt->orig_bkpt, 1, PDB_MEM_READ);
+ pdb_access_memory(target, address, &breakpoint_opcode, 1, PDB_MEM_WRITE);
- list_add(&bkpt->list, &bwcpoint_list.list);
+ list_add_tail(&bkpt->list, &bwcpoint_list);
printk("breakpoint_set %d:%lx OLD: 0x%x\n",
- target->pid, address, bkpt->old_value);
+ target->pid, address, bkpt->orig_bkpt);
+ pdb_bwc_print_list();
return rc;
}
@@ -276,7 +407,7 @@ pdb_remove_memory_breakpoint (struct task_struct *target,
printk ("remove breakpoint %d:%lx\n", target->pid, address);
struct list_head *entry;
- list_for_each(entry, &bwcpoint_list.list)
+ list_for_each(entry, &bwcpoint_list)
{
bkpt = list_entry(entry, bwcpoint_t, list);
if ( target->pid == bkpt->process &&
@@ -285,17 +416,223 @@ pdb_remove_memory_breakpoint (struct task_struct *target,
break;
}
- if (bkpt == &bwcpoint_list || bkpt == NULL)
+ if (entry == &bwcpoint_list)
{
printk ("error: no breakpoint found\n");
return -1;
}
+ pdb_access_memory(target, address, &bkpt->orig_bkpt, 1, PDB_MEM_WRITE);
+
list_del(&bkpt->list);
+ kfree(bkpt);
- pdb_access_memory(target, address, &bkpt->old_value, 1, 1);
+ pdb_bwc_print_list();
- kfree(bkpt);
+ return rc;
+}
+
+#define PDB_PTE_UPDATE 1
+#define PDB_PTE_RESTORE 2
+
+int
+pdb_change_pte (struct task_struct *target, bwcpoint_p bwc, int mode)
+{
+ int rc = 0;
+ pgd_t *pgd;
+ pud_t *pud;
+ pmd_t *pmd;
+ pte_t *ptep;
+
+ pgd = pgd_offset(target->mm, bwc->address);
+ if (pgd_none(*pgd) || unlikely(pgd_bad(*pgd))) return -1;
+
+ pud = pud_offset(pgd, bwc->address);
+ if (pud_none(*pud) || unlikely(pud_bad(*pud))) return -2;
+
+ pmd = pmd_offset(pud, bwc->address);
+ if (pmd_none(*pmd) || unlikely(pmd_bad(*pmd))) return -3;
+
+ ptep = pte_offset_map(pmd, bwc->address);
+ if (!ptep) return -4;
+
+ switch ( mode )
+ {
+ case PDB_PTE_UPDATE: /* added or removed a watchpoint. update pte. */
+ {
+ pte_t new_pte;
+
+ if ( pte_val(bwc->parent->orig_pte) == 0 ) /* new watchpoint page */
+ {
+ bwc->parent->orig_pte = *ptep;
+ }
+
+ new_pte = bwc->parent->orig_pte;
+
+ if ( !list_empty(&bwc->parent->watchpt_read_list) ||
+ !list_empty(&bwc->parent->watchpt_access_list) )
+ {
+ new_pte = pte_rdprotect(new_pte);
+ }
+
+ if ( !list_empty(&bwc->parent->watchpt_write_list) ||
+ !list_empty(&bwc->parent->watchpt_access_list) )
+ {
+ new_pte = pte_wrprotect(new_pte);
+ }
+
+ if ( pte_val(new_pte) != pte_val(*ptep) )
+ {
+ *ptep = new_pte;
+ flush_tlb_mm(target->mm);
+ }
+ break;
+ }
+ case PDB_PTE_RESTORE : /* suspend watchpoint by restoring original pte */
+ {
+ *ptep = bwc->parent->orig_pte;
+ flush_tlb_mm(target->mm);
+ break;
+ }
+ default :
+ {
+ printk("(linux) unknown mode %d %d\n", mode, __LINE__);
+ break;
+ }
+ }
+
+ pte_unmap(ptep); /* can i flush the tlb before pte_unmap? */
+
+ return rc;
+}
+
+int
+pdb_insert_watchpoint (struct task_struct *target, pdb_op_watchpt_p watchpt)
+{
+ int rc = 0;
+
+ bwcpoint_p bwc_watch;
+ bwcpoint_p bwc_entry;
+ struct list_head *ptr;
+ unsigned long page = watchpt->address & PAGE_MASK;
+ struct list_head *watchpoint_list;
+
+ printk("insert watchpoint: %d %x %x\n",
+ watchpt->type, watchpt->address, watchpt->length);
+
+ list_for_each(ptr, &bwcpoint_list) /* find existing bwc page entry */
+ {
+ bwc_watch = list_entry(ptr, bwcpoint_t, list);
+
+ if (bwc_watch->address == page) goto got_bwc_watch;
+ }
+
+ _pdb_bwcpoint_alloc(bwc_watch); /* create new bwc:watch */
+ if ( bwc_watch == NULL ) return -1;
+
+ bwc_watch->type = BWC_WATCH;
+ bwc_watch->process = target->pid;
+ bwc_watch->address = page;
+
+ list_add_tail(&bwc_watch->list, &bwcpoint_list);
+
+ got_bwc_watch:
+
+ switch (watchpt->type)
+ {
+ case BWC_WATCH_READ:
+ watchpoint_list = &bwc_watch->watchpt_read_list; break;
+ case BWC_WATCH_WRITE:
+ watchpoint_list = &bwc_watch->watchpt_write_list; break;
+ case BWC_WATCH_ACCESS:
+ watchpoint_list = &bwc_watch->watchpt_access_list; break;
+ default:
+ printk("unknown type %d\n", watchpt->type); return -2;
+ }
+
+ _pdb_bwcpoint_alloc(bwc_entry); /* create new bwc:entry */
+ if ( bwc_entry == NULL ) return -1;
+
+ bwc_entry->process = target->pid;
+ bwc_entry->address = watchpt->address;
+ bwc_entry->length = watchpt->length;
+ bwc_entry->type = watchpt->type;
+ bwc_entry->parent = bwc_watch;
+
+ list_add_tail(&bwc_entry->list, watchpoint_list);
+ pdb_change_pte(target, bwc_entry, PDB_PTE_UPDATE);
+
+ pdb_bwc_print_list();
+
+ return rc;
+}
+
+int
+pdb_remove_watchpoint (struct task_struct *target, pdb_op_watchpt_p watchpt)
+{
+ int rc = 0;
+ bwcpoint_p bwc_watch = (bwcpoint_p) NULL;
+ bwcpoint_p bwc_entry = (bwcpoint_p) NULL;
+ unsigned long page = watchpt->address & PAGE_MASK;
+ struct list_head *ptr;
+ struct list_head *watchpoint_list;
+
+ printk("remove watchpoint: %d %x %x\n",
+ watchpt->type, watchpt->address, watchpt->length);
+
+ list_for_each(ptr, &bwcpoint_list) /* find bwc page entry */
+ {
+ bwc_watch = list_entry(ptr, bwcpoint_t, list);
+ if (bwc_watch->address == page) break;
+ }
+
+ if ( !bwc_watch )
+ {
+ printk("(linux) delete watchpoint: can't find bwc page 0x%08x\n",
+ watchpt->address);
+ return -1;
+ }
+
+ switch (watchpt->type)
+ {
+ case BWC_WATCH_READ:
+ watchpoint_list = &bwc_watch->watchpt_read_list; break;
+ case BWC_WATCH_WRITE:
+ watchpoint_list = &bwc_watch->watchpt_write_list; break;
+ case BWC_WATCH_ACCESS:
+ watchpoint_list = &bwc_watch->watchpt_access_list; break;
+ default:
+ printk("unknown type %d\n", watchpt->type); return -2;
+ }
+
+ list_for_each(ptr, watchpoint_list) /* find watchpoint */
+ {
+ bwc_entry = list_entry(ptr, bwcpoint_t, list);
+ if ( bwc_entry->address == watchpt->address &&
+ bwc_entry->length == watchpt->length ) break;
+ }
+
+ if ( !bwc_entry ) /* or ptr == watchpoint_list */
+ {
+ printk("(linux) delete watchpoint: can't find watchpoint 0x%08x\n",
+ watchpt->address);
+ return -1;
+ }
+
+ list_del(&bwc_entry->list);
+ pdb_change_pte(target, bwc_entry, PDB_PTE_UPDATE);
+ kfree(bwc_entry);
+
+
+ if ( list_empty(&bwc_watch->watchpt_read_list) &&
+ list_empty(&bwc_watch->watchpt_write_list) &&
+ list_empty(&bwc_watch->watchpt_access_list) )
+ {
+ list_del(&bwc_watch->list);
+ kfree(bwc_watch);
+ }
+
+ pdb_bwc_print_list();
return rc;
}
@@ -312,16 +649,24 @@ pdb_exceptions_notify (struct notifier_block *self, unsigned long val,
switch (val)
{
case DIE_DEBUG:
- if (pdb_debug_fn(args->regs, args->trapnr, args->err))
+ if ( pdb_debug_fn(args->regs, args->trapnr, args->err) )
return NOTIFY_STOP;
break;
case DIE_TRAP:
- if (args->trapnr == 3 && pdb_int3_fn(args->regs, args->err))
+ if ( args->trapnr == 3 && pdb_int3_fn(args->regs, args->err) )
return NOTIFY_STOP;
break;
case DIE_INT3: /* without kprobes, we should never see DIE_INT3 */
- case DIE_GPF:
+ if ( pdb_int3_fn(args->regs, args->err) )
+ return NOTIFY_STOP;
+ break;
case DIE_PAGE_FAULT:
+ if ( pdb_page_fault_fn(args->regs, args->trapnr, args->err) )
+ return NOTIFY_STOP;
+ break;
+ case DIE_GPF:
+ printk("---------------GPF\n");
+ break;
default:
break;
}
@@ -330,70 +675,110 @@ pdb_exceptions_notify (struct notifier_block *self, unsigned long val,
}
-int
+static int
pdb_debug_fn (struct pt_regs *regs, long error_code,
unsigned int condition)
{
pdb_response_t resp;
bwcpoint_p bkpt = NULL;
-
struct list_head *entry;
- list_for_each(entry, &bwcpoint_list.list)
+
+ printk("pdb_debug_fn\n");
+
+ list_for_each(entry, &bwcpoint_list)
{
bkpt = list_entry(entry, bwcpoint_t, list);
if ( current->pid == bkpt->process &&
- bkpt->type == BWC_DEBUG )
+ (bkpt->type == BWC_DEBUG || /* single step */
+ bkpt->type == BWC_WATCH_STEP)) /* single step over watchpoint */
break;
}
- if (bkpt == &bwcpoint_list || bkpt == NULL)
+ if (entry == &bwcpoint_list)
{
printk("not my debug 0x%x 0x%lx\n", current->pid, regs->eip);
return 0;
}
- list_del(&bkpt->list);
-
pdb_suspend(current);
- printk("(pdb) debug pid: %d, eip: 0x%08lx\n", current->pid, regs->eip);
+ printk("(pdb) %s pid: %d, eip: 0x%08lx\n",
+ bkpt->type == BWC_DEBUG ? "debug" : "watch-step",
+ current->pid, regs->eip);
regs->eflags &= ~X86_EFLAGS_TF;
set_tsk_thread_flag(current, TIF_SINGLESTEP);
- resp.operation = PDB_OPCODE_STEP;
+ switch (bkpt->type)
+ {
+ case BWC_DEBUG:
+ resp.operation = PDB_OPCODE_STEP;
+ break;
+ case BWC_WATCH_STEP:
+ {
+ struct list_head *watchpoint_list;
+ bwcpoint_p watch_page = bkpt->watchpoint->parent;
+
+ switch (bkpt->watchpoint->type)
+ {
+ case BWC_WATCH_READ:
+ watchpoint_list = &watch_page->watchpt_read_list; break;
+ case BWC_WATCH_WRITE:
+ watchpoint_list = &watch_page->watchpt_write_list; break;
+ case BWC_WATCH_ACCESS:
+ watchpoint_list = &watch_page->watchpt_access_list; break;
+ default:
+ printk("unknown type %d\n", bkpt->watchpoint->type); return 0;
+ }
+
+ resp.operation = PDB_OPCODE_WATCHPOINT;
+ list_del_init(&bkpt->watchpoint->list);
+ list_add_tail(&bkpt->watchpoint->list, watchpoint_list);
+ pdb_change_pte(current, bkpt->watchpoint, PDB_PTE_UPDATE);
+ pdb_bwc_print_list();
+ break;
+ }
+ default:
+ printk("unknown breakpoint type %d %d\n", __LINE__, bkpt->type);
+ return 0;
+ }
+
resp.process = current->pid;
resp.status = PDB_RESPONSE_OKAY;
pdb_send_response(&resp);
+ list_del(&bkpt->list);
+ kfree(bkpt);
+
return 1;
}
-int
+static int
pdb_int3_fn (struct pt_regs *regs, long error_code)
{
pdb_response_t resp;
bwcpoint_p bkpt = NULL;
+ memory_t address = regs->eip - 1;
struct list_head *entry;
- list_for_each(entry, &bwcpoint_list.list)
+ list_for_each(entry, &bwcpoint_list)
{
bkpt = list_entry(entry, bwcpoint_t, list);
if ( current->pid == bkpt->process &&
- regs->eip == bkpt->address &&
+ address == bkpt->address &&
bkpt->type == BWC_INT3 )
break;
}
- if (bkpt == &bwcpoint_list || bkpt == NULL)
+ if (entry == &bwcpoint_list)
{
- printk("not my int3 bkpt 0x%x 0x%lx\n", current->pid, regs->eip);
+ printk("not my int3 bkpt 0x%x 0x%lx\n", current->pid, address);
return 0;
}
- printk("(pdb) int3 pid: %d, eip: 0x%08lx\n", current->pid, regs->eip);
+ printk("(pdb) int3 pid: %d, eip: 0x%08lx\n", current->pid, address);
pdb_suspend(current);
@@ -406,6 +791,54 @@ pdb_int3_fn (struct pt_regs *regs, long error_code)
return 1;
}
+static int
+pdb_page_fault_fn (struct pt_regs *regs, long error_code,
+ unsigned int condition)
+{
+ unsigned long cr2;
+ unsigned long cr3;
+ bwcpoint_p bwc;
+ bwcpoint_p watchpt;
+ bwcpoint_p bkpt;
+
+ __asm__ __volatile__ ("movl %%cr3,%0" : "=r" (cr3) : );
+ __asm__ __volatile__ ("movl %%cr2,%0" : "=r" (cr2) : );
+
+ bwc = pdb_search_watchpoint(current->pid, cr2);
+ if ( !bwc )
+ {
+ return 0; /* not mine */
+ }
+
+ printk("page_fault cr2:%08lx err:%lx eip:%08lx\n",
+ cr2, error_code, regs->eip);
+
+ /* disable the watchpoint */
+ watchpt = bwc->watchpoint;
+ list_del_init(&bwc->list);
+ list_add_tail(&bwc->list, &bwc->parent->watchpt_disabled_list);
+ pdb_change_pte(current, bwc, PDB_PTE_RESTORE);
+
+ /* single step the faulting instruction */
+ regs->eflags |= X86_EFLAGS_TF;
+
+ /* create a bwcpoint entry so we know what to do once we regain control */
+ _pdb_bwcpoint_alloc(bkpt);
+ if ( bkpt == NULL ) return -1;
+
+ bkpt->process = current->pid;
+ bkpt->address = 0;
+ bkpt->type = BWC_WATCH_STEP;
+ bkpt->watchpoint = bwc;
+
+ /* add to head so we see it first the next time we break */
+ list_add(&bkpt->list, &bwcpoint_list);
+
+ pdb_bwc_print_list();
+ return 1;
+}
+
+
/*
* Local variables:
* mode: C