Add support for setting hardware breakpoints from ptrace on arm64.
Implement get/fill_dbregs on arm64. This is used by ptrace with the PT_GETDBREGS and PT_SETDBREGS requests. It allows userspace to set hardware breakpoints. The struct dbreg is based on Linux to ease adding hardware breakpoint support to debuggers. Reviewed by: jhb Sponsored by: DARPA, AFRL Differential Revision: https://reviews.freebsd.org/D22195
This commit is contained in:
parent
21da9f14f6
commit
05f39d1a2d
@ -36,6 +36,7 @@ __FBSDID("$FreeBSD$");
|
|||||||
#include <sys/types.h>
|
#include <sys/types.h>
|
||||||
#include <sys/kdb.h>
|
#include <sys/kdb.h>
|
||||||
#include <sys/pcpu.h>
|
#include <sys/pcpu.h>
|
||||||
|
#include <sys/proc.h>
|
||||||
#include <sys/systm.h>
|
#include <sys/systm.h>
|
||||||
|
|
||||||
#include <machine/armreg.h>
|
#include <machine/armreg.h>
|
||||||
@ -59,6 +60,10 @@ static struct debug_monitor_state kernel_monitor = {
|
|||||||
.dbg_flags = DBGMON_KERNEL
|
.dbg_flags = DBGMON_KERNEL
|
||||||
};
|
};
|
||||||
|
|
||||||
|
/* Called from the exception handlers */
|
||||||
|
void dbg_monitor_enter(struct thread *);
|
||||||
|
void dbg_monitor_exit(struct thread *, struct trapframe *);
|
||||||
|
|
||||||
/* Watchpoints/breakpoints control register bitfields */
|
/* Watchpoints/breakpoints control register bitfields */
|
||||||
#define DBG_WATCH_CTRL_LEN_1 (0x1 << 5)
|
#define DBG_WATCH_CTRL_LEN_1 (0x1 << 5)
|
||||||
#define DBG_WATCH_CTRL_LEN_2 (0x3 << 5)
|
#define DBG_WATCH_CTRL_LEN_2 (0x3 << 5)
|
||||||
@ -497,3 +502,57 @@ dbg_monitor_init(void)
|
|||||||
|
|
||||||
dbg_enable();
|
dbg_enable();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void
|
||||||
|
dbg_monitor_enter(struct thread *thread)
|
||||||
|
{
|
||||||
|
int i;
|
||||||
|
|
||||||
|
if ((kernel_monitor.dbg_flags & DBGMON_ENABLED) != 0) {
|
||||||
|
/* Install the kernel version of the registers */
|
||||||
|
dbg_register_sync(&kernel_monitor);
|
||||||
|
} else if ((thread->td_pcb->pcb_dbg_regs.dbg_flags & DBGMON_ENABLED) != 0) {
|
||||||
|
/* Disable the user breakpoints until we return to userspace */
|
||||||
|
for (i = 0; i < dbg_watchpoint_num; i++) {
|
||||||
|
dbg_wb_write_reg(DBG_REG_BASE_WCR, i, 0);
|
||||||
|
dbg_wb_write_reg(DBG_REG_BASE_WVR, i, 0);
|
||||||
|
}
|
||||||
|
|
||||||
|
for (i = 0; i < dbg_breakpoint_num; ++i) {
|
||||||
|
dbg_wb_write_reg(DBG_REG_BASE_BCR, i, 0);
|
||||||
|
dbg_wb_write_reg(DBG_REG_BASE_BVR, i, 0);
|
||||||
|
}
|
||||||
|
WRITE_SPECIALREG(mdscr_el1,
|
||||||
|
READ_SPECIALREG(mdscr_el1) &
|
||||||
|
~(DBG_MDSCR_MDE | DBG_MDSCR_KDE));
|
||||||
|
isb();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void
|
||||||
|
dbg_monitor_exit(struct thread *thread, struct trapframe *frame)
|
||||||
|
{
|
||||||
|
int i;
|
||||||
|
|
||||||
|
frame->tf_spsr |= PSR_D;
|
||||||
|
if ((thread->td_pcb->pcb_dbg_regs.dbg_flags & DBGMON_ENABLED) != 0) {
|
||||||
|
/* Install the kernel version of the registers */
|
||||||
|
dbg_register_sync(&thread->td_pcb->pcb_dbg_regs);
|
||||||
|
frame->tf_spsr &= ~PSR_D;
|
||||||
|
} else if ((kernel_monitor.dbg_flags & DBGMON_ENABLED) != 0) {
|
||||||
|
/* Disable the user breakpoints until we return to userspace */
|
||||||
|
for (i = 0; i < dbg_watchpoint_num; i++) {
|
||||||
|
dbg_wb_write_reg(DBG_REG_BASE_WCR, i, 0);
|
||||||
|
dbg_wb_write_reg(DBG_REG_BASE_WVR, i, 0);
|
||||||
|
}
|
||||||
|
|
||||||
|
for (i = 0; i < dbg_breakpoint_num; ++i) {
|
||||||
|
dbg_wb_write_reg(DBG_REG_BASE_BCR, i, 0);
|
||||||
|
dbg_wb_write_reg(DBG_REG_BASE_BVR, i, 0);
|
||||||
|
}
|
||||||
|
WRITE_SPECIALREG(mdscr_el1,
|
||||||
|
READ_SPECIALREG(mdscr_el1) &
|
||||||
|
~(DBG_MDSCR_MDE | DBG_MDSCR_KDE));
|
||||||
|
isb();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
@ -73,6 +73,9 @@ __FBSDID("$FreeBSD$");
|
|||||||
mov w0, #1
|
mov w0, #1
|
||||||
blr x1
|
blr x1
|
||||||
1:
|
1:
|
||||||
|
|
||||||
|
ldr x0, [x18, #(PC_CURTHREAD)]
|
||||||
|
bl dbg_monitor_enter
|
||||||
.endif
|
.endif
|
||||||
msr daifclr, #8 /* Enable the debug exception */
|
msr daifclr, #8 /* Enable the debug exception */
|
||||||
.endm
|
.endm
|
||||||
@ -87,6 +90,10 @@ __FBSDID("$FreeBSD$");
|
|||||||
msr daifset, #10
|
msr daifset, #10
|
||||||
.endif
|
.endif
|
||||||
.if \el == 0
|
.if \el == 0
|
||||||
|
ldr x0, [x18, #PC_CURTHREAD]
|
||||||
|
mov x1, sp
|
||||||
|
bl dbg_monitor_exit
|
||||||
|
|
||||||
/* Remove the SSBD (CVE-2018-3639) workaround if needed */
|
/* Remove the SSBD (CVE-2018-3639) workaround if needed */
|
||||||
ldr x1, [x18, #PC_SSBD]
|
ldr x1, [x18, #PC_SSBD]
|
||||||
cbz x1, 1f
|
cbz x1, 1f
|
||||||
|
@ -320,7 +320,7 @@ static struct mrs_field id_aa64dfr0_fields[] = {
|
|||||||
MRS_FIELD(ID_AA64DFR0, CTX_CMPs, false, MRS_EXACT,
|
MRS_FIELD(ID_AA64DFR0, CTX_CMPs, false, MRS_EXACT,
|
||||||
id_aa64dfr0_ctx_cmps),
|
id_aa64dfr0_ctx_cmps),
|
||||||
MRS_FIELD(ID_AA64DFR0, WRPs, false, MRS_EXACT, id_aa64dfr0_wrps),
|
MRS_FIELD(ID_AA64DFR0, WRPs, false, MRS_EXACT, id_aa64dfr0_wrps),
|
||||||
MRS_FIELD(ID_AA64DFR0, BRPs, false, MRS_EXACT, id_aa64dfr0_brps),
|
MRS_FIELD(ID_AA64DFR0, BRPs, false, MRS_LOWER, id_aa64dfr0_brps),
|
||||||
MRS_FIELD(ID_AA64DFR0, PMUVer, false, MRS_EXACT, id_aa64dfr0_pmuver),
|
MRS_FIELD(ID_AA64DFR0, PMUVer, false, MRS_EXACT, id_aa64dfr0_pmuver),
|
||||||
MRS_FIELD(ID_AA64DFR0, TraceVer, false, MRS_EXACT,
|
MRS_FIELD(ID_AA64DFR0, TraceVer, false, MRS_EXACT,
|
||||||
id_aa64dfr0_tracever),
|
id_aa64dfr0_tracever),
|
||||||
|
@ -281,17 +281,60 @@ set_fpregs(struct thread *td, struct fpreg *regs)
|
|||||||
int
|
int
|
||||||
fill_dbregs(struct thread *td, struct dbreg *regs)
|
fill_dbregs(struct thread *td, struct dbreg *regs)
|
||||||
{
|
{
|
||||||
|
struct debug_monitor_state *monitor;
|
||||||
|
int count, i;
|
||||||
|
uint8_t debug_ver, nbkpts;
|
||||||
|
|
||||||
printf("ARM64TODO: fill_dbregs");
|
memset(regs, 0, sizeof(*regs));
|
||||||
return (EDOOFUS);
|
|
||||||
|
extract_user_id_field(ID_AA64DFR0_EL1, ID_AA64DFR0_DebugVer_SHIFT,
|
||||||
|
&debug_ver);
|
||||||
|
extract_user_id_field(ID_AA64DFR0_EL1, ID_AA64DFR0_BRPs_SHIFT,
|
||||||
|
&nbkpts);
|
||||||
|
|
||||||
|
/*
|
||||||
|
* The BRPs field contains the number of breakpoints - 1. Armv8-A
|
||||||
|
* allows the hardware to provide 2-16 breakpoints so this won't
|
||||||
|
* overflow an 8 bit value.
|
||||||
|
*/
|
||||||
|
count = nbkpts + 1;
|
||||||
|
|
||||||
|
regs->db_info = debug_ver;
|
||||||
|
regs->db_info <<= 8;
|
||||||
|
regs->db_info |= count;
|
||||||
|
|
||||||
|
monitor = &td->td_pcb->pcb_dbg_regs;
|
||||||
|
if ((monitor->dbg_flags & DBGMON_ENABLED) != 0) {
|
||||||
|
for (i = 0; i < count; i++) {
|
||||||
|
regs->db_regs[i].dbr_addr = monitor->dbg_bvr[i];
|
||||||
|
regs->db_regs[i].dbr_ctrl = monitor->dbg_bcr[i];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return (0);
|
||||||
}
|
}
|
||||||
|
|
||||||
int
|
int
|
||||||
set_dbregs(struct thread *td, struct dbreg *regs)
|
set_dbregs(struct thread *td, struct dbreg *regs)
|
||||||
{
|
{
|
||||||
|
struct debug_monitor_state *monitor;
|
||||||
|
int count;
|
||||||
|
int i;
|
||||||
|
|
||||||
printf("ARM64TODO: set_dbregs");
|
monitor = &td->td_pcb->pcb_dbg_regs;
|
||||||
return (EDOOFUS);
|
count = 0;
|
||||||
|
monitor->dbg_enable_count = 0;
|
||||||
|
for (i = 0; i < DBG_BRP_MAX; i++) {
|
||||||
|
/* TODO: Check these values */
|
||||||
|
monitor->dbg_bvr[i] = regs->db_regs[i].dbr_addr;
|
||||||
|
monitor->dbg_bcr[i] = regs->db_regs[i].dbr_ctrl;
|
||||||
|
if ((monitor->dbg_bcr[i] & 1) != 0)
|
||||||
|
monitor->dbg_enable_count++;
|
||||||
|
}
|
||||||
|
if (monitor->dbg_enable_count > 0)
|
||||||
|
monitor->dbg_flags |= DBGMON_ENABLED;
|
||||||
|
|
||||||
|
return (0);
|
||||||
}
|
}
|
||||||
|
|
||||||
#ifdef COMPAT_FREEBSD32
|
#ifdef COMPAT_FREEBSD32
|
||||||
|
@ -482,6 +482,7 @@ do_el0_sync(struct thread *td, struct trapframe *frame)
|
|||||||
call_trapsignal(td, SIGBUS, BUS_ADRALN, (void *)frame->tf_elr);
|
call_trapsignal(td, SIGBUS, BUS_ADRALN, (void *)frame->tf_elr);
|
||||||
userret(td, frame);
|
userret(td, frame);
|
||||||
break;
|
break;
|
||||||
|
case EXCP_BRKPT_EL0:
|
||||||
case EXCP_BRK:
|
case EXCP_BRK:
|
||||||
call_trapsignal(td, SIGTRAP, TRAP_BRKPT, (void *)frame->tf_elr);
|
call_trapsignal(td, SIGTRAP, TRAP_BRKPT, (void *)frame->tf_elr);
|
||||||
userret(td, frame);
|
userret(td, frame);
|
||||||
|
@ -163,6 +163,7 @@
|
|||||||
#define EXCP_SP_ALIGN 0x26 /* SP slignment fault */
|
#define EXCP_SP_ALIGN 0x26 /* SP slignment fault */
|
||||||
#define EXCP_TRAP_FP 0x2c /* Trapped FP exception */
|
#define EXCP_TRAP_FP 0x2c /* Trapped FP exception */
|
||||||
#define EXCP_SERROR 0x2f /* SError interrupt */
|
#define EXCP_SERROR 0x2f /* SError interrupt */
|
||||||
|
#define EXCP_BRKPT_EL0 0x30 /* Hardware breakpoint, from same EL */
|
||||||
#define EXCP_SOFTSTP_EL0 0x32 /* Software Step, from lower EL */
|
#define EXCP_SOFTSTP_EL0 0x32 /* Software Step, from lower EL */
|
||||||
#define EXCP_SOFTSTP_EL1 0x33 /* Software Step, from same EL */
|
#define EXCP_SOFTSTP_EL1 0x33 /* Software Step, from same EL */
|
||||||
#define EXCP_WATCHPT_EL1 0x35 /* Watchpoint, from same EL */
|
#define EXCP_WATCHPT_EL1 0x35 /* Watchpoint, from same EL */
|
||||||
|
@ -31,6 +31,7 @@
|
|||||||
|
|
||||||
#ifndef LOCORE
|
#ifndef LOCORE
|
||||||
|
|
||||||
|
#include <machine/debug_monitor.h>
|
||||||
#include <machine/vfp.h>
|
#include <machine/vfp.h>
|
||||||
|
|
||||||
struct trapframe;
|
struct trapframe;
|
||||||
@ -66,6 +67,8 @@ struct pcb {
|
|||||||
* Place last to simplify the asm to access the rest if the struct.
|
* Place last to simplify the asm to access the rest if the struct.
|
||||||
*/
|
*/
|
||||||
struct vfpstate pcb_fpustate;
|
struct vfpstate pcb_fpustate;
|
||||||
|
|
||||||
|
struct debug_monitor_state pcb_dbg_regs;
|
||||||
};
|
};
|
||||||
|
|
||||||
#ifdef _KERNEL
|
#ifdef _KERNEL
|
||||||
|
@ -60,7 +60,14 @@ struct fpreg32 {
|
|||||||
};
|
};
|
||||||
|
|
||||||
struct dbreg {
|
struct dbreg {
|
||||||
int dummy;
|
uint32_t db_info;
|
||||||
|
uint32_t db_pad;
|
||||||
|
|
||||||
|
struct {
|
||||||
|
uint64_t dbr_addr;
|
||||||
|
uint32_t dbr_ctrl;
|
||||||
|
uint32_t dbr_pad;
|
||||||
|
} db_regs[16];
|
||||||
};
|
};
|
||||||
|
|
||||||
struct dbreg32 {
|
struct dbreg32 {
|
||||||
|
Loading…
x
Reference in New Issue
Block a user