/* * 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 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; } 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: * :: * 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, ""); } 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 [:]:. * if 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, };