debug_monitor.c revision 305773
11558Srgrimes/*- 21558Srgrimes * Copyright (c) 2014 The FreeBSD Foundation 31558Srgrimes * All rights reserved. 41558Srgrimes * 51558Srgrimes * This software was developed by Semihalf under 61558Srgrimes * the sponsorship of the FreeBSD Foundation. 71558Srgrimes * 81558Srgrimes * Redistribution and use in source and binary forms, with or without 91558Srgrimes * modification, are permitted provided that the following conditions 101558Srgrimes * are met: 111558Srgrimes * 1. Redistributions of source code must retain the above copyright 121558Srgrimes * notice, this list of conditions and the following disclaimer. 131558Srgrimes * 2. Redistributions in binary form must reproduce the above copyright 141558Srgrimes * notice, this list of conditions and the following disclaimer in the 151558Srgrimes * documentation and/or other materials provided with the distribution. 161558Srgrimes * 171558Srgrimes * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND 181558Srgrimes * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 191558Srgrimes * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 201558Srgrimes * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE 211558Srgrimes * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 221558Srgrimes * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 231558Srgrimes * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 241558Srgrimes * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 251558Srgrimes * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 261558Srgrimes * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 271558Srgrimes * SUCH DAMAGE. 281558Srgrimes */ 291558Srgrimes 301558Srgrimes#include "opt_ddb.h" 311558Srgrimes 321558Srgrimes#include <sys/cdefs.h> 331558Srgrimes__FBSDID("$FreeBSD: stable/11/sys/arm64/arm64/debug_monitor.c 305773 2016-09-13 16:22:50Z andrew $"); 3437663Scharnier 351558Srgrimes#include <sys/param.h> 361558Srgrimes#include <sys/types.h> 372999Swollman#include <sys/kdb.h> 381558Srgrimes#include <sys/pcpu.h> 39105267Scharnier#include <sys/systm.h> 401558Srgrimes 4137663Scharnier#include <machine/armreg.h> 42105267Scharnier#include <machine/cpu.h> 4337663Scharnier#include <machine/debug_monitor.h> 441558Srgrimes#include <machine/kdb.h> 45105267Scharnier 46105267Scharnier#include <ddb/ddb.h> 47105267Scharnier#include <ddb/db_sym.h> 481558Srgrimes 491558Srgrimesenum dbg_t { 5074462Salfred DBG_TYPE_BREAKPOINT = 0, 511558Srgrimes DBG_TYPE_WATCHPOINT = 1, 521558Srgrimes}; 5324330Sguido 5496622Siedowsestatic int dbg_watchpoint_num; 5596622Siedowsestatic int dbg_breakpoint_num; 561558Srgrimesstatic int dbg_ref_count_mde[MAXCPU]; 571558Srgrimesstatic int dbg_ref_count_kde[MAXCPU]; 58109363Smbr 591558Srgrimes/* Watchpoints/breakpoints control register bitfields */ 6074462Salfred#define DBG_WATCH_CTRL_LEN_1 (0x1 << 5) 6174462Salfred#define DBG_WATCH_CTRL_LEN_2 (0x3 << 5) 621558Srgrimes#define DBG_WATCH_CTRL_LEN_4 (0xf << 5) 639336Sdfr#define DBG_WATCH_CTRL_LEN_8 (0xff << 5) 6483653Speter#define DBG_WATCH_CTRL_LEN_MASK(x) ((x) & (0xff << 5)) 6523681Speter#define DBG_WATCH_CTRL_EXEC (0x0 << 3) 6677162Sru#define DBG_WATCH_CTRL_LOAD (0x1 << 3) 6777223Sru#define DBG_WATCH_CTRL_STORE (0x2 << 3) 6823681Speter#define DBG_WATCH_CTRL_ACCESS_MASK(x) ((x) & (0x3 << 3)) 691558Srgrimes 701558Srgrimes/* Common for breakpoint and watchpoint */ 711558Srgrimes#define DBG_WB_CTRL_EL1 (0x1 << 1) 721558Srgrimes#define DBG_WB_CTRL_EL0 (0x2 << 1) 7337663Scharnier#define DBG_WB_CTRL_ELX_MASK(x) ((x) & (0x3 << 1)) 741558Srgrimes#define DBG_WB_CTRL_E (0x1 << 0) 751558Srgrimes 76149433Spjd#define DBG_REG_BASE_BVR 0 77103949Smike#define DBG_REG_BASE_BCR (DBG_REG_BASE_BVR + 16) 781558Srgrimes#define DBG_REG_BASE_WVR (DBG_REG_BASE_BCR + 16) 791558Srgrimes#define DBG_REG_BASE_WCR (DBG_REG_BASE_WVR + 16) 801558Srgrimes 811558Srgrimes/* Watchpoint/breakpoint helpers */ 821558Srgrimes#define DBG_WB_WVR "wvr" 831558Srgrimes#define DBG_WB_WCR "wcr" 841558Srgrimes#define DBG_WB_BVR "bvr" 851558Srgrimes#define DBG_WB_BCR "bcr" 861558Srgrimes 871558Srgrimes#define DBG_WB_READ(reg, num, val) do { \ 881558Srgrimes __asm __volatile("mrs %0, dbg" reg #num "_el1" : "=r" (val)); \ 891558Srgrimes} while (0) 901558Srgrimes 911558Srgrimes#define DBG_WB_WRITE(reg, num, val) do { \ 921558Srgrimes __asm __volatile("msr dbg" reg #num "_el1, %0" :: "r" (val)); \ 931558Srgrimes} while (0) 941558Srgrimes 951558Srgrimes#define READ_WB_REG_CASE(reg, num, offset, val) \ 961558Srgrimes case (num + offset): \ 971558Srgrimes DBG_WB_READ(reg, num, val); \ 981558Srgrimes break 991558Srgrimes 1001558Srgrimes#define WRITE_WB_REG_CASE(reg, num, offset, val) \ 1011558Srgrimes case (num + offset): \ 1021558Srgrimes DBG_WB_WRITE(reg, num, val); \ 1031558Srgrimes break 1041558Srgrimes 1051558Srgrimes#define SWITCH_CASES_READ_WB_REG(reg, offset, val) \ 1061558Srgrimes READ_WB_REG_CASE(reg, 0, offset, val); \ 1071558Srgrimes READ_WB_REG_CASE(reg, 1, offset, val); \ 1081558Srgrimes READ_WB_REG_CASE(reg, 2, offset, val); \ 1099336Sdfr READ_WB_REG_CASE(reg, 3, offset, val); \ 1101558Srgrimes READ_WB_REG_CASE(reg, 4, offset, val); \ 1111558Srgrimes READ_WB_REG_CASE(reg, 5, offset, val); \ 1121558Srgrimes READ_WB_REG_CASE(reg, 6, offset, val); \ 1131558Srgrimes READ_WB_REG_CASE(reg, 7, offset, val); \ 1141558Srgrimes READ_WB_REG_CASE(reg, 8, offset, val); \ 1151558Srgrimes READ_WB_REG_CASE(reg, 9, offset, val); \ 1161558Srgrimes READ_WB_REG_CASE(reg, 10, offset, val); \ 1171558Srgrimes READ_WB_REG_CASE(reg, 11, offset, val); \ 11827447Sdfr READ_WB_REG_CASE(reg, 12, offset, val); \ 1191558Srgrimes READ_WB_REG_CASE(reg, 13, offset, val); \ 1201558Srgrimes READ_WB_REG_CASE(reg, 14, offset, val); \ 1211558Srgrimes READ_WB_REG_CASE(reg, 15, offset, val) 1221558Srgrimes 1231558Srgrimes#define SWITCH_CASES_WRITE_WB_REG(reg, offset, val) \ 12474462Salfred WRITE_WB_REG_CASE(reg, 0, offset, val); \ 12575801Siedowse WRITE_WB_REG_CASE(reg, 1, offset, val); \ 12642144Sdfr WRITE_WB_REG_CASE(reg, 2, offset, val); \ 1271558Srgrimes WRITE_WB_REG_CASE(reg, 3, offset, val); \ 1281558Srgrimes WRITE_WB_REG_CASE(reg, 4, offset, val); \ 1291558Srgrimes WRITE_WB_REG_CASE(reg, 5, offset, val); \ 13074462Salfred WRITE_WB_REG_CASE(reg, 6, offset, val); \ 1311558Srgrimes WRITE_WB_REG_CASE(reg, 7, offset, val); \ 1321558Srgrimes WRITE_WB_REG_CASE(reg, 8, offset, val); \ 1331558Srgrimes WRITE_WB_REG_CASE(reg, 9, offset, val); \ 1341558Srgrimes WRITE_WB_REG_CASE(reg, 10, offset, val); \ 1351558Srgrimes WRITE_WB_REG_CASE(reg, 11, offset, val); \ 1361558Srgrimes WRITE_WB_REG_CASE(reg, 12, offset, val); \ 1371558Srgrimes WRITE_WB_REG_CASE(reg, 13, offset, val); \ 1381558Srgrimes WRITE_WB_REG_CASE(reg, 14, offset, val); \ 1391558Srgrimes WRITE_WB_REG_CASE(reg, 15, offset, val) 1401558Srgrimes 1411558Srgrimesstatic uint64_t 1421558Srgrimesdbg_wb_read_reg(int reg, int n) 14375641Siedowse{ 1447401Swpaul uint64_t val = 0; 1451558Srgrimes 1461558Srgrimes switch (reg + n) { 1479336Sdfr SWITCH_CASES_READ_WB_REG(DBG_WB_WVR, DBG_REG_BASE_WVR, val); 1481558Srgrimes SWITCH_CASES_READ_WB_REG(DBG_WB_WCR, DBG_REG_BASE_WCR, val); 1491558Srgrimes SWITCH_CASES_READ_WB_REG(DBG_WB_BVR, DBG_REG_BASE_BVR, val); 1501558Srgrimes SWITCH_CASES_READ_WB_REG(DBG_WB_BCR, DBG_REG_BASE_BCR, val); 1511558Srgrimes default: 1529336Sdfr db_printf("trying to read from wrong debug register %d\n", n); 1539336Sdfr } 1549336Sdfr 1559336Sdfr return val; 1569336Sdfr} 1579336Sdfr 1581558Srgrimesstatic void 15992882Simpdbg_wb_write_reg(int reg, int n, uint64_t val) 16092882Simp{ 16192882Simp switch (reg + n) { 16292882Simp SWITCH_CASES_WRITE_WB_REG(DBG_WB_WVR, DBG_REG_BASE_WVR, val); 16392882Simp SWITCH_CASES_WRITE_WB_REG(DBG_WB_WCR, DBG_REG_BASE_WCR, val); 16492882Simp SWITCH_CASES_WRITE_WB_REG(DBG_WB_BVR, DBG_REG_BASE_BVR, val); 16575801Siedowse SWITCH_CASES_WRITE_WB_REG(DBG_WB_BCR, DBG_REG_BASE_BCR, val); 16692882Simp default: 16775635Siedowse db_printf("trying to write to wrong debug register %d\n", n); 16892882Simp } 16992882Simp isb(); 17092882Simp} 17192882Simp 17292882Simpvoid 17392882Simpkdb_cpu_set_singlestep(void) 17492882Simp{ 17592882Simp 17692882Simp kdb_frame->tf_spsr |= DBG_SPSR_SS; 17792882Simp WRITE_SPECIALREG(MDSCR_EL1, READ_SPECIALREG(MDSCR_EL1) | 17892882Simp DBG_MDSCR_SS | DBG_MDSCR_KDE); 17992882Simp 18092882Simp /* 18192882Simp * Disable breakpoints and watchpoints, e.g. stepping 18292882Simp * over watched instruction will trigger break exception instead of 18392882Simp * single-step exception and locks CPU on that instruction for ever. 18492882Simp */ 18592882Simp if (dbg_ref_count_mde[PCPU_GET(cpuid)] > 0) { 18692882Simp WRITE_SPECIALREG(MDSCR_EL1, 18792882Simp READ_SPECIALREG(MDSCR_EL1) & ~DBG_MDSCR_MDE); 18892882Simp } 18975754Siedowse} 19075801Siedowse 19192882Simpvoid 19292882Simpkdb_cpu_clear_singlestep(void) 19392882Simp{ 19492882Simp 195100117Salfred WRITE_SPECIALREG(MDSCR_EL1, READ_SPECIALREG(MDSCR_EL1) & 19675801Siedowse ~(DBG_MDSCR_SS | DBG_MDSCR_KDE)); 19775801Siedowse 19875801Siedowse /* Restore breakpoints and watchpoints */ 19992882Simp if (dbg_ref_count_mde[PCPU_GET(cpuid)] > 0) { 20092882Simp WRITE_SPECIALREG(MDSCR_EL1, 20192882Simp READ_SPECIALREG(MDSCR_EL1) | DBG_MDSCR_MDE); 20292882Simp } 203100117Salfred 20492882Simp if (dbg_ref_count_kde[PCPU_GET(cpuid)] > 0) { 20592882Simp WRITE_SPECIALREG(MDSCR_EL1, 20692882Simp READ_SPECIALREG(MDSCR_EL1) | DBG_MDSCR_KDE); 2071558Srgrimes } 2081558Srgrimes} 2091558Srgrimes 2101558Srgrimesstatic const char * 2111558Srgrimesdbg_watchtype_str(uint32_t type) 21272650Sgreen{ 21391354Sdd switch (type) { 21472650Sgreen case DBG_WATCH_CTRL_EXEC: 2151558Srgrimes return ("execute"); 21672650Sgreen case DBG_WATCH_CTRL_STORE: 21772650Sgreen return ("write"); 2181558Srgrimes case DBG_WATCH_CTRL_LOAD: 21925087Sdfr return ("read"); 2209336Sdfr case DBG_WATCH_CTRL_LOAD | DBG_WATCH_CTRL_STORE: 2219336Sdfr return ("read/write"); 222121767Speter default: 22375754Siedowse return ("invalid"); 22474462Salfred } 2251558Srgrimes} 22674462Salfred 22774462Salfredstatic int 228149433Spjddbg_watchtype_len(uint32_t len) 22975801Siedowse{ 2301558Srgrimes switch (len) { 2311558Srgrimes case DBG_WATCH_CTRL_LEN_1: 23283653Speter return (1); 2331558Srgrimes case DBG_WATCH_CTRL_LEN_2: 2341558Srgrimes return (2); 2351558Srgrimes case DBG_WATCH_CTRL_LEN_4: 23675801Siedowse return (4); 237100336Sjoerg case DBG_WATCH_CTRL_LEN_8: 23874462Salfred return (8); 2391558Srgrimes default: 2401558Srgrimes return (0); 2411558Srgrimes } 24292882Simp} 2431558Srgrimes 2441558Srgrimesvoid 2451558Srgrimesdbg_show_watchpoint(void) 2461558Srgrimes{ 2471558Srgrimes uint32_t wcr, len, type; 2481558Srgrimes uint64_t addr; 2491558Srgrimes int i; 2501558Srgrimes 2511558Srgrimes db_printf("\nhardware watchpoints:\n"); 2521558Srgrimes db_printf(" watch status type len address symbol\n"); 2531558Srgrimes db_printf(" ----- -------- ---------- --- ------------------ ------------------\n"); 2541558Srgrimes for (i = 0; i < dbg_watchpoint_num; i++) { 2551558Srgrimes wcr = dbg_wb_read_reg(DBG_REG_BASE_WCR, i); 2561558Srgrimes if ((wcr & DBG_WB_CTRL_E) != 0) { 2571558Srgrimes type = DBG_WATCH_CTRL_ACCESS_MASK(wcr); 2581558Srgrimes len = DBG_WATCH_CTRL_LEN_MASK(wcr); 2591558Srgrimes addr = dbg_wb_read_reg(DBG_REG_BASE_WVR, i); 26075754Siedowse db_printf(" %-5d %-8s %10s %3d 0x%16lx ", 261126572Sbms i, "enabled", dbg_watchtype_str(type), 262126572Sbms dbg_watchtype_len(len), addr); 263126572Sbms db_printsym((db_addr_t)addr, DB_STGY_ANY); 26474462Salfred db_printf("\n"); 26574462Salfred } else { 266149433Spjd db_printf(" %-5d disabled\n", i); 26774462Salfred } 26874462Salfred } 269109363Smbr} 27074462Salfred 271126572Sbms 272126572Sbmsstatic int 2731558Srgrimesdbg_find_free_slot(enum dbg_t type) 27475635Siedowse{ 275126643Smarkm u_int max, reg, i; 27675635Siedowse 27774462Salfred switch(type) { 278150214Spjd case DBG_TYPE_BREAKPOINT: 279149433Spjd max = dbg_breakpoint_num; 280149433Spjd reg = DBG_REG_BASE_BCR; 281149433Spjd 282149433Spjd break; 283149433Spjd case DBG_TYPE_WATCHPOINT: 28474462Salfred max = dbg_watchpoint_num; 28574462Salfred reg = DBG_REG_BASE_WCR; 28674462Salfred break; 28774462Salfred default: 28874462Salfred db_printf("Unsupported debug type\n"); 28974462Salfred return (i); 29083687Speter } 29183687Speter 29283687Speter for (i = 0; i < max; i++) { 29383687Speter if ((dbg_wb_read_reg(reg, i) & DBG_WB_CTRL_E) == 0) 2942999Swollman return (i); 2952999Swollman } 296126572Sbms 2971558Srgrimes return (-1); 29825087Sdfr} 29925087Sdfr 30025087Sdfrstatic int 3019336Sdfrdbg_find_slot(enum dbg_t type, db_expr_t addr) 3029336Sdfr{ 3039336Sdfr u_int max, reg_addr, reg_ctrl, i; 3049336Sdfr 3059336Sdfr switch(type) { 3069336Sdfr case DBG_TYPE_BREAKPOINT: 3078688Sphk max = dbg_breakpoint_num; 3088688Sphk reg_addr = DBG_REG_BASE_BVR; 3098688Sphk reg_ctrl = DBG_REG_BASE_BCR; 31031656Sguido break; 311121767Speter case DBG_TYPE_WATCHPOINT: 31231656Sguido max = dbg_watchpoint_num; 313126572Sbms reg_addr = DBG_REG_BASE_WVR; 314126572Sbms reg_ctrl = DBG_REG_BASE_WCR; 315126572Sbms break; 316126572Sbms default: 317126572Sbms db_printf("Unsupported debug type\n"); 318126572Sbms return (i); 319126572Sbms } 3201558Srgrimes 32137663Scharnier for (i = 0; i < max; i++) { 3221558Srgrimes if ((dbg_wb_read_reg(reg_addr, i) == addr) && 3231558Srgrimes ((dbg_wb_read_reg(reg_ctrl, i) & DBG_WB_CTRL_E) != 0)) 3241558Srgrimes return (i); 3251558Srgrimes } 3261558Srgrimes 3271558Srgrimes return (-1); 3281558Srgrimes} 3291558Srgrimes 3301558Srgrimesstatic void 3311558Srgrimesdbg_enable_monitor(enum dbg_el_t el) 3321558Srgrimes{ 3331558Srgrimes uint64_t reg_mdcr = 0; 3341558Srgrimes 33537663Scharnier /* 3361558Srgrimes * There is no need to have debug monitor on permanently, thus we are 3371558Srgrimes * refcounting and turn it on only if any of CPU is going to use that. 33837663Scharnier */ 3391558Srgrimes if (atomic_fetchadd_int(&dbg_ref_count_mde[PCPU_GET(cpuid)], 1) == 0) 3401558Srgrimes reg_mdcr = DBG_MDSCR_MDE; 34137663Scharnier 3421558Srgrimes if ((el == DBG_FROM_EL1) && 3431558Srgrimes atomic_fetchadd_int(&dbg_ref_count_kde[PCPU_GET(cpuid)], 1) == 0) 3441558Srgrimes reg_mdcr |= DBG_MDSCR_KDE; 3451558Srgrimes 3461558Srgrimes if (reg_mdcr) 34775754Siedowse WRITE_SPECIALREG(MDSCR_EL1, READ_SPECIALREG(MDSCR_EL1) | reg_mdcr); 34874462Salfred} 349149433Spjd 350149433Spjdstatic void 351149433Spjddbg_disable_monitor(enum dbg_el_t el) 35274462Salfred{ 35374462Salfred uint64_t reg_mdcr = 0; 35474462Salfred 35574462Salfred if (atomic_fetchadd_int(&dbg_ref_count_mde[PCPU_GET(cpuid)], -1) == 1) 35674791Salfred reg_mdcr = DBG_MDSCR_MDE; 35774791Salfred 358109363Smbr if ((el == DBG_FROM_EL1) && 359109363Smbr atomic_fetchadd_int(&dbg_ref_count_kde[PCPU_GET(cpuid)], -1) == 1) 360109363Smbr reg_mdcr |= DBG_MDSCR_KDE; 36174791Salfred 36274791Salfred if (reg_mdcr) 36374462Salfred WRITE_SPECIALREG(MDSCR_EL1, READ_SPECIALREG(MDSCR_EL1) & ~reg_mdcr); 36474462Salfred} 36574462Salfred 36674462Salfredint 36774462Salfreddbg_setup_watchpoint(db_expr_t addr, db_expr_t size, enum dbg_el_t el, 36874462Salfred enum dbg_access_t access) 36974462Salfred{ 37074462Salfred uint64_t wcr_size, wcr_priv, wcr_access; 371117684Srwatson u_int i; 37274462Salfred 37374462Salfred i = dbg_find_free_slot(DBG_TYPE_WATCHPOINT); 37474462Salfred if (i == -1) { 37574462Salfred db_printf("Can not find slot for watchpoint, max %d" 376117684Srwatson " watchpoints supported\n", dbg_watchpoint_num); 377117684Srwatson return (i); 37874462Salfred } 37974462Salfred 38074462Salfred switch(size) { 38174462Salfred case 1: 38274791Salfred wcr_size = DBG_WATCH_CTRL_LEN_1; 38374791Salfred break; 38424759Sguido case 2: 38583687Speter wcr_size = DBG_WATCH_CTRL_LEN_2; 38683687Speter break; 38783687Speter case 4: 38824759Sguido wcr_size = DBG_WATCH_CTRL_LEN_4; 38924759Sguido break; 39024759Sguido case 8: 39124330Sguido wcr_size = DBG_WATCH_CTRL_LEN_8; 392126572Sbms break; 393126572Sbms default: 394126572Sbms db_printf("Unsupported address size for watchpoint\n"); 395126572Sbms return (-1); 396126572Sbms } 397126572Sbms 398126572Sbms switch(el) { 399126572Sbms case DBG_FROM_EL0: 400126572Sbms wcr_priv = DBG_WB_CTRL_EL0; 401126572Sbms break; 402126572Sbms case DBG_FROM_EL1: 40374462Salfred wcr_priv = DBG_WB_CTRL_EL1; 404126572Sbms break; 405126572Sbms default: 406126572Sbms db_printf("Unsupported exception level for watchpoint\n"); 407126572Sbms return (-1); 408126572Sbms } 409126572Sbms 410126572Sbms switch(access) { 411126572Sbms case HW_BREAKPOINT_X: 41274462Salfred wcr_access = DBG_WATCH_CTRL_EXEC; 41374462Salfred break; 41474462Salfred case HW_BREAKPOINT_R: 41574462Salfred wcr_access = DBG_WATCH_CTRL_LOAD; 41674462Salfred break; 41774462Salfred case HW_BREAKPOINT_W: 41874462Salfred wcr_access = DBG_WATCH_CTRL_STORE; 41974462Salfred break; 42074462Salfred case HW_BREAKPOINT_RW: 42174462Salfred wcr_access = DBG_WATCH_CTRL_LOAD | DBG_WATCH_CTRL_STORE; 42274462Salfred break; 42374462Salfred default: 42474462Salfred db_printf("Unsupported exception level for watchpoint\n"); 42574462Salfred return (-1); 42674462Salfred } 42774462Salfred 42874462Salfred dbg_wb_write_reg(DBG_REG_BASE_WVR, i, addr); 42974462Salfred dbg_wb_write_reg(DBG_REG_BASE_WCR, i, wcr_size | wcr_access | wcr_priv | 43074462Salfred DBG_WB_CTRL_E); 431126572Sbms dbg_enable_monitor(el); 432126572Sbms return (0); 433126572Sbms} 434126572Sbms 435126572Sbmsint 436126572Sbmsdbg_remove_watchpoint(db_expr_t addr, db_expr_t size, enum dbg_el_t el) 437126572Sbms{ 438126572Sbms u_int i; 43974462Salfred 440109363Smbr i = dbg_find_slot(DBG_TYPE_WATCHPOINT, addr); 44174462Salfred if (i == -1) { 44274462Salfred db_printf("Can not find watchpoint for address 0%lx\n", addr); 44374462Salfred return (i); 44474462Salfred } 44574462Salfred 44674462Salfred dbg_wb_write_reg(DBG_REG_BASE_WCR, i, 0); 44774462Salfred dbg_disable_monitor(el); 44874462Salfred return (0); 44974462Salfred} 45074462Salfred 45174462Salfredvoid 45274462Salfreddbg_monitor_init(void) 45374462Salfred{ 45474462Salfred u_int i; 45574462Salfred 45674462Salfred /* Clear OS lock */ 45774462Salfred WRITE_SPECIALREG(OSLAR_EL1, 0); 45874791Salfred 459126572Sbms /* Find out many breakpoints and watchpoints we can use */ 460126572Sbms dbg_watchpoint_num = ((READ_SPECIALREG(ID_AA64DFR0_EL1) >> 20) & 0xf) + 1; 461126572Sbms dbg_breakpoint_num = ((READ_SPECIALREG(ID_AA64DFR0_EL1) >> 12) & 0xf) + 1; 462126572Sbms 463126572Sbms if (bootverbose && PCPU_GET(cpuid) == 0) { 464126572Sbms db_printf("%d watchpoints and %d breakpoints supported\n", 465126572Sbms dbg_watchpoint_num, dbg_breakpoint_num); 466126572Sbms } 467126572Sbms 46874462Salfred /* 46974462Salfred * We have limited number of {watch,break}points, each consists of 47074462Salfred * two registers: 47174462Salfred * - wcr/bcr regsiter configurates corresponding {watch,break}point 47274462Salfred * behaviour 47374462Salfred * - wvr/bvr register keeps address we are hunting for 47474462Salfred * 47574462Salfred * Reset all breakpoints and watchpoints. 47674462Salfred */ 47774462Salfred for (i = 0; i < dbg_watchpoint_num; ++i) { 47874462Salfred dbg_wb_write_reg(DBG_REG_BASE_WCR, i, 0); 47974462Salfred dbg_wb_write_reg(DBG_REG_BASE_WVR, i, 0); 48074462Salfred } 48174462Salfred 48274462Salfred for (i = 0; i < dbg_breakpoint_num; ++i) { 48374462Salfred dbg_wb_write_reg(DBG_REG_BASE_BCR, i, 0); 48474462Salfred dbg_wb_write_reg(DBG_REG_BASE_BVR, i, 0); 48574462Salfred } 48674791Salfred 487126572Sbms dbg_enable(); 488126572Sbms} 489126572Sbms