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