xref: /qemu/tests/tcg/plugins/patch.c (revision 597639c4273d1433b0a47c8533b90ccce29f84e5)
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