162306a36Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0
262306a36Sopenharmony_ci#include <test_util.h>
362306a36Sopenharmony_ci#include <kvm_util.h>
462306a36Sopenharmony_ci#include <processor.h>
562306a36Sopenharmony_ci#include <linux/bitfield.h>
662306a36Sopenharmony_ci
762306a36Sopenharmony_ci#define MDSCR_KDE	(1 << 13)
862306a36Sopenharmony_ci#define MDSCR_MDE	(1 << 15)
962306a36Sopenharmony_ci#define MDSCR_SS	(1 << 0)
1062306a36Sopenharmony_ci
1162306a36Sopenharmony_ci#define DBGBCR_LEN8	(0xff << 5)
1262306a36Sopenharmony_ci#define DBGBCR_EXEC	(0x0 << 3)
1362306a36Sopenharmony_ci#define DBGBCR_EL1	(0x1 << 1)
1462306a36Sopenharmony_ci#define DBGBCR_E	(0x1 << 0)
1562306a36Sopenharmony_ci#define DBGBCR_LBN_SHIFT	16
1662306a36Sopenharmony_ci#define DBGBCR_BT_SHIFT		20
1762306a36Sopenharmony_ci#define DBGBCR_BT_ADDR_LINK_CTX	(0x1 << DBGBCR_BT_SHIFT)
1862306a36Sopenharmony_ci#define DBGBCR_BT_CTX_LINK	(0x3 << DBGBCR_BT_SHIFT)
1962306a36Sopenharmony_ci
2062306a36Sopenharmony_ci#define DBGWCR_LEN8	(0xff << 5)
2162306a36Sopenharmony_ci#define DBGWCR_RD	(0x1 << 3)
2262306a36Sopenharmony_ci#define DBGWCR_WR	(0x2 << 3)
2362306a36Sopenharmony_ci#define DBGWCR_EL1	(0x1 << 1)
2462306a36Sopenharmony_ci#define DBGWCR_E	(0x1 << 0)
2562306a36Sopenharmony_ci#define DBGWCR_LBN_SHIFT	16
2662306a36Sopenharmony_ci#define DBGWCR_WT_SHIFT		20
2762306a36Sopenharmony_ci#define DBGWCR_WT_LINK		(0x1 << DBGWCR_WT_SHIFT)
2862306a36Sopenharmony_ci
2962306a36Sopenharmony_ci#define SPSR_D		(1 << 9)
3062306a36Sopenharmony_ci#define SPSR_SS		(1 << 21)
3162306a36Sopenharmony_ci
3262306a36Sopenharmony_ciextern unsigned char sw_bp, sw_bp2, hw_bp, hw_bp2, bp_svc, bp_brk, hw_wp, ss_start, hw_bp_ctx;
3362306a36Sopenharmony_ciextern unsigned char iter_ss_begin, iter_ss_end;
3462306a36Sopenharmony_cistatic volatile uint64_t sw_bp_addr, hw_bp_addr;
3562306a36Sopenharmony_cistatic volatile uint64_t wp_addr, wp_data_addr;
3662306a36Sopenharmony_cistatic volatile uint64_t svc_addr;
3762306a36Sopenharmony_cistatic volatile uint64_t ss_addr[4], ss_idx;
3862306a36Sopenharmony_ci#define  PC(v)  ((uint64_t)&(v))
3962306a36Sopenharmony_ci
4062306a36Sopenharmony_ci#define GEN_DEBUG_WRITE_REG(reg_name)			\
4162306a36Sopenharmony_cistatic void write_##reg_name(int num, uint64_t val)	\
4262306a36Sopenharmony_ci{							\
4362306a36Sopenharmony_ci	switch (num) {					\
4462306a36Sopenharmony_ci	case 0:						\
4562306a36Sopenharmony_ci		write_sysreg(val, reg_name##0_el1);	\
4662306a36Sopenharmony_ci		break;					\
4762306a36Sopenharmony_ci	case 1:						\
4862306a36Sopenharmony_ci		write_sysreg(val, reg_name##1_el1);	\
4962306a36Sopenharmony_ci		break;					\
5062306a36Sopenharmony_ci	case 2:						\
5162306a36Sopenharmony_ci		write_sysreg(val, reg_name##2_el1);	\
5262306a36Sopenharmony_ci		break;					\
5362306a36Sopenharmony_ci	case 3:						\
5462306a36Sopenharmony_ci		write_sysreg(val, reg_name##3_el1);	\
5562306a36Sopenharmony_ci		break;					\
5662306a36Sopenharmony_ci	case 4:						\
5762306a36Sopenharmony_ci		write_sysreg(val, reg_name##4_el1);	\
5862306a36Sopenharmony_ci		break;					\
5962306a36Sopenharmony_ci	case 5:						\
6062306a36Sopenharmony_ci		write_sysreg(val, reg_name##5_el1);	\
6162306a36Sopenharmony_ci		break;					\
6262306a36Sopenharmony_ci	case 6:						\
6362306a36Sopenharmony_ci		write_sysreg(val, reg_name##6_el1);	\
6462306a36Sopenharmony_ci		break;					\
6562306a36Sopenharmony_ci	case 7:						\
6662306a36Sopenharmony_ci		write_sysreg(val, reg_name##7_el1);	\
6762306a36Sopenharmony_ci		break;					\
6862306a36Sopenharmony_ci	case 8:						\
6962306a36Sopenharmony_ci		write_sysreg(val, reg_name##8_el1);	\
7062306a36Sopenharmony_ci		break;					\
7162306a36Sopenharmony_ci	case 9:						\
7262306a36Sopenharmony_ci		write_sysreg(val, reg_name##9_el1);	\
7362306a36Sopenharmony_ci		break;					\
7462306a36Sopenharmony_ci	case 10:					\
7562306a36Sopenharmony_ci		write_sysreg(val, reg_name##10_el1);	\
7662306a36Sopenharmony_ci		break;					\
7762306a36Sopenharmony_ci	case 11:					\
7862306a36Sopenharmony_ci		write_sysreg(val, reg_name##11_el1);	\
7962306a36Sopenharmony_ci		break;					\
8062306a36Sopenharmony_ci	case 12:					\
8162306a36Sopenharmony_ci		write_sysreg(val, reg_name##12_el1);	\
8262306a36Sopenharmony_ci		break;					\
8362306a36Sopenharmony_ci	case 13:					\
8462306a36Sopenharmony_ci		write_sysreg(val, reg_name##13_el1);	\
8562306a36Sopenharmony_ci		break;					\
8662306a36Sopenharmony_ci	case 14:					\
8762306a36Sopenharmony_ci		write_sysreg(val, reg_name##14_el1);	\
8862306a36Sopenharmony_ci		break;					\
8962306a36Sopenharmony_ci	case 15:					\
9062306a36Sopenharmony_ci		write_sysreg(val, reg_name##15_el1);	\
9162306a36Sopenharmony_ci		break;					\
9262306a36Sopenharmony_ci	default:					\
9362306a36Sopenharmony_ci		GUEST_ASSERT(0);			\
9462306a36Sopenharmony_ci	}						\
9562306a36Sopenharmony_ci}
9662306a36Sopenharmony_ci
9762306a36Sopenharmony_ci/* Define write_dbgbcr()/write_dbgbvr()/write_dbgwcr()/write_dbgwvr() */
9862306a36Sopenharmony_ciGEN_DEBUG_WRITE_REG(dbgbcr)
9962306a36Sopenharmony_ciGEN_DEBUG_WRITE_REG(dbgbvr)
10062306a36Sopenharmony_ciGEN_DEBUG_WRITE_REG(dbgwcr)
10162306a36Sopenharmony_ciGEN_DEBUG_WRITE_REG(dbgwvr)
10262306a36Sopenharmony_ci
10362306a36Sopenharmony_cistatic void reset_debug_state(void)
10462306a36Sopenharmony_ci{
10562306a36Sopenharmony_ci	uint8_t brps, wrps, i;
10662306a36Sopenharmony_ci	uint64_t dfr0;
10762306a36Sopenharmony_ci
10862306a36Sopenharmony_ci	asm volatile("msr daifset, #8");
10962306a36Sopenharmony_ci
11062306a36Sopenharmony_ci	write_sysreg(0, osdlr_el1);
11162306a36Sopenharmony_ci	write_sysreg(0, oslar_el1);
11262306a36Sopenharmony_ci	isb();
11362306a36Sopenharmony_ci
11462306a36Sopenharmony_ci	write_sysreg(0, mdscr_el1);
11562306a36Sopenharmony_ci	write_sysreg(0, contextidr_el1);
11662306a36Sopenharmony_ci
11762306a36Sopenharmony_ci	/* Reset all bcr/bvr/wcr/wvr registers */
11862306a36Sopenharmony_ci	dfr0 = read_sysreg(id_aa64dfr0_el1);
11962306a36Sopenharmony_ci	brps = FIELD_GET(ARM64_FEATURE_MASK(ID_AA64DFR0_BRPS), dfr0);
12062306a36Sopenharmony_ci	for (i = 0; i <= brps; i++) {
12162306a36Sopenharmony_ci		write_dbgbcr(i, 0);
12262306a36Sopenharmony_ci		write_dbgbvr(i, 0);
12362306a36Sopenharmony_ci	}
12462306a36Sopenharmony_ci	wrps = FIELD_GET(ARM64_FEATURE_MASK(ID_AA64DFR0_WRPS), dfr0);
12562306a36Sopenharmony_ci	for (i = 0; i <= wrps; i++) {
12662306a36Sopenharmony_ci		write_dbgwcr(i, 0);
12762306a36Sopenharmony_ci		write_dbgwvr(i, 0);
12862306a36Sopenharmony_ci	}
12962306a36Sopenharmony_ci
13062306a36Sopenharmony_ci	isb();
13162306a36Sopenharmony_ci}
13262306a36Sopenharmony_ci
13362306a36Sopenharmony_cistatic void enable_os_lock(void)
13462306a36Sopenharmony_ci{
13562306a36Sopenharmony_ci	write_sysreg(1, oslar_el1);
13662306a36Sopenharmony_ci	isb();
13762306a36Sopenharmony_ci
13862306a36Sopenharmony_ci	GUEST_ASSERT(read_sysreg(oslsr_el1) & 2);
13962306a36Sopenharmony_ci}
14062306a36Sopenharmony_ci
14162306a36Sopenharmony_cistatic void enable_monitor_debug_exceptions(void)
14262306a36Sopenharmony_ci{
14362306a36Sopenharmony_ci	uint32_t mdscr;
14462306a36Sopenharmony_ci
14562306a36Sopenharmony_ci	asm volatile("msr daifclr, #8");
14662306a36Sopenharmony_ci
14762306a36Sopenharmony_ci	mdscr = read_sysreg(mdscr_el1) | MDSCR_KDE | MDSCR_MDE;
14862306a36Sopenharmony_ci	write_sysreg(mdscr, mdscr_el1);
14962306a36Sopenharmony_ci	isb();
15062306a36Sopenharmony_ci}
15162306a36Sopenharmony_ci
15262306a36Sopenharmony_cistatic void install_wp(uint8_t wpn, uint64_t addr)
15362306a36Sopenharmony_ci{
15462306a36Sopenharmony_ci	uint32_t wcr;
15562306a36Sopenharmony_ci
15662306a36Sopenharmony_ci	wcr = DBGWCR_LEN8 | DBGWCR_RD | DBGWCR_WR | DBGWCR_EL1 | DBGWCR_E;
15762306a36Sopenharmony_ci	write_dbgwcr(wpn, wcr);
15862306a36Sopenharmony_ci	write_dbgwvr(wpn, addr);
15962306a36Sopenharmony_ci
16062306a36Sopenharmony_ci	isb();
16162306a36Sopenharmony_ci
16262306a36Sopenharmony_ci	enable_monitor_debug_exceptions();
16362306a36Sopenharmony_ci}
16462306a36Sopenharmony_ci
16562306a36Sopenharmony_cistatic void install_hw_bp(uint8_t bpn, uint64_t addr)
16662306a36Sopenharmony_ci{
16762306a36Sopenharmony_ci	uint32_t bcr;
16862306a36Sopenharmony_ci
16962306a36Sopenharmony_ci	bcr = DBGBCR_LEN8 | DBGBCR_EXEC | DBGBCR_EL1 | DBGBCR_E;
17062306a36Sopenharmony_ci	write_dbgbcr(bpn, bcr);
17162306a36Sopenharmony_ci	write_dbgbvr(bpn, addr);
17262306a36Sopenharmony_ci	isb();
17362306a36Sopenharmony_ci
17462306a36Sopenharmony_ci	enable_monitor_debug_exceptions();
17562306a36Sopenharmony_ci}
17662306a36Sopenharmony_ci
17762306a36Sopenharmony_cistatic void install_wp_ctx(uint8_t addr_wp, uint8_t ctx_bp, uint64_t addr,
17862306a36Sopenharmony_ci			   uint64_t ctx)
17962306a36Sopenharmony_ci{
18062306a36Sopenharmony_ci	uint32_t wcr;
18162306a36Sopenharmony_ci	uint64_t ctx_bcr;
18262306a36Sopenharmony_ci
18362306a36Sopenharmony_ci	/* Setup a context-aware breakpoint for Linked Context ID Match */
18462306a36Sopenharmony_ci	ctx_bcr = DBGBCR_LEN8 | DBGBCR_EXEC | DBGBCR_EL1 | DBGBCR_E |
18562306a36Sopenharmony_ci		  DBGBCR_BT_CTX_LINK;
18662306a36Sopenharmony_ci	write_dbgbcr(ctx_bp, ctx_bcr);
18762306a36Sopenharmony_ci	write_dbgbvr(ctx_bp, ctx);
18862306a36Sopenharmony_ci
18962306a36Sopenharmony_ci	/* Setup a linked watchpoint (linked to the context-aware breakpoint) */
19062306a36Sopenharmony_ci	wcr = DBGWCR_LEN8 | DBGWCR_RD | DBGWCR_WR | DBGWCR_EL1 | DBGWCR_E |
19162306a36Sopenharmony_ci	      DBGWCR_WT_LINK | ((uint32_t)ctx_bp << DBGWCR_LBN_SHIFT);
19262306a36Sopenharmony_ci	write_dbgwcr(addr_wp, wcr);
19362306a36Sopenharmony_ci	write_dbgwvr(addr_wp, addr);
19462306a36Sopenharmony_ci	isb();
19562306a36Sopenharmony_ci
19662306a36Sopenharmony_ci	enable_monitor_debug_exceptions();
19762306a36Sopenharmony_ci}
19862306a36Sopenharmony_ci
19962306a36Sopenharmony_civoid install_hw_bp_ctx(uint8_t addr_bp, uint8_t ctx_bp, uint64_t addr,
20062306a36Sopenharmony_ci		       uint64_t ctx)
20162306a36Sopenharmony_ci{
20262306a36Sopenharmony_ci	uint32_t addr_bcr, ctx_bcr;
20362306a36Sopenharmony_ci
20462306a36Sopenharmony_ci	/* Setup a context-aware breakpoint for Linked Context ID Match */
20562306a36Sopenharmony_ci	ctx_bcr = DBGBCR_LEN8 | DBGBCR_EXEC | DBGBCR_EL1 | DBGBCR_E |
20662306a36Sopenharmony_ci		  DBGBCR_BT_CTX_LINK;
20762306a36Sopenharmony_ci	write_dbgbcr(ctx_bp, ctx_bcr);
20862306a36Sopenharmony_ci	write_dbgbvr(ctx_bp, ctx);
20962306a36Sopenharmony_ci
21062306a36Sopenharmony_ci	/*
21162306a36Sopenharmony_ci	 * Setup a normal breakpoint for Linked Address Match, and link it
21262306a36Sopenharmony_ci	 * to the context-aware breakpoint.
21362306a36Sopenharmony_ci	 */
21462306a36Sopenharmony_ci	addr_bcr = DBGBCR_LEN8 | DBGBCR_EXEC | DBGBCR_EL1 | DBGBCR_E |
21562306a36Sopenharmony_ci		   DBGBCR_BT_ADDR_LINK_CTX |
21662306a36Sopenharmony_ci		   ((uint32_t)ctx_bp << DBGBCR_LBN_SHIFT);
21762306a36Sopenharmony_ci	write_dbgbcr(addr_bp, addr_bcr);
21862306a36Sopenharmony_ci	write_dbgbvr(addr_bp, addr);
21962306a36Sopenharmony_ci	isb();
22062306a36Sopenharmony_ci
22162306a36Sopenharmony_ci	enable_monitor_debug_exceptions();
22262306a36Sopenharmony_ci}
22362306a36Sopenharmony_ci
22462306a36Sopenharmony_cistatic void install_ss(void)
22562306a36Sopenharmony_ci{
22662306a36Sopenharmony_ci	uint32_t mdscr;
22762306a36Sopenharmony_ci
22862306a36Sopenharmony_ci	asm volatile("msr daifclr, #8");
22962306a36Sopenharmony_ci
23062306a36Sopenharmony_ci	mdscr = read_sysreg(mdscr_el1) | MDSCR_KDE | MDSCR_SS;
23162306a36Sopenharmony_ci	write_sysreg(mdscr, mdscr_el1);
23262306a36Sopenharmony_ci	isb();
23362306a36Sopenharmony_ci}
23462306a36Sopenharmony_ci
23562306a36Sopenharmony_cistatic volatile char write_data;
23662306a36Sopenharmony_ci
23762306a36Sopenharmony_cistatic void guest_code(uint8_t bpn, uint8_t wpn, uint8_t ctx_bpn)
23862306a36Sopenharmony_ci{
23962306a36Sopenharmony_ci	uint64_t ctx = 0xabcdef;	/* a random context number */
24062306a36Sopenharmony_ci
24162306a36Sopenharmony_ci	/* Software-breakpoint */
24262306a36Sopenharmony_ci	reset_debug_state();
24362306a36Sopenharmony_ci	asm volatile("sw_bp: brk #0");
24462306a36Sopenharmony_ci	GUEST_ASSERT_EQ(sw_bp_addr, PC(sw_bp));
24562306a36Sopenharmony_ci
24662306a36Sopenharmony_ci	/* Hardware-breakpoint */
24762306a36Sopenharmony_ci	reset_debug_state();
24862306a36Sopenharmony_ci	install_hw_bp(bpn, PC(hw_bp));
24962306a36Sopenharmony_ci	asm volatile("hw_bp: nop");
25062306a36Sopenharmony_ci	GUEST_ASSERT_EQ(hw_bp_addr, PC(hw_bp));
25162306a36Sopenharmony_ci
25262306a36Sopenharmony_ci	/* Hardware-breakpoint + svc */
25362306a36Sopenharmony_ci	reset_debug_state();
25462306a36Sopenharmony_ci	install_hw_bp(bpn, PC(bp_svc));
25562306a36Sopenharmony_ci	asm volatile("bp_svc: svc #0");
25662306a36Sopenharmony_ci	GUEST_ASSERT_EQ(hw_bp_addr, PC(bp_svc));
25762306a36Sopenharmony_ci	GUEST_ASSERT_EQ(svc_addr, PC(bp_svc) + 4);
25862306a36Sopenharmony_ci
25962306a36Sopenharmony_ci	/* Hardware-breakpoint + software-breakpoint */
26062306a36Sopenharmony_ci	reset_debug_state();
26162306a36Sopenharmony_ci	install_hw_bp(bpn, PC(bp_brk));
26262306a36Sopenharmony_ci	asm volatile("bp_brk: brk #0");
26362306a36Sopenharmony_ci	GUEST_ASSERT_EQ(sw_bp_addr, PC(bp_brk));
26462306a36Sopenharmony_ci	GUEST_ASSERT_EQ(hw_bp_addr, PC(bp_brk));
26562306a36Sopenharmony_ci
26662306a36Sopenharmony_ci	/* Watchpoint */
26762306a36Sopenharmony_ci	reset_debug_state();
26862306a36Sopenharmony_ci	install_wp(wpn, PC(write_data));
26962306a36Sopenharmony_ci	write_data = 'x';
27062306a36Sopenharmony_ci	GUEST_ASSERT_EQ(write_data, 'x');
27162306a36Sopenharmony_ci	GUEST_ASSERT_EQ(wp_data_addr, PC(write_data));
27262306a36Sopenharmony_ci
27362306a36Sopenharmony_ci	/* Single-step */
27462306a36Sopenharmony_ci	reset_debug_state();
27562306a36Sopenharmony_ci	install_ss();
27662306a36Sopenharmony_ci	ss_idx = 0;
27762306a36Sopenharmony_ci	asm volatile("ss_start:\n"
27862306a36Sopenharmony_ci		     "mrs x0, esr_el1\n"
27962306a36Sopenharmony_ci		     "add x0, x0, #1\n"
28062306a36Sopenharmony_ci		     "msr daifset, #8\n"
28162306a36Sopenharmony_ci		     : : : "x0");
28262306a36Sopenharmony_ci	GUEST_ASSERT_EQ(ss_addr[0], PC(ss_start));
28362306a36Sopenharmony_ci	GUEST_ASSERT_EQ(ss_addr[1], PC(ss_start) + 4);
28462306a36Sopenharmony_ci	GUEST_ASSERT_EQ(ss_addr[2], PC(ss_start) + 8);
28562306a36Sopenharmony_ci
28662306a36Sopenharmony_ci	/* OS Lock does not block software-breakpoint */
28762306a36Sopenharmony_ci	reset_debug_state();
28862306a36Sopenharmony_ci	enable_os_lock();
28962306a36Sopenharmony_ci	sw_bp_addr = 0;
29062306a36Sopenharmony_ci	asm volatile("sw_bp2: brk #0");
29162306a36Sopenharmony_ci	GUEST_ASSERT_EQ(sw_bp_addr, PC(sw_bp2));
29262306a36Sopenharmony_ci
29362306a36Sopenharmony_ci	/* OS Lock blocking hardware-breakpoint */
29462306a36Sopenharmony_ci	reset_debug_state();
29562306a36Sopenharmony_ci	enable_os_lock();
29662306a36Sopenharmony_ci	install_hw_bp(bpn, PC(hw_bp2));
29762306a36Sopenharmony_ci	hw_bp_addr = 0;
29862306a36Sopenharmony_ci	asm volatile("hw_bp2: nop");
29962306a36Sopenharmony_ci	GUEST_ASSERT_EQ(hw_bp_addr, 0);
30062306a36Sopenharmony_ci
30162306a36Sopenharmony_ci	/* OS Lock blocking watchpoint */
30262306a36Sopenharmony_ci	reset_debug_state();
30362306a36Sopenharmony_ci	enable_os_lock();
30462306a36Sopenharmony_ci	write_data = '\0';
30562306a36Sopenharmony_ci	wp_data_addr = 0;
30662306a36Sopenharmony_ci	install_wp(wpn, PC(write_data));
30762306a36Sopenharmony_ci	write_data = 'x';
30862306a36Sopenharmony_ci	GUEST_ASSERT_EQ(write_data, 'x');
30962306a36Sopenharmony_ci	GUEST_ASSERT_EQ(wp_data_addr, 0);
31062306a36Sopenharmony_ci
31162306a36Sopenharmony_ci	/* OS Lock blocking single-step */
31262306a36Sopenharmony_ci	reset_debug_state();
31362306a36Sopenharmony_ci	enable_os_lock();
31462306a36Sopenharmony_ci	ss_addr[0] = 0;
31562306a36Sopenharmony_ci	install_ss();
31662306a36Sopenharmony_ci	ss_idx = 0;
31762306a36Sopenharmony_ci	asm volatile("mrs x0, esr_el1\n\t"
31862306a36Sopenharmony_ci		     "add x0, x0, #1\n\t"
31962306a36Sopenharmony_ci		     "msr daifset, #8\n\t"
32062306a36Sopenharmony_ci		     : : : "x0");
32162306a36Sopenharmony_ci	GUEST_ASSERT_EQ(ss_addr[0], 0);
32262306a36Sopenharmony_ci
32362306a36Sopenharmony_ci	/* Linked hardware-breakpoint */
32462306a36Sopenharmony_ci	hw_bp_addr = 0;
32562306a36Sopenharmony_ci	reset_debug_state();
32662306a36Sopenharmony_ci	install_hw_bp_ctx(bpn, ctx_bpn, PC(hw_bp_ctx), ctx);
32762306a36Sopenharmony_ci	/* Set context id */
32862306a36Sopenharmony_ci	write_sysreg(ctx, contextidr_el1);
32962306a36Sopenharmony_ci	isb();
33062306a36Sopenharmony_ci	asm volatile("hw_bp_ctx: nop");
33162306a36Sopenharmony_ci	write_sysreg(0, contextidr_el1);
33262306a36Sopenharmony_ci	GUEST_ASSERT_EQ(hw_bp_addr, PC(hw_bp_ctx));
33362306a36Sopenharmony_ci
33462306a36Sopenharmony_ci	/* Linked watchpoint */
33562306a36Sopenharmony_ci	reset_debug_state();
33662306a36Sopenharmony_ci	install_wp_ctx(wpn, ctx_bpn, PC(write_data), ctx);
33762306a36Sopenharmony_ci	/* Set context id */
33862306a36Sopenharmony_ci	write_sysreg(ctx, contextidr_el1);
33962306a36Sopenharmony_ci	isb();
34062306a36Sopenharmony_ci	write_data = 'x';
34162306a36Sopenharmony_ci	GUEST_ASSERT_EQ(write_data, 'x');
34262306a36Sopenharmony_ci	GUEST_ASSERT_EQ(wp_data_addr, PC(write_data));
34362306a36Sopenharmony_ci
34462306a36Sopenharmony_ci	GUEST_DONE();
34562306a36Sopenharmony_ci}
34662306a36Sopenharmony_ci
34762306a36Sopenharmony_cistatic void guest_sw_bp_handler(struct ex_regs *regs)
34862306a36Sopenharmony_ci{
34962306a36Sopenharmony_ci	sw_bp_addr = regs->pc;
35062306a36Sopenharmony_ci	regs->pc += 4;
35162306a36Sopenharmony_ci}
35262306a36Sopenharmony_ci
35362306a36Sopenharmony_cistatic void guest_hw_bp_handler(struct ex_regs *regs)
35462306a36Sopenharmony_ci{
35562306a36Sopenharmony_ci	hw_bp_addr = regs->pc;
35662306a36Sopenharmony_ci	regs->pstate |= SPSR_D;
35762306a36Sopenharmony_ci}
35862306a36Sopenharmony_ci
35962306a36Sopenharmony_cistatic void guest_wp_handler(struct ex_regs *regs)
36062306a36Sopenharmony_ci{
36162306a36Sopenharmony_ci	wp_data_addr = read_sysreg(far_el1);
36262306a36Sopenharmony_ci	wp_addr = regs->pc;
36362306a36Sopenharmony_ci	regs->pstate |= SPSR_D;
36462306a36Sopenharmony_ci}
36562306a36Sopenharmony_ci
36662306a36Sopenharmony_cistatic void guest_ss_handler(struct ex_regs *regs)
36762306a36Sopenharmony_ci{
36862306a36Sopenharmony_ci	__GUEST_ASSERT(ss_idx < 4, "Expected index < 4, got '%u'", ss_idx);
36962306a36Sopenharmony_ci	ss_addr[ss_idx++] = regs->pc;
37062306a36Sopenharmony_ci	regs->pstate |= SPSR_SS;
37162306a36Sopenharmony_ci}
37262306a36Sopenharmony_ci
37362306a36Sopenharmony_cistatic void guest_svc_handler(struct ex_regs *regs)
37462306a36Sopenharmony_ci{
37562306a36Sopenharmony_ci	svc_addr = regs->pc;
37662306a36Sopenharmony_ci}
37762306a36Sopenharmony_ci
37862306a36Sopenharmony_cistatic void guest_code_ss(int test_cnt)
37962306a36Sopenharmony_ci{
38062306a36Sopenharmony_ci	uint64_t i;
38162306a36Sopenharmony_ci	uint64_t bvr, wvr, w_bvr, w_wvr;
38262306a36Sopenharmony_ci
38362306a36Sopenharmony_ci	for (i = 0; i < test_cnt; i++) {
38462306a36Sopenharmony_ci		/* Bits [1:0] of dbg{b,w}vr are RES0 */
38562306a36Sopenharmony_ci		w_bvr = i << 2;
38662306a36Sopenharmony_ci		w_wvr = i << 2;
38762306a36Sopenharmony_ci
38862306a36Sopenharmony_ci		/*
38962306a36Sopenharmony_ci		 * Enable Single Step execution.  Note!  This _must_ be a bare
39062306a36Sopenharmony_ci		 * ucall as the ucall() path uses atomic operations to manage
39162306a36Sopenharmony_ci		 * the ucall structures, and the built-in "atomics" are usually
39262306a36Sopenharmony_ci		 * implemented via exclusive access instructions.  The exlusive
39362306a36Sopenharmony_ci		 * monitor is cleared on ERET, and so taking debug exceptions
39462306a36Sopenharmony_ci		 * during a LDREX=>STREX sequence will prevent forward progress
39562306a36Sopenharmony_ci		 * and hang the guest/test.
39662306a36Sopenharmony_ci		 */
39762306a36Sopenharmony_ci		GUEST_UCALL_NONE();
39862306a36Sopenharmony_ci
39962306a36Sopenharmony_ci		/*
40062306a36Sopenharmony_ci		 * The userspace will verify that the pc is as expected during
40162306a36Sopenharmony_ci		 * single step execution between iter_ss_begin and iter_ss_end.
40262306a36Sopenharmony_ci		 */
40362306a36Sopenharmony_ci		asm volatile("iter_ss_begin:nop\n");
40462306a36Sopenharmony_ci
40562306a36Sopenharmony_ci		write_sysreg(w_bvr, dbgbvr0_el1);
40662306a36Sopenharmony_ci		write_sysreg(w_wvr, dbgwvr0_el1);
40762306a36Sopenharmony_ci		bvr = read_sysreg(dbgbvr0_el1);
40862306a36Sopenharmony_ci		wvr = read_sysreg(dbgwvr0_el1);
40962306a36Sopenharmony_ci
41062306a36Sopenharmony_ci		/* Userspace disables Single Step when the end is nigh. */
41162306a36Sopenharmony_ci		asm volatile("iter_ss_end:\n");
41262306a36Sopenharmony_ci
41362306a36Sopenharmony_ci		GUEST_ASSERT_EQ(bvr, w_bvr);
41462306a36Sopenharmony_ci		GUEST_ASSERT_EQ(wvr, w_wvr);
41562306a36Sopenharmony_ci	}
41662306a36Sopenharmony_ci	GUEST_DONE();
41762306a36Sopenharmony_ci}
41862306a36Sopenharmony_ci
41962306a36Sopenharmony_cistatic int debug_version(uint64_t id_aa64dfr0)
42062306a36Sopenharmony_ci{
42162306a36Sopenharmony_ci	return FIELD_GET(ARM64_FEATURE_MASK(ID_AA64DFR0_DEBUGVER), id_aa64dfr0);
42262306a36Sopenharmony_ci}
42362306a36Sopenharmony_ci
42462306a36Sopenharmony_cistatic void test_guest_debug_exceptions(uint8_t bpn, uint8_t wpn, uint8_t ctx_bpn)
42562306a36Sopenharmony_ci{
42662306a36Sopenharmony_ci	struct kvm_vcpu *vcpu;
42762306a36Sopenharmony_ci	struct kvm_vm *vm;
42862306a36Sopenharmony_ci	struct ucall uc;
42962306a36Sopenharmony_ci
43062306a36Sopenharmony_ci	vm = vm_create_with_one_vcpu(&vcpu, guest_code);
43162306a36Sopenharmony_ci
43262306a36Sopenharmony_ci	vm_init_descriptor_tables(vm);
43362306a36Sopenharmony_ci	vcpu_init_descriptor_tables(vcpu);
43462306a36Sopenharmony_ci
43562306a36Sopenharmony_ci	vm_install_sync_handler(vm, VECTOR_SYNC_CURRENT,
43662306a36Sopenharmony_ci				ESR_EC_BRK_INS, guest_sw_bp_handler);
43762306a36Sopenharmony_ci	vm_install_sync_handler(vm, VECTOR_SYNC_CURRENT,
43862306a36Sopenharmony_ci				ESR_EC_HW_BP_CURRENT, guest_hw_bp_handler);
43962306a36Sopenharmony_ci	vm_install_sync_handler(vm, VECTOR_SYNC_CURRENT,
44062306a36Sopenharmony_ci				ESR_EC_WP_CURRENT, guest_wp_handler);
44162306a36Sopenharmony_ci	vm_install_sync_handler(vm, VECTOR_SYNC_CURRENT,
44262306a36Sopenharmony_ci				ESR_EC_SSTEP_CURRENT, guest_ss_handler);
44362306a36Sopenharmony_ci	vm_install_sync_handler(vm, VECTOR_SYNC_CURRENT,
44462306a36Sopenharmony_ci				ESR_EC_SVC64, guest_svc_handler);
44562306a36Sopenharmony_ci
44662306a36Sopenharmony_ci	/* Specify bpn/wpn/ctx_bpn to be tested */
44762306a36Sopenharmony_ci	vcpu_args_set(vcpu, 3, bpn, wpn, ctx_bpn);
44862306a36Sopenharmony_ci	pr_debug("Use bpn#%d, wpn#%d and ctx_bpn#%d\n", bpn, wpn, ctx_bpn);
44962306a36Sopenharmony_ci
45062306a36Sopenharmony_ci	vcpu_run(vcpu);
45162306a36Sopenharmony_ci	switch (get_ucall(vcpu, &uc)) {
45262306a36Sopenharmony_ci	case UCALL_ABORT:
45362306a36Sopenharmony_ci		REPORT_GUEST_ASSERT(uc);
45462306a36Sopenharmony_ci		break;
45562306a36Sopenharmony_ci	case UCALL_DONE:
45662306a36Sopenharmony_ci		goto done;
45762306a36Sopenharmony_ci	default:
45862306a36Sopenharmony_ci		TEST_FAIL("Unknown ucall %lu", uc.cmd);
45962306a36Sopenharmony_ci	}
46062306a36Sopenharmony_ci
46162306a36Sopenharmony_cidone:
46262306a36Sopenharmony_ci	kvm_vm_free(vm);
46362306a36Sopenharmony_ci}
46462306a36Sopenharmony_ci
46562306a36Sopenharmony_civoid test_single_step_from_userspace(int test_cnt)
46662306a36Sopenharmony_ci{
46762306a36Sopenharmony_ci	struct kvm_vcpu *vcpu;
46862306a36Sopenharmony_ci	struct kvm_vm *vm;
46962306a36Sopenharmony_ci	struct ucall uc;
47062306a36Sopenharmony_ci	struct kvm_run *run;
47162306a36Sopenharmony_ci	uint64_t pc, cmd;
47262306a36Sopenharmony_ci	uint64_t test_pc = 0;
47362306a36Sopenharmony_ci	bool ss_enable = false;
47462306a36Sopenharmony_ci	struct kvm_guest_debug debug = {};
47562306a36Sopenharmony_ci
47662306a36Sopenharmony_ci	vm = vm_create_with_one_vcpu(&vcpu, guest_code_ss);
47762306a36Sopenharmony_ci	run = vcpu->run;
47862306a36Sopenharmony_ci	vcpu_args_set(vcpu, 1, test_cnt);
47962306a36Sopenharmony_ci
48062306a36Sopenharmony_ci	while (1) {
48162306a36Sopenharmony_ci		vcpu_run(vcpu);
48262306a36Sopenharmony_ci		if (run->exit_reason != KVM_EXIT_DEBUG) {
48362306a36Sopenharmony_ci			cmd = get_ucall(vcpu, &uc);
48462306a36Sopenharmony_ci			if (cmd == UCALL_ABORT) {
48562306a36Sopenharmony_ci				REPORT_GUEST_ASSERT(uc);
48662306a36Sopenharmony_ci				/* NOT REACHED */
48762306a36Sopenharmony_ci			} else if (cmd == UCALL_DONE) {
48862306a36Sopenharmony_ci				break;
48962306a36Sopenharmony_ci			}
49062306a36Sopenharmony_ci
49162306a36Sopenharmony_ci			TEST_ASSERT(cmd == UCALL_NONE,
49262306a36Sopenharmony_ci				    "Unexpected ucall cmd 0x%lx", cmd);
49362306a36Sopenharmony_ci
49462306a36Sopenharmony_ci			debug.control = KVM_GUESTDBG_ENABLE |
49562306a36Sopenharmony_ci					KVM_GUESTDBG_SINGLESTEP;
49662306a36Sopenharmony_ci			ss_enable = true;
49762306a36Sopenharmony_ci			vcpu_guest_debug_set(vcpu, &debug);
49862306a36Sopenharmony_ci			continue;
49962306a36Sopenharmony_ci		}
50062306a36Sopenharmony_ci
50162306a36Sopenharmony_ci		TEST_ASSERT(ss_enable, "Unexpected KVM_EXIT_DEBUG");
50262306a36Sopenharmony_ci
50362306a36Sopenharmony_ci		/* Check if the current pc is expected. */
50462306a36Sopenharmony_ci		vcpu_get_reg(vcpu, ARM64_CORE_REG(regs.pc), &pc);
50562306a36Sopenharmony_ci		TEST_ASSERT(!test_pc || pc == test_pc,
50662306a36Sopenharmony_ci			    "Unexpected pc 0x%lx (expected 0x%lx)",
50762306a36Sopenharmony_ci			    pc, test_pc);
50862306a36Sopenharmony_ci
50962306a36Sopenharmony_ci		if ((pc + 4) == (uint64_t)&iter_ss_end) {
51062306a36Sopenharmony_ci			test_pc = 0;
51162306a36Sopenharmony_ci			debug.control = KVM_GUESTDBG_ENABLE;
51262306a36Sopenharmony_ci			ss_enable = false;
51362306a36Sopenharmony_ci			vcpu_guest_debug_set(vcpu, &debug);
51462306a36Sopenharmony_ci			continue;
51562306a36Sopenharmony_ci		}
51662306a36Sopenharmony_ci
51762306a36Sopenharmony_ci		/*
51862306a36Sopenharmony_ci		 * If the current pc is between iter_ss_bgin and
51962306a36Sopenharmony_ci		 * iter_ss_end, the pc for the next KVM_EXIT_DEBUG should
52062306a36Sopenharmony_ci		 * be the current pc + 4.
52162306a36Sopenharmony_ci		 */
52262306a36Sopenharmony_ci		if ((pc >= (uint64_t)&iter_ss_begin) &&
52362306a36Sopenharmony_ci		    (pc < (uint64_t)&iter_ss_end))
52462306a36Sopenharmony_ci			test_pc = pc + 4;
52562306a36Sopenharmony_ci		else
52662306a36Sopenharmony_ci			test_pc = 0;
52762306a36Sopenharmony_ci	}
52862306a36Sopenharmony_ci
52962306a36Sopenharmony_ci	kvm_vm_free(vm);
53062306a36Sopenharmony_ci}
53162306a36Sopenharmony_ci
53262306a36Sopenharmony_ci/*
53362306a36Sopenharmony_ci * Run debug testing using the various breakpoint#, watchpoint# and
53462306a36Sopenharmony_ci * context-aware breakpoint# with the given ID_AA64DFR0_EL1 configuration.
53562306a36Sopenharmony_ci */
53662306a36Sopenharmony_civoid test_guest_debug_exceptions_all(uint64_t aa64dfr0)
53762306a36Sopenharmony_ci{
53862306a36Sopenharmony_ci	uint8_t brp_num, wrp_num, ctx_brp_num, normal_brp_num, ctx_brp_base;
53962306a36Sopenharmony_ci	int b, w, c;
54062306a36Sopenharmony_ci
54162306a36Sopenharmony_ci	/* Number of breakpoints */
54262306a36Sopenharmony_ci	brp_num = FIELD_GET(ARM64_FEATURE_MASK(ID_AA64DFR0_BRPS), aa64dfr0) + 1;
54362306a36Sopenharmony_ci	__TEST_REQUIRE(brp_num >= 2, "At least two breakpoints are required");
54462306a36Sopenharmony_ci
54562306a36Sopenharmony_ci	/* Number of watchpoints */
54662306a36Sopenharmony_ci	wrp_num = FIELD_GET(ARM64_FEATURE_MASK(ID_AA64DFR0_WRPS), aa64dfr0) + 1;
54762306a36Sopenharmony_ci
54862306a36Sopenharmony_ci	/* Number of context aware breakpoints */
54962306a36Sopenharmony_ci	ctx_brp_num = FIELD_GET(ARM64_FEATURE_MASK(ID_AA64DFR0_CTX_CMPS), aa64dfr0) + 1;
55062306a36Sopenharmony_ci
55162306a36Sopenharmony_ci	pr_debug("%s brp_num:%d, wrp_num:%d, ctx_brp_num:%d\n", __func__,
55262306a36Sopenharmony_ci		 brp_num, wrp_num, ctx_brp_num);
55362306a36Sopenharmony_ci
55462306a36Sopenharmony_ci	/* Number of normal (non-context aware) breakpoints */
55562306a36Sopenharmony_ci	normal_brp_num = brp_num - ctx_brp_num;
55662306a36Sopenharmony_ci
55762306a36Sopenharmony_ci	/* Lowest context aware breakpoint number */
55862306a36Sopenharmony_ci	ctx_brp_base = normal_brp_num;
55962306a36Sopenharmony_ci
56062306a36Sopenharmony_ci	/* Run tests with all supported breakpoints/watchpoints */
56162306a36Sopenharmony_ci	for (c = ctx_brp_base; c < ctx_brp_base + ctx_brp_num; c++) {
56262306a36Sopenharmony_ci		for (b = 0; b < normal_brp_num; b++) {
56362306a36Sopenharmony_ci			for (w = 0; w < wrp_num; w++)
56462306a36Sopenharmony_ci				test_guest_debug_exceptions(b, w, c);
56562306a36Sopenharmony_ci		}
56662306a36Sopenharmony_ci	}
56762306a36Sopenharmony_ci}
56862306a36Sopenharmony_ci
56962306a36Sopenharmony_cistatic void help(char *name)
57062306a36Sopenharmony_ci{
57162306a36Sopenharmony_ci	puts("");
57262306a36Sopenharmony_ci	printf("Usage: %s [-h] [-i iterations of the single step test]\n", name);
57362306a36Sopenharmony_ci	puts("");
57462306a36Sopenharmony_ci	exit(0);
57562306a36Sopenharmony_ci}
57662306a36Sopenharmony_ci
57762306a36Sopenharmony_ciint main(int argc, char *argv[])
57862306a36Sopenharmony_ci{
57962306a36Sopenharmony_ci	struct kvm_vcpu *vcpu;
58062306a36Sopenharmony_ci	struct kvm_vm *vm;
58162306a36Sopenharmony_ci	int opt;
58262306a36Sopenharmony_ci	int ss_iteration = 10000;
58362306a36Sopenharmony_ci	uint64_t aa64dfr0;
58462306a36Sopenharmony_ci
58562306a36Sopenharmony_ci	vm = vm_create_with_one_vcpu(&vcpu, guest_code);
58662306a36Sopenharmony_ci	vcpu_get_reg(vcpu, KVM_ARM64_SYS_REG(SYS_ID_AA64DFR0_EL1), &aa64dfr0);
58762306a36Sopenharmony_ci	__TEST_REQUIRE(debug_version(aa64dfr0) >= 6,
58862306a36Sopenharmony_ci		       "Armv8 debug architecture not supported.");
58962306a36Sopenharmony_ci	kvm_vm_free(vm);
59062306a36Sopenharmony_ci
59162306a36Sopenharmony_ci	while ((opt = getopt(argc, argv, "i:")) != -1) {
59262306a36Sopenharmony_ci		switch (opt) {
59362306a36Sopenharmony_ci		case 'i':
59462306a36Sopenharmony_ci			ss_iteration = atoi_positive("Number of iterations", optarg);
59562306a36Sopenharmony_ci			break;
59662306a36Sopenharmony_ci		case 'h':
59762306a36Sopenharmony_ci		default:
59862306a36Sopenharmony_ci			help(argv[0]);
59962306a36Sopenharmony_ci			break;
60062306a36Sopenharmony_ci		}
60162306a36Sopenharmony_ci	}
60262306a36Sopenharmony_ci
60362306a36Sopenharmony_ci	test_guest_debug_exceptions_all(aa64dfr0);
60462306a36Sopenharmony_ci	test_single_step_from_userspace(ss_iteration);
60562306a36Sopenharmony_ci
60662306a36Sopenharmony_ci	return 0;
60762306a36Sopenharmony_ci}
608