xref: /netbsd-src/sys/arch/riscv/riscv/db_machdep.c (revision b426528770b1ff548bd444360bac04d668221a78)
1 /*	$NetBSD: db_machdep.c,v 1.14 2024/11/25 22:04:14 skrll Exp $	*/
2 
3 /*-
4  * Copyright (c) 2014 The NetBSD Foundation, Inc.
5  * All rights reserved.
6  *
7  * This code is derived from software contributed to The NetBSD Foundation
8  * by Matt Thomas of 3am Software Foundry.
9  *
10  * Redistribution and use in source and binary forms, with or without
11  * modification, are permitted provided that the following conditions
12  * are met:
13  * 1. Redistributions of source code must retain the above copyright
14  *    notice, this list of conditions and the following disclaimer.
15  * 2. Redistributions in binary form must reproduce the above copyright
16  *    notice, this list of conditions and the following disclaimer in the
17  *    documentation and/or other materials provided with the distribution.
18  *
19  * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS
20  * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
21  * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
22  * PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS
23  * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
24  * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
25  * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
26  * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
27  * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
28  * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
29  * POSSIBILITY OF SUCH DAMAGE.
30  */
31 
32 #include <sys/cdefs.h>
33 
34 __RCSID("$NetBSD: db_machdep.c,v 1.14 2024/11/25 22:04:14 skrll Exp $");
35 
36 #include <sys/param.h>
37 
38 #include <sys/cpu.h>
39 #include <sys/systm.h>
40 
41 #include <riscv/insn.h>
42 #include <riscv/db_machdep.h>
43 
44 #include <ddb/db_user.h>
45 #include <ddb/db_access.h>
46 #include <ddb/db_interface.h>
47 #include <ddb/db_extern.h>
48 #include <ddb/db_variables.h>
49 #include <ddb/db_output.h>
50 
51 #ifndef _KERNEL
52 #include <stddef.h>
53 #endif
54 
55 static int db_rw_ddbreg(const struct db_variable *, db_expr_t *, int);
56 
57 const struct db_variable db_regs[] = {
58 	{ "ra", (void *)offsetof(struct trapframe, tf_ra), db_rw_ddbreg, NULL },
59 	{ "sp", (void *)offsetof(struct trapframe, tf_sp), db_rw_ddbreg, NULL },
60 	{ "gp", (void *)offsetof(struct trapframe, tf_gp), db_rw_ddbreg, NULL },
61 	{ "tp", (void *)offsetof(struct trapframe, tf_tp), db_rw_ddbreg, NULL },
62 	{ "s0", (void *)offsetof(struct trapframe, tf_s0), db_rw_ddbreg, NULL },
63 	{ "s1", (void *)offsetof(struct trapframe, tf_s1), db_rw_ddbreg, NULL },
64 	{ "s2", (void *)offsetof(struct trapframe, tf_s2), db_rw_ddbreg, NULL },
65 	{ "s3", (void *)offsetof(struct trapframe, tf_s3), db_rw_ddbreg, NULL },
66 	{ "s4", (void *)offsetof(struct trapframe, tf_s4), db_rw_ddbreg, NULL },
67 	{ "s5", (void *)offsetof(struct trapframe, tf_s5), db_rw_ddbreg, NULL },
68 	{ "s6", (void *)offsetof(struct trapframe, tf_s6), db_rw_ddbreg, NULL },
69 	{ "s7", (void *)offsetof(struct trapframe, tf_s7), db_rw_ddbreg, NULL },
70 	{ "s8", (void *)offsetof(struct trapframe, tf_s8), db_rw_ddbreg, NULL },
71 	{ "s9", (void *)offsetof(struct trapframe, tf_s9), db_rw_ddbreg, NULL },
72 	{ "s10", (void *)offsetof(struct trapframe, tf_s10), db_rw_ddbreg, NULL },
73 	{ "s11", (void *)offsetof(struct trapframe, tf_s11), db_rw_ddbreg, NULL },
74 	{ "a0", (void *)offsetof(struct trapframe, tf_a0), db_rw_ddbreg, NULL },
75 	{ "a1", (void *)offsetof(struct trapframe, tf_a1), db_rw_ddbreg, NULL },
76 	{ "a2", (void *)offsetof(struct trapframe, tf_a2), db_rw_ddbreg, NULL },
77 	{ "a3", (void *)offsetof(struct trapframe, tf_a3), db_rw_ddbreg, NULL },
78 	{ "a4", (void *)offsetof(struct trapframe, tf_a4), db_rw_ddbreg, NULL },
79 	{ "a5", (void *)offsetof(struct trapframe, tf_a5), db_rw_ddbreg, NULL },
80 	{ "a6", (void *)offsetof(struct trapframe, tf_a6), db_rw_ddbreg, NULL },
81 	{ "a7", (void *)offsetof(struct trapframe, tf_a7), db_rw_ddbreg, NULL },
82 	{ "t0", (void *)offsetof(struct trapframe, tf_t0), db_rw_ddbreg, NULL },
83 	{ "t1", (void *)offsetof(struct trapframe, tf_t1), db_rw_ddbreg, NULL },
84 	{ "t2", (void *)offsetof(struct trapframe, tf_t2), db_rw_ddbreg, NULL },
85 	{ "t3", (void *)offsetof(struct trapframe, tf_t3), db_rw_ddbreg, NULL },
86 	{ "t4", (void *)offsetof(struct trapframe, tf_t4), db_rw_ddbreg, NULL },
87 	{ "t5", (void *)offsetof(struct trapframe, tf_t5), db_rw_ddbreg, NULL },
88 	{ "t6", (void *)offsetof(struct trapframe, tf_t6), db_rw_ddbreg, NULL },
89 	{ "pc", (void *)offsetof(struct trapframe, tf_pc), db_rw_ddbreg, NULL },
90 	{ "status", (void *)offsetof(struct trapframe, tf_sr), db_rw_ddbreg, "i" },
91 	{ "cause", (void *)offsetof(struct trapframe, tf_cause), db_rw_ddbreg, "i" },
92 	{ "tval", (void *)offsetof(struct trapframe, tf_tval), db_rw_ddbreg, NULL },
93 };
94 const struct db_variable * const db_eregs = db_regs + __arraycount(db_regs);
95 
96 int
97 db_rw_ddbreg(const struct db_variable *vp, db_expr_t *valp, int rw)
98 {
99 	struct trapframe * const tf = &ddb_regs;
100 	const uintptr_t addr = (uintptr_t)tf + (uintptr_t)vp->valuep;
101 	if (vp->modif != NULL && vp->modif[0] == 'i') {
102 		if (rw == DB_VAR_GET) {
103 			*valp = *(const uint32_t *)addr;
104 		} else {
105 			*(uint32_t *)addr = *valp;
106 		}
107 	} else {
108 		if (rw == DB_VAR_GET) {
109 			*valp = *(const register_t *)addr;
110 		} else {
111 			*(register_t *)addr = *valp;
112 		}
113 	}
114 	return 0;
115 }
116 
117 // These are for the software implementation of single-stepping.
118 //
119 // XXX none of this checks for 16-bit instructions; it should all be
120 // converted to the newer decoding macros. Also, XXX it does not look
121 // like the MI parts in ddb is going to work in the presence of 16-bit
122 // instructions anyway.
123 //
124 // returns true is the instruction might branch
125 bool
126 inst_branch(uint32_t insn)
127 {
128 	return OPCODE_P(insn, BRANCH);
129 }
130 
131 // returns true is the instruction might branch
132 bool
133 inst_call(uint32_t insn)
134 {
135 	const union riscv_insn ri = { .val = insn };
136 	return (OPCODE_P(insn, JAL) && ri.type_u.u_rd == 1)
137 	    || (OPCODE_P(insn, JALR) && ri.type_i.i_rd == 1);
138 }
139 
140 // return true if the instructon is an uncondition branch/jump.
141 bool
142 inst_unconditional_flow_transfer(uint32_t insn)
143 {
144 	// we should check for beq xN,xN but why use that instead of jal x0,...
145 	return OPCODE_P(insn, JAL) || OPCODE_P(insn, JALR);
146 }
147 
148 bool
149 inst_return(uint32_t insn)
150 {
151 	const union riscv_insn ri = { .val = insn };
152 	return OPCODE_P(insn, JALR) && ri.type_i.i_rs1 == 1;
153 }
154 
155 bool
156 inst_load(uint32_t insn)
157 {
158 	return OPCODE_P(insn, LOAD) || OPCODE_P(insn, LOADFP);
159 }
160 
161 bool
162 inst_store(uint32_t insn)
163 {
164 	return OPCODE_P(insn, STORE) || OPCODE_P(insn, STOREFP);
165 }
166 
167 static inline register_t
168 get_reg_value(const db_regs_t *tf, u_int regno)
169 {
170 	return (regno == 0 ? 0 : tf->tf_reg[regno - 1]);
171 }
172 
173 db_addr_t
174 branch_taken(uint32_t insn, db_addr_t pc, db_regs_t *tf)
175 {
176 	const union riscv_insn i = { .val = insn };
177 	intptr_t displacement;
178 
179 	if (OPCODE_P(insn, JALR)) {
180 		return i.type_i.i_imm11to0 + get_reg_value(tf, i.type_i.i_rs1);
181 	}
182 	if (OPCODE_P(insn, JAL)) {
183 		displacement = i.type_j.j_imm20 << 20;
184 		displacement |= i.type_j.j_imm19to12 << 12;
185 		displacement |= i.type_j.j_imm11 << 11;
186 		displacement |= i.type_j.j_imm10to1 << 1;
187 	} else {
188 		register_t rs1 = get_reg_value(tf, i.type_b.b_rs1);
189 		register_t rs2 = get_reg_value(tf, i.type_b.b_rs2);
190 		bool branch_p; // = false;
191 		switch (i.type_b.b_funct3 & 0b110U) {
192 		case 0b000U:
193 			branch_p = (rs1 == rs2);
194 			break;
195 		case 0b010U:
196 			branch_p = ((rs1 & (1 << (i.type_b.b_rs2))) != 0);
197 			break;
198 		case 0b100U:
199 			branch_p = (rs1 < rs2);
200 			break;
201 		default: // stupid gcc
202 		case 0b110U:
203 			branch_p = ((uregister_t)rs1 < (uregister_t)rs2);
204 			break;
205 		}
206 
207 		if (i.type_b.b_funct3 & 1)
208 			branch_p = !branch_p;
209 
210 		if (!branch_p) {
211 			displacement = 4;
212 		} else {
213 			displacement = i.type_b.b_imm12 << 12;
214 			displacement |= i.type_b.b_imm11 << 11;
215 			displacement |= i.type_b.b_imm10to5 << 5;
216 			displacement |= i.type_b.b_imm4to1 << 1;
217 		}
218 	}
219 
220 	return pc + displacement;
221 }
222 
223 db_addr_t
224 next_instr_address(db_addr_t pc, bool bdslot_p)
225 {
226 	return pc + (bdslot_p ? 0 : 4);
227 }
228