350147a871
qdev_alias_all_properties() aliases a DeviceState's qdev properties onto an Object. This is used for VirtioPCIProxy types so that --device virtio-blk-pci has properties of its embedded --device virtio-blk-device object. Currently this function is implemented using qdev properties. Change the function to use QOM object class properties instead. This works because qdev properties create QOM object class properties, but it also catches any QOM object class-only properties that have no qdev properties. This change ensures that properties of devices are shown with --device foo,\? even if they are QOM object class properties. Signed-off-by: Stefan Hajnoczi <stefanha@redhat.com> Message-ID: <20231220134755.814917-2-stefanha@redhat.com> Reviewed-by: Kevin Wolf <kwolf@redhat.com> Signed-off-by: Kevin Wolf <kwolf@redhat.com>
1094 lines
30 KiB
C
1094 lines
30 KiB
C
#include "qemu/osdep.h"
|
|
#include "hw/qdev-properties.h"
|
|
#include "qapi/error.h"
|
|
#include "qapi/qapi-types-misc.h"
|
|
#include "qapi/qmp/qerror.h"
|
|
#include "qapi/qmp/qlist.h"
|
|
#include "qemu/ctype.h"
|
|
#include "qemu/error-report.h"
|
|
#include "qapi/visitor.h"
|
|
#include "qemu/units.h"
|
|
#include "qemu/cutils.h"
|
|
#include "qdev-prop-internal.h"
|
|
#include "qom/qom-qobject.h"
|
|
|
|
void qdev_prop_set_after_realize(DeviceState *dev, const char *name,
|
|
Error **errp)
|
|
{
|
|
if (dev->id) {
|
|
error_setg(errp, "Attempt to set property '%s' on device '%s' "
|
|
"(type '%s') after it was realized", name, dev->id,
|
|
object_get_typename(OBJECT(dev)));
|
|
} else {
|
|
error_setg(errp, "Attempt to set property '%s' on anonymous device "
|
|
"(type '%s') after it was realized", name,
|
|
object_get_typename(OBJECT(dev)));
|
|
}
|
|
}
|
|
|
|
/* returns: true if property is allowed to be set, false otherwise */
|
|
static bool qdev_prop_allow_set(Object *obj, const char *name,
|
|
const PropertyInfo *info, Error **errp)
|
|
{
|
|
DeviceState *dev = DEVICE(obj);
|
|
|
|
if (dev->realized && !info->realized_set_allowed) {
|
|
qdev_prop_set_after_realize(dev, name, errp);
|
|
return false;
|
|
}
|
|
return true;
|
|
}
|
|
|
|
void qdev_prop_allow_set_link_before_realize(const Object *obj,
|
|
const char *name,
|
|
Object *val, Error **errp)
|
|
{
|
|
DeviceState *dev = DEVICE(obj);
|
|
|
|
if (dev->realized) {
|
|
error_setg(errp, "Attempt to set link property '%s' on device '%s' "
|
|
"(type '%s') after it was realized",
|
|
name, dev->id, object_get_typename(obj));
|
|
}
|
|
}
|
|
|
|
void *object_field_prop_ptr(Object *obj, Property *prop)
|
|
{
|
|
void *ptr = obj;
|
|
ptr += prop->offset;
|
|
return ptr;
|
|
}
|
|
|
|
static void field_prop_get(Object *obj, Visitor *v, const char *name,
|
|
void *opaque, Error **errp)
|
|
{
|
|
Property *prop = opaque;
|
|
return prop->info->get(obj, v, name, opaque, errp);
|
|
}
|
|
|
|
/**
|
|
* field_prop_getter: Return getter function to be used for property
|
|
*
|
|
* Return value can be NULL if @info has no getter function.
|
|
*/
|
|
static ObjectPropertyAccessor *field_prop_getter(const PropertyInfo *info)
|
|
{
|
|
return info->get ? field_prop_get : NULL;
|
|
}
|
|
|
|
static void field_prop_set(Object *obj, Visitor *v, const char *name,
|
|
void *opaque, Error **errp)
|
|
{
|
|
Property *prop = opaque;
|
|
|
|
if (!qdev_prop_allow_set(obj, name, prop->info, errp)) {
|
|
return;
|
|
}
|
|
|
|
return prop->info->set(obj, v, name, opaque, errp);
|
|
}
|
|
|
|
/**
|
|
* field_prop_setter: Return setter function to be used for property
|
|
*
|
|
* Return value can be NULL if @info has not setter function.
|
|
*/
|
|
static ObjectPropertyAccessor *field_prop_setter(const PropertyInfo *info)
|
|
{
|
|
return info->set ? field_prop_set : NULL;
|
|
}
|
|
|
|
void qdev_propinfo_get_enum(Object *obj, Visitor *v, const char *name,
|
|
void *opaque, Error **errp)
|
|
{
|
|
Property *prop = opaque;
|
|
int *ptr = object_field_prop_ptr(obj, prop);
|
|
|
|
visit_type_enum(v, name, ptr, prop->info->enum_table, errp);
|
|
}
|
|
|
|
void qdev_propinfo_set_enum(Object *obj, Visitor *v, const char *name,
|
|
void *opaque, Error **errp)
|
|
{
|
|
Property *prop = opaque;
|
|
int *ptr = object_field_prop_ptr(obj, prop);
|
|
|
|
visit_type_enum(v, name, ptr, prop->info->enum_table, errp);
|
|
}
|
|
|
|
void qdev_propinfo_set_default_value_enum(ObjectProperty *op,
|
|
const Property *prop)
|
|
{
|
|
object_property_set_default_str(op,
|
|
qapi_enum_lookup(prop->info->enum_table, prop->defval.i));
|
|
}
|
|
|
|
const PropertyInfo qdev_prop_enum = {
|
|
.name = "enum",
|
|
.get = qdev_propinfo_get_enum,
|
|
.set = qdev_propinfo_set_enum,
|
|
.set_default_value = qdev_propinfo_set_default_value_enum,
|
|
};
|
|
|
|
/* Bit */
|
|
|
|
static uint32_t qdev_get_prop_mask(Property *prop)
|
|
{
|
|
assert(prop->info == &qdev_prop_bit);
|
|
return 0x1 << prop->bitnr;
|
|
}
|
|
|
|
static void bit_prop_set(Object *obj, Property *props, bool val)
|
|
{
|
|
uint32_t *p = object_field_prop_ptr(obj, props);
|
|
uint32_t mask = qdev_get_prop_mask(props);
|
|
if (val) {
|
|
*p |= mask;
|
|
} else {
|
|
*p &= ~mask;
|
|
}
|
|
}
|
|
|
|
static void prop_get_bit(Object *obj, Visitor *v, const char *name,
|
|
void *opaque, Error **errp)
|
|
{
|
|
Property *prop = opaque;
|
|
uint32_t *p = object_field_prop_ptr(obj, prop);
|
|
bool value = (*p & qdev_get_prop_mask(prop)) != 0;
|
|
|
|
visit_type_bool(v, name, &value, errp);
|
|
}
|
|
|
|
static void prop_set_bit(Object *obj, Visitor *v, const char *name,
|
|
void *opaque, Error **errp)
|
|
{
|
|
Property *prop = opaque;
|
|
bool value;
|
|
|
|
if (!visit_type_bool(v, name, &value, errp)) {
|
|
return;
|
|
}
|
|
bit_prop_set(obj, prop, value);
|
|
}
|
|
|
|
static void set_default_value_bool(ObjectProperty *op, const Property *prop)
|
|
{
|
|
object_property_set_default_bool(op, prop->defval.u);
|
|
}
|
|
|
|
const PropertyInfo qdev_prop_bit = {
|
|
.name = "bool",
|
|
.description = "on/off",
|
|
.get = prop_get_bit,
|
|
.set = prop_set_bit,
|
|
.set_default_value = set_default_value_bool,
|
|
};
|
|
|
|
/* Bit64 */
|
|
|
|
static uint64_t qdev_get_prop_mask64(Property *prop)
|
|
{
|
|
assert(prop->info == &qdev_prop_bit64);
|
|
return 0x1ull << prop->bitnr;
|
|
}
|
|
|
|
static void bit64_prop_set(Object *obj, Property *props, bool val)
|
|
{
|
|
uint64_t *p = object_field_prop_ptr(obj, props);
|
|
uint64_t mask = qdev_get_prop_mask64(props);
|
|
if (val) {
|
|
*p |= mask;
|
|
} else {
|
|
*p &= ~mask;
|
|
}
|
|
}
|
|
|
|
static void prop_get_bit64(Object *obj, Visitor *v, const char *name,
|
|
void *opaque, Error **errp)
|
|
{
|
|
Property *prop = opaque;
|
|
uint64_t *p = object_field_prop_ptr(obj, prop);
|
|
bool value = (*p & qdev_get_prop_mask64(prop)) != 0;
|
|
|
|
visit_type_bool(v, name, &value, errp);
|
|
}
|
|
|
|
static void prop_set_bit64(Object *obj, Visitor *v, const char *name,
|
|
void *opaque, Error **errp)
|
|
{
|
|
Property *prop = opaque;
|
|
bool value;
|
|
|
|
if (!visit_type_bool(v, name, &value, errp)) {
|
|
return;
|
|
}
|
|
bit64_prop_set(obj, prop, value);
|
|
}
|
|
|
|
const PropertyInfo qdev_prop_bit64 = {
|
|
.name = "bool",
|
|
.description = "on/off",
|
|
.get = prop_get_bit64,
|
|
.set = prop_set_bit64,
|
|
.set_default_value = set_default_value_bool,
|
|
};
|
|
|
|
/* --- bool --- */
|
|
|
|
static void get_bool(Object *obj, Visitor *v, const char *name, void *opaque,
|
|
Error **errp)
|
|
{
|
|
Property *prop = opaque;
|
|
bool *ptr = object_field_prop_ptr(obj, prop);
|
|
|
|
visit_type_bool(v, name, ptr, errp);
|
|
}
|
|
|
|
static void set_bool(Object *obj, Visitor *v, const char *name, void *opaque,
|
|
Error **errp)
|
|
{
|
|
Property *prop = opaque;
|
|
bool *ptr = object_field_prop_ptr(obj, prop);
|
|
|
|
visit_type_bool(v, name, ptr, errp);
|
|
}
|
|
|
|
const PropertyInfo qdev_prop_bool = {
|
|
.name = "bool",
|
|
.get = get_bool,
|
|
.set = set_bool,
|
|
.set_default_value = set_default_value_bool,
|
|
};
|
|
|
|
/* --- 8bit integer --- */
|
|
|
|
static void get_uint8(Object *obj, Visitor *v, const char *name, void *opaque,
|
|
Error **errp)
|
|
{
|
|
Property *prop = opaque;
|
|
uint8_t *ptr = object_field_prop_ptr(obj, prop);
|
|
|
|
visit_type_uint8(v, name, ptr, errp);
|
|
}
|
|
|
|
static void set_uint8(Object *obj, Visitor *v, const char *name, void *opaque,
|
|
Error **errp)
|
|
{
|
|
Property *prop = opaque;
|
|
uint8_t *ptr = object_field_prop_ptr(obj, prop);
|
|
|
|
visit_type_uint8(v, name, ptr, errp);
|
|
}
|
|
|
|
void qdev_propinfo_set_default_value_int(ObjectProperty *op,
|
|
const Property *prop)
|
|
{
|
|
object_property_set_default_int(op, prop->defval.i);
|
|
}
|
|
|
|
void qdev_propinfo_set_default_value_uint(ObjectProperty *op,
|
|
const Property *prop)
|
|
{
|
|
object_property_set_default_uint(op, prop->defval.u);
|
|
}
|
|
|
|
const PropertyInfo qdev_prop_uint8 = {
|
|
.name = "uint8",
|
|
.get = get_uint8,
|
|
.set = set_uint8,
|
|
.set_default_value = qdev_propinfo_set_default_value_uint,
|
|
};
|
|
|
|
/* --- 16bit integer --- */
|
|
|
|
static void get_uint16(Object *obj, Visitor *v, const char *name,
|
|
void *opaque, Error **errp)
|
|
{
|
|
Property *prop = opaque;
|
|
uint16_t *ptr = object_field_prop_ptr(obj, prop);
|
|
|
|
visit_type_uint16(v, name, ptr, errp);
|
|
}
|
|
|
|
static void set_uint16(Object *obj, Visitor *v, const char *name,
|
|
void *opaque, Error **errp)
|
|
{
|
|
Property *prop = opaque;
|
|
uint16_t *ptr = object_field_prop_ptr(obj, prop);
|
|
|
|
visit_type_uint16(v, name, ptr, errp);
|
|
}
|
|
|
|
const PropertyInfo qdev_prop_uint16 = {
|
|
.name = "uint16",
|
|
.get = get_uint16,
|
|
.set = set_uint16,
|
|
.set_default_value = qdev_propinfo_set_default_value_uint,
|
|
};
|
|
|
|
/* --- 32bit integer --- */
|
|
|
|
static void get_uint32(Object *obj, Visitor *v, const char *name,
|
|
void *opaque, Error **errp)
|
|
{
|
|
Property *prop = opaque;
|
|
uint32_t *ptr = object_field_prop_ptr(obj, prop);
|
|
|
|
visit_type_uint32(v, name, ptr, errp);
|
|
}
|
|
|
|
static void set_uint32(Object *obj, Visitor *v, const char *name,
|
|
void *opaque, Error **errp)
|
|
{
|
|
Property *prop = opaque;
|
|
uint32_t *ptr = object_field_prop_ptr(obj, prop);
|
|
|
|
visit_type_uint32(v, name, ptr, errp);
|
|
}
|
|
|
|
void qdev_propinfo_get_int32(Object *obj, Visitor *v, const char *name,
|
|
void *opaque, Error **errp)
|
|
{
|
|
Property *prop = opaque;
|
|
int32_t *ptr = object_field_prop_ptr(obj, prop);
|
|
|
|
visit_type_int32(v, name, ptr, errp);
|
|
}
|
|
|
|
static void set_int32(Object *obj, Visitor *v, const char *name, void *opaque,
|
|
Error **errp)
|
|
{
|
|
Property *prop = opaque;
|
|
int32_t *ptr = object_field_prop_ptr(obj, prop);
|
|
|
|
visit_type_int32(v, name, ptr, errp);
|
|
}
|
|
|
|
const PropertyInfo qdev_prop_uint32 = {
|
|
.name = "uint32",
|
|
.get = get_uint32,
|
|
.set = set_uint32,
|
|
.set_default_value = qdev_propinfo_set_default_value_uint,
|
|
};
|
|
|
|
const PropertyInfo qdev_prop_int32 = {
|
|
.name = "int32",
|
|
.get = qdev_propinfo_get_int32,
|
|
.set = set_int32,
|
|
.set_default_value = qdev_propinfo_set_default_value_int,
|
|
};
|
|
|
|
/* --- 64bit integer --- */
|
|
|
|
static void get_uint64(Object *obj, Visitor *v, const char *name,
|
|
void *opaque, Error **errp)
|
|
{
|
|
Property *prop = opaque;
|
|
uint64_t *ptr = object_field_prop_ptr(obj, prop);
|
|
|
|
visit_type_uint64(v, name, ptr, errp);
|
|
}
|
|
|
|
static void set_uint64(Object *obj, Visitor *v, const char *name,
|
|
void *opaque, Error **errp)
|
|
{
|
|
Property *prop = opaque;
|
|
uint64_t *ptr = object_field_prop_ptr(obj, prop);
|
|
|
|
visit_type_uint64(v, name, ptr, errp);
|
|
}
|
|
|
|
static void get_int64(Object *obj, Visitor *v, const char *name,
|
|
void *opaque, Error **errp)
|
|
{
|
|
Property *prop = opaque;
|
|
int64_t *ptr = object_field_prop_ptr(obj, prop);
|
|
|
|
visit_type_int64(v, name, ptr, errp);
|
|
}
|
|
|
|
static void set_int64(Object *obj, Visitor *v, const char *name,
|
|
void *opaque, Error **errp)
|
|
{
|
|
Property *prop = opaque;
|
|
int64_t *ptr = object_field_prop_ptr(obj, prop);
|
|
|
|
visit_type_int64(v, name, ptr, errp);
|
|
}
|
|
|
|
const PropertyInfo qdev_prop_uint64 = {
|
|
.name = "uint64",
|
|
.get = get_uint64,
|
|
.set = set_uint64,
|
|
.set_default_value = qdev_propinfo_set_default_value_uint,
|
|
};
|
|
|
|
const PropertyInfo qdev_prop_int64 = {
|
|
.name = "int64",
|
|
.get = get_int64,
|
|
.set = set_int64,
|
|
.set_default_value = qdev_propinfo_set_default_value_int,
|
|
};
|
|
|
|
static void set_uint64_checkmask(Object *obj, Visitor *v, const char *name,
|
|
void *opaque, Error **errp)
|
|
{
|
|
Property *prop = opaque;
|
|
uint64_t *ptr = object_field_prop_ptr(obj, prop);
|
|
|
|
visit_type_uint64(v, name, ptr, errp);
|
|
if (*ptr & ~prop->bitmask) {
|
|
error_setg(errp, "Property value for '%s' has bits outside mask '0x%" PRIx64 "'",
|
|
name, prop->bitmask);
|
|
}
|
|
}
|
|
|
|
const PropertyInfo qdev_prop_uint64_checkmask = {
|
|
.name = "uint64",
|
|
.get = get_uint64,
|
|
.set = set_uint64_checkmask,
|
|
};
|
|
|
|
/* --- string --- */
|
|
|
|
static void release_string(Object *obj, const char *name, void *opaque)
|
|
{
|
|
Property *prop = opaque;
|
|
g_free(*(char **)object_field_prop_ptr(obj, prop));
|
|
}
|
|
|
|
static void get_string(Object *obj, Visitor *v, const char *name,
|
|
void *opaque, Error **errp)
|
|
{
|
|
Property *prop = opaque;
|
|
char **ptr = object_field_prop_ptr(obj, prop);
|
|
|
|
if (!*ptr) {
|
|
char *str = (char *)"";
|
|
visit_type_str(v, name, &str, errp);
|
|
} else {
|
|
visit_type_str(v, name, ptr, errp);
|
|
}
|
|
}
|
|
|
|
static void set_string(Object *obj, Visitor *v, const char *name,
|
|
void *opaque, Error **errp)
|
|
{
|
|
Property *prop = opaque;
|
|
char **ptr = object_field_prop_ptr(obj, prop);
|
|
char *str;
|
|
|
|
if (!visit_type_str(v, name, &str, errp)) {
|
|
return;
|
|
}
|
|
g_free(*ptr);
|
|
*ptr = str;
|
|
}
|
|
|
|
const PropertyInfo qdev_prop_string = {
|
|
.name = "str",
|
|
.release = release_string,
|
|
.get = get_string,
|
|
.set = set_string,
|
|
};
|
|
|
|
/* --- on/off/auto --- */
|
|
|
|
const PropertyInfo qdev_prop_on_off_auto = {
|
|
.name = "OnOffAuto",
|
|
.description = "on/off/auto",
|
|
.enum_table = &OnOffAuto_lookup,
|
|
.get = qdev_propinfo_get_enum,
|
|
.set = qdev_propinfo_set_enum,
|
|
.set_default_value = qdev_propinfo_set_default_value_enum,
|
|
};
|
|
|
|
/* --- 32bit unsigned int 'size' type --- */
|
|
|
|
void qdev_propinfo_get_size32(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 = *ptr;
|
|
|
|
visit_type_size(v, name, &value, errp);
|
|
}
|
|
|
|
static void set_size32(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 (value > UINT32_MAX) {
|
|
error_setg(errp,
|
|
"Property %s.%s doesn't take value %" PRIu64
|
|
" (maximum: %u)",
|
|
object_get_typename(obj), name, value, UINT32_MAX);
|
|
return;
|
|
}
|
|
|
|
*ptr = value;
|
|
}
|
|
|
|
const PropertyInfo qdev_prop_size32 = {
|
|
.name = "size",
|
|
.get = qdev_propinfo_get_size32,
|
|
.set = set_size32,
|
|
.set_default_value = qdev_propinfo_set_default_value_uint,
|
|
};
|
|
|
|
/* --- support for array properties --- */
|
|
|
|
typedef struct ArrayElementList ArrayElementList;
|
|
|
|
struct ArrayElementList {
|
|
ArrayElementList *next;
|
|
void *value;
|
|
};
|
|
|
|
/*
|
|
* Given an array property @parent_prop in @obj, return a Property for a
|
|
* specific element of the array. Arrays are backed by an uint32_t length field
|
|
* and an element array. @elem points at an element in this element array.
|
|
*/
|
|
static Property array_elem_prop(Object *obj, Property *parent_prop,
|
|
const char *name, char *elem)
|
|
{
|
|
return (Property) {
|
|
.info = parent_prop->arrayinfo,
|
|
.name = name,
|
|
/*
|
|
* This ugly piece of pointer arithmetic sets up the offset so
|
|
* that when the underlying release hook calls qdev_get_prop_ptr
|
|
* they get the right answer despite the array element not actually
|
|
* being inside the device struct.
|
|
*/
|
|
.offset = (uintptr_t)elem - (uintptr_t)obj,
|
|
};
|
|
}
|
|
|
|
/*
|
|
* Object property release callback for array properties: We call the
|
|
* underlying element's property release hook for each element.
|
|
*
|
|
* Note that it is the responsibility of the individual device's deinit
|
|
* to free the array proper.
|
|
*/
|
|
static void release_prop_array(Object *obj, const char *name, void *opaque)
|
|
{
|
|
Property *prop = opaque;
|
|
uint32_t *alenptr = object_field_prop_ptr(obj, prop);
|
|
void **arrayptr = (void *)obj + prop->arrayoffset;
|
|
char *elem = *arrayptr;
|
|
int i;
|
|
|
|
if (!prop->arrayinfo->release) {
|
|
return;
|
|
}
|
|
|
|
for (i = 0; i < *alenptr; i++) {
|
|
Property elem_prop = array_elem_prop(obj, prop, name, elem);
|
|
prop->arrayinfo->release(obj, NULL, &elem_prop);
|
|
elem += prop->arrayfieldsize;
|
|
}
|
|
}
|
|
|
|
/*
|
|
* Setter for an array property. This sets both the array length (which
|
|
* is technically the property field in the object) and the array itself
|
|
* (a pointer to which is stored in the additional field described by
|
|
* prop->arrayoffset).
|
|
*/
|
|
static void set_prop_array(Object *obj, Visitor *v, const char *name,
|
|
void *opaque, Error **errp)
|
|
{
|
|
ERRP_GUARD();
|
|
Property *prop = opaque;
|
|
uint32_t *alenptr = object_field_prop_ptr(obj, prop);
|
|
void **arrayptr = (void *)obj + prop->arrayoffset;
|
|
ArrayElementList *list, *elem, *next;
|
|
const size_t size = sizeof(*list);
|
|
char *elemptr;
|
|
bool ok = true;
|
|
|
|
if (*alenptr) {
|
|
error_setg(errp, "array size property %s may not be set more than once",
|
|
name);
|
|
return;
|
|
}
|
|
|
|
if (!visit_start_list(v, name, (GenericList **) &list, size, errp)) {
|
|
return;
|
|
}
|
|
|
|
/* Read the whole input into a temporary list */
|
|
elem = list;
|
|
while (elem) {
|
|
Property elem_prop;
|
|
|
|
elem->value = g_malloc0(prop->arrayfieldsize);
|
|
elem_prop = array_elem_prop(obj, prop, name, elem->value);
|
|
prop->arrayinfo->set(obj, v, NULL, &elem_prop, errp);
|
|
if (*errp) {
|
|
ok = false;
|
|
goto out_obj;
|
|
}
|
|
if (*alenptr == INT_MAX) {
|
|
error_setg(errp, "array is too big");
|
|
return;
|
|
}
|
|
(*alenptr)++;
|
|
elem = (ArrayElementList *) visit_next_list(v, (GenericList*) elem,
|
|
size);
|
|
}
|
|
|
|
ok = visit_check_list(v, errp);
|
|
out_obj:
|
|
visit_end_list(v, (void**) &list);
|
|
|
|
if (!ok) {
|
|
for (elem = list; elem; elem = next) {
|
|
Property elem_prop = array_elem_prop(obj, prop, name,
|
|
elem->value);
|
|
if (prop->arrayinfo->release) {
|
|
prop->arrayinfo->release(obj, NULL, &elem_prop);
|
|
}
|
|
next = elem->next;
|
|
g_free(elem->value);
|
|
g_free(elem);
|
|
}
|
|
return;
|
|
}
|
|
|
|
/*
|
|
* Now that we know how big the array has to be, move the data over to a
|
|
* linear array and free the temporary list.
|
|
*/
|
|
*arrayptr = g_malloc_n(*alenptr, prop->arrayfieldsize);
|
|
elemptr = *arrayptr;
|
|
for (elem = list; elem; elem = next) {
|
|
memcpy(elemptr, elem->value, prop->arrayfieldsize);
|
|
elemptr += prop->arrayfieldsize;
|
|
next = elem->next;
|
|
g_free(elem->value);
|
|
g_free(elem);
|
|
}
|
|
}
|
|
|
|
static void get_prop_array(Object *obj, Visitor *v, const char *name,
|
|
void *opaque, Error **errp)
|
|
{
|
|
ERRP_GUARD();
|
|
Property *prop = opaque;
|
|
uint32_t *alenptr = object_field_prop_ptr(obj, prop);
|
|
void **arrayptr = (void *)obj + prop->arrayoffset;
|
|
char *elemptr = *arrayptr;
|
|
ArrayElementList *list = NULL, *elem;
|
|
ArrayElementList **tail = &list;
|
|
const size_t size = sizeof(*list);
|
|
int i;
|
|
bool ok;
|
|
|
|
/* At least the string output visitor needs a real list */
|
|
for (i = 0; i < *alenptr; i++) {
|
|
elem = g_new0(ArrayElementList, 1);
|
|
elem->value = elemptr;
|
|
elemptr += prop->arrayfieldsize;
|
|
|
|
*tail = elem;
|
|
tail = &elem->next;
|
|
}
|
|
|
|
if (!visit_start_list(v, name, (GenericList **) &list, size, errp)) {
|
|
return;
|
|
}
|
|
|
|
elem = list;
|
|
while (elem) {
|
|
Property elem_prop = array_elem_prop(obj, prop, name, elem->value);
|
|
prop->arrayinfo->get(obj, v, NULL, &elem_prop, errp);
|
|
if (*errp) {
|
|
goto out_obj;
|
|
}
|
|
elem = (ArrayElementList *) visit_next_list(v, (GenericList*) elem,
|
|
size);
|
|
}
|
|
|
|
/* visit_check_list() can only fail for input visitors */
|
|
ok = visit_check_list(v, errp);
|
|
assert(ok);
|
|
|
|
out_obj:
|
|
visit_end_list(v, (void**) &list);
|
|
|
|
while (list) {
|
|
elem = list;
|
|
list = elem->next;
|
|
g_free(elem);
|
|
}
|
|
}
|
|
|
|
static void default_prop_array(ObjectProperty *op, const Property *prop)
|
|
{
|
|
object_property_set_default_list(op);
|
|
}
|
|
|
|
const PropertyInfo qdev_prop_array = {
|
|
.name = "list",
|
|
.get = get_prop_array,
|
|
.set = set_prop_array,
|
|
.release = release_prop_array,
|
|
.set_default_value = default_prop_array,
|
|
};
|
|
|
|
/* --- public helpers --- */
|
|
|
|
static Property *qdev_prop_walk(Property *props, const char *name)
|
|
{
|
|
if (!props) {
|
|
return NULL;
|
|
}
|
|
while (props->name) {
|
|
if (strcmp(props->name, name) == 0) {
|
|
return props;
|
|
}
|
|
props++;
|
|
}
|
|
return NULL;
|
|
}
|
|
|
|
static Property *qdev_prop_find(DeviceState *dev, const char *name)
|
|
{
|
|
ObjectClass *class;
|
|
Property *prop;
|
|
|
|
/* device properties */
|
|
class = object_get_class(OBJECT(dev));
|
|
do {
|
|
prop = qdev_prop_walk(DEVICE_CLASS(class)->props_, name);
|
|
if (prop) {
|
|
return prop;
|
|
}
|
|
class = object_class_get_parent(class);
|
|
} while (class != object_class_by_name(TYPE_DEVICE));
|
|
|
|
return NULL;
|
|
}
|
|
|
|
void error_set_from_qdev_prop_error(Error **errp, int ret, Object *obj,
|
|
const char *name, const char *value)
|
|
{
|
|
switch (ret) {
|
|
case -EEXIST:
|
|
error_setg(errp, "Property '%s.%s' can't take value '%s', it's in use",
|
|
object_get_typename(obj), name, value);
|
|
break;
|
|
default:
|
|
case -EINVAL:
|
|
error_setg(errp, QERR_PROPERTY_VALUE_BAD,
|
|
object_get_typename(obj), name, value);
|
|
break;
|
|
case -ENOENT:
|
|
error_setg(errp, "Property '%s.%s' can't find value '%s'",
|
|
object_get_typename(obj), name, value);
|
|
break;
|
|
case 0:
|
|
break;
|
|
}
|
|
}
|
|
|
|
void qdev_prop_set_bit(DeviceState *dev, const char *name, bool value)
|
|
{
|
|
object_property_set_bool(OBJECT(dev), name, value, &error_abort);
|
|
}
|
|
|
|
void qdev_prop_set_uint8(DeviceState *dev, const char *name, uint8_t value)
|
|
{
|
|
object_property_set_int(OBJECT(dev), name, value, &error_abort);
|
|
}
|
|
|
|
void qdev_prop_set_uint16(DeviceState *dev, const char *name, uint16_t value)
|
|
{
|
|
object_property_set_int(OBJECT(dev), name, value, &error_abort);
|
|
}
|
|
|
|
void qdev_prop_set_uint32(DeviceState *dev, const char *name, uint32_t value)
|
|
{
|
|
object_property_set_int(OBJECT(dev), name, value, &error_abort);
|
|
}
|
|
|
|
void qdev_prop_set_int32(DeviceState *dev, const char *name, int32_t value)
|
|
{
|
|
object_property_set_int(OBJECT(dev), name, value, &error_abort);
|
|
}
|
|
|
|
void qdev_prop_set_uint64(DeviceState *dev, const char *name, uint64_t value)
|
|
{
|
|
object_property_set_int(OBJECT(dev), name, value, &error_abort);
|
|
}
|
|
|
|
void qdev_prop_set_string(DeviceState *dev, const char *name, const char *value)
|
|
{
|
|
object_property_set_str(OBJECT(dev), name, value, &error_abort);
|
|
}
|
|
|
|
void qdev_prop_set_enum(DeviceState *dev, const char *name, int value)
|
|
{
|
|
Property *prop;
|
|
|
|
prop = qdev_prop_find(dev, name);
|
|
object_property_set_str(OBJECT(dev), name,
|
|
qapi_enum_lookup(prop->info->enum_table, value),
|
|
&error_abort);
|
|
}
|
|
|
|
void qdev_prop_set_array(DeviceState *dev, const char *name, QList *values)
|
|
{
|
|
object_property_set_qobject(OBJECT(dev), name, QOBJECT(values),
|
|
&error_abort);
|
|
qobject_unref(values);
|
|
}
|
|
|
|
static GPtrArray *global_props(void)
|
|
{
|
|
static GPtrArray *gp;
|
|
|
|
if (!gp) {
|
|
gp = g_ptr_array_new();
|
|
}
|
|
|
|
return gp;
|
|
}
|
|
|
|
void qdev_prop_register_global(GlobalProperty *prop)
|
|
{
|
|
g_ptr_array_add(global_props(), prop);
|
|
}
|
|
|
|
const GlobalProperty *qdev_find_global_prop(Object *obj,
|
|
const char *name)
|
|
{
|
|
GPtrArray *props = global_props();
|
|
const GlobalProperty *p;
|
|
int i;
|
|
|
|
for (i = 0; i < props->len; i++) {
|
|
p = g_ptr_array_index(props, i);
|
|
if (object_dynamic_cast(obj, p->driver)
|
|
&& !strcmp(p->property, name)) {
|
|
return p;
|
|
}
|
|
}
|
|
return NULL;
|
|
}
|
|
|
|
int qdev_prop_check_globals(void)
|
|
{
|
|
int i, ret = 0;
|
|
|
|
for (i = 0; i < global_props()->len; i++) {
|
|
GlobalProperty *prop;
|
|
ObjectClass *oc;
|
|
DeviceClass *dc;
|
|
|
|
prop = g_ptr_array_index(global_props(), i);
|
|
if (prop->used) {
|
|
continue;
|
|
}
|
|
oc = object_class_by_name(prop->driver);
|
|
oc = object_class_dynamic_cast(oc, TYPE_DEVICE);
|
|
if (!oc) {
|
|
warn_report("global %s.%s has invalid class name",
|
|
prop->driver, prop->property);
|
|
ret = 1;
|
|
continue;
|
|
}
|
|
dc = DEVICE_CLASS(oc);
|
|
if (!dc->hotpluggable && !prop->used) {
|
|
warn_report("global %s.%s=%s not used",
|
|
prop->driver, prop->property, prop->value);
|
|
ret = 1;
|
|
continue;
|
|
}
|
|
}
|
|
return ret;
|
|
}
|
|
|
|
void qdev_prop_set_globals(DeviceState *dev)
|
|
{
|
|
object_apply_global_props(OBJECT(dev), global_props(),
|
|
dev->hotplugged ? NULL : &error_fatal);
|
|
}
|
|
|
|
/* --- 64bit unsigned int 'size' type --- */
|
|
|
|
static void get_size(Object *obj, Visitor *v, const char *name, void *opaque,
|
|
Error **errp)
|
|
{
|
|
Property *prop = opaque;
|
|
uint64_t *ptr = object_field_prop_ptr(obj, prop);
|
|
|
|
visit_type_size(v, name, ptr, errp);
|
|
}
|
|
|
|
static void set_size(Object *obj, Visitor *v, const char *name, void *opaque,
|
|
Error **errp)
|
|
{
|
|
Property *prop = opaque;
|
|
uint64_t *ptr = object_field_prop_ptr(obj, prop);
|
|
|
|
visit_type_size(v, name, ptr, errp);
|
|
}
|
|
|
|
const PropertyInfo qdev_prop_size = {
|
|
.name = "size",
|
|
.get = get_size,
|
|
.set = set_size,
|
|
.set_default_value = qdev_propinfo_set_default_value_uint,
|
|
};
|
|
|
|
/* --- object link property --- */
|
|
|
|
static ObjectProperty *create_link_property(ObjectClass *oc, const char *name,
|
|
Property *prop)
|
|
{
|
|
return object_class_property_add_link(oc, name, prop->link_type,
|
|
prop->offset,
|
|
qdev_prop_allow_set_link_before_realize,
|
|
OBJ_PROP_LINK_STRONG);
|
|
}
|
|
|
|
const PropertyInfo qdev_prop_link = {
|
|
.name = "link",
|
|
.create = create_link_property,
|
|
};
|
|
|
|
void qdev_property_add_static(DeviceState *dev, Property *prop)
|
|
{
|
|
Object *obj = OBJECT(dev);
|
|
ObjectProperty *op;
|
|
|
|
assert(!prop->info->create);
|
|
|
|
op = object_property_add(obj, prop->name, prop->info->name,
|
|
field_prop_getter(prop->info),
|
|
field_prop_setter(prop->info),
|
|
prop->info->release,
|
|
prop);
|
|
|
|
object_property_set_description(obj, prop->name,
|
|
prop->info->description);
|
|
|
|
if (prop->set_default) {
|
|
prop->info->set_default_value(op, prop);
|
|
if (op->init) {
|
|
op->init(obj, op);
|
|
}
|
|
}
|
|
}
|
|
|
|
static void qdev_class_add_property(DeviceClass *klass, const char *name,
|
|
Property *prop)
|
|
{
|
|
ObjectClass *oc = OBJECT_CLASS(klass);
|
|
ObjectProperty *op;
|
|
|
|
if (prop->info->create) {
|
|
op = prop->info->create(oc, name, prop);
|
|
} else {
|
|
op = object_class_property_add(oc,
|
|
name, prop->info->name,
|
|
field_prop_getter(prop->info),
|
|
field_prop_setter(prop->info),
|
|
prop->info->release,
|
|
prop);
|
|
}
|
|
if (prop->set_default) {
|
|
prop->info->set_default_value(op, prop);
|
|
}
|
|
object_class_property_set_description(oc, name, prop->info->description);
|
|
}
|
|
|
|
/**
|
|
* Legacy property handling
|
|
*/
|
|
|
|
static void qdev_get_legacy_property(Object *obj, Visitor *v,
|
|
const char *name, void *opaque,
|
|
Error **errp)
|
|
{
|
|
Property *prop = opaque;
|
|
|
|
char buffer[1024];
|
|
char *ptr = buffer;
|
|
|
|
prop->info->print(obj, prop, buffer, sizeof(buffer));
|
|
visit_type_str(v, name, &ptr, errp);
|
|
}
|
|
|
|
/**
|
|
* qdev_class_add_legacy_property:
|
|
* @dev: Device to add the property to.
|
|
* @prop: The qdev property definition.
|
|
*
|
|
* Add a legacy QOM property to @dev for qdev property @prop.
|
|
*
|
|
* Legacy properties are string versions of QOM properties. The format of
|
|
* the string depends on the property type. Legacy properties are only
|
|
* needed for "info qtree".
|
|
*
|
|
* Do not use this in new code! QOM Properties added through this interface
|
|
* will be given names in the "legacy" namespace.
|
|
*/
|
|
static void qdev_class_add_legacy_property(DeviceClass *dc, Property *prop)
|
|
{
|
|
g_autofree char *name = NULL;
|
|
|
|
/* Register pointer properties as legacy properties */
|
|
if (!prop->info->print && prop->info->get) {
|
|
return;
|
|
}
|
|
|
|
name = g_strdup_printf("legacy-%s", prop->name);
|
|
object_class_property_add(OBJECT_CLASS(dc), name, "str",
|
|
prop->info->print ? qdev_get_legacy_property : prop->info->get,
|
|
NULL, NULL, prop);
|
|
}
|
|
|
|
void device_class_set_props(DeviceClass *dc, Property *props)
|
|
{
|
|
Property *prop;
|
|
|
|
dc->props_ = props;
|
|
for (prop = props; prop && prop->name; prop++) {
|
|
qdev_class_add_legacy_property(dc, prop);
|
|
qdev_class_add_property(dc, prop->name, prop);
|
|
}
|
|
}
|
|
|
|
void qdev_alias_all_properties(DeviceState *target, Object *source)
|
|
{
|
|
ObjectClass *class;
|
|
ObjectPropertyIterator iter;
|
|
ObjectProperty *prop;
|
|
|
|
class = object_get_class(OBJECT(target));
|
|
|
|
object_class_property_iter_init(&iter, class);
|
|
while ((prop = object_property_iter_next(&iter))) {
|
|
if (object_property_find(source, prop->name)) {
|
|
continue; /* skip duplicate properties */
|
|
}
|
|
|
|
object_property_add_alias(source, prop->name,
|
|
OBJECT(target), prop->name);
|
|
}
|
|
}
|