mirror of
https://github.com/xemu-project/xemu.git
synced 2025-04-02 11:11:48 -04:00
pci_devfn properties accept either a string or an integer as input. To implement this, set_pci_devfn() first tries to visit the option as a string, and if that fails, it visits it as an integer instead. While the QemuOpts visitor happens to accept this, it is invalid according to the visitor interface. QObject input visitors run into an assertion failure when this is done. QObject input visitors are used with the JSON syntax version of -device on the command line: $ ./qemu-system-x86_64 -enable-kvm -M q35 -device pcie-pci-bridge,id=pci.1,bus=pcie.0 -blockdev null-co,node-name=disk -device '{ "driver": "virtio-blk-pci", "drive": "disk", "id": "virtio-disk0", "bus": "pci.1", "addr": 1 }' qemu-system-x86_64: ../qapi/qobject-input-visitor.c:143: QObject *qobject_input_try_get_object(QObjectInputVisitor *, const char *, _Bool): Assertion `removed' failed. The proper way to accept both strings and integers is using the alternate mechanism, which tells us the type of the input before it's visited. With this information, we can directly visit it as the right type. This fixes set_pci_devfn() by using the alternate mechanism. Cc: qemu-stable@nongnu.org Reported-by: Peter Maydell <peter.maydell@linaro.org> Signed-off-by: Kevin Wolf <kwolf@redhat.com> Message-ID: <20241119120353.57812-1-kwolf@redhat.com> Acked-by: Paolo Bonzini <pbonzini@redhat.com> Reviewed-by: Markus Armbruster <armbru@redhat.com> Signed-off-by: Kevin Wolf <kwolf@redhat.com>
1285 lines
34 KiB
C
1285 lines
34 KiB
C
/*
|
|
* qdev property parsing
|
|
* (parts specific for qemu-system-*)
|
|
*
|
|
* This file is based on code from hw/qdev-properties.c from
|
|
* commit 074a86fccd185616469dfcdc0e157f438aebba18,
|
|
* Copyright (c) Gerd Hoffmann <kraxel@redhat.com> and other contributors.
|
|
*
|
|
* This work is licensed under the terms of the GNU GPL, version 2 or later.
|
|
* See the COPYING file in the top-level directory.
|
|
*/
|
|
|
|
#include "qemu/osdep.h"
|
|
#include "hw/qdev-properties.h"
|
|
#include "hw/qdev-properties-system.h"
|
|
#include "qapi/error.h"
|
|
#include "qapi/visitor.h"
|
|
#include "qapi/qapi-types-block.h"
|
|
#include "qapi/qapi-types-machine.h"
|
|
#include "qapi/qapi-types-migration.h"
|
|
#include "qapi/qapi-visit-virtio.h"
|
|
#include "qapi/qmp/qerror.h"
|
|
#include "qemu/ctype.h"
|
|
#include "qemu/cutils.h"
|
|
#include "qemu/units.h"
|
|
#include "qemu/uuid.h"
|
|
#include "qemu/error-report.h"
|
|
#include "qdev-prop-internal.h"
|
|
|
|
#include "audio/audio.h"
|
|
#include "chardev/char-fe.h"
|
|
#include "sysemu/block-backend.h"
|
|
#include "sysemu/blockdev.h"
|
|
#include "net/net.h"
|
|
#include "hw/pci/pci.h"
|
|
#include "hw/pci/pcie.h"
|
|
#include "hw/i386/x86.h"
|
|
#include "util/block-helpers.h"
|
|
|
|
static bool check_prop_still_unset(Object *obj, const char *name,
|
|
const void *old_val, const char *new_val,
|
|
bool allow_override, Error **errp)
|
|
{
|
|
const GlobalProperty *prop = qdev_find_global_prop(obj, name);
|
|
|
|
if (!old_val || (!prop && allow_override)) {
|
|
return true;
|
|
}
|
|
|
|
if (prop) {
|
|
error_setg(errp, "-global %s.%s=... conflicts with %s=%s",
|
|
prop->driver, prop->property, name, new_val);
|
|
} else {
|
|
/* Error message is vague, but a better one would be hard */
|
|
error_setg(errp, "%s=%s conflicts, and override is not implemented",
|
|
name, new_val);
|
|
}
|
|
return false;
|
|
}
|
|
|
|
bool qdev_prop_sanitize_s390x_loadparm(uint8_t *loadparm, const char *str,
|
|
Error **errp)
|
|
{
|
|
int i, len;
|
|
|
|
len = strlen(str);
|
|
if (len > 8) {
|
|
error_setg(errp, "'loadparm' can only contain up to 8 characters");
|
|
return false;
|
|
}
|
|
|
|
for (i = 0; i < len; i++) {
|
|
uint8_t c = qemu_toupper(str[i]); /* mimic HMC */
|
|
|
|
if (qemu_isalnum(c) || c == '.' || c == ' ') {
|
|
loadparm[i] = c;
|
|
} else {
|
|
error_setg(errp,
|
|
"invalid character in 'loadparm': '%c' (ASCII 0x%02x)",
|
|
c, c);
|
|
return false;
|
|
}
|
|
}
|
|
|
|
return true;
|
|
}
|
|
|
|
/* --- drive --- */
|
|
|
|
static void get_drive(Object *obj, Visitor *v, const char *name, void *opaque,
|
|
Error **errp)
|
|
{
|
|
Property *prop = opaque;
|
|
void **ptr = object_field_prop_ptr(obj, prop);
|
|
const char *value;
|
|
char *p;
|
|
|
|
if (*ptr) {
|
|
value = blk_name(*ptr);
|
|
if (!*value) {
|
|
BlockDriverState *bs = blk_bs(*ptr);
|
|
if (bs) {
|
|
value = bdrv_get_node_name(bs);
|
|
}
|
|
}
|
|
} else {
|
|
value = "";
|
|
}
|
|
|
|
p = g_strdup(value);
|
|
visit_type_str(v, name, &p, errp);
|
|
g_free(p);
|
|
}
|
|
|
|
static void set_drive_helper(Object *obj, Visitor *v, const char *name,
|
|
void *opaque, bool iothread, Error **errp)
|
|
{
|
|
DeviceState *dev = DEVICE(obj);
|
|
Property *prop = opaque;
|
|
void **ptr = object_field_prop_ptr(obj, prop);
|
|
char *str;
|
|
BlockBackend *blk;
|
|
bool blk_created = false;
|
|
int ret;
|
|
BlockDriverState *bs;
|
|
AioContext *ctx;
|
|
|
|
if (!visit_type_str(v, name, &str, errp)) {
|
|
return;
|
|
}
|
|
|
|
if (!check_prop_still_unset(obj, name, *ptr, str, true, errp)) {
|
|
return;
|
|
}
|
|
|
|
if (*ptr) {
|
|
/* BlockBackend already exists. So, we want to change attached node */
|
|
blk = *ptr;
|
|
ctx = blk_get_aio_context(blk);
|
|
bs = bdrv_lookup_bs(NULL, str, errp);
|
|
if (!bs) {
|
|
return;
|
|
}
|
|
|
|
if (ctx != bdrv_get_aio_context(bs)) {
|
|
error_setg(errp, "Different aio context is not supported for new "
|
|
"node");
|
|
}
|
|
|
|
blk_replace_bs(blk, bs, errp);
|
|
return;
|
|
}
|
|
|
|
if (!*str) {
|
|
g_free(str);
|
|
*ptr = NULL;
|
|
return;
|
|
}
|
|
|
|
blk = blk_by_name(str);
|
|
if (!blk) {
|
|
bs = bdrv_lookup_bs(NULL, str, NULL);
|
|
if (bs) {
|
|
/*
|
|
* If the device supports iothreads, it will make sure to move the
|
|
* block node to the right AioContext if necessary (or fail if this
|
|
* isn't possible because of other users). Devices that are not
|
|
* aware of iothreads require their BlockBackends to be in the main
|
|
* AioContext.
|
|
*/
|
|
ctx = bdrv_get_aio_context(bs);
|
|
blk = blk_new(iothread ? ctx : qemu_get_aio_context(),
|
|
0, BLK_PERM_ALL);
|
|
blk_created = true;
|
|
|
|
ret = blk_insert_bs(blk, bs, errp);
|
|
if (ret < 0) {
|
|
goto fail;
|
|
}
|
|
}
|
|
}
|
|
if (!blk) {
|
|
error_setg(errp, "Property '%s.%s' can't find value '%s'",
|
|
object_get_typename(OBJECT(dev)), name, str);
|
|
goto fail;
|
|
}
|
|
if (blk_attach_dev(blk, dev) < 0) {
|
|
DriveInfo *dinfo = blk_legacy_dinfo(blk);
|
|
|
|
if (dinfo && dinfo->type != IF_NONE) {
|
|
error_setg(errp, "Drive '%s' is already in use because "
|
|
"it has been automatically connected to another "
|
|
"device (did you need 'if=none' in the drive options?)",
|
|
str);
|
|
} else {
|
|
error_setg(errp, "Drive '%s' is already in use by another device",
|
|
str);
|
|
}
|
|
goto fail;
|
|
}
|
|
|
|
*ptr = blk;
|
|
|
|
fail:
|
|
if (blk_created) {
|
|
/* If we need to keep a reference, blk_attach_dev() took it */
|
|
blk_unref(blk);
|
|
}
|
|
|
|
g_free(str);
|
|
}
|
|
|
|
static void set_drive(Object *obj, Visitor *v, const char *name, void *opaque,
|
|
Error **errp)
|
|
{
|
|
set_drive_helper(obj, v, name, opaque, false, errp);
|
|
}
|
|
|
|
static void set_drive_iothread(Object *obj, Visitor *v, const char *name,
|
|
void *opaque, Error **errp)
|
|
{
|
|
set_drive_helper(obj, v, name, opaque, true, errp);
|
|
}
|
|
|
|
static void release_drive(Object *obj, const char *name, void *opaque)
|
|
{
|
|
DeviceState *dev = DEVICE(obj);
|
|
Property *prop = opaque;
|
|
BlockBackend **ptr = object_field_prop_ptr(obj, prop);
|
|
|
|
if (*ptr) {
|
|
blockdev_auto_del(*ptr);
|
|
blk_detach_dev(*ptr, dev);
|
|
}
|
|
}
|
|
|
|
const PropertyInfo qdev_prop_drive = {
|
|
.name = "str",
|
|
.description = "Node name or ID of a block device to use as a backend",
|
|
.realized_set_allowed = true,
|
|
.get = get_drive,
|
|
.set = set_drive,
|
|
.release = release_drive,
|
|
};
|
|
|
|
const PropertyInfo qdev_prop_drive_iothread = {
|
|
.name = "str",
|
|
.description = "Node name or ID of a block device to use as a backend",
|
|
.realized_set_allowed = true,
|
|
.get = get_drive,
|
|
.set = set_drive_iothread,
|
|
.release = release_drive,
|
|
};
|
|
|
|
/* --- character device --- */
|
|
|
|
static void get_chr(Object *obj, Visitor *v, const char *name, void *opaque,
|
|
Error **errp)
|
|
{
|
|
CharBackend *be = object_field_prop_ptr(obj, opaque);
|
|
char *p;
|
|
|
|
p = g_strdup(be->chr && be->chr->label ? be->chr->label : "");
|
|
visit_type_str(v, name, &p, errp);
|
|
g_free(p);
|
|
}
|
|
|
|
static void set_chr(Object *obj, Visitor *v, const char *name, void *opaque,
|
|
Error **errp)
|
|
{
|
|
ERRP_GUARD();
|
|
Property *prop = opaque;
|
|
CharBackend *be = object_field_prop_ptr(obj, prop);
|
|
Chardev *s;
|
|
char *str;
|
|
|
|
if (!visit_type_str(v, name, &str, errp)) {
|
|
return;
|
|
}
|
|
|
|
/*
|
|
* TODO Should this really be an error? If no, the old value
|
|
* needs to be released before we store the new one.
|
|
*/
|
|
if (!check_prop_still_unset(obj, name, be->chr, str, false, errp)) {
|
|
return;
|
|
}
|
|
|
|
if (!*str) {
|
|
g_free(str);
|
|
be->chr = NULL;
|
|
return;
|
|
}
|
|
|
|
s = qemu_chr_find(str);
|
|
if (s == NULL) {
|
|
error_setg(errp, "Property '%s.%s' can't find value '%s'",
|
|
object_get_typename(obj), name, str);
|
|
} else if (!qemu_chr_fe_init(be, s, errp)) {
|
|
error_prepend(errp, "Property '%s.%s' can't take value '%s': ",
|
|
object_get_typename(obj), name, str);
|
|
}
|
|
g_free(str);
|
|
}
|
|
|
|
static void release_chr(Object *obj, const char *name, void *opaque)
|
|
{
|
|
Property *prop = opaque;
|
|
CharBackend *be = object_field_prop_ptr(obj, prop);
|
|
|
|
qemu_chr_fe_deinit(be, false);
|
|
}
|
|
|
|
const PropertyInfo qdev_prop_chr = {
|
|
.name = "str",
|
|
.description = "ID of a chardev to use as a backend",
|
|
.get = get_chr,
|
|
.set = set_chr,
|
|
.release = release_chr,
|
|
};
|
|
|
|
/* --- mac address --- */
|
|
|
|
/*
|
|
* accepted syntax versions:
|
|
* 01:02:03:04:05:06
|
|
* 01-02-03-04-05-06
|
|
*/
|
|
static void get_mac(Object *obj, Visitor *v, const char *name, void *opaque,
|
|
Error **errp)
|
|
{
|
|
Property *prop = opaque;
|
|
MACAddr *mac = object_field_prop_ptr(obj, prop);
|
|
char buffer[2 * 6 + 5 + 1];
|
|
char *p = buffer;
|
|
|
|
snprintf(buffer, sizeof(buffer), "%02x:%02x:%02x:%02x:%02x:%02x",
|
|
mac->a[0], mac->a[1], mac->a[2],
|
|
mac->a[3], mac->a[4], mac->a[5]);
|
|
|
|
visit_type_str(v, name, &p, errp);
|
|
}
|
|
|
|
static void set_mac(Object *obj, Visitor *v, const char *name, void *opaque,
|
|
Error **errp)
|
|
{
|
|
Property *prop = opaque;
|
|
MACAddr *mac = object_field_prop_ptr(obj, prop);
|
|
int i, pos;
|
|
char *str;
|
|
const char *p;
|
|
|
|
if (!visit_type_str(v, name, &str, errp)) {
|
|
return;
|
|
}
|
|
|
|
for (i = 0, pos = 0; i < 6; i++, pos += 3) {
|
|
long val;
|
|
|
|
if (!qemu_isxdigit(str[pos])) {
|
|
goto inval;
|
|
}
|
|
if (!qemu_isxdigit(str[pos + 1])) {
|
|
goto inval;
|
|
}
|
|
if (i == 5) {
|
|
if (str[pos + 2] != '\0') {
|
|
goto inval;
|
|
}
|
|
} else {
|
|
if (str[pos + 2] != ':' && str[pos + 2] != '-') {
|
|
goto inval;
|
|
}
|
|
}
|
|
if (qemu_strtol(str + pos, &p, 16, &val) < 0 || val > 0xff) {
|
|
goto inval;
|
|
}
|
|
mac->a[i] = val;
|
|
}
|
|
g_free(str);
|
|
return;
|
|
|
|
inval:
|
|
error_set_from_qdev_prop_error(errp, EINVAL, obj, name, str);
|
|
g_free(str);
|
|
}
|
|
|
|
const PropertyInfo qdev_prop_macaddr = {
|
|
.name = "str",
|
|
.description = "Ethernet 6-byte MAC Address, example: 52:54:00:12:34:56",
|
|
.get = get_mac,
|
|
.set = set_mac,
|
|
};
|
|
|
|
void qdev_prop_set_macaddr(DeviceState *dev, const char *name,
|
|
const uint8_t *value)
|
|
{
|
|
char str[2 * 6 + 5 + 1];
|
|
snprintf(str, sizeof(str), "%02x:%02x:%02x:%02x:%02x:%02x",
|
|
value[0], value[1], value[2], value[3], value[4], value[5]);
|
|
|
|
object_property_set_str(OBJECT(dev), name, str, &error_abort);
|
|
}
|
|
|
|
/* --- netdev device --- */
|
|
static void get_netdev(Object *obj, Visitor *v, const char *name,
|
|
void *opaque, Error **errp)
|
|
{
|
|
Property *prop = opaque;
|
|
NICPeers *peers_ptr = object_field_prop_ptr(obj, prop);
|
|
char *p = g_strdup(peers_ptr->ncs[0] ? peers_ptr->ncs[0]->name : "");
|
|
|
|
visit_type_str(v, name, &p, errp);
|
|
g_free(p);
|
|
}
|
|
|
|
static void set_netdev(Object *obj, Visitor *v, const char *name,
|
|
void *opaque, Error **errp)
|
|
{
|
|
Property *prop = opaque;
|
|
NICPeers *peers_ptr = object_field_prop_ptr(obj, prop);
|
|
NetClientState **ncs = peers_ptr->ncs;
|
|
NetClientState *peers[MAX_QUEUE_NUM];
|
|
int queues, err = 0, i = 0;
|
|
char *str;
|
|
|
|
if (!visit_type_str(v, name, &str, errp)) {
|
|
return;
|
|
}
|
|
|
|
queues = qemu_find_net_clients_except(str, peers,
|
|
NET_CLIENT_DRIVER_NIC,
|
|
MAX_QUEUE_NUM);
|
|
if (queues == 0) {
|
|
err = -ENOENT;
|
|
goto out;
|
|
}
|
|
|
|
if (queues > MAX_QUEUE_NUM) {
|
|
error_setg(errp, "queues of backend '%s'(%d) exceeds QEMU limitation(%d)",
|
|
str, queues, MAX_QUEUE_NUM);
|
|
goto out;
|
|
}
|
|
|
|
for (i = 0; i < queues; i++) {
|
|
if (peers[i]->peer) {
|
|
err = -EEXIST;
|
|
goto out;
|
|
}
|
|
|
|
/*
|
|
* TODO Should this really be an error? If no, the old value
|
|
* needs to be released before we store the new one.
|
|
*/
|
|
if (!check_prop_still_unset(obj, name, ncs[i], str, false, errp)) {
|
|
goto out;
|
|
}
|
|
|
|
if (peers[i]->info->check_peer_type) {
|
|
if (!peers[i]->info->check_peer_type(peers[i], obj->class, errp)) {
|
|
goto out;
|
|
}
|
|
}
|
|
|
|
ncs[i] = peers[i];
|
|
ncs[i]->queue_index = i;
|
|
}
|
|
|
|
peers_ptr->queues = queues;
|
|
|
|
out:
|
|
error_set_from_qdev_prop_error(errp, err, obj, prop->name, str);
|
|
g_free(str);
|
|
}
|
|
|
|
const PropertyInfo qdev_prop_netdev = {
|
|
.name = "str",
|
|
.description = "ID of a netdev to use as a backend",
|
|
.get = get_netdev,
|
|
.set = set_netdev,
|
|
};
|
|
|
|
|
|
/* --- audiodev --- */
|
|
static void get_audiodev(Object *obj, Visitor *v, const char* name,
|
|
void *opaque, Error **errp)
|
|
{
|
|
Property *prop = opaque;
|
|
QEMUSoundCard *card = object_field_prop_ptr(obj, prop);
|
|
char *p = g_strdup(audio_get_id(card));
|
|
|
|
visit_type_str(v, name, &p, errp);
|
|
g_free(p);
|
|
}
|
|
|
|
static void set_audiodev(Object *obj, Visitor *v, const char* name,
|
|
void *opaque, Error **errp)
|
|
{
|
|
Property *prop = opaque;
|
|
QEMUSoundCard *card = object_field_prop_ptr(obj, prop);
|
|
AudioState *state;
|
|
g_autofree char *str = NULL;
|
|
|
|
if (!visit_type_str(v, name, &str, errp)) {
|
|
return;
|
|
}
|
|
|
|
state = audio_state_by_name(str, errp);
|
|
if (state) {
|
|
card->state = state;
|
|
}
|
|
}
|
|
|
|
const PropertyInfo qdev_prop_audiodev = {
|
|
.name = "str",
|
|
.description = "ID of an audiodev to use as a backend",
|
|
/* release done on shutdown */
|
|
.get = get_audiodev,
|
|
.set = set_audiodev,
|
|
};
|
|
|
|
bool qdev_prop_set_drive_err(DeviceState *dev, const char *name,
|
|
BlockBackend *value, Error **errp)
|
|
{
|
|
const char *ref = "";
|
|
|
|
if (value) {
|
|
ref = blk_name(value);
|
|
if (!*ref) {
|
|
const BlockDriverState *bs = blk_bs(value);
|
|
if (bs) {
|
|
ref = bdrv_get_node_name(bs);
|
|
}
|
|
}
|
|
}
|
|
|
|
return object_property_set_str(OBJECT(dev), name, ref, errp);
|
|
}
|
|
|
|
void qdev_prop_set_drive(DeviceState *dev, const char *name,
|
|
BlockBackend *value)
|
|
{
|
|
qdev_prop_set_drive_err(dev, name, value, &error_abort);
|
|
}
|
|
|
|
void qdev_prop_set_chr(DeviceState *dev, const char *name,
|
|
Chardev *value)
|
|
{
|
|
assert(!value || value->label);
|
|
object_property_set_str(OBJECT(dev), name, value ? value->label : "",
|
|
&error_abort);
|
|
}
|
|
|
|
void qdev_prop_set_netdev(DeviceState *dev, const char *name,
|
|
NetClientState *value)
|
|
{
|
|
assert(!value || value->name);
|
|
object_property_set_str(OBJECT(dev), name, value ? value->name : "",
|
|
&error_abort);
|
|
}
|
|
|
|
void qdev_set_nic_properties(DeviceState *dev, NICInfo *nd)
|
|
{
|
|
qdev_prop_set_macaddr(dev, "mac", nd->macaddr.a);
|
|
if (nd->netdev) {
|
|
qdev_prop_set_netdev(dev, "netdev", nd->netdev);
|
|
}
|
|
if (nd->nvectors != DEV_NVECTORS_UNSPECIFIED &&
|
|
object_property_find(OBJECT(dev), "vectors")) {
|
|
qdev_prop_set_uint32(dev, "vectors", nd->nvectors);
|
|
}
|
|
nd->instantiated = 1;
|
|
}
|
|
|
|
/* --- lost tick policy --- */
|
|
|
|
static void qdev_propinfo_set_losttickpolicy(Object *obj, Visitor *v,
|
|
const char *name, void *opaque,
|
|
Error **errp)
|
|
{
|
|
Property *prop = opaque;
|
|
int *ptr = object_field_prop_ptr(obj, prop);
|
|
int value;
|
|
|
|
if (!visit_type_enum(v, name, &value, prop->info->enum_table, errp)) {
|
|
return;
|
|
}
|
|
|
|
if (value == LOST_TICK_POLICY_SLEW) {
|
|
MachineState *ms = MACHINE(qdev_get_machine());
|
|
|
|
if (!object_dynamic_cast(OBJECT(ms), TYPE_X86_MACHINE)) {
|
|
error_setg(errp,
|
|
"the 'slew' policy is only available for x86 machines");
|
|
return;
|
|
}
|
|
}
|
|
|
|
*ptr = value;
|
|
}
|
|
|
|
QEMU_BUILD_BUG_ON(sizeof(LostTickPolicy) != sizeof(int));
|
|
|
|
const PropertyInfo qdev_prop_losttickpolicy = {
|
|
.name = "LostTickPolicy",
|
|
.enum_table = &LostTickPolicy_lookup,
|
|
.get = qdev_propinfo_get_enum,
|
|
.set = qdev_propinfo_set_losttickpolicy,
|
|
.set_default_value = qdev_propinfo_set_default_value_enum,
|
|
};
|
|
|
|
/* --- blocksize --- */
|
|
|
|
static void set_blocksize(Object *obj, Visitor *v, const char *name,
|
|
void *opaque, Error **errp)
|
|
{
|
|
Property *prop = opaque;
|
|
uint32_t *ptr = object_field_prop_ptr(obj, prop);
|
|
uint64_t value;
|
|
|
|
if (!visit_type_size(v, name, &value, errp)) {
|
|
return;
|
|
}
|
|
if (!check_block_size(name, value, errp)) {
|
|
return;
|
|
}
|
|
*ptr = value;
|
|
}
|
|
|
|
const PropertyInfo qdev_prop_blocksize = {
|
|
.name = "size",
|
|
.description = "A power of two between " MIN_BLOCK_SIZE_STR
|
|
" and " MAX_BLOCK_SIZE_STR,
|
|
.get = qdev_propinfo_get_size32,
|
|
.set = set_blocksize,
|
|
.set_default_value = qdev_propinfo_set_default_value_uint,
|
|
};
|
|
|
|
/* --- Block device error handling policy --- */
|
|
|
|
QEMU_BUILD_BUG_ON(sizeof(BlockdevOnError) != sizeof(int));
|
|
|
|
const PropertyInfo qdev_prop_blockdev_on_error = {
|
|
.name = "BlockdevOnError",
|
|
.description = "Error handling policy, "
|
|
"report/ignore/enospc/stop/auto",
|
|
.enum_table = &BlockdevOnError_lookup,
|
|
.get = qdev_propinfo_get_enum,
|
|
.set = qdev_propinfo_set_enum,
|
|
.set_default_value = qdev_propinfo_set_default_value_enum,
|
|
};
|
|
|
|
/* --- BIOS CHS translation */
|
|
|
|
QEMU_BUILD_BUG_ON(sizeof(BiosAtaTranslation) != sizeof(int));
|
|
|
|
const PropertyInfo qdev_prop_bios_chs_trans = {
|
|
.name = "BiosAtaTranslation",
|
|
.description = "Logical CHS translation algorithm, "
|
|
"auto/none/lba/large/rechs",
|
|
.enum_table = &BiosAtaTranslation_lookup,
|
|
.get = qdev_propinfo_get_enum,
|
|
.set = qdev_propinfo_set_enum,
|
|
.set_default_value = qdev_propinfo_set_default_value_enum,
|
|
};
|
|
|
|
/* --- FDC default drive types */
|
|
|
|
const PropertyInfo qdev_prop_fdc_drive_type = {
|
|
.name = "FdcDriveType",
|
|
.description = "FDC drive type, "
|
|
"144/288/120/none/auto",
|
|
.enum_table = &FloppyDriveType_lookup,
|
|
.get = qdev_propinfo_get_enum,
|
|
.set = qdev_propinfo_set_enum,
|
|
.set_default_value = qdev_propinfo_set_default_value_enum,
|
|
};
|
|
|
|
/* --- MultiFDCompression --- */
|
|
|
|
const PropertyInfo qdev_prop_multifd_compression = {
|
|
.name = "MultiFDCompression",
|
|
.description = "multifd_compression values, "
|
|
"none/zlib/zstd/qpl/uadk/qatzip",
|
|
.enum_table = &MultiFDCompression_lookup,
|
|
.get = qdev_propinfo_get_enum,
|
|
.set = qdev_propinfo_set_enum,
|
|
.set_default_value = qdev_propinfo_set_default_value_enum,
|
|
};
|
|
|
|
/* --- MigMode --- */
|
|
|
|
QEMU_BUILD_BUG_ON(sizeof(MigMode) != sizeof(int));
|
|
|
|
const PropertyInfo qdev_prop_mig_mode = {
|
|
.name = "MigMode",
|
|
.description = "mig_mode values, "
|
|
"normal,cpr-reboot",
|
|
.enum_table = &MigMode_lookup,
|
|
.get = qdev_propinfo_get_enum,
|
|
.set = qdev_propinfo_set_enum,
|
|
.set_default_value = qdev_propinfo_set_default_value_enum,
|
|
};
|
|
|
|
/* --- GranuleMode --- */
|
|
|
|
QEMU_BUILD_BUG_ON(sizeof(GranuleMode) != sizeof(int));
|
|
|
|
const PropertyInfo qdev_prop_granule_mode = {
|
|
.name = "GranuleMode",
|
|
.description = "granule_mode values, "
|
|
"4k, 8k, 16k, 64k, host",
|
|
.enum_table = &GranuleMode_lookup,
|
|
.get = qdev_propinfo_get_enum,
|
|
.set = qdev_propinfo_set_enum,
|
|
.set_default_value = qdev_propinfo_set_default_value_enum,
|
|
};
|
|
|
|
const PropertyInfo qdev_prop_zero_page_detection = {
|
|
.name = "ZeroPageDetection",
|
|
.description = "zero_page_detection values, "
|
|
"none,legacy,multifd",
|
|
.enum_table = &ZeroPageDetection_lookup,
|
|
.get = qdev_propinfo_get_enum,
|
|
.set = qdev_propinfo_set_enum,
|
|
.set_default_value = qdev_propinfo_set_default_value_enum,
|
|
};
|
|
|
|
/* --- Reserved Region --- */
|
|
|
|
/*
|
|
* Accepted syntax:
|
|
* <low address>:<high address>:<type>
|
|
* where low/high addresses are uint64_t in hexadecimal
|
|
* and type is a non-negative decimal integer
|
|
*/
|
|
static void get_reserved_region(Object *obj, Visitor *v, const char *name,
|
|
void *opaque, Error **errp)
|
|
{
|
|
Property *prop = opaque;
|
|
ReservedRegion *rr = object_field_prop_ptr(obj, prop);
|
|
char buffer[64];
|
|
char *p = buffer;
|
|
int rc;
|
|
|
|
rc = snprintf(buffer, sizeof(buffer), "0x%"PRIx64":0x%"PRIx64":%u",
|
|
range_lob(&rr->range), range_upb(&rr->range), rr->type);
|
|
assert(rc < sizeof(buffer));
|
|
|
|
visit_type_str(v, name, &p, errp);
|
|
}
|
|
|
|
static void set_reserved_region(Object *obj, Visitor *v, const char *name,
|
|
void *opaque, Error **errp)
|
|
{
|
|
Property *prop = opaque;
|
|
ReservedRegion *rr = object_field_prop_ptr(obj, prop);
|
|
const char *endptr;
|
|
uint64_t lob, upb;
|
|
char *str;
|
|
int ret;
|
|
|
|
if (!visit_type_str(v, name, &str, errp)) {
|
|
return;
|
|
}
|
|
|
|
ret = qemu_strtou64(str, &endptr, 16, &lob);
|
|
if (ret) {
|
|
error_setg(errp, "start address of '%s'"
|
|
" must be a hexadecimal integer", name);
|
|
goto out;
|
|
}
|
|
if (*endptr != ':') {
|
|
goto separator_error;
|
|
}
|
|
|
|
ret = qemu_strtou64(endptr + 1, &endptr, 16, &upb);
|
|
if (ret) {
|
|
error_setg(errp, "end address of '%s'"
|
|
" must be a hexadecimal integer", name);
|
|
goto out;
|
|
}
|
|
if (*endptr != ':') {
|
|
goto separator_error;
|
|
}
|
|
|
|
range_set_bounds(&rr->range, lob, upb);
|
|
|
|
ret = qemu_strtoui(endptr + 1, &endptr, 10, &rr->type);
|
|
if (ret) {
|
|
error_setg(errp, "type of '%s'"
|
|
" must be a non-negative decimal integer", name);
|
|
}
|
|
goto out;
|
|
|
|
separator_error:
|
|
error_setg(errp, "reserved region fields must be separated with ':'");
|
|
out:
|
|
g_free(str);
|
|
return;
|
|
}
|
|
|
|
const PropertyInfo qdev_prop_reserved_region = {
|
|
.name = "reserved_region",
|
|
.description = "Reserved Region, example: 0xFEE00000:0xFEEFFFFF:0",
|
|
.get = get_reserved_region,
|
|
.set = set_reserved_region,
|
|
};
|
|
|
|
/* --- pci address --- */
|
|
|
|
/*
|
|
* bus-local address, i.e. "$slot" or "$slot.$fn"
|
|
*/
|
|
static void set_pci_devfn(Object *obj, Visitor *v, const char *name,
|
|
void *opaque, Error **errp)
|
|
{
|
|
Property *prop = opaque;
|
|
g_autofree GenericAlternate *alt;
|
|
int32_t value, *ptr = object_field_prop_ptr(obj, prop);
|
|
unsigned int slot, fn, n;
|
|
g_autofree char *str = NULL;
|
|
|
|
if (!visit_start_alternate(v, name, &alt, sizeof(*alt), errp)) {
|
|
return;
|
|
}
|
|
|
|
switch (alt->type) {
|
|
case QTYPE_QSTRING:
|
|
if (!visit_type_str(v, name, &str, errp)) {
|
|
goto out;
|
|
}
|
|
|
|
if (sscanf(str, "%x.%x%n", &slot, &fn, &n) != 2) {
|
|
fn = 0;
|
|
if (sscanf(str, "%x%n", &slot, &n) != 1) {
|
|
goto invalid;
|
|
}
|
|
}
|
|
if (str[n] != '\0' || fn > 7 || slot > 31) {
|
|
goto invalid;
|
|
}
|
|
*ptr = slot << 3 | fn;
|
|
break;
|
|
|
|
case QTYPE_QNUM:
|
|
if (!visit_type_int32(v, name, &value, errp)) {
|
|
goto out;
|
|
}
|
|
if (value < -1 || value > 255) {
|
|
error_setg(errp, QERR_INVALID_PARAMETER_VALUE,
|
|
name ? name : "null", "a value between -1 and 255");
|
|
goto out;
|
|
}
|
|
*ptr = value;
|
|
break;
|
|
|
|
default:
|
|
error_setg(errp, "Invalid parameter type for '%s', expected int or str",
|
|
name ? name : "null");
|
|
goto out;
|
|
}
|
|
|
|
goto out;
|
|
|
|
invalid:
|
|
error_set_from_qdev_prop_error(errp, EINVAL, obj, name, str);
|
|
out:
|
|
visit_end_alternate(v, (void **) &alt);
|
|
}
|
|
|
|
static int print_pci_devfn(Object *obj, Property *prop, char *dest,
|
|
size_t len)
|
|
{
|
|
int32_t *ptr = object_field_prop_ptr(obj, prop);
|
|
|
|
if (*ptr == -1) {
|
|
return snprintf(dest, len, "<unset>");
|
|
} else {
|
|
return snprintf(dest, len, "%02x.%x", *ptr >> 3, *ptr & 7);
|
|
}
|
|
}
|
|
|
|
const PropertyInfo qdev_prop_pci_devfn = {
|
|
.name = "int32",
|
|
.description = "Slot and optional function number, example: 06.0 or 06",
|
|
.print = print_pci_devfn,
|
|
.get = qdev_propinfo_get_int32,
|
|
.set = set_pci_devfn,
|
|
.set_default_value = qdev_propinfo_set_default_value_int,
|
|
};
|
|
|
|
/* --- pci host address --- */
|
|
|
|
static void get_pci_host_devaddr(Object *obj, Visitor *v, const char *name,
|
|
void *opaque, Error **errp)
|
|
{
|
|
Property *prop = opaque;
|
|
PCIHostDeviceAddress *addr = object_field_prop_ptr(obj, prop);
|
|
char buffer[] = "ffff:ff:ff.f";
|
|
char *p = buffer;
|
|
int rc = 0;
|
|
|
|
/*
|
|
* Catch "invalid" device reference from vfio-pci and allow the
|
|
* default buffer representing the non-existent device to be used.
|
|
*/
|
|
if (~addr->domain || ~addr->bus || ~addr->slot || ~addr->function) {
|
|
rc = snprintf(buffer, sizeof(buffer), "%04x:%02x:%02x.%0d",
|
|
addr->domain, addr->bus, addr->slot, addr->function);
|
|
assert(rc == sizeof(buffer) - 1);
|
|
}
|
|
|
|
visit_type_str(v, name, &p, errp);
|
|
}
|
|
|
|
/*
|
|
* Parse [<domain>:]<bus>:<slot>.<func>
|
|
* if <domain> is not supplied, it's assumed to be 0.
|
|
*/
|
|
static void set_pci_host_devaddr(Object *obj, Visitor *v, const char *name,
|
|
void *opaque, Error **errp)
|
|
{
|
|
Property *prop = opaque;
|
|
PCIHostDeviceAddress *addr = object_field_prop_ptr(obj, prop);
|
|
char *str, *p;
|
|
char *e;
|
|
unsigned long val;
|
|
unsigned long dom = 0, bus = 0;
|
|
unsigned int slot = 0, func = 0;
|
|
|
|
if (!visit_type_str(v, name, &str, errp)) {
|
|
return;
|
|
}
|
|
|
|
p = str;
|
|
val = strtoul(p, &e, 16);
|
|
if (e == p || *e != ':') {
|
|
goto inval;
|
|
}
|
|
bus = val;
|
|
|
|
p = e + 1;
|
|
val = strtoul(p, &e, 16);
|
|
if (e == p) {
|
|
goto inval;
|
|
}
|
|
if (*e == ':') {
|
|
dom = bus;
|
|
bus = val;
|
|
p = e + 1;
|
|
val = strtoul(p, &e, 16);
|
|
if (e == p) {
|
|
goto inval;
|
|
}
|
|
}
|
|
slot = val;
|
|
|
|
if (*e != '.') {
|
|
goto inval;
|
|
}
|
|
p = e + 1;
|
|
val = strtoul(p, &e, 10);
|
|
if (e == p) {
|
|
goto inval;
|
|
}
|
|
func = val;
|
|
|
|
if (dom > 0xffff || bus > 0xff || slot > 0x1f || func > 7) {
|
|
goto inval;
|
|
}
|
|
|
|
if (*e) {
|
|
goto inval;
|
|
}
|
|
|
|
addr->domain = dom;
|
|
addr->bus = bus;
|
|
addr->slot = slot;
|
|
addr->function = func;
|
|
|
|
g_free(str);
|
|
return;
|
|
|
|
inval:
|
|
error_set_from_qdev_prop_error(errp, EINVAL, obj, name, str);
|
|
g_free(str);
|
|
}
|
|
|
|
const PropertyInfo qdev_prop_pci_host_devaddr = {
|
|
.name = "str",
|
|
.description = "Address (bus/device/function) of "
|
|
"the host device, example: 04:10.0",
|
|
.get = get_pci_host_devaddr,
|
|
.set = set_pci_host_devaddr,
|
|
};
|
|
|
|
/* --- OffAutoPCIBAR off/auto/bar0/bar1/bar2/bar3/bar4/bar5 --- */
|
|
|
|
const PropertyInfo qdev_prop_off_auto_pcibar = {
|
|
.name = "OffAutoPCIBAR",
|
|
.description = "off/auto/bar0/bar1/bar2/bar3/bar4/bar5",
|
|
.enum_table = &OffAutoPCIBAR_lookup,
|
|
.get = qdev_propinfo_get_enum,
|
|
.set = qdev_propinfo_set_enum,
|
|
.set_default_value = qdev_propinfo_set_default_value_enum,
|
|
};
|
|
|
|
/* --- PCIELinkSpeed 2_5/5/8/16/32/64 -- */
|
|
|
|
static void get_prop_pcielinkspeed(Object *obj, Visitor *v, const char *name,
|
|
void *opaque, Error **errp)
|
|
{
|
|
Property *prop = opaque;
|
|
PCIExpLinkSpeed *p = object_field_prop_ptr(obj, prop);
|
|
int speed;
|
|
|
|
switch (*p) {
|
|
case QEMU_PCI_EXP_LNK_2_5GT:
|
|
speed = PCIE_LINK_SPEED_2_5;
|
|
break;
|
|
case QEMU_PCI_EXP_LNK_5GT:
|
|
speed = PCIE_LINK_SPEED_5;
|
|
break;
|
|
case QEMU_PCI_EXP_LNK_8GT:
|
|
speed = PCIE_LINK_SPEED_8;
|
|
break;
|
|
case QEMU_PCI_EXP_LNK_16GT:
|
|
speed = PCIE_LINK_SPEED_16;
|
|
break;
|
|
case QEMU_PCI_EXP_LNK_32GT:
|
|
speed = PCIE_LINK_SPEED_32;
|
|
break;
|
|
case QEMU_PCI_EXP_LNK_64GT:
|
|
speed = PCIE_LINK_SPEED_64;
|
|
break;
|
|
default:
|
|
/* Unreachable */
|
|
abort();
|
|
}
|
|
|
|
visit_type_enum(v, name, &speed, prop->info->enum_table, errp);
|
|
}
|
|
|
|
static void set_prop_pcielinkspeed(Object *obj, Visitor *v, const char *name,
|
|
void *opaque, Error **errp)
|
|
{
|
|
Property *prop = opaque;
|
|
PCIExpLinkSpeed *p = object_field_prop_ptr(obj, prop);
|
|
int speed;
|
|
|
|
if (!visit_type_enum(v, name, &speed, prop->info->enum_table,
|
|
errp)) {
|
|
return;
|
|
}
|
|
|
|
switch (speed) {
|
|
case PCIE_LINK_SPEED_2_5:
|
|
*p = QEMU_PCI_EXP_LNK_2_5GT;
|
|
break;
|
|
case PCIE_LINK_SPEED_5:
|
|
*p = QEMU_PCI_EXP_LNK_5GT;
|
|
break;
|
|
case PCIE_LINK_SPEED_8:
|
|
*p = QEMU_PCI_EXP_LNK_8GT;
|
|
break;
|
|
case PCIE_LINK_SPEED_16:
|
|
*p = QEMU_PCI_EXP_LNK_16GT;
|
|
break;
|
|
case PCIE_LINK_SPEED_32:
|
|
*p = QEMU_PCI_EXP_LNK_32GT;
|
|
break;
|
|
case PCIE_LINK_SPEED_64:
|
|
*p = QEMU_PCI_EXP_LNK_64GT;
|
|
break;
|
|
default:
|
|
/* Unreachable */
|
|
abort();
|
|
}
|
|
}
|
|
|
|
const PropertyInfo qdev_prop_pcie_link_speed = {
|
|
.name = "PCIELinkSpeed",
|
|
.description = "2_5/5/8/16/32/64",
|
|
.enum_table = &PCIELinkSpeed_lookup,
|
|
.get = get_prop_pcielinkspeed,
|
|
.set = set_prop_pcielinkspeed,
|
|
.set_default_value = qdev_propinfo_set_default_value_enum,
|
|
};
|
|
|
|
/* --- PCIELinkWidth 1/2/4/8/12/16/32 -- */
|
|
|
|
static void get_prop_pcielinkwidth(Object *obj, Visitor *v, const char *name,
|
|
void *opaque, Error **errp)
|
|
{
|
|
Property *prop = opaque;
|
|
PCIExpLinkWidth *p = object_field_prop_ptr(obj, prop);
|
|
int width;
|
|
|
|
switch (*p) {
|
|
case QEMU_PCI_EXP_LNK_X1:
|
|
width = PCIE_LINK_WIDTH_1;
|
|
break;
|
|
case QEMU_PCI_EXP_LNK_X2:
|
|
width = PCIE_LINK_WIDTH_2;
|
|
break;
|
|
case QEMU_PCI_EXP_LNK_X4:
|
|
width = PCIE_LINK_WIDTH_4;
|
|
break;
|
|
case QEMU_PCI_EXP_LNK_X8:
|
|
width = PCIE_LINK_WIDTH_8;
|
|
break;
|
|
case QEMU_PCI_EXP_LNK_X12:
|
|
width = PCIE_LINK_WIDTH_12;
|
|
break;
|
|
case QEMU_PCI_EXP_LNK_X16:
|
|
width = PCIE_LINK_WIDTH_16;
|
|
break;
|
|
case QEMU_PCI_EXP_LNK_X32:
|
|
width = PCIE_LINK_WIDTH_32;
|
|
break;
|
|
default:
|
|
/* Unreachable */
|
|
abort();
|
|
}
|
|
|
|
visit_type_enum(v, name, &width, prop->info->enum_table, errp);
|
|
}
|
|
|
|
static void set_prop_pcielinkwidth(Object *obj, Visitor *v, const char *name,
|
|
void *opaque, Error **errp)
|
|
{
|
|
Property *prop = opaque;
|
|
PCIExpLinkWidth *p = object_field_prop_ptr(obj, prop);
|
|
int width;
|
|
|
|
if (!visit_type_enum(v, name, &width, prop->info->enum_table,
|
|
errp)) {
|
|
return;
|
|
}
|
|
|
|
switch (width) {
|
|
case PCIE_LINK_WIDTH_1:
|
|
*p = QEMU_PCI_EXP_LNK_X1;
|
|
break;
|
|
case PCIE_LINK_WIDTH_2:
|
|
*p = QEMU_PCI_EXP_LNK_X2;
|
|
break;
|
|
case PCIE_LINK_WIDTH_4:
|
|
*p = QEMU_PCI_EXP_LNK_X4;
|
|
break;
|
|
case PCIE_LINK_WIDTH_8:
|
|
*p = QEMU_PCI_EXP_LNK_X8;
|
|
break;
|
|
case PCIE_LINK_WIDTH_12:
|
|
*p = QEMU_PCI_EXP_LNK_X12;
|
|
break;
|
|
case PCIE_LINK_WIDTH_16:
|
|
*p = QEMU_PCI_EXP_LNK_X16;
|
|
break;
|
|
case PCIE_LINK_WIDTH_32:
|
|
*p = QEMU_PCI_EXP_LNK_X32;
|
|
break;
|
|
default:
|
|
/* Unreachable */
|
|
abort();
|
|
}
|
|
}
|
|
|
|
const PropertyInfo qdev_prop_pcie_link_width = {
|
|
.name = "PCIELinkWidth",
|
|
.description = "1/2/4/8/12/16/32",
|
|
.enum_table = &PCIELinkWidth_lookup,
|
|
.get = get_prop_pcielinkwidth,
|
|
.set = set_prop_pcielinkwidth,
|
|
.set_default_value = qdev_propinfo_set_default_value_enum,
|
|
};
|
|
|
|
/* --- UUID --- */
|
|
|
|
static void get_uuid(Object *obj, Visitor *v, const char *name, void *opaque,
|
|
Error **errp)
|
|
{
|
|
Property *prop = opaque;
|
|
QemuUUID *uuid = object_field_prop_ptr(obj, prop);
|
|
char buffer[UUID_STR_LEN];
|
|
char *p = buffer;
|
|
|
|
qemu_uuid_unparse(uuid, buffer);
|
|
|
|
visit_type_str(v, name, &p, errp);
|
|
}
|
|
|
|
#define UUID_VALUE_AUTO "auto"
|
|
|
|
static void set_uuid(Object *obj, Visitor *v, const char *name, void *opaque,
|
|
Error **errp)
|
|
{
|
|
Property *prop = opaque;
|
|
QemuUUID *uuid = object_field_prop_ptr(obj, prop);
|
|
char *str;
|
|
|
|
if (!visit_type_str(v, name, &str, errp)) {
|
|
return;
|
|
}
|
|
|
|
if (!strcmp(str, UUID_VALUE_AUTO)) {
|
|
qemu_uuid_generate(uuid);
|
|
} else if (qemu_uuid_parse(str, uuid) < 0) {
|
|
error_set_from_qdev_prop_error(errp, EINVAL, obj, name, str);
|
|
}
|
|
g_free(str);
|
|
}
|
|
|
|
static void set_default_uuid_auto(ObjectProperty *op, const Property *prop)
|
|
{
|
|
object_property_set_default_str(op, UUID_VALUE_AUTO);
|
|
}
|
|
|
|
const PropertyInfo qdev_prop_uuid = {
|
|
.name = "str",
|
|
.description = "UUID (aka GUID) or \"" UUID_VALUE_AUTO
|
|
"\" for random value (default)",
|
|
.get = get_uuid,
|
|
.set = set_uuid,
|
|
.set_default_value = set_default_uuid_auto,
|
|
};
|
|
|
|
/* --- s390 cpu entitlement policy --- */
|
|
|
|
QEMU_BUILD_BUG_ON(sizeof(S390CpuEntitlement) != sizeof(int));
|
|
|
|
const PropertyInfo qdev_prop_cpus390entitlement = {
|
|
.name = "S390CpuEntitlement",
|
|
.description = "low/medium (default)/high",
|
|
.enum_table = &S390CpuEntitlement_lookup,
|
|
.get = qdev_propinfo_get_enum,
|
|
.set = qdev_propinfo_set_enum,
|
|
.set_default_value = qdev_propinfo_set_default_value_enum,
|
|
};
|
|
|
|
/* --- IOThreadVirtQueueMappingList --- */
|
|
|
|
static void get_iothread_vq_mapping_list(Object *obj, Visitor *v,
|
|
const char *name, void *opaque, Error **errp)
|
|
{
|
|
IOThreadVirtQueueMappingList **prop_ptr =
|
|
object_field_prop_ptr(obj, opaque);
|
|
|
|
visit_type_IOThreadVirtQueueMappingList(v, name, prop_ptr, errp);
|
|
}
|
|
|
|
static void set_iothread_vq_mapping_list(Object *obj, Visitor *v,
|
|
const char *name, void *opaque, Error **errp)
|
|
{
|
|
IOThreadVirtQueueMappingList **prop_ptr =
|
|
object_field_prop_ptr(obj, opaque);
|
|
IOThreadVirtQueueMappingList *list;
|
|
|
|
if (!visit_type_IOThreadVirtQueueMappingList(v, name, &list, errp)) {
|
|
return;
|
|
}
|
|
|
|
qapi_free_IOThreadVirtQueueMappingList(*prop_ptr);
|
|
*prop_ptr = list;
|
|
}
|
|
|
|
static void release_iothread_vq_mapping_list(Object *obj,
|
|
const char *name, void *opaque)
|
|
{
|
|
IOThreadVirtQueueMappingList **prop_ptr =
|
|
object_field_prop_ptr(obj, opaque);
|
|
|
|
qapi_free_IOThreadVirtQueueMappingList(*prop_ptr);
|
|
*prop_ptr = NULL;
|
|
}
|
|
|
|
const PropertyInfo qdev_prop_iothread_vq_mapping_list = {
|
|
.name = "IOThreadVirtQueueMappingList",
|
|
.description = "IOThread virtqueue mapping list [{\"iothread\":\"<id>\", "
|
|
"\"vqs\":[1,2,3,...]},...]",
|
|
.get = get_iothread_vq_mapping_list,
|
|
.set = set_iothread_vq_mapping_list,
|
|
.release = release_iothread_vq_mapping_list,
|
|
};
|