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