Home | History | Annotate | Line # | Download | only in riscv
      1 /*	$NetBSD: db_machdep.c,v 1.15 2025/12/10 21:33:02 andvar 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.15 2025/12/10 21:33:02 andvar 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 instruction 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