diff options
Diffstat (limited to 'hw/core/qdev-properties-system.c')
-rw-r--r-- | hw/core/qdev-properties-system.c | 1121 |
1 files changed, 1121 insertions, 0 deletions
diff --git a/hw/core/qdev-properties-system.c b/hw/core/qdev-properties-system.c new file mode 100644 index 00000000..a91f6056 --- /dev/null +++ b/hw/core/qdev-properties-system.c @@ -0,0 +1,1121 @@ +/* + * 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/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 "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; +} + + +/* --- 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 alread 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"); + } + + aio_context_acquire(ctx); + blk_replace_bs(blk, bs, errp); + aio_context_release(ctx); + 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 = iothread ? bdrv_get_aio_context(bs) : qemu_get_aio_context(); + blk = blk_new(ctx, 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) { + AioContext *ctx = blk_get_aio_context(*ptr); + + aio_context_acquire(ctx); + blockdev_auto_del(*ptr); + blk_detach_dev(*ptr, dev); + aio_context_release(ctx); + } +} + +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) +{ + 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, 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; + int err = 0; + char *str; + + if (!visit_type_str(v, name, &str, errp)) { + return; + } + + state = audio_state_by_name(str); + + if (!state) { + err = -ENOENT; + goto out; + } + card->state = state; + +out: + error_set_from_qdev_prop_error(errp, err, obj, name, str); + g_free(str); +} + +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 --- */ + +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_enum, + .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) +{ + DeviceState *dev = DEVICE(obj); + Property *prop = opaque; + uint32_t *ptr = object_field_prop_ptr(obj, prop); + uint64_t value; + Error *local_err = NULL; + + if (!visit_type_size(v, name, &value, errp)) { + return; + } + check_block_size(dev->id ? : "", name, value, &local_err); + if (local_err) { + error_propagate(errp, local_err); + 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", + .enum_table = &MultiFDCompression_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", + rr->low, rr->high, 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); + Error *local_err = NULL; + const char *endptr; + char *str; + int ret; + + visit_type_str(v, name, &str, &local_err); + if (local_err) { + error_propagate(errp, local_err); + return; + } + + ret = qemu_strtou64(str, &endptr, 16, &rr->low); + 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, &rr->high); + if (ret) { + error_setg(errp, "end address of '%s'" + " must be a hexadecimal integer", name); + goto out; + } + if (*endptr != ':') { + goto separator_error; + } + + 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; + int32_t value, *ptr = object_field_prop_ptr(obj, prop); + unsigned int slot, fn, n; + char *str; + + if (!visit_type_str(v, name, &str, NULL)) { + if (!visit_type_int32(v, name, &value, errp)) { + return; + } + if (value < -1 || value > 255) { + error_setg(errp, QERR_INVALID_PARAMETER_VALUE, + name ? name : "null", "a value between -1 and 255"); + return; + } + *ptr = value; + return; + } + + 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; + g_free(str); + return; + +invalid: + error_set_from_qdev_prop_error(errp, EINVAL, obj, name, str); + g_free(str); +} + +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 -- */ + +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; + 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; + default: + /* Unreachable */ + abort(); + } +} + +const PropertyInfo qdev_prop_pcie_link_speed = { + .name = "PCIELinkSpeed", + .description = "2_5/5/8/16", + .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_FMT_LEN + 1]; + 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, +}; |