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 */ 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 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 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 */ 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 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 */ 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