1 /*
2 * SPDX-License-Identifier: GPL-2.0-or-later
3 *
4 * This plugin patches instructions matching a pattern to a different
5 * instruction as they execute
6 *
7 */
8
9 #include "glib.h"
10 #include "glibconfig.h"
11
12 #include <qemu-plugin.h>
13 #include <string.h>
14 #include <stdio.h>
15
16 QEMU_PLUGIN_EXPORT int qemu_plugin_version = QEMU_PLUGIN_VERSION;
17
18 static bool use_hwaddr;
19 static GByteArray *target_data;
20 static GByteArray *patch_data;
21
22 /**
23 * Parse a string of hexadecimal digits into a GByteArray. The string must be
24 * even length
25 */
str_to_bytes(const char * str)26 static GByteArray *str_to_bytes(const char *str)
27 {
28 size_t len = strlen(str);
29
30 if (len == 0 || len % 2 != 0) {
31 return NULL;
32 }
33
34 GByteArray *bytes = g_byte_array_new();
35 char byte[3] = {0};
36 guint8 value = 0;
37
38 for (size_t i = 0; i < len; i += 2) {
39 byte[0] = str[i];
40 byte[1] = str[i + 1];
41 value = (guint8)g_ascii_strtoull(byte, NULL, 16);
42 g_byte_array_append(bytes, &value, 1);
43 }
44
45 return bytes;
46 }
47
patch_hwaddr(unsigned int vcpu_index,void * userdata)48 static void patch_hwaddr(unsigned int vcpu_index, void *userdata)
49 {
50 uintptr_t addr = (uintptr_t) userdata;
51 g_autoptr(GString) str = g_string_new(NULL);
52 g_string_printf(str, "patching: @0x%"
53 PRIxPTR "\n",
54 addr);
55 qemu_plugin_outs(str->str);
56
57 enum qemu_plugin_hwaddr_operation_result result =
58 qemu_plugin_write_memory_hwaddr(addr, patch_data);
59
60
61 if (result != QEMU_PLUGIN_HWADDR_OPERATION_OK) {
62 g_autoptr(GString) errmsg = g_string_new(NULL);
63 g_string_printf(errmsg, "Failed to write memory: %d\n", result);
64 qemu_plugin_outs(errmsg->str);
65 return;
66 }
67
68 GByteArray *read_data = g_byte_array_new();
69
70 result = qemu_plugin_read_memory_hwaddr(addr, read_data,
71 patch_data->len);
72
73 qemu_plugin_outs("Reading memory...\n");
74
75 if (result != QEMU_PLUGIN_HWADDR_OPERATION_OK) {
76 g_autoptr(GString) errmsg = g_string_new(NULL);
77 g_string_printf(errmsg, "Failed to read memory: %d\n", result);
78 qemu_plugin_outs(errmsg->str);
79 return;
80 }
81
82 if (memcmp(patch_data->data, read_data->data, patch_data->len) != 0) {
83 qemu_plugin_outs("Failed to read back written data\n");
84 }
85
86 qemu_plugin_outs("Success!\n");
87
88 return;
89 }
90
patch_vaddr(unsigned int vcpu_index,void * userdata)91 static void patch_vaddr(unsigned int vcpu_index, void *userdata)
92 {
93 uintptr_t addr = (uintptr_t) userdata;
94 uint64_t hwaddr = 0;
95 if (!qemu_plugin_translate_vaddr(addr, &hwaddr)) {
96 qemu_plugin_outs("Failed to translate vaddr\n");
97 return;
98 }
99 g_autoptr(GString) str = g_string_new(NULL);
100 g_string_printf(str, "patching: @0x%"
101 PRIxPTR " hw: @0x%" PRIx64 "\n",
102 addr, hwaddr);
103 qemu_plugin_outs(str->str);
104
105 qemu_plugin_outs("Writing memory (vaddr)...\n");
106
107 if (!qemu_plugin_write_memory_vaddr(addr, patch_data)) {
108 qemu_plugin_outs("Failed to write memory\n");
109 return;
110 }
111
112 qemu_plugin_outs("Reading memory (vaddr)...\n");
113
114 g_autoptr(GByteArray) read_data = g_byte_array_new();
115
116 if (!qemu_plugin_read_memory_vaddr(addr, read_data, patch_data->len)) {
117 qemu_plugin_outs("Failed to read memory\n");
118 return;
119 }
120
121 if (memcmp(patch_data->data, read_data->data, patch_data->len) != 0) {
122 qemu_plugin_outs("Failed to read back written data\n");
123 }
124
125 qemu_plugin_outs("Success!\n");
126
127 return;
128 }
129
130 /*
131 * Callback on translation of a translation block.
132 */
vcpu_tb_trans_cb(qemu_plugin_id_t id,struct qemu_plugin_tb * tb)133 static void vcpu_tb_trans_cb(qemu_plugin_id_t id, struct qemu_plugin_tb *tb)
134 {
135 g_autoptr(GByteArray) insn_data = g_byte_array_new();
136 uintptr_t addr = 0;
137
138 for (size_t i = 0; i < qemu_plugin_tb_n_insns(tb); i++) {
139 struct qemu_plugin_insn *insn = qemu_plugin_tb_get_insn(tb, i);
140 uint64_t vaddr = qemu_plugin_insn_vaddr(insn);
141
142 if (use_hwaddr) {
143 uint64_t hwaddr = 0;
144 if (!qemu_plugin_translate_vaddr(vaddr, &hwaddr)) {
145 qemu_plugin_outs("Failed to translate vaddr\n");
146 continue;
147 }
148 /*
149 * As we cannot emulate 64 bit systems on 32 bit hosts we
150 * should never see the top bits set, hence we can safely
151 * cast to uintptr_t.
152 */
153 g_assert(hwaddr <= UINTPTR_MAX);
154 addr = (uintptr_t) hwaddr;
155 } else {
156 g_assert(vaddr <= UINTPTR_MAX);
157 addr = (uintptr_t) vaddr;
158 }
159
160 g_byte_array_set_size(insn_data, qemu_plugin_insn_size(insn));
161 qemu_plugin_insn_data(insn, insn_data->data, insn_data->len);
162
163 if (insn_data->len >= target_data->len &&
164 !memcmp(insn_data->data, target_data->data,
165 MIN(target_data->len, insn_data->len))) {
166 if (use_hwaddr) {
167 qemu_plugin_register_vcpu_tb_exec_cb(tb, patch_hwaddr,
168 QEMU_PLUGIN_CB_NO_REGS,
169 (void *) addr);
170 } else {
171 qemu_plugin_register_vcpu_tb_exec_cb(tb, patch_vaddr,
172 QEMU_PLUGIN_CB_NO_REGS,
173 (void *) addr);
174 }
175 }
176 }
177 }
178
usage(void)179 static void usage(void)
180 {
181 fprintf(stderr, "Usage: <lib>,target=<bytes>,patch=<new_bytes>"
182 "[,use_hwaddr=true|false]");
183 }
184
185 /*
186 * Called when the plugin is installed
187 */
qemu_plugin_install(qemu_plugin_id_t id,const qemu_info_t * info,int argc,char ** argv)188 QEMU_PLUGIN_EXPORT int qemu_plugin_install(qemu_plugin_id_t id,
189 const qemu_info_t *info, int argc,
190 char **argv)
191 {
192
193 use_hwaddr = true;
194 target_data = NULL;
195 patch_data = NULL;
196
197 if (argc > 4) {
198 usage();
199 return -1;
200 }
201
202 for (size_t i = 0; i < argc; i++) {
203 char *opt = argv[i];
204 g_auto(GStrv) tokens = g_strsplit(opt, "=", 2);
205 if (g_strcmp0(tokens[0], "use_hwaddr") == 0) {
206 if (!qemu_plugin_bool_parse(tokens[0], tokens[1], &use_hwaddr)) {
207 fprintf(stderr,
208 "Failed to parse boolean argument use_hwaddr\n");
209 return -1;
210 }
211 } else if (g_strcmp0(tokens[0], "target") == 0) {
212 target_data = str_to_bytes(tokens[1]);
213 if (!target_data) {
214 fprintf(stderr,
215 "Failed to parse target bytes.\n");
216 return -1;
217 }
218 } else if (g_strcmp0(tokens[0], "patch") == 0) {
219 patch_data = str_to_bytes(tokens[1]);
220 if (!patch_data) {
221 fprintf(stderr, "Failed to parse patch bytes.\n");
222 return -1;
223 }
224 } else {
225 fprintf(stderr, "Unknown argument: %s\n", tokens[0]);
226 usage();
227 return -1;
228 }
229 }
230
231 if (!target_data) {
232 fprintf(stderr, "target argument is required\n");
233 usage();
234 return -1;
235 }
236
237 if (!patch_data) {
238 fprintf(stderr, "patch argument is required\n");
239 usage();
240 return -1;
241 }
242
243 if (target_data->len != patch_data->len) {
244 fprintf(stderr, "Target and patch data must be the same length\n");
245 return -1;
246 }
247
248 qemu_plugin_register_vcpu_tb_trans_cb(id, vcpu_tb_trans_cb);
249
250 return 0;
251 }
252