#ifdef CONFIG_HARDWARE_DEBUG_API
#include <string.h>
#include <util.h>
#include <arch/model/statedata.h>
#include <arch/machine/debug.h>
#include <arch/kernel/vspace.h>
#include <arch/machine/registerset.h>
#include <armv/debug.h>
#include <mode/machine/debug.h>
#include <plat/machine/devices.h>
#include <api/constants.h>
#define DBGDSCR_MDBGEN (BIT(15))
#define DBGDSCR_HDBGEN (BIT(14))
#define DBGDSCR_USER_ACCESS_DISABLE (BIT(12))
#define DBGLSR_LOCK_IMPLEMENTED (BIT(0))
#define DBGLSR_LOCK_ENABLED (BIT(1))
#define DBGLAR_UNLOCK_VALUE (0xC5ACCE55u)
#define DBGOSLAR_LOCK_VALUE (0xC5ACCE55u)
#define DBGOSLSR_GET_OSLOCK_MODEL(v) ((((v) >> 2u) & 0x2u) | ((v) & 0x1u))
#define DBGOSLSR_LOCK_MODEL_NO_OSLOCK (0u)
#define DBGOSLSR_LOCK_MODEL_OSLOCK_AND_OSSR (1u)
#define DBGOSLSR_LOCK_MODEL_OSLOCK_ONLY (2u)
#define DBGPRSR_OSLOCK (BIT(5))
#define DBGPRSR_OS_DLOCK (BIT(6))
#define DBGOSDLR_LOCK_ENABLE (BIT(0))
#define DBGAUTHSTATUS_NSI_IMPLEMENTED (BIT(1))
#define DBGAUTHSTATUS_NSI_ENABLED (BIT(0))
#define DBGAUTHSTATUS_SI_IMPLEMENTED (BIT(5))
#define DBGAUTHSTATUS_SI_ENABLED (BIT(4))
#define DBGDRAR_VALID (MASK(2))
#define DBGDSAR_VALID (MASK(2))
#define DBGSDER_ENABLE_SECURE_USER_INVASIVE_DEBUG (BIT(0))
enum breakpoint_privilege {
DBGBCR_PRIV_RESERVED = 0u,
DBGBCR_PRIV_PRIVILEGED = 1u,
DBGBCR_PRIV_USER = 2u,
DBGBCR_BCR_PRIV_EITHER = 3u
};
enum watchpoint_privilege {
DBGWCR_PRIV_RESERVED = 0u,
DBGWCR_PRIV_PRIVILEGED = 1u,
DBGWCR_PRIV_USER = 2u,
DBGWCR_PRIV_EITHER = 3u
};
enum watchpoint_access {
DBGWCR_ACCESS_RESERVED = 0u,
DBGWCR_ACCESS_LOAD = 1u,
DBGWCR_ACCESS_STORE = 2u,
DBGWCR_ACCESS_EITHER = 3u
};
typedef struct debug_state {
bool_t is_available, coprocessor_is_baseline_only, watchpoint_8b_supported,
non_secure_invasive_unavailable, secure_invasive_unavailable,
cpu_is_in_secure_mode, single_step_supported, breakpoints_supported,
watchpoints_supported;
uint8_t debug_armv;
uint8_t didr_version, oem_variant, oem_revision;
} debug_state_t;
static debug_state_t dbg;
bool_t
byte8WatchpointsSupported(void)
{
return dbg.watchpoint_8b_supported;
}
#define SCR "p15, 0, %0, c1, c1, 0"
#define DBGDIDR "p14,0,%0,c0,c0,0"
#define DBGDRCR ""
#define DBGVCR "p15, 0, %0, c0, c7, 0"
#define DBGDRAR_32 "p14,0,%0,c1,c0,0"
#define DBGDRAR_64 "p14,0,%Q0,%R0,c1"
#define DBGDSAR_32 "p14,0,%0,c2,c0,0"
#define DBGDSAR_64 "p14,0,%Q0,%R0,c2"
#define DBGPRSR "p14, 0, %0, c1, c5, 4"
#define DBGOSLAR "p14,0,%0,c1,c0,4"
#define DBGOSLSR "p14,0,%0,c1,c1,4"
#define DBGOSDLR "p14, 0, %0, c1, c3, 4"
#define DBGDEVID2 "p14,0,%0,c7,c0,7"
#define DBGDEVID1 "p14,0,%0,c7,c1,7"
#define DBGDEVID "p14,0,%0,c7,c2,7"
#define DBGDEVTYPE ""
#define DBGAUTHSTATUS "p14,0,%0,c7,c14,6"
#endif
#if !defined(CONFIG_VERIFICATION_BUILD) && (defined(CONFIG_HARDWARE_DEBUG_API) || defined(CONFIG_ARM_HYPERVISOR_SUPPORT))
#define DBGBCR_ENABLE (BIT(0))
#define DBGWCR_ENABLE (BIT(0))
#define MAKE_P14(crn, crm, opc2) "p14, 0, %0, c" #crn ", c" #crm ", " #opc2
#define MAKE_DBGBVR(num) MAKE_P14(0, num, 4)
#define MAKE_DBGBCR(num) MAKE_P14(0, num, 5)
#define MAKE_DBGWVR(num) MAKE_P14(0, num, 6)
#define MAKE_DBGWCR(num) MAKE_P14(0, num, 7)
#define MAKE_DBGXVR(num) MAKE_P14(1, num, 1)
#define DEBUG_GENERATE_READ_FN(_name, _reg) \
static word_t \
_name(uint16_t bp_num) \
{ \
word_t ret; \
\
switch (bp_num) { \
case 1: \
MRC(MAKE_ ## _reg(1), ret); \
return ret; \
case 2: \
MRC(MAKE_ ## _reg(2), ret); \
return ret; \
case 3: \
MRC(MAKE_ ## _reg(3), ret); \
return ret; \
case 4: \
MRC(MAKE_ ## _reg(4), ret); \
return ret; \
case 5: \
MRC(MAKE_ ## _reg(5), ret); \
return ret; \
case 6: \
MRC(MAKE_ ## _reg(6), ret); \
return ret; \
case 7: \
MRC(MAKE_ ## _reg(7), ret); \
return ret; \
case 8: \
MRC(MAKE_ ## _reg(8), ret); \
return ret; \
case 9: \
MRC(MAKE_ ## _reg(9), ret); \
return ret; \
case 10: \
MRC(MAKE_ ## _reg(10), ret); \
return ret; \
case 11: \
MRC(MAKE_ ## _reg(11), ret); \
return ret; \
case 12: \
MRC(MAKE_ ## _reg(12), ret); \
return ret; \
case 13: \
MRC(MAKE_ ## _reg(13), ret); \
return ret; \
case 14: \
MRC(MAKE_ ## _reg(14), ret); \
return ret; \
case 15: \
MRC(MAKE_ ## _reg(15), ret); \
return ret; \
default: \
assert(bp_num == 0); \
MRC(MAKE_ ## _reg(0), ret); \
return ret; \
} \
}
#define DEBUG_GENERATE_WRITE_FN(_name, _reg) \
static void \
_name(uint16_t bp_num, word_t val) \
{ \
switch (bp_num) { \
case 1: \
MCR(MAKE_ ## _reg(1), val); \
return; \
case 2: \
MCR(MAKE_ ## _reg(2), val); \
return; \
case 3: \
MCR(MAKE_ ## _reg(3), val); \
return; \
case 4: \
MCR(MAKE_ ## _reg(4), val); \
return; \
case 5: \
MCR(MAKE_ ## _reg(5), val); \
return; \
case 6: \
MCR(MAKE_ ## _reg(6), val); \
return; \
case 7: \
MCR(MAKE_ ## _reg(7), val); \
return; \
case 8: \
MCR(MAKE_ ## _reg(8), val); \
return; \
case 9: \
MCR(MAKE_ ## _reg(9), val); \
return; \
case 10: \
MCR(MAKE_ ## _reg(10), val); \
return; \
case 11: \
MCR(MAKE_ ## _reg(11), val); \
return; \
case 12: \
MCR(MAKE_ ## _reg(12), val); \
return; \
case 13: \
MCR(MAKE_ ## _reg(13), val); \
return; \
case 14: \
MCR(MAKE_ ## _reg(14), val); \
return; \
case 15: \
MCR(MAKE_ ## _reg(15), val); \
return; \
default: \
assert(bp_num == 0); \
MCR(MAKE_ ## _reg(0), val); \
return; \
} \
}
DEBUG_GENERATE_READ_FN(readBcrCp, DBGBCR)
DEBUG_GENERATE_READ_FN(readBvrCp, DBGBVR)
DEBUG_GENERATE_READ_FN(readWcrCp, DBGWCR)
DEBUG_GENERATE_READ_FN(readWvrCp, DBGWVR)
DEBUG_GENERATE_WRITE_FN(writeBcrCp, DBGBCR)
DEBUG_GENERATE_WRITE_FN(writeBvrCp, DBGBVR)
DEBUG_GENERATE_WRITE_FN(writeWcrCp, DBGWCR)
DEBUG_GENERATE_WRITE_FN(writeWvrCp, DBGWVR)
static word_t
readBcrContext(arch_tcb_t *at, uint16_t index)
{
assert(index < seL4_NumExclusiveBreakpoints);
return at->tcbContext.breakpointState.breakpoint[index].cr;
}
static word_t
readBvrContext(arch_tcb_t *at, uint16_t index)
{
assert(index < seL4_NumExclusiveBreakpoints);
return at->tcbContext.breakpointState.breakpoint[index].vr;
}
static word_t
readWcrContext(arch_tcb_t *at, uint16_t index)
{
assert(index < seL4_NumExclusiveWatchpoints);
return at->tcbContext.breakpointState.watchpoint[index].cr;
}
static word_t
readWvrContext(arch_tcb_t *at, uint16_t index)
{
assert(index < seL4_NumExclusiveWatchpoints);
return at->tcbContext.breakpointState.watchpoint[index].vr;
}
static void
writeBcrContext(arch_tcb_t *at, uint16_t index, word_t val)
{
assert(index < seL4_NumExclusiveBreakpoints);
at->tcbContext.breakpointState.breakpoint[index].cr = val;
}
static void
writeBvrContext(arch_tcb_t *at, uint16_t index, word_t val)
{
assert(index < seL4_NumExclusiveBreakpoints);
at->tcbContext.breakpointState.breakpoint[index].vr = val;
}
static void
writeWcrContext(arch_tcb_t *at, uint16_t index, word_t val)
{
assert(index < seL4_NumExclusiveWatchpoints);
at->tcbContext.breakpointState.watchpoint[index].cr = val;
}
static void
writeWvrContext(arch_tcb_t *at, uint16_t index, word_t val)
{
assert(index < seL4_NumExclusiveWatchpoints);
at->tcbContext.breakpointState.watchpoint[index].vr = val;
}
#endif
#ifdef CONFIG_HARDWARE_DEBUG_API
UNUSED static void
dumpBpsAndWpsCp(int nBp, int nWp)
{
int i;
for (i = 0; i < nBp; i++) {
userError("CP BP %d: Bcr %lx, Bvr %lx", i, readBcrCp(i), readBvrCp(i));
}
for (i = 0; i < nWp; i++) {
userError("CP WP %d: Wcr %lx, Wvr %lx", i, readWcrCp(i), readWvrCp(i));
}
}
UNUSED static void
dumpBpsAndWpsContext(arch_tcb_t *at, int nBp, int nWp)
{
int i;
for (i = 0; i < nBp; i++) {
userError("Ctxt BP %d: Bcr %lx, Bvr %lx", i, readBcrContext(at, i), readBvrContext(at, i));
}
for (i = 0; i < nWp; i++) {
userError("Ctxt WP %d: Wcr %lx, Wvr %lx", i, readWcrContext(at, i), readWvrContext(at, i));
}
}
static word_t
convertSizeToArch(word_t size)
{
switch (size) {
case 1:
return 0x1;
case 2:
return 0x3;
case 8:
return 0xFF;
default:
assert(size == 4);
return 0xF;
}
}
static word_t
convertArchToSize(word_t archsize)
{
switch (archsize) {
case 0x1:
return 1;
case 0x3:
return 2;
case 0xFF:
return 8;
default:
assert(archsize == 0xF);
return 4;
}
}
static word_t
convertAccessToArch(word_t access)
{
switch (access) {
case seL4_BreakOnRead:
return DBGWCR_ACCESS_LOAD;
case seL4_BreakOnWrite:
return DBGWCR_ACCESS_STORE;
default:
assert(access == seL4_BreakOnReadWrite);
return DBGWCR_ACCESS_EITHER;
}
}
static word_t
convertArchToAccess(word_t archaccess)
{
switch (archaccess) {
case DBGWCR_ACCESS_LOAD:
return seL4_BreakOnRead;
case DBGWCR_ACCESS_STORE:
return seL4_BreakOnWrite;
default:
assert(archaccess == DBGWCR_ACCESS_EITHER);
return seL4_BreakOnReadWrite;
}
}
static uint16_t
getBpNumFromType(uint16_t bp_num, word_t type)
{
assert(type == seL4_InstructionBreakpoint || type == seL4_DataBreakpoint
|| type == seL4_SingleStep);
switch (type) {
case seL4_InstructionBreakpoint:
case seL4_SingleStep:
return bp_num;
default:
assert(type == seL4_DataBreakpoint);
return bp_num + seL4_NumExclusiveBreakpoints;
}
}
static inline word_t
getMethodOfEntry(void)
{
dbg_dscr_t dscr;
dscr.words[0] = readDscrCp();
return dbg_dscr_get_methodOfEntry(dscr);
}
void
setBreakpoint(arch_tcb_t *at,
uint16_t bp_num,
word_t vaddr, word_t type, word_t size, word_t rw)
{
bp_num = convertBpNumToArch(bp_num);
if (type == seL4_InstructionBreakpoint) {
dbg_bcr_t bcr;
writeBvrContext(at, bp_num, vaddr);
bcr.words[0] = readBcrContext(at, bp_num);
bcr = dbg_bcr_set_enabled(bcr, 1);
bcr = dbg_bcr_set_linkedBrp(bcr, 0);
bcr = dbg_bcr_set_supervisorAccess(bcr, DBGBCR_PRIV_USER);
bcr = dbg_bcr_set_byteAddressSelect(bcr, convertSizeToArch(4));
bcr = Arch_setupBcr(bcr, true);
writeBcrContext(at, bp_num, bcr.words[0]);
} else {
dbg_wcr_t wcr;
writeWvrContext(at, bp_num, vaddr);
wcr.words[0] = readWcrContext(at, bp_num);
wcr = dbg_wcr_set_enabled(wcr, 1);
wcr = dbg_wcr_set_supervisorAccess(wcr, DBGWCR_PRIV_USER);
wcr = dbg_wcr_set_byteAddressSelect(wcr, convertSizeToArch(size));
wcr = dbg_wcr_set_loadStore(wcr, convertAccessToArch(rw));
wcr = dbg_wcr_set_enableLinking(wcr, 0);
wcr = dbg_wcr_set_linkedBrp(wcr, 0);
wcr = Arch_setupWcr(wcr);
writeWcrContext(at, bp_num, wcr.words[0]);
}
}
getBreakpoint_t
getBreakpoint(arch_tcb_t *at, uint16_t bp_num)
{
getBreakpoint_t ret;
ret.type = getTypeFromBpNum(bp_num);
bp_num = convertBpNumToArch(bp_num);
if (ret.type == seL4_InstructionBreakpoint) {
dbg_bcr_t bcr;
bcr.words[0] = readBcrContext(at, bp_num);
if (Arch_breakpointIsMismatch(bcr) == true) {
ret.type = seL4_SingleStep;
};
ret.size = 0;
ret.rw = seL4_BreakOnRead;
ret.vaddr = readBvrContext(at, bp_num);
ret.is_enabled = dbg_bcr_get_enabled(bcr);
} else {
dbg_wcr_t wcr;
wcr.words[0] = readWcrContext(at, bp_num);
ret.size = convertArchToSize(dbg_wcr_get_byteAddressSelect(wcr));
ret.rw = convertArchToAccess(dbg_wcr_get_loadStore(wcr));
ret.vaddr = readWvrContext(at, bp_num);
ret.is_enabled = dbg_wcr_get_enabled(wcr);
}
return ret;
}
void
unsetBreakpoint(arch_tcb_t *at, uint16_t bp_num)
{
word_t type;
type = getTypeFromBpNum(bp_num);
bp_num = convertBpNumToArch(bp_num);
if (type == seL4_InstructionBreakpoint) {
dbg_bcr_t bcr;
bcr.words[0] = readBcrContext(at, bp_num);
bcr = dbg_bcr_set_enabled(bcr, 0);
writeBcrContext(at, bp_num, bcr.words[0]);
writeBvrContext(at, bp_num, 0);
} else {
dbg_wcr_t wcr;
wcr.words[0] = readWcrContext(at, bp_num);
wcr = dbg_wcr_set_enabled(wcr, 0);
writeWcrContext(at, bp_num, wcr.words[0]);
writeWvrContext(at, bp_num, 0);
}
}
bool_t
configureSingleStepping(arch_tcb_t *at,
uint16_t bp_num,
word_t n_instr,
bool_t is_reply)
{
if (is_reply) {
bp_num = at->tcbContext.breakpointState.single_step_hw_bp_num;
} else {
bp_num = convertBpNumToArch(bp_num);
}
dbg_bcr_t bcr;
bcr.words[0] = readBcrContext(at, bp_num);
if (n_instr > 0) {
bcr = dbg_bcr_set_enabled(bcr, 1);
at->tcbContext.breakpointState.single_step_enabled = true;
} else {
bcr = dbg_bcr_set_enabled(bcr, 0);
at->tcbContext.breakpointState.single_step_enabled = false;
}
bcr = dbg_bcr_set_linkedBrp(bcr, 0);
bcr = dbg_bcr_set_supervisorAccess(bcr, DBGBCR_PRIV_USER);
bcr = dbg_bcr_set_byteAddressSelect(bcr, convertSizeToArch(1));
bcr = Arch_setupBcr(bcr, false);
writeBvrContext(at, bp_num, 0);
writeBcrContext(at, bp_num, bcr.words[0]);
at->tcbContext.breakpointState.n_instructions = n_instr;
at->tcbContext.breakpointState.single_step_hw_bp_num = bp_num;
return true;
}
BOOT_CODE static void
initVersionInfo(void)
{
dbg_didr_t didr;
didr.words[0] = getDIDR();
dbg.oem_revision = dbg_didr_get_revision(didr);
dbg.oem_variant = dbg_didr_get_variant(didr);
dbg.didr_version = dbg_didr_get_version(didr);
dbg.coprocessor_is_baseline_only = true;
dbg.breakpoints_supported = dbg.watchpoints_supported =
dbg.single_step_supported = true;
switch (dbg.didr_version) {
case 0x1:
dbg.debug_armv = 0x60;
dbg.single_step_supported = false;
break;
case 0x2:
dbg.debug_armv = 0x61;
break;
case 0x3:
dbg.debug_armv = 0x70;
dbg.coprocessor_is_baseline_only = false;
break;
case 0x4:
dbg.debug_armv = 0x70;
break;
case 0x5:
dbg.debug_armv = 0x71;
dbg.coprocessor_is_baseline_only = false;
break;
case 0x6:
dbg.debug_armv = 0x80;
dbg.coprocessor_is_baseline_only = false;
break;
default:
dbg.is_available = false;
dbg.debug_armv = 0;
return;
}
dbg.is_available = true;
}
BOOT_CODE static void
disableAllBpsAndWps(void)
{
int i;
for (i = 0; i < seL4_NumExclusiveBreakpoints; i++) {
writeBvrCp(i, 0);
writeBcrCp(i, readBcrCp(i) & ~DBGBCR_ENABLE);
}
for (i = 0; i < seL4_NumExclusiveWatchpoints; i++) {
writeWvrCp(i, 0);
writeWcrCp(i, readWcrCp(i) & ~DBGWCR_ENABLE);
}
isb();
}
BOOT_CODE bool_t
Arch_initHardwareBreakpoints(void)
{
word_t dbgosdlr, dbgoslsr;
memset(&dbg, 0, sizeof(dbg));
initVersionInfo();
if (dbg.is_available == false) {
printf("Debug architecture not implemented.\n");
return false;
}
printf("DIDRv: %x, armv %x, coproc baseline only? %s.\n",
dbg.didr_version, dbg.debug_armv,
((dbg.coprocessor_is_baseline_only) ? "yes" : "no"));
if (dbg.debug_armv > 0x61) {
if (dbg.coprocessor_is_baseline_only) {
printf("ARMDBG: No reliable access to DBG regs.\n");
return dbg.is_available = false;
}
dbg.cpu_is_in_secure_mode = !(readDscrCp() & DBGDSCR_SECURE_MODE_DISABLED);
if (dbg.cpu_is_in_secure_mode) {
word_t sder;
printf("CPU is in secure mode. Enabling debugging in secure user mode.\n");
MRC(DBGSDER, sder);
MCR(DBGSDER, sder
| DBGSDER_ENABLE_SECURE_USER_INVASIVE_DEBUG);
}
if (dbg.debug_armv == 0x71) {
MRC(DBGOSDLR, dbgosdlr);
MCR(DBGOSDLR, dbgosdlr & ~DBGOSDLR_LOCK_ENABLE);
} else if (dbg.debug_armv == 0x70) {
}
MRC(DBGOSLSR, dbgoslsr);
if (DBGOSLSR_GET_OSLOCK_MODEL(dbgoslsr) != DBGOSLSR_LOCK_MODEL_NO_OSLOCK) {
MCR(DBGOSLAR, ~DBGOSLAR_LOCK_VALUE);
}
disableAllBpsAndWps();
if (!enableMonitorMode()) {
return dbg.is_available = false;
}
} else {
if (!enableMonitorMode()) {
return dbg.is_available = false;
}
disableAllBpsAndWps();
}
dbg.watchpoint_8b_supported = watchpoint8bSupported();
return true;
}
static int
getAndResetActiveBreakpoint(word_t vaddr, word_t reason)
{
word_t align_mask;
int i, ret = -1;
if (reason == seL4_InstructionBreakpoint) {
for (i = 0; i < seL4_NumExclusiveBreakpoints; i++) {
dbg_bcr_t bcr;
word_t bvr = readBvrCp(i);
bcr.words[0] = readBcrCp(i);
align_mask = convertArchToSize(dbg_bcr_get_byteAddressSelect(bcr));
align_mask = ~(align_mask - 1);
if (bvr != (vaddr & align_mask) || !dbg_bcr_get_enabled(bcr)) {
continue;
}
ret = i;
return ret;
}
}
if (reason == seL4_DataBreakpoint) {
for (i = 0; i < seL4_NumExclusiveWatchpoints; i++) {
dbg_wcr_t wcr;
word_t wvr = readWvrCp(i);
wcr.words[0] = readWcrCp(i);
align_mask = convertArchToSize(dbg_wcr_get_byteAddressSelect(wcr));
align_mask = ~(align_mask - 1);
if (wvr != (vaddr & align_mask) || !dbg_wcr_get_enabled(wcr)) {
continue;
}
ret = i;
return ret;
}
}
return ret;
}
typedef struct fault_status {
uint8_t status;
bool_t is_long_desc_format;
} fault_status_t;
static fault_status_t
getFaultStatus(word_t hsr_or_fsr)
{
fault_status_t ret;
#ifdef CONFIG_ARM_HYPERVISOR_SUPPORT
ret.is_long_desc_format = true;
ret.status = hsr_or_fsr & 0x3F;
#else
if (hsr_or_fsr & BIT(FSR_LPAE_SHIFT)) {
ret.is_long_desc_format = true;
ret.status = hsr_or_fsr & 0x3F;
} else {
ret.is_long_desc_format = false;
ret.status = (hsr_or_fsr & BIT(FSR_STATUS_BIT4_SHIFT)) >> FSR_STATUS_BIT4_SHIFT;
ret.status <<= 4;
ret.status = hsr_or_fsr & 0xF;
}
#endif
return ret;
}
bool_t
isDebugFault(word_t hsr_or_fsr)
{
fault_status_t fs;
fs = getFaultStatus(hsr_or_fsr);
if (fs.is_long_desc_format) {
if (fs.status == FSR_LONGDESC_STATUS_DEBUG_EVENT) {
return true;
}
} else {
if (fs.status == FSR_SHORTDESC_STATUS_DEBUG_EVENT) {
return true;
}
}
if (getMethodOfEntry() == DEBUG_ENTRY_ASYNC_WATCHPOINT) {
userError("Debug: Watchpoint delivered as async abort.");
return true;
}
return false;
}
seL4_Fault_t
handleUserLevelDebugException(word_t fault_vaddr)
{
#ifdef TRACK_KERNEL_ENTRIES
ksKernelEntry.path = Entry_DebugFault;
ksKernelEntry.word = fault_vaddr;
#endif
word_t method_of_entry = getMethodOfEntry();
int i, active_bp;
seL4_Fault_t ret;
word_t bp_reason, bp_vaddr;
switch (method_of_entry) {
case DEBUG_ENTRY_BREAKPOINT:
bp_reason = seL4_InstructionBreakpoint;
bp_vaddr = fault_vaddr;
for (i = 0; i < seL4_NumExclusiveBreakpoints; i++) {
dbg_bcr_t bcr;
bcr.words[0] = readBcrCp(i);
if (!dbg_bcr_get_enabled(bcr) || Arch_breakpointIsMismatch(bcr) != true) {
continue;
}
bp_reason = seL4_SingleStep;
active_bp = i;
break;
}
break;
case DEBUG_ENTRY_SYNC_WATCHPOINT:
bp_reason = seL4_DataBreakpoint;
#ifdef CONFIG_ARM_HYPERVISOR_SUPPORT
bp_vaddr = getHDFAR();
#else
bp_vaddr = getFAR();
#endif
break;
case DEBUG_ENTRY_ASYNC_WATCHPOINT:
bp_reason = seL4_DataBreakpoint;
bp_vaddr = getWFAR();
break;
default:
assert(method_of_entry == DEBUG_ENTRY_EXPLICIT_BKPT);
bp_reason = seL4_SoftwareBreakRequest;
bp_vaddr = fault_vaddr;
active_bp = 0;
}
if (method_of_entry != DEBUG_ENTRY_EXPLICIT_BKPT
&& bp_reason != seL4_SingleStep) {
active_bp = getAndResetActiveBreakpoint(bp_vaddr,
bp_reason);
assert(active_bp >= 0);
}
if (bp_reason != seL4_SoftwareBreakRequest) {
active_bp = getBpNumFromType(active_bp, bp_reason);
}
ret = seL4_Fault_DebugException_new(bp_vaddr, active_bp, bp_reason);
return ret;
}
#endif
#if !defined(CONFIG_VERIFICATION_BUILD) && (defined(CONFIG_HARDWARE_DEBUG_API) || defined(CONFIG_ARM_HYPERVISOR_SUPPORT))
void
Arch_initBreakpointContext(user_breakpoint_state_t *uds)
{
memset(uds, 0, sizeof(*uds));
for (int i = 0; i < seL4_NumExclusiveBreakpoints; i++) {
uds->breakpoint[i].cr = readBcrCp(i) & ~DBGBCR_ENABLE;
}
for (int i = 0; i < seL4_NumExclusiveWatchpoints; i++) {
uds->watchpoint[i].cr = readWcrCp(i) & ~DBGWCR_ENABLE;
}
}
void
loadAllDisabledBreakpointState(void)
{
int i;
for (i = 0; i < seL4_NumExclusiveBreakpoints; i++) {
writeBcrCp(i, readBcrCp(i) & ~DBGBCR_ENABLE);
}
for (i = 0; i < seL4_NumExclusiveWatchpoints; i++) {
writeWcrCp(i, readWcrCp(i) & ~DBGWCR_ENABLE);
}
}
void
saveAllBreakpointState(arch_tcb_t *at)
{
int i;
assert(at != NULL);
for (i = 0; i < seL4_NumExclusiveBreakpoints; i++) {
writeBvrContext(at, i, readBvrCp(i));
writeBcrContext(at, i, readBcrCp(i));
}
for (i = 0; i < seL4_NumExclusiveWatchpoints; i++) {
writeWvrContext(at, i, readWvrCp(i));
writeWcrContext(at, i, readWcrCp(i));
}
}
void
Arch_debugAssociateVCPUTCB(tcb_t *t)
{
assert(seL4_NumHWBreakpoints < sizeof(word_t) * 8);
t->tcbArch.tcbContext.breakpointState.used_breakpoints_bf = MASK(seL4_NumHWBreakpoints);
}
void
Arch_debugDissociateVCPUTCB(tcb_t *t)
{
t->tcbArch.tcbContext.breakpointState.used_breakpoints_bf = 0;
}
static void
loadBreakpointState(arch_tcb_t *at)
{
int i;
assert(at != NULL);
for (i = 0; i < seL4_NumExclusiveBreakpoints; i++) {
if (at->tcbContext.breakpointState.used_breakpoints_bf & BIT(i)) {
writeBvrCp(i, readBvrContext(at, i));
writeBcrCp(i, readBcrContext(at, i));
} else {
writeBcrCp(i, readBcrCp(i) & ~DBGBCR_ENABLE);
}
}
for (i = 0; i < seL4_NumExclusiveWatchpoints; i++) {
if (at->tcbContext.breakpointState.used_breakpoints_bf &
BIT(i + seL4_NumExclusiveBreakpoints)) {
writeWvrCp(i, readWvrContext(at, i));
writeWcrCp(i, readWcrContext(at, i));
} else {
writeWcrCp(i, readWcrCp(i) & ~DBGWCR_ENABLE);
}
}
}
void
restore_user_debug_context(tcb_t *target_thread)
{
assert(target_thread != NULL);
if (target_thread->tcbArch.tcbContext.breakpointState.used_breakpoints_bf == 0) {
loadAllDisabledBreakpointState();
} else {
loadBreakpointState(&target_thread->tcbArch);
}
}
#endif