xref: /netbsd-src/sys/arch/mips/mips/db_disasm.c (revision f82d7874c259b2a6cc59b714f844919f32bf7b51)
1 /*	$NetBSD: db_disasm.c,v 1.19 2007/02/28 04:21:53 thorpej Exp $	*/
2 
3 /*-
4  * Copyright (c) 1991, 1993
5  *	The Regents of the University of California.  All rights reserved.
6  *
7  * This code is derived from software contributed to Berkeley by
8  * Ralph Campbell.
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  * 3. Neither the name of the University nor the names of its contributors
19  *    may be used to endorse or promote products derived from this software
20  *    without specific prior written permission.
21  *
22  * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
23  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
24  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
25  * ARE DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
26  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
27  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
28  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
29  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
30  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
31  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
32  * SUCH DAMAGE.
33  *
34  *	from: @(#)kadb.c	8.1 (Berkeley) 6/10/93
35  */
36 
37 #include <sys/cdefs.h>
38 __KERNEL_RCSID(0, "$NetBSD: db_disasm.c,v 1.19 2007/02/28 04:21:53 thorpej Exp $");
39 
40 #include <sys/types.h>
41 #include <sys/systm.h>
42 #include <sys/param.h>
43 
44 #include <machine/reg.h>
45 #include <machine/cpu.h>
46 #include <mips/mips_opcode.h>
47 /*#include <machine/param.h>*/
48 #include <machine/db_machdep.h>
49 
50 #include <ddb/db_interface.h>
51 #include <ddb/db_output.h>
52 #include <ddb/db_extern.h>
53 #include <ddb/db_sym.h>
54 
55 static const char * const op_name[64] = {
56 /* 0 */ "spec", "bcond","j",	"jal",	"beq",	"bne",	"blez", "bgtz",
57 /* 8 */ "addi", "addiu","slti", "sltiu","andi", "ori",	"xori", "lui",
58 /*16 */ "cop0", "cop1", "cop2", "cop3", "beql", "bnel", "blezl","bgtzl",
59 /*24 */ "daddi","daddiu","ldl", "ldr",	"op34", "op35", "op36", "op37",
60 /*32 */ "lb",	"lh",	"lwl",	"lw",	"lbu",	"lhu",	"lwr",	"lwu",
61 /*40 */ "sb",	"sh",	"swl",	"sw",	"sdl",	"sdr",	"swr",	"cache",
62 /*48 */ "ll",	"lwc1", "lwc2", "lwc3", "lld",	"ldc1", "ldc2", "ld",
63 /*56 */ "sc",	"swc1", "swc2", "swc3", "scd",	"sdc1", "sdc2", "sd"
64 };
65 
66 static const char * const spec_name[64] = {
67 /* 0 */ "sll",	"spec01","srl", "sra",	"sllv", "spec05","srlv","srav",
68 /* 8 */ "jr",	"jalr", "spec12","spec13","syscall","break","spec16","sync",
69 /*16 */ "mfhi", "mthi", "mflo", "mtlo", "dsllv","spec25","dsrlv","dsrav",
70 /*24 */ "mult", "multu","div",	"divu", "dmult","dmultu","ddiv","ddivu",
71 /*32 */ "add",	"addu", "sub",	"subu", "and",	"or",	"xor",	"nor",
72 /*40 */ "spec50","spec51","slt","sltu", "dadd","daddu","dsub","dsubu",
73 /*48 */ "tge","tgeu","tlt","tltu","teq","spec65","tne","spec67",
74 /*56 */ "dsll","spec71","dsrl","dsra","dsll32","spec75","dsrl32","dsra32"
75 };
76 
77 static const char * const spec2_name[4] = {		/* QED RM4650, R5000, etc. */
78 /* 0 */ "mad", "madu", "mul", "spec3"
79 };
80 
81 static const char * const bcond_name[32] = {
82 /* 0 */ "bltz", "bgez", "bltzl", "bgezl", "?", "?", "?", "?",
83 /* 8 */ "tgei", "tgeiu", "tlti", "tltiu", "teqi", "?", "tnei", "?",
84 /*16 */ "bltzal", "bgezal", "bltzall", "bgezall", "?", "?", "?", "?",
85 /*24 */ "?", "?", "?", "?", "?", "?", "?", "?",
86 };
87 
88 static const char * const cop1_name[64] = {
89 /* 0 */ "fadd",  "fsub", "fmpy", "fdiv", "fsqrt","fabs", "fmov", "fneg",
90 /* 8 */ "fop08","fop09","fop0a","fop0b","fop0c","fop0d","fop0e","fop0f",
91 /*16 */ "fop10","fop11","fop12","fop13","fop14","fop15","fop16","fop17",
92 /*24 */ "fop18","fop19","fop1a","fop1b","fop1c","fop1d","fop1e","fop1f",
93 /*32 */ "fcvts","fcvtd","fcvte","fop23","fcvtw","fop25","fop26","fop27",
94 /*40 */ "fop28","fop29","fop2a","fop2b","fop2c","fop2d","fop2e","fop2f",
95 /*48 */ "fcmp.f","fcmp.un","fcmp.eq","fcmp.ueq","fcmp.olt","fcmp.ult",
96 	"fcmp.ole","fcmp.ule",
97 /*56 */ "fcmp.sf","fcmp.ngle","fcmp.seq","fcmp.ngl","fcmp.lt","fcmp.nge",
98 	"fcmp.le","fcmp.ngt"
99 };
100 
101 static const char * const fmt_name[16] = {
102 	"s",	"d",	"e",	"fmt3",
103 	"w",	"fmt5", "fmt6", "fmt7",
104 	"fmt8", "fmt9", "fmta", "fmtb",
105 	"fmtc", "fmtd", "fmte", "fmtf"
106 };
107 
108 #if defined(__mips_n32) || defined(__mips_n64)
109 static const char * const reg_name[32] = {
110 	"zero", "at",	"v0",	"v1",	"a0",	"a1",	"a2",	"a3",
111 	"a4",	"a5",	"a6",	"a7",	"t0",	"t1",	"t2",	"t3",
112 	"s0",	"s1",	"s2",	"s3",	"s4",	"s5",	"s6",	"s7",
113 	"t8",	"t9",	"k0",	"k1",	"gp",	"sp",	"s8",	"ra"
114 };
115 #else
116 static const char * const reg_name[32] = {
117 	"zero", "at",	"v0",	"v1",	"a0",	"a1",	"a2",	"a3",
118 	"t0",	"t1",	"t2",	"t3",	"t4",	"t5",	"t6",	"t7",
119 	"s0",	"s1",	"s2",	"s3",	"s4",	"s5",	"s6",	"s7",
120 	"t8",	"t9",	"k0",	"k1",	"gp",	"sp",	"s8",	"ra"
121 };
122 #endif /* __mips_n32 || __mips_n64 */
123 
124 static const char * const c0_opname[64] = {
125 	"c0op00","tlbr",  "tlbwi", "c0op03","c0op04","c0op05","tlbwr", "c0op07",
126 	"tlbp",	 "c0op11","c0op12","c0op13","c0op14","c0op15","c0op16","c0op17",
127 	"rfe",	 "c0op21","c0op22","c0op23","c0op24","c0op25","c0op26","c0op27",
128 	"eret",  "c0op31","c0op32","c0op33","c0op34","c0op35","c0op36","c0op37",
129 	"c0op40","c0op41","c0op42","c0op43","c0op44","c0op45","c0op46","c0op47",
130 	"c0op50","c0op51","c0op52","c0op53","c0op54","c0op55","c0op56","c0op57",
131 	"c0op60","c0op61","c0op62","c0op63","c0op64","c0op65","c0op66","c0op67",
132 	"c0op70","c0op71","c0op72","c0op73","c0op74","c0op75","c0op77","c0op77",
133 };
134 
135 static const char * const c0_reg[32] = {
136 	"index",    "random",   "tlblo0",  "tlblo1",
137 	"context",  "pagemask", "wired",   "cp0r7",
138 	"badvaddr", "count",    "tlbhi",   "compare",
139 	"status",   "cause",    "epc",     "prid",
140 	"config",   "lladdr",   "watchlo", "watchhi",
141 	"xcontext", "cp0r21",   "cp0r22",  "debug",
142 	"depc",     "perfcnt",  "ecc",     "cacheerr",
143 	"taglo",    "taghi",    "errepc",  "desave"
144 };
145 
146 static void print_addr(db_addr_t);
147 
148 /*
149  * Disassemble instruction at 'loc'.  'altfmt' specifies an
150  * (optional) alternate format (altfmt for vax: don't assume
151  * that each external label is a procedure entry mask).
152  * Return address of start of next instruction.
153  * Since this function is used by 'examine' and by 'step'
154  * "next instruction" does NOT mean the next instruction to
155  * be executed but the 'linear' next instruction.
156  */
157 db_addr_t
158 db_disasm(db_addr_t loc, bool altfmt)
159 {
160 	u_int32_t instr;
161 
162 	/*
163 	 * Take some care with addresses to not UTLB here as it
164 	 * loses the current debugging context.  KSEG2 not checked.
165 	 */
166 	if (loc < MIPS_KSEG0_START) {
167 		instr = fuword((void *)loc);
168 		if (instr == 0xffffffff) {
169 			/* "sd ra, -1(ra)" is unlikely */
170 			db_printf("invalid address.\n");
171 			return loc;
172 		}
173 	}
174 	else {
175 		instr =  *(u_int32_t *)loc;
176 	}
177 
178 	return (db_disasm_insn(instr, loc, altfmt));
179 }
180 
181 
182 /*
183  * Disassemble instruction 'insn' nominally at 'loc'.
184  * 'loc' may in fact contain a breakpoint instruction.
185  */
186 db_addr_t
187 db_disasm_insn(int insn, db_addr_t loc, bool altfmt)
188 {
189 	bool bdslot = false;
190 	InstFmt i;
191 
192 	i.word = insn;
193 
194 	switch (i.JType.op) {
195 	case OP_SPECIAL:
196 		if (i.word == 0) {
197 			db_printf("nop");
198 			break;
199 		}
200 		/* XXX
201 		 * "addu" is a "move" only in 32-bit mode.  What's the correct
202 		 * answer - never decode addu/daddu as "move"?
203 		 */
204 		if (i.RType.func == OP_ADDU && i.RType.rt == 0) {
205 			db_printf("move\t%s,%s",
206 			    reg_name[i.RType.rd],
207 			    reg_name[i.RType.rs]);
208 			break;
209 		}
210 		db_printf("%s", spec_name[i.RType.func]);
211 		switch (i.RType.func) {
212 		case OP_SLL:
213 		case OP_SRL:
214 		case OP_SRA:
215 		case OP_DSLL:
216 
217 		case OP_DSRL:
218 		case OP_DSRA:
219 		case OP_DSLL32:
220 		case OP_DSRL32:
221 		case OP_DSRA32:
222 			db_printf("\t%s,%s,%d",
223 			    reg_name[i.RType.rd],
224 			    reg_name[i.RType.rt],
225 			    i.RType.shamt);
226 			break;
227 
228 		case OP_SLLV:
229 		case OP_SRLV:
230 		case OP_SRAV:
231 		case OP_DSLLV:
232 		case OP_DSRLV:
233 		case OP_DSRAV:
234 			db_printf("\t%s,%s,%s",
235 			    reg_name[i.RType.rd],
236 			    reg_name[i.RType.rt],
237 			    reg_name[i.RType.rs]);
238 			break;
239 
240 		case OP_MFHI:
241 		case OP_MFLO:
242 			db_printf("\t%s", reg_name[i.RType.rd]);
243 			break;
244 
245 		case OP_JR:
246 		case OP_JALR:
247 			db_printf("\t%s", reg_name[i.RType.rs]);
248 			bdslot = true;
249 			break;
250 		case OP_MTLO:
251 		case OP_MTHI:
252 			db_printf("\t%s", reg_name[i.RType.rs]);
253 			break;
254 
255 		case OP_MULT:
256 		case OP_MULTU:
257 		case OP_DMULT:
258 		case OP_DMULTU:
259 		case OP_DIV:
260 		case OP_DIVU:
261 		case OP_DDIV:
262 		case OP_DDIVU:
263 			db_printf("\t%s,%s",
264 			    reg_name[i.RType.rs],
265 			    reg_name[i.RType.rt]);
266 			break;
267 
268 
269 		case OP_SYSCALL:
270 		case OP_SYNC:
271 			break;
272 
273 		case OP_BREAK:
274 			db_printf("\t%d", (i.RType.rs << 5) | i.RType.rt);
275 			break;
276 
277 		default:
278 			db_printf("\t%s,%s,%s",
279 			    reg_name[i.RType.rd],
280 			    reg_name[i.RType.rs],
281 			    reg_name[i.RType.rt]);
282 		}
283 		break;
284 
285 	case OP_SPECIAL2:
286 		if (i.RType.func == OP_MUL)
287 			db_printf("%s\t%s,%s,%s",
288 				spec2_name[i.RType.func & 0x3],
289 		    		reg_name[i.RType.rd],
290 		    		reg_name[i.RType.rs],
291 		    		reg_name[i.RType.rt]);
292 		else
293 			db_printf("%s\t%s,%s",
294 				spec2_name[i.RType.func & 0x3],
295 		    		reg_name[i.RType.rs],
296 		    		reg_name[i.RType.rt]);
297 
298 		break;
299 
300 	case OP_BCOND:
301 		db_printf("%s\t%s,", bcond_name[i.IType.rt],
302 		    reg_name[i.IType.rs]);
303 		goto pr_displ;
304 
305 	case OP_BLEZ:
306 	case OP_BLEZL:
307 	case OP_BGTZ:
308 	case OP_BGTZL:
309 		db_printf("%s\t%s,", op_name[i.IType.op],
310 		    reg_name[i.IType.rs]);
311 		goto pr_displ;
312 
313 	case OP_BEQ:
314 	case OP_BEQL:
315 		if (i.IType.rs == 0 && i.IType.rt == 0) {
316 			db_printf("b\t");
317 			goto pr_displ;
318 		}
319 		/* FALLTHROUGH */
320 	case OP_BNE:
321 	case OP_BNEL:
322 		db_printf("%s\t%s,%s,", op_name[i.IType.op],
323 		    reg_name[i.IType.rs],
324 		    reg_name[i.IType.rt]);
325 	pr_displ:
326 		print_addr(loc + 4 + ((short)i.IType.imm << 2));
327 		bdslot = true;
328 		break;
329 
330 	case OP_COP0:
331 		switch (i.RType.rs) {
332 		case OP_BCx:
333 		case OP_BCy:
334 
335 			db_printf("bc0%c\t",
336 			    "ft"[i.RType.rt & COPz_BC_TF_MASK]);
337 			goto pr_displ;
338 
339 		case OP_MT:
340 			db_printf("mtc0\t%s,%s",
341 			    reg_name[i.RType.rt],
342 			    c0_reg[i.RType.rd]);
343 			break;
344 
345 		case OP_DMT:
346 			db_printf("dmtc0\t%s,%s",
347 			    reg_name[i.RType.rt],
348 			    c0_reg[i.RType.rd]);
349 			break;
350 
351 		case OP_MF:
352 			db_printf("mfc0\t%s,%s",
353 			    reg_name[i.RType.rt],
354 			    c0_reg[i.RType.rd]);
355 			break;
356 
357 		case OP_DMF:
358 			db_printf("dmfc0\t%s,%s",
359 			    reg_name[i.RType.rt],
360 			    c0_reg[i.RType.rd]);
361 			break;
362 
363 		default:
364 			db_printf("%s", c0_opname[i.FRType.func]);
365 		}
366 		break;
367 
368 	case OP_COP1:
369 		switch (i.RType.rs) {
370 		case OP_BCx:
371 		case OP_BCy:
372 			db_printf("bc1%c\t",
373 			    "ft"[i.RType.rt & COPz_BC_TF_MASK]);
374 			goto pr_displ;
375 
376 		case OP_MT:
377 			db_printf("mtc1\t%s,f%d",
378 			    reg_name[i.RType.rt],
379 			    i.RType.rd);
380 			break;
381 
382 		case OP_MF:
383 			db_printf("mfc1\t%s,f%d",
384 			    reg_name[i.RType.rt],
385 			    i.RType.rd);
386 			break;
387 
388 		case OP_CT:
389 			db_printf("ctc1\t%s,f%d",
390 			    reg_name[i.RType.rt],
391 			    i.RType.rd);
392 			break;
393 
394 		case OP_CF:
395 			db_printf("cfc1\t%s,f%d",
396 			    reg_name[i.RType.rt],
397 			    i.RType.rd);
398 			break;
399 
400 		default:
401 			db_printf("%s.%s\tf%d,f%d,f%d",
402 			    cop1_name[i.FRType.func],
403 			    fmt_name[i.FRType.fmt],
404 			    i.FRType.fd, i.FRType.fs, i.FRType.ft);
405 		}
406 		break;
407 
408 	case OP_J:
409 	case OP_JAL:
410 		db_printf("%s\t", op_name[i.JType.op]);
411 		print_addr((loc & 0xF0000000) | (i.JType.target << 2));
412 		bdslot = true;
413 		break;
414 
415 	case OP_LWC1:
416 	case OP_SWC1:
417 		db_printf("%s\tf%d,", op_name[i.IType.op],
418 		    i.IType.rt);
419 		goto loadstore;
420 
421 	case OP_LB:
422 	case OP_LH:
423 	case OP_LW:
424 	case OP_LD:
425 	case OP_LBU:
426 	case OP_LHU:
427 	case OP_LWU:
428 	case OP_SB:
429 	case OP_SH:
430 	case OP_SW:
431 	case OP_SD:
432 		db_printf("%s\t%s,", op_name[i.IType.op],
433 		    reg_name[i.IType.rt]);
434 	loadstore:
435 		db_printf("%d(%s)", (short)i.IType.imm,
436 		    reg_name[i.IType.rs]);
437 		break;
438 
439 	case OP_ORI:
440 	case OP_XORI:
441 		if (i.IType.rs == 0) {
442 			db_printf("li\t%s,0x%x",
443 			    reg_name[i.IType.rt],
444 			    i.IType.imm);
445 			break;
446 		}
447 		/* FALLTHROUGH */
448 	case OP_ANDI:
449 		db_printf("%s\t%s,%s,0x%x", op_name[i.IType.op],
450 		    reg_name[i.IType.rt],
451 		    reg_name[i.IType.rs],
452 		    i.IType.imm);
453 		break;
454 
455 	case OP_LUI:
456 		db_printf("%s\t%s,0x%x", op_name[i.IType.op],
457 		    reg_name[i.IType.rt],
458 		    i.IType.imm);
459 		break;
460 
461 	case OP_CACHE:
462 		db_printf("%s\t0x%x,0x%x(%s)",
463 		    op_name[i.IType.op],
464 		    i.IType.rt,
465 		    i.IType.imm,
466 		    reg_name[i.IType.rs]);
467 		break;
468 
469 	case OP_ADDI:
470 	case OP_DADDI:
471 	case OP_ADDIU:
472 	case OP_DADDIU:
473 		if (i.IType.rs == 0) {
474 			db_printf("li\t%s,%d",
475 			    reg_name[i.IType.rt],
476 			    (short)i.IType.imm);
477 			break;
478 		}
479 		/* FALLTHROUGH */
480 	default:
481 		db_printf("%s\t%s,%s,%d", op_name[i.IType.op],
482 		    reg_name[i.IType.rt],
483 		    reg_name[i.IType.rs],
484 		    (short)i.IType.imm);
485 	}
486 	db_printf("\n");
487 	if (bdslot) {
488 		db_printf("\t\tbdslot:\t");
489 		db_disasm(loc+4, false);
490 		return (loc + 8);
491 	}
492 	return (loc + 4);
493 }
494 
495 static void
496 print_addr(db_addr_t loc)
497 {
498 	db_expr_t diff;
499 	db_sym_t sym;
500 	const char *symname;
501 
502 	diff = INT_MAX;
503 	symname = NULL;
504 	sym = db_search_symbol(loc, DB_STGY_ANY, &diff);
505 	db_symbol_values(sym, &symname, 0);
506 
507 	if (symname) {
508 		if (diff == 0)
509 			db_printf("%s", symname);
510 		else
511 			db_printf("<%s+%lx>", symname, diff);
512 		db_printf("\t[addr:0x%08lx]", loc);
513 	} else {
514 		db_printf("0x%08lx", loc);
515 	}
516 }
517