mirror of
https://github.com/qemu/qemu.git
synced 2025-08-03 15:02:52 +00:00

Thanks to72d277a7
,1ed2cb32
, and others, EDID (Extended Display Identification Data) is propagated by QEMU such that a virtual display presents legitimate metadata (e.g., name, serial number, preferred resolutions, etc.) to its connected guest. This change adds the ability to specify the EDID name for a particular virtio-vga display. Previously, every virtual display would have the same name: "QEMU Monitor". Now, we can inject names of displays in order to test guest behavior that is specific to display names. We provide the ability to inject the display name from the frontend since this is guest visible data. Furthermore, this makes it clear where N potential display outputs would get their name from (which will be added in a future change). Note that we have elected to use a struct here for output data for extensibility - we intend to add per-output fields like resolution in a future change. It should be noted that EDID names longer than 12 bytes will be truncated per spec (I think?). Testing: verified that when I specified 2 outputs for a virtio-gpu with edid_name set, the names matched those that I configured with my vnc display. -display vnc=localhost:0,id=aaa,display=vga,head=0 \ -display vnc=localhost:1,id=bbb,display=vga,head=1 \ -device '{"driver":"virtio-vga", "max_outputs":2, "id":"vga", "outputs":[ { "name":"AAA" }, { "name":"BBB" } ]}' Signed-off-by: Andrew Keesler <ankeesler@google.com> Reviewed-by: Marc-André Lureau <marcandre.lureau@redhat.com> Message-Id: <20250709121126.2946088-2-ankeesler@google.com>
1346 lines
36 KiB
C
1346 lines
36 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 "system/block-backend.h"
|
|
#include "system/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)
|
|
{
|
|
const 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);
|
|
const 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");
|
|
return;
|
|
}
|
|
|
|
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);
|
|
const 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 = {
|
|
.type = "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 = {
|
|
.type = "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();
|
|
const 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)
|
|
{
|
|
const Property *prop = opaque;
|
|
CharBackend *be = object_field_prop_ptr(obj, prop);
|
|
|
|
qemu_chr_fe_deinit(be, false);
|
|
}
|
|
|
|
const PropertyInfo qdev_prop_chr = {
|
|
.type = "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)
|
|
{
|
|
const 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)
|
|
{
|
|
const 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 = {
|
|
.type = "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)
|
|
{
|
|
const 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)
|
|
{
|
|
const 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 = {
|
|
.type = "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)
|
|
{
|
|
const 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)
|
|
{
|
|
const 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 = {
|
|
.type = "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)
|
|
{
|
|
const 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 = {
|
|
.type = "LostTickPolicy",
|
|
.description = "Policy for handling lost ticks (discard/delay/slew)",
|
|
.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)
|
|
{
|
|
const 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 = {
|
|
.type = "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 = {
|
|
.type = "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 = {
|
|
.type = "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 = {
|
|
.type = "FloppyDriveType",
|
|
.description = "Floppy 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 = {
|
|
.type = "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 = {
|
|
.type = "MigMode",
|
|
.description = "Migration mode (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 = {
|
|
.type = "GranuleMode",
|
|
.description = "Granule page size (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 = {
|
|
.type = "ZeroPageDetection",
|
|
.description = "Zero page detection (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)
|
|
{
|
|
const 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)
|
|
{
|
|
const 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);
|
|
}
|
|
|
|
const PropertyInfo qdev_prop_reserved_region = {
|
|
.type = "str",
|
|
.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)
|
|
{
|
|
const 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, const 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 = {
|
|
.type = "str",
|
|
.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)
|
|
{
|
|
const 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)
|
|
{
|
|
const 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 = {
|
|
.type = "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 = {
|
|
.type = "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)
|
|
{
|
|
const 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)
|
|
{
|
|
const 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 = {
|
|
.type = "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)
|
|
{
|
|
const 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)
|
|
{
|
|
const 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 = {
|
|
.type = "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)
|
|
{
|
|
const 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)
|
|
{
|
|
const 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 = {
|
|
.type = "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 = {
|
|
.type = "S390CpuEntitlement",
|
|
.description = "auto/low/medium/high (default medium)",
|
|
.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 = {
|
|
.type = "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,
|
|
};
|
|
|
|
/* --- Endian modes */
|
|
|
|
const PropertyInfo qdev_prop_endian_mode = {
|
|
.type = "EndianMode",
|
|
.description = "Endian mode, big/little/unspecified",
|
|
.enum_table = &EndianMode_lookup,
|
|
.get = qdev_propinfo_get_enum,
|
|
.set = qdev_propinfo_set_enum,
|
|
.set_default_value = qdev_propinfo_set_default_value_enum,
|
|
};
|
|
|
|
const PropertyInfo qdev_prop_vmapple_virtio_blk_variant = {
|
|
.type = "VMAppleVirtioBlkVariant",
|
|
.description = "unspecified/root/aux",
|
|
.enum_table = &VMAppleVirtioBlkVariant_lookup,
|
|
.get = qdev_propinfo_get_enum,
|
|
.set = qdev_propinfo_set_enum,
|
|
.set_default_value = qdev_propinfo_set_default_value_enum,
|
|
};
|
|
|
|
/* --- VirtIOGPUOutputList --- */
|
|
|
|
static void get_virtio_gpu_output_list(Object *obj, Visitor *v,
|
|
const char *name, void *opaque, Error **errp)
|
|
{
|
|
VirtIOGPUOutputList **prop_ptr =
|
|
object_field_prop_ptr(obj, opaque);
|
|
|
|
visit_type_VirtIOGPUOutputList(v, name, prop_ptr, errp);
|
|
}
|
|
|
|
static void set_virtio_gpu_output_list(Object *obj, Visitor *v,
|
|
const char *name, void *opaque, Error **errp)
|
|
{
|
|
VirtIOGPUOutputList **prop_ptr =
|
|
object_field_prop_ptr(obj, opaque);
|
|
VirtIOGPUOutputList *list;
|
|
|
|
if (!visit_type_VirtIOGPUOutputList(v, name, &list, errp)) {
|
|
return;
|
|
}
|
|
|
|
qapi_free_VirtIOGPUOutputList(*prop_ptr);
|
|
*prop_ptr = list;
|
|
}
|
|
|
|
static void release_virtio_gpu_output_list(Object *obj,
|
|
const char *name, void *opaque)
|
|
{
|
|
VirtIOGPUOutputList **prop_ptr =
|
|
object_field_prop_ptr(obj, opaque);
|
|
|
|
qapi_free_VirtIOGPUOutputList(*prop_ptr);
|
|
*prop_ptr = NULL;
|
|
}
|
|
|
|
const PropertyInfo qdev_prop_virtio_gpu_output_list = {
|
|
.type = "VirtIOGPUOutputList",
|
|
.description = "VirtIO GPU output list [{\"name\":\"<name>\"},...]",
|
|
.get = get_virtio_gpu_output_list,
|
|
.set = set_virtio_gpu_output_list,
|
|
.release = release_virtio_gpu_output_list,
|
|
};
|