xref: /openbmc/qemu/contrib/plugins/stoptrigger.c (revision 58fc249d)
1*58fc249dSSimon Hamelin /*
2*58fc249dSSimon Hamelin  * Copyright (C) 2024, Simon Hamelin <simon.hamelin@grenoble-inp.org>
3*58fc249dSSimon Hamelin  *
4*58fc249dSSimon Hamelin  * Stop execution once a given address is reached or if the
5*58fc249dSSimon Hamelin  * count of executed instructions reached a specified limit
6*58fc249dSSimon Hamelin  *
7*58fc249dSSimon Hamelin  * License: GNU GPL, version 2 or later.
8*58fc249dSSimon Hamelin  *   See the COPYING file in the top-level directory.
9*58fc249dSSimon Hamelin  */
10*58fc249dSSimon Hamelin 
11*58fc249dSSimon Hamelin #include <assert.h>
12*58fc249dSSimon Hamelin #include <glib.h>
13*58fc249dSSimon Hamelin #include <inttypes.h>
14*58fc249dSSimon Hamelin #include <stdio.h>
15*58fc249dSSimon Hamelin #include <stdlib.h>
16*58fc249dSSimon Hamelin 
17*58fc249dSSimon Hamelin #include <qemu-plugin.h>
18*58fc249dSSimon Hamelin 
19*58fc249dSSimon Hamelin QEMU_PLUGIN_EXPORT int qemu_plugin_version = QEMU_PLUGIN_VERSION;
20*58fc249dSSimon Hamelin 
21*58fc249dSSimon Hamelin /* Scoreboard to track executed instructions count */
22*58fc249dSSimon Hamelin typedef struct {
23*58fc249dSSimon Hamelin     uint64_t insn_count;
24*58fc249dSSimon Hamelin } InstructionsCount;
25*58fc249dSSimon Hamelin static struct qemu_plugin_scoreboard *insn_count_sb;
26*58fc249dSSimon Hamelin static qemu_plugin_u64 insn_count;
27*58fc249dSSimon Hamelin 
28*58fc249dSSimon Hamelin static uint64_t icount;
29*58fc249dSSimon Hamelin static int icount_exit_code;
30*58fc249dSSimon Hamelin 
31*58fc249dSSimon Hamelin static bool exit_on_icount;
32*58fc249dSSimon Hamelin static bool exit_on_address;
33*58fc249dSSimon Hamelin 
34*58fc249dSSimon Hamelin /* Map trigger addresses to exit code */
35*58fc249dSSimon Hamelin static GHashTable *addrs_ht;
36*58fc249dSSimon Hamelin 
exit_emulation(int return_code,char * message)37*58fc249dSSimon Hamelin static void exit_emulation(int return_code, char *message)
38*58fc249dSSimon Hamelin {
39*58fc249dSSimon Hamelin     qemu_plugin_outs(message);
40*58fc249dSSimon Hamelin     g_free(message);
41*58fc249dSSimon Hamelin     exit(return_code);
42*58fc249dSSimon Hamelin }
43*58fc249dSSimon Hamelin 
exit_icount_reached(unsigned int cpu_index,void * udata)44*58fc249dSSimon Hamelin static void exit_icount_reached(unsigned int cpu_index, void *udata)
45*58fc249dSSimon Hamelin {
46*58fc249dSSimon Hamelin     uint64_t insn_vaddr = GPOINTER_TO_UINT(udata);
47*58fc249dSSimon Hamelin     char *msg = g_strdup_printf("icount reached at 0x%" PRIx64 ", exiting\n",
48*58fc249dSSimon Hamelin                                 insn_vaddr);
49*58fc249dSSimon Hamelin 
50*58fc249dSSimon Hamelin     exit_emulation(icount_exit_code, msg);
51*58fc249dSSimon Hamelin }
52*58fc249dSSimon Hamelin 
exit_address_reached(unsigned int cpu_index,void * udata)53*58fc249dSSimon Hamelin static void exit_address_reached(unsigned int cpu_index, void *udata)
54*58fc249dSSimon Hamelin {
55*58fc249dSSimon Hamelin     uint64_t insn_vaddr = GPOINTER_TO_UINT(udata);
56*58fc249dSSimon Hamelin     char *msg = g_strdup_printf("0x%" PRIx64 " reached, exiting\n", insn_vaddr);
57*58fc249dSSimon Hamelin     int exit_code;
58*58fc249dSSimon Hamelin 
59*58fc249dSSimon Hamelin     exit_code = GPOINTER_TO_INT(
60*58fc249dSSimon Hamelin         g_hash_table_lookup(addrs_ht, GUINT_TO_POINTER(insn_vaddr)));
61*58fc249dSSimon Hamelin 
62*58fc249dSSimon Hamelin     exit_emulation(exit_code, msg);
63*58fc249dSSimon Hamelin }
64*58fc249dSSimon Hamelin 
vcpu_tb_trans(qemu_plugin_id_t id,struct qemu_plugin_tb * tb)65*58fc249dSSimon Hamelin static void vcpu_tb_trans(qemu_plugin_id_t id, struct qemu_plugin_tb *tb)
66*58fc249dSSimon Hamelin {
67*58fc249dSSimon Hamelin     size_t tb_n = qemu_plugin_tb_n_insns(tb);
68*58fc249dSSimon Hamelin     for (size_t i = 0; i < tb_n; i++) {
69*58fc249dSSimon Hamelin         struct qemu_plugin_insn *insn = qemu_plugin_tb_get_insn(tb, i);
70*58fc249dSSimon Hamelin         gpointer insn_vaddr = GUINT_TO_POINTER(qemu_plugin_insn_vaddr(insn));
71*58fc249dSSimon Hamelin 
72*58fc249dSSimon Hamelin         if (exit_on_icount) {
73*58fc249dSSimon Hamelin             /* Increment and check scoreboard for each instruction */
74*58fc249dSSimon Hamelin             qemu_plugin_register_vcpu_insn_exec_inline_per_vcpu(
75*58fc249dSSimon Hamelin                 insn, QEMU_PLUGIN_INLINE_ADD_U64, insn_count, 1);
76*58fc249dSSimon Hamelin             qemu_plugin_register_vcpu_insn_exec_cond_cb(
77*58fc249dSSimon Hamelin                 insn, exit_icount_reached, QEMU_PLUGIN_CB_NO_REGS,
78*58fc249dSSimon Hamelin                 QEMU_PLUGIN_COND_EQ, insn_count, icount + 1, insn_vaddr);
79*58fc249dSSimon Hamelin         }
80*58fc249dSSimon Hamelin 
81*58fc249dSSimon Hamelin         if (exit_on_address) {
82*58fc249dSSimon Hamelin             if (g_hash_table_contains(addrs_ht, insn_vaddr)) {
83*58fc249dSSimon Hamelin                 /* Exit triggered by address */
84*58fc249dSSimon Hamelin                 qemu_plugin_register_vcpu_insn_exec_cb(
85*58fc249dSSimon Hamelin                     insn, exit_address_reached, QEMU_PLUGIN_CB_NO_REGS,
86*58fc249dSSimon Hamelin                     insn_vaddr);
87*58fc249dSSimon Hamelin             }
88*58fc249dSSimon Hamelin         }
89*58fc249dSSimon Hamelin     }
90*58fc249dSSimon Hamelin }
91*58fc249dSSimon Hamelin 
plugin_exit(qemu_plugin_id_t id,void * p)92*58fc249dSSimon Hamelin static void plugin_exit(qemu_plugin_id_t id, void *p)
93*58fc249dSSimon Hamelin {
94*58fc249dSSimon Hamelin     g_hash_table_destroy(addrs_ht);
95*58fc249dSSimon Hamelin     qemu_plugin_scoreboard_free(insn_count_sb);
96*58fc249dSSimon Hamelin }
97*58fc249dSSimon Hamelin 
qemu_plugin_install(qemu_plugin_id_t id,const qemu_info_t * info,int argc,char ** argv)98*58fc249dSSimon Hamelin QEMU_PLUGIN_EXPORT int qemu_plugin_install(qemu_plugin_id_t id,
99*58fc249dSSimon Hamelin                                            const qemu_info_t *info, int argc,
100*58fc249dSSimon Hamelin                                            char **argv)
101*58fc249dSSimon Hamelin {
102*58fc249dSSimon Hamelin     addrs_ht = g_hash_table_new(NULL, g_direct_equal);
103*58fc249dSSimon Hamelin 
104*58fc249dSSimon Hamelin     insn_count_sb = qemu_plugin_scoreboard_new(sizeof(InstructionsCount));
105*58fc249dSSimon Hamelin     insn_count = qemu_plugin_scoreboard_u64_in_struct(
106*58fc249dSSimon Hamelin         insn_count_sb, InstructionsCount, insn_count);
107*58fc249dSSimon Hamelin 
108*58fc249dSSimon Hamelin     for (int i = 0; i < argc; i++) {
109*58fc249dSSimon Hamelin         char *opt = argv[i];
110*58fc249dSSimon Hamelin         g_auto(GStrv) tokens = g_strsplit(opt, "=", 2);
111*58fc249dSSimon Hamelin         if (g_strcmp0(tokens[0], "icount") == 0) {
112*58fc249dSSimon Hamelin             g_auto(GStrv) icount_tokens = g_strsplit(tokens[1], ":", 2);
113*58fc249dSSimon Hamelin             icount = g_ascii_strtoull(icount_tokens[0], NULL, 0);
114*58fc249dSSimon Hamelin             if (icount < 1 || g_strrstr(icount_tokens[0], "-") != NULL) {
115*58fc249dSSimon Hamelin                 fprintf(stderr,
116*58fc249dSSimon Hamelin                         "icount parsing failed: '%s' must be a positive "
117*58fc249dSSimon Hamelin                         "integer\n",
118*58fc249dSSimon Hamelin                         icount_tokens[0]);
119*58fc249dSSimon Hamelin                 return -1;
120*58fc249dSSimon Hamelin             }
121*58fc249dSSimon Hamelin             if (icount_tokens[1]) {
122*58fc249dSSimon Hamelin                 icount_exit_code = g_ascii_strtoull(icount_tokens[1], NULL, 0);
123*58fc249dSSimon Hamelin             }
124*58fc249dSSimon Hamelin             exit_on_icount = true;
125*58fc249dSSimon Hamelin         } else if (g_strcmp0(tokens[0], "addr") == 0) {
126*58fc249dSSimon Hamelin             g_auto(GStrv) addr_tokens = g_strsplit(tokens[1], ":", 2);
127*58fc249dSSimon Hamelin             uint64_t exit_addr = g_ascii_strtoull(addr_tokens[0], NULL, 0);
128*58fc249dSSimon Hamelin             int exit_code = 0;
129*58fc249dSSimon Hamelin             if (addr_tokens[1]) {
130*58fc249dSSimon Hamelin                 exit_code = g_ascii_strtoull(addr_tokens[1], NULL, 0);
131*58fc249dSSimon Hamelin             }
132*58fc249dSSimon Hamelin             g_hash_table_insert(addrs_ht, GUINT_TO_POINTER(exit_addr),
133*58fc249dSSimon Hamelin                                 GINT_TO_POINTER(exit_code));
134*58fc249dSSimon Hamelin             exit_on_address = true;
135*58fc249dSSimon Hamelin         } else {
136*58fc249dSSimon Hamelin             fprintf(stderr, "option parsing failed: %s\n", opt);
137*58fc249dSSimon Hamelin             return -1;
138*58fc249dSSimon Hamelin         }
139*58fc249dSSimon Hamelin     }
140*58fc249dSSimon Hamelin 
141*58fc249dSSimon Hamelin     if (!exit_on_icount && !exit_on_address) {
142*58fc249dSSimon Hamelin         fprintf(stderr, "'icount' or 'addr' argument missing\n");
143*58fc249dSSimon Hamelin         return -1;
144*58fc249dSSimon Hamelin     }
145*58fc249dSSimon Hamelin 
146*58fc249dSSimon Hamelin     /* Register translation block and exit callbacks */
147*58fc249dSSimon Hamelin     qemu_plugin_register_vcpu_tb_trans_cb(id, vcpu_tb_trans);
148*58fc249dSSimon Hamelin     qemu_plugin_register_atexit_cb(id, plugin_exit, NULL);
149*58fc249dSSimon Hamelin 
150*58fc249dSSimon Hamelin     return 0;
151*58fc249dSSimon Hamelin }
152