1 /*
2  * Copyright (C) 2014-2020 Paul Cercueil <paul@crapouillou.net>
3  *
4  * This library is free software; you can redistribute it and/or
5  * modify it under the terms of the GNU Lesser General Public
6  * License as published by the Free Software Foundation; either
7  * version 2.1 of the License, or (at your option) any later version.
8  *
9  * This library is distributed in the hope that it will be useful,
10  * but WITHOUT ANY WARRANTY; without even the implied warranty of
11  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
12  * Lesser General Public License for more details.
13  */
14 
15 #include "config.h"
16 
17 #if ENABLE_DISASSEMBLER
18 #include <dis-asm.h>
19 #endif
20 #include <stdbool.h>
21 #include <stdlib.h>
22 #include <string.h>
23 
24 #include "debug.h"
25 #include "disassembler.h"
26 #include "lightrec-private.h"
27 #include "memmanager.h"
28 
is_unconditional_jump(const struct opcode * op)29 static bool is_unconditional_jump(const struct opcode *op)
30 {
31 	switch (op->i.op) {
32 	case OP_SPECIAL:
33 		return op->r.op == OP_SPECIAL_JR || op->r.op == OP_SPECIAL_JALR;
34 	case OP_J:
35 	case OP_JAL:
36 		return true;
37 	case OP_BEQ:
38 	case OP_BLEZ:
39 		return op->i.rs == op->i.rt;
40 	case OP_REGIMM:
41 		return (op->r.rt == OP_REGIMM_BGEZ ||
42 			op->r.rt == OP_REGIMM_BGEZAL) && op->i.rs == 0;
43 	default:
44 		return false;
45 	}
46 }
47 
is_syscall(const struct opcode * op)48 static bool is_syscall(const struct opcode *op)
49 {
50 	return (op->i.op == OP_SPECIAL && (op->r.op == OP_SPECIAL_SYSCALL ||
51 					   op->r.op == OP_SPECIAL_BREAK)) ||
52 		(op->i.op == OP_CP0 && (op->r.rs == OP_CP0_MTC0 ||
53 					op->r.rs == OP_CP0_CTC0) &&
54 		 (op->r.rd == 12 || op->r.rd == 13));
55 }
56 
lightrec_free_opcode_list(struct lightrec_state * state,struct opcode * list)57 void lightrec_free_opcode_list(struct lightrec_state *state, struct opcode *list)
58 {
59 	struct opcode *next;
60 
61 	while (list) {
62 		next = list->next;
63 		lightrec_free(state, MEM_FOR_IR, sizeof(*list), list);
64 		list = next;
65 	}
66 }
67 
lightrec_disassemble(struct lightrec_state * state,const u32 * src,unsigned int * len)68 struct opcode * lightrec_disassemble(struct lightrec_state *state,
69 				     const u32 *src, unsigned int *len)
70 {
71 	struct opcode *head = NULL;
72 	bool stop_next = false;
73 	struct opcode *curr, *last;
74 	unsigned int i;
75 
76 	for (i = 0, last = NULL; ; i++, last = curr) {
77 		curr = lightrec_calloc(state, MEM_FOR_IR, sizeof(*curr));
78 		if (!curr) {
79 			pr_err("Unable to allocate memory\n");
80 			lightrec_free_opcode_list(state, head);
81 			return NULL;
82 		}
83 
84 		if (!last)
85 			head = curr;
86 		else
87 			last->next = curr;
88 
89 		/* TODO: Take care of endianness */
90 		curr->opcode = LE32TOH(*src++);
91 		curr->offset = i;
92 
93 		/* NOTE: The block disassembly ends after the opcode that
94 		 * follows an unconditional jump (delay slot) */
95 		if (stop_next || is_syscall(curr))
96 			break;
97 		else if (is_unconditional_jump(curr))
98 			stop_next = true;
99 	}
100 
101 	if (len)
102 		*len = (i + 1) * sizeof(u32);
103 
104 	return head;
105 }
106 
lightrec_cycles_of_opcode(union code code)107 unsigned int lightrec_cycles_of_opcode(union code code)
108 {
109 	switch (code.i.op) {
110 	case OP_META_REG_UNLOAD:
111 	case OP_META_SYNC:
112 		return 0;
113 	default:
114 		return 2;
115 	}
116 }
117 
118 #if ENABLE_DISASSEMBLER
lightrec_print_disassembly(const struct block * block,const u32 * code,unsigned int length)119 void lightrec_print_disassembly(const struct block *block,
120 				const u32 *code, unsigned int length)
121 {
122 	struct disassemble_info info;
123 	unsigned int i;
124 
125 	memset(&info, 0, sizeof(info));
126 	init_disassemble_info(&info, stdout, (fprintf_ftype) fprintf);
127 
128 	info.buffer = (bfd_byte *) code;
129 	info.buffer_vma = (bfd_vma)(uintptr_t) code;
130 	info.buffer_length = length;
131 	info.flavour = bfd_target_unknown_flavour;
132 	info.arch = bfd_arch_mips;
133 	info.mach = bfd_mach_mips3000;
134 	disassemble_init_for_target(&info);
135 
136 	for (i = 0; i < length; i += 4) {
137 		void print_insn_little_mips(bfd_vma, struct disassemble_info *);
138 		putc('\t', stdout);
139 		print_insn_little_mips((bfd_vma)(uintptr_t) code++, &info);
140 		putc('\n', stdout);
141 	}
142 }
143 #endif
144