cachepc-qemu

Fork of AMDESE/qemu with changes for cachepc side-channel attack
git clone https://git.sinitax.com/sinitax/cachepc-qemu
Log | Files | Refs | Submodules | LICENSE | sfeed.txt

qmp-dispatch.c (7990B)


      1/*
      2 * Core Definitions for QAPI/QMP Dispatch
      3 *
      4 * Copyright IBM, Corp. 2011
      5 *
      6 * Authors:
      7 *  Anthony Liguori   <aliguori@us.ibm.com>
      8 *
      9 * This work is licensed under the terms of the GNU LGPL, version 2.1 or later.
     10 * See the COPYING.LIB file in the top-level directory.
     11 *
     12 */
     13
     14#include "qemu/osdep.h"
     15
     16#include "block/aio.h"
     17#include "qapi/compat-policy.h"
     18#include "qapi/error.h"
     19#include "qapi/qmp/dispatch.h"
     20#include "qapi/qmp/qdict.h"
     21#include "qapi/qmp/qjson.h"
     22#include "qapi/qobject-input-visitor.h"
     23#include "qapi/qobject-output-visitor.h"
     24#include "sysemu/runstate.h"
     25#include "qapi/qmp/qbool.h"
     26#include "qemu/coroutine.h"
     27#include "qemu/main-loop.h"
     28
     29CompatPolicy compat_policy;
     30
     31Visitor *qobject_input_visitor_new_qmp(QObject *obj)
     32{
     33    Visitor *v = qobject_input_visitor_new(obj);
     34
     35    qobject_input_visitor_set_policy(v, compat_policy.deprecated_input);
     36    return v;
     37}
     38
     39Visitor *qobject_output_visitor_new_qmp(QObject **result)
     40{
     41    Visitor *v = qobject_output_visitor_new(result);
     42
     43    qobject_output_visitor_set_policy(v, compat_policy.deprecated_output);
     44    return v;
     45}
     46
     47static QDict *qmp_dispatch_check_obj(QDict *dict, bool allow_oob,
     48                                     Error **errp)
     49{
     50    const char *exec_key = NULL;
     51    const QDictEntry *ent;
     52    const char *arg_name;
     53    const QObject *arg_obj;
     54
     55    for (ent = qdict_first(dict); ent;
     56         ent = qdict_next(dict, ent)) {
     57        arg_name = qdict_entry_key(ent);
     58        arg_obj = qdict_entry_value(ent);
     59
     60        if (!strcmp(arg_name, "execute")
     61            || (!strcmp(arg_name, "exec-oob") && allow_oob)) {
     62            if (qobject_type(arg_obj) != QTYPE_QSTRING) {
     63                error_setg(errp, "QMP input member '%s' must be a string",
     64                           arg_name);
     65                return NULL;
     66            }
     67            if (exec_key) {
     68                error_setg(errp, "QMP input member '%s' clashes with '%s'",
     69                           arg_name, exec_key);
     70                return NULL;
     71            }
     72            exec_key = arg_name;
     73        } else if (!strcmp(arg_name, "arguments")) {
     74            if (qobject_type(arg_obj) != QTYPE_QDICT) {
     75                error_setg(errp,
     76                           "QMP input member 'arguments' must be an object");
     77                return NULL;
     78            }
     79        } else if (!strcmp(arg_name, "id")) {
     80            continue;
     81        } else {
     82            error_setg(errp, "QMP input member '%s' is unexpected",
     83                       arg_name);
     84            return NULL;
     85        }
     86    }
     87
     88    if (!exec_key) {
     89        error_setg(errp, "QMP input lacks member 'execute'");
     90        return NULL;
     91    }
     92
     93    return dict;
     94}
     95
     96QDict *qmp_error_response(Error *err)
     97{
     98    QDict *rsp;
     99
    100    rsp = qdict_from_jsonf_nofail("{ 'error': { 'class': %s, 'desc': %s } }",
    101                                  QapiErrorClass_str(error_get_class(err)),
    102                                  error_get_pretty(err));
    103    error_free(err);
    104    return rsp;
    105}
    106
    107/*
    108 * Does @qdict look like a command to be run out-of-band?
    109 */
    110bool qmp_is_oob(const QDict *dict)
    111{
    112    return qdict_haskey(dict, "exec-oob")
    113        && !qdict_haskey(dict, "execute");
    114}
    115
    116typedef struct QmpDispatchBH {
    117    const QmpCommand *cmd;
    118    Monitor *cur_mon;
    119    QDict *args;
    120    QObject **ret;
    121    Error **errp;
    122    Coroutine *co;
    123} QmpDispatchBH;
    124
    125static void do_qmp_dispatch_bh(void *opaque)
    126{
    127    QmpDispatchBH *data = opaque;
    128
    129    assert(monitor_cur() == NULL);
    130    monitor_set_cur(qemu_coroutine_self(), data->cur_mon);
    131    data->cmd->fn(data->args, data->ret, data->errp);
    132    monitor_set_cur(qemu_coroutine_self(), NULL);
    133    aio_co_wake(data->co);
    134}
    135
    136/*
    137 * Runs outside of coroutine context for OOB commands, but in coroutine
    138 * context for everything else.
    139 */
    140QDict *qmp_dispatch(const QmpCommandList *cmds, QObject *request,
    141                    bool allow_oob, Monitor *cur_mon)
    142{
    143    Error *err = NULL;
    144    bool oob;
    145    const char *command;
    146    QDict *args;
    147    const QmpCommand *cmd;
    148    QDict *dict;
    149    QObject *id;
    150    QObject *ret = NULL;
    151    QDict *rsp = NULL;
    152
    153    dict = qobject_to(QDict, request);
    154    if (!dict) {
    155        id = NULL;
    156        error_setg(&err, "QMP input must be a JSON object");
    157        goto out;
    158    }
    159
    160    id = qdict_get(dict, "id");
    161
    162    if (!qmp_dispatch_check_obj(dict, allow_oob, &err)) {
    163        goto out;
    164    }
    165
    166    command = qdict_get_try_str(dict, "execute");
    167    oob = false;
    168    if (!command) {
    169        assert(allow_oob);
    170        command = qdict_get_str(dict, "exec-oob");
    171        oob = true;
    172    }
    173    cmd = qmp_find_command(cmds, command);
    174    if (cmd == NULL) {
    175        error_set(&err, ERROR_CLASS_COMMAND_NOT_FOUND,
    176                  "The command %s has not been found", command);
    177        goto out;
    178    }
    179    if (cmd->options & QCO_DEPRECATED) {
    180        switch (compat_policy.deprecated_input) {
    181        case COMPAT_POLICY_INPUT_ACCEPT:
    182            break;
    183        case COMPAT_POLICY_INPUT_REJECT:
    184            error_set(&err, ERROR_CLASS_COMMAND_NOT_FOUND,
    185                      "Deprecated command %s disabled by policy",
    186                      command);
    187            goto out;
    188        case COMPAT_POLICY_INPUT_CRASH:
    189        default:
    190            abort();
    191        }
    192    }
    193    if (!cmd->enabled) {
    194        error_set(&err, ERROR_CLASS_COMMAND_NOT_FOUND,
    195                  "Command %s has been disabled%s%s",
    196                  command,
    197                  cmd->disable_reason ? ": " : "",
    198                  cmd->disable_reason ?: "");
    199        goto out;
    200    }
    201    if (oob && !(cmd->options & QCO_ALLOW_OOB)) {
    202        error_setg(&err, "The command %s does not support OOB",
    203                   command);
    204        goto out;
    205    }
    206
    207    if (!qmp_command_available(cmd, &err)) {
    208        goto out;
    209    }
    210
    211    if (!qdict_haskey(dict, "arguments")) {
    212        args = qdict_new();
    213    } else {
    214        args = qdict_get_qdict(dict, "arguments");
    215        qobject_ref(args);
    216    }
    217
    218    assert(!(oob && qemu_in_coroutine()));
    219    assert(monitor_cur() == NULL);
    220    if (!!(cmd->options & QCO_COROUTINE) == qemu_in_coroutine()) {
    221        monitor_set_cur(qemu_coroutine_self(), cur_mon);
    222        cmd->fn(args, &ret, &err);
    223        monitor_set_cur(qemu_coroutine_self(), NULL);
    224    } else {
    225       /*
    226        * Actual context doesn't match the one the command needs.
    227        *
    228        * Case 1: we are in coroutine context, but command does not
    229        * have QCO_COROUTINE.  We need to drop out of coroutine
    230        * context for executing it.
    231        *
    232        * Case 2: we are outside coroutine context, but command has
    233        * QCO_COROUTINE.  Can't actually happen, because we get here
    234        * outside coroutine context only when executing a command
    235        * out of band, and OOB commands never have QCO_COROUTINE.
    236        */
    237        assert(!oob && qemu_in_coroutine() && !(cmd->options & QCO_COROUTINE));
    238
    239        QmpDispatchBH data = {
    240            .cur_mon    = cur_mon,
    241            .cmd        = cmd,
    242            .args       = args,
    243            .ret        = &ret,
    244            .errp       = &err,
    245            .co         = qemu_coroutine_self(),
    246        };
    247        aio_bh_schedule_oneshot(qemu_get_aio_context(), do_qmp_dispatch_bh,
    248                                &data);
    249        qemu_coroutine_yield();
    250    }
    251    qobject_unref(args);
    252    if (err) {
    253        /* or assert(!ret) after reviewing all handlers: */
    254        qobject_unref(ret);
    255        goto out;
    256    }
    257
    258    if (cmd->options & QCO_NO_SUCCESS_RESP) {
    259        g_assert(!ret);
    260        return NULL;
    261    } else if (!ret) {
    262        /*
    263         * When the command's schema has no 'returns', cmd->fn()
    264         * leaves @ret null.  The QMP spec calls for an empty object
    265         * then; supply it.
    266         */
    267        ret = QOBJECT(qdict_new());
    268    }
    269
    270    rsp = qdict_new();
    271    qdict_put_obj(rsp, "return", ret);
    272
    273out:
    274    if (err) {
    275        assert(!rsp);
    276        rsp = qmp_error_response(err);
    277    }
    278
    279    assert(rsp);
    280
    281    if (id) {
    282        qdict_put_obj(rsp, "id", qobject_ref(id));
    283    }
    284
    285    return rsp;
    286}