From a37039a47b9e2b132a27824b9d273cd21aaccb7c Mon Sep 17 00:00:00 2001 From: Andrew Fasano Date: Fri, 1 Dec 2023 17:28:38 -0500 Subject: [PATCH] Add QMP callback --- panda/include/panda/callbacks/cb-defs.h | 35 ++++++++++++++ panda/include/panda/callbacks/cb-support.h | 1 + .../include/panda/callbacks/cb-trampolines.h | 1 + panda/src/callbacks.c | 1 + panda/src/cb-support.c | 2 + qapi/qmp-dispatch.c | 47 ++++++++++++++++--- 6 files changed, 80 insertions(+), 7 deletions(-) diff --git a/panda/include/panda/callbacks/cb-defs.h b/panda/include/panda/callbacks/cb-defs.h index 6170f6d699f..39f94216cc0 100644 --- a/panda/include/panda/callbacks/cb-defs.h +++ b/panda/include/panda/callbacks/cb-defs.h @@ -57,6 +57,7 @@ typedef enum panda_cb_type { PANDA_CB_HD_WRITE, // Each HDD write PANDA_CB_GUEST_HYPERCALL, // Hypercall from the guest (e.g. CPUID) PANDA_CB_MONITOR, // Monitor callback + PANDA_CB_QMP, // QMP callback PANDA_CB_CPU_RESTORE_STATE, // In cpu_restore_state() (fault/exception) PANDA_CB_BEFORE_LOADVM, // at start of replay, before loadvm PANDA_CB_ASID_CHANGED, // When CPU asid (address space identifier) changes @@ -592,6 +593,23 @@ typedef union panda_cb { */ int (*monitor)(Monitor *mon, const char *cmd); + /* Callback ID: PANDA_CB_QMP + + qmp: + Called when someone sends an unhandled QMP command + + Arguments: + char *command: the command string as json + char *args: the arguments string as json + char **result: pointer to a json result or NULL + + Helper call location: TBA + + Return value: + bool: true IFF the command was handled by the plugin + */ + bool (*qmp)(char *command, char* args, char **result); + /* Callback ID: PANDA_CB_CPU_RESTORE_STATE @@ -1544,6 +1562,23 @@ typedef union panda_cb_with_context { */ int (*monitor)(void* context, Monitor *mon, const char *cmd); + /* Callback ID: PANDA_CB_QMP + + qmp: + Called when someone sends an unhandled QMP command + + Arguments: + char *command: the command string as json + char *args: the arguments string as json + char **result: pointer to a json result or NULL + + Helper call location: TBA + + Return value: + bool: true IFF the command was handled by the plugin + */ + bool (*qmp)(void* context, char *command, char* args, char **result); + /* Callback ID: PANDA_CB_CPU_RESTORE_STATE diff --git a/panda/include/panda/callbacks/cb-support.h b/panda/include/panda/callbacks/cb-support.h index af894b7f4d5..b7009d5b971 100644 --- a/panda/include/panda/callbacks/cb-support.h +++ b/panda/include/panda/callbacks/cb-support.h @@ -75,6 +75,7 @@ bool panda_callbacks_after_find_fast(CPUState *cpu, TranslationBlock *tb, bool b int panda_callbacks_insn_exec(CPUState *env, target_ptr_t pc); int panda_callbacks_after_insn_exec(CPUState *env, target_ptr_t pc); int panda_callbacks_monitor(Monitor *mon, const char *cmd); +bool panda_callbacks_qmp(char *command, char *args, char **result); int panda_callbacks_before_loadvm(void); void panda_callbacks_replay_hd_transfer(CPUState *env, uint32_t type, target_ptr_t src_addr, target_ptr_t dest_addr, size_t num_bytes); void panda_callbacks_after_machine_init(CPUState *env); diff --git a/panda/include/panda/callbacks/cb-trampolines.h b/panda/include/panda/callbacks/cb-trampolines.h index f13ca5be3a3..2fbae221422 100644 --- a/panda/include/panda/callbacks/cb-trampolines.h +++ b/panda/include/panda/callbacks/cb-trampolines.h @@ -19,6 +19,7 @@ void panda_cb_trampoline_phys_mem_after_write(void* context, CPUState *env, targ int panda_cb_trampoline_insn_exec(void* context, CPUState *env, target_ptr_t pc); int panda_cb_trampoline_after_insn_exec(void* context, CPUState *env, target_ptr_t pc); int panda_cb_trampoline_monitor(void* context, Monitor *mon, const char *cmd); +bool panda_cb_trampoline_qmp(void* context, char *command, char *args, char **result); //int panda_cb_trampoline_before_loadvm(void* context); void panda_cb_trampoline_replay_hd_transfer(void* context, CPUState *env, uint32_t type, target_ptr_t src_addr, target_ptr_t dest_addr, size_t num_bytes); void panda_cb_trampoline_after_machine_init(void* context, CPUState *env); diff --git a/panda/src/callbacks.c b/panda/src/callbacks.c index 787e71fe3f7..73ec90c1bb0 100644 --- a/panda/src/callbacks.c +++ b/panda/src/callbacks.c @@ -474,6 +474,7 @@ panda_cb_with_context panda_get_cb_trampoline(panda_cb_type type) { CASE_CB_TRAMPOLINE(HD_WRITE,hd_write) CASE_CB_TRAMPOLINE(GUEST_HYPERCALL,guest_hypercall) CASE_CB_TRAMPOLINE(MONITOR,monitor) + CASE_CB_TRAMPOLINE(QMP,qmp) CASE_CB_TRAMPOLINE(CPU_RESTORE_STATE,cpu_restore_state) //CASE_CB_TRAMPOLINE(BEFORE_LOADVM,before_loadvm) diff --git a/panda/src/cb-support.c b/panda/src/cb-support.c index beb9517ca73..0f72386ed18 100644 --- a/panda/src/cb-support.c +++ b/panda/src/cb-support.c @@ -101,7 +101,9 @@ void panda_cb_trampoline_start_block_exec(void* context, CPUState *cpu, Translat // these aren't used MAKE_CALLBACK(void, HD_READ, hd_read, CPUState*, env); MAKE_CALLBACK(void, HD_WRITE, hd_write, CPUState*, env); + MAKE_CALLBACK(int, MONITOR, monitor, Monitor*, mon, const char*, cmd); +MAKE_CALLBACK(bool, QMP, qmp, char*, cmd, char*, args, char **, result); // Helper - get a physical address static inline hwaddr get_paddr(CPUState *cpu, target_ptr_t addr, void *ram_ptr) { diff --git a/qapi/qmp-dispatch.c b/qapi/qmp-dispatch.c index dc502129d80..5bc2b2d36d4 100644 --- a/qapi/qmp-dispatch.c +++ b/qapi/qmp-dispatch.c @@ -19,6 +19,8 @@ #include "qapi/qmp/qjson.h" #include "qapi-types.h" #include "qapi/qmp/qerror.h" +//#include "panda/callbacks/cb-support.h" +extern bool panda_callbacks_qmp(char *command, char* args, char **result); static QDict *qmp_dispatch_check_obj(const QObject *request, Error **errp) { @@ -83,24 +85,55 @@ static QObject *do_qmp_dispatch(QmpCommandList *cmds, QObject *request, command = qdict_get_str(dict, "execute"); cmd = qmp_find_command(cmds, command); + + if (!qdict_haskey(dict, "arguments")) { + args = qdict_new(); + } else { + args = qdict_get_qdict(dict, "arguments"); + QINCREF(args); + } + if (cmd == NULL) { + // Call any PANDA consumers of the unhandled command + // Provide them with arguments in json format. + // If any plugin returns true, we assume it handled the command + // and we expect a json output in result. + char *result = NULL; + const QString* cmd_args_q = qobject_to_json(QOBJECT(args)); + const char *cmd_args = qstring_get_str(cmd_args_q); + + if (panda_callbacks_qmp((char*)command, (char*)cmd_args, &result)) { + if (result != NULL) { + // We have a return value from the callback. Let's convert it to a qobject + ret = qobject_from_json(result, &local_err); + if (local_err) { + printf("PANDA ERROR decoding result json in callback"); + error_propagate(errp, local_err); + return NULL; + } + } + + if (!ret) { + printf("PANDA WARNING: a qmp callback consumer returned TRUE without providing \ + a return value! Creating empty dictionary"); + + ret = QOBJECT(qdict_new()); + } + QDECREF(args); + return ret; + } + error_set(errp, ERROR_CLASS_COMMAND_NOT_FOUND, "The command %s has not been found", command); return NULL; } + if (!cmd->enabled) { error_setg(errp, "The command %s has been disabled for this instance", command); return NULL; } - if (!qdict_haskey(dict, "arguments")) { - args = qdict_new(); - } else { - args = qdict_get_qdict(dict, "arguments"); - QINCREF(args); - } - cmd->fn(args, &ret, &local_err); if (local_err) { error_propagate(errp, local_err);