Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
qdev-properties.c: Separate core from the code used only by qemu-syst…
…em-* This separates the qdev properties code in two parts: - qdev-properties.c, that contains most of the qdev properties code; - qdev-properties-system.c for code specific for qemu-system-*, containing: - Property types: drive, chr, netdev, vlan, that depend on code that won't be included on *-user - qemu_add_globals(), that depends on qemu-config.o. This change should help on two things: - Allowing DeviceState to be used by *-user without pulling dependencies that are specific for qemu-system-*; - Writing qdev unit tests without pulling too many dependencies. The copyright/license of qdev-properties.c isn't explicitly stated at the file, so add a simple copyright/license header pointing to the commit ID of the original file. Signed-off-by: Eduardo Habkost <ehabkost@redhat.com> Signed-off-by: Andreas Färber <afaerber@suse.de>
- Loading branch information
Showing
5 changed files
with
360 additions
and
340 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,357 @@ | ||
/* | ||
* qdev property parsing and global properties | ||
* (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 "net.h" | ||
#include "qdev.h" | ||
#include "qerror.h" | ||
#include "blockdev.h" | ||
#include "hw/block-common.h" | ||
#include "net/hub.h" | ||
#include "qapi/qapi-visit-core.h" | ||
|
||
static void get_pointer(Object *obj, Visitor *v, Property *prop, | ||
const char *(*print)(void *ptr), | ||
const char *name, Error **errp) | ||
{ | ||
DeviceState *dev = DEVICE(obj); | ||
void **ptr = qdev_get_prop_ptr(dev, prop); | ||
char *p; | ||
|
||
p = (char *) (*ptr ? print(*ptr) : ""); | ||
visit_type_str(v, &p, name, errp); | ||
} | ||
|
||
static void set_pointer(Object *obj, Visitor *v, Property *prop, | ||
int (*parse)(DeviceState *dev, const char *str, | ||
void **ptr), | ||
const char *name, Error **errp) | ||
{ | ||
DeviceState *dev = DEVICE(obj); | ||
Error *local_err = NULL; | ||
void **ptr = qdev_get_prop_ptr(dev, prop); | ||
char *str; | ||
int ret; | ||
|
||
if (dev->state != DEV_STATE_CREATED) { | ||
error_set(errp, QERR_PERMISSION_DENIED); | ||
return; | ||
} | ||
|
||
visit_type_str(v, &str, name, &local_err); | ||
if (local_err) { | ||
error_propagate(errp, local_err); | ||
return; | ||
} | ||
if (!*str) { | ||
g_free(str); | ||
*ptr = NULL; | ||
return; | ||
} | ||
ret = parse(dev, str, ptr); | ||
error_set_from_qdev_prop_error(errp, ret, dev, prop, str); | ||
g_free(str); | ||
} | ||
|
||
/* --- drive --- */ | ||
|
||
static int parse_drive(DeviceState *dev, const char *str, void **ptr) | ||
{ | ||
BlockDriverState *bs; | ||
|
||
bs = bdrv_find(str); | ||
if (bs == NULL) { | ||
return -ENOENT; | ||
} | ||
if (bdrv_attach_dev(bs, dev) < 0) { | ||
return -EEXIST; | ||
} | ||
*ptr = bs; | ||
return 0; | ||
} | ||
|
||
static void release_drive(Object *obj, const char *name, void *opaque) | ||
{ | ||
DeviceState *dev = DEVICE(obj); | ||
Property *prop = opaque; | ||
BlockDriverState **ptr = qdev_get_prop_ptr(dev, prop); | ||
|
||
if (*ptr) { | ||
bdrv_detach_dev(*ptr, dev); | ||
blockdev_auto_del(*ptr); | ||
} | ||
} | ||
|
||
static const char *print_drive(void *ptr) | ||
{ | ||
return bdrv_get_device_name(ptr); | ||
} | ||
|
||
static void get_drive(Object *obj, Visitor *v, void *opaque, | ||
const char *name, Error **errp) | ||
{ | ||
get_pointer(obj, v, opaque, print_drive, name, errp); | ||
} | ||
|
||
static void set_drive(Object *obj, Visitor *v, void *opaque, | ||
const char *name, Error **errp) | ||
{ | ||
set_pointer(obj, v, opaque, parse_drive, name, errp); | ||
} | ||
|
||
PropertyInfo qdev_prop_drive = { | ||
.name = "drive", | ||
.get = get_drive, | ||
.set = set_drive, | ||
.release = release_drive, | ||
}; | ||
|
||
/* --- character device --- */ | ||
|
||
static int parse_chr(DeviceState *dev, const char *str, void **ptr) | ||
{ | ||
CharDriverState *chr = qemu_chr_find(str); | ||
if (chr == NULL) { | ||
return -ENOENT; | ||
} | ||
if (chr->avail_connections < 1) { | ||
return -EEXIST; | ||
} | ||
*ptr = chr; | ||
--chr->avail_connections; | ||
return 0; | ||
} | ||
|
||
static void release_chr(Object *obj, const char *name, void *opaque) | ||
{ | ||
DeviceState *dev = DEVICE(obj); | ||
Property *prop = opaque; | ||
CharDriverState **ptr = qdev_get_prop_ptr(dev, prop); | ||
|
||
if (*ptr) { | ||
qemu_chr_add_handlers(*ptr, NULL, NULL, NULL, NULL); | ||
} | ||
} | ||
|
||
|
||
static const char *print_chr(void *ptr) | ||
{ | ||
CharDriverState *chr = ptr; | ||
|
||
return chr->label ? chr->label : ""; | ||
} | ||
|
||
static void get_chr(Object *obj, Visitor *v, void *opaque, | ||
const char *name, Error **errp) | ||
{ | ||
get_pointer(obj, v, opaque, print_chr, name, errp); | ||
} | ||
|
||
static void set_chr(Object *obj, Visitor *v, void *opaque, | ||
const char *name, Error **errp) | ||
{ | ||
set_pointer(obj, v, opaque, parse_chr, name, errp); | ||
} | ||
|
||
PropertyInfo qdev_prop_chr = { | ||
.name = "chr", | ||
.get = get_chr, | ||
.set = set_chr, | ||
.release = release_chr, | ||
}; | ||
|
||
/* --- netdev device --- */ | ||
|
||
static int parse_netdev(DeviceState *dev, const char *str, void **ptr) | ||
{ | ||
NetClientState *netdev = qemu_find_netdev(str); | ||
|
||
if (netdev == NULL) { | ||
return -ENOENT; | ||
} | ||
if (netdev->peer) { | ||
return -EEXIST; | ||
} | ||
*ptr = netdev; | ||
return 0; | ||
} | ||
|
||
static const char *print_netdev(void *ptr) | ||
{ | ||
NetClientState *netdev = ptr; | ||
|
||
return netdev->name ? netdev->name : ""; | ||
} | ||
|
||
static void get_netdev(Object *obj, Visitor *v, void *opaque, | ||
const char *name, Error **errp) | ||
{ | ||
get_pointer(obj, v, opaque, print_netdev, name, errp); | ||
} | ||
|
||
static void set_netdev(Object *obj, Visitor *v, void *opaque, | ||
const char *name, Error **errp) | ||
{ | ||
set_pointer(obj, v, opaque, parse_netdev, name, errp); | ||
} | ||
|
||
PropertyInfo qdev_prop_netdev = { | ||
.name = "netdev", | ||
.get = get_netdev, | ||
.set = set_netdev, | ||
}; | ||
|
||
/* --- vlan --- */ | ||
|
||
static int print_vlan(DeviceState *dev, Property *prop, char *dest, size_t len) | ||
{ | ||
NetClientState **ptr = qdev_get_prop_ptr(dev, prop); | ||
|
||
if (*ptr) { | ||
int id; | ||
if (!net_hub_id_for_client(*ptr, &id)) { | ||
return snprintf(dest, len, "%d", id); | ||
} | ||
} | ||
|
||
return snprintf(dest, len, "<null>"); | ||
} | ||
|
||
static void get_vlan(Object *obj, Visitor *v, void *opaque, | ||
const char *name, Error **errp) | ||
{ | ||
DeviceState *dev = DEVICE(obj); | ||
Property *prop = opaque; | ||
NetClientState **ptr = qdev_get_prop_ptr(dev, prop); | ||
int32_t id = -1; | ||
|
||
if (*ptr) { | ||
int hub_id; | ||
if (!net_hub_id_for_client(*ptr, &hub_id)) { | ||
id = hub_id; | ||
} | ||
} | ||
|
||
visit_type_int32(v, &id, name, errp); | ||
} | ||
|
||
static void set_vlan(Object *obj, Visitor *v, void *opaque, | ||
const char *name, Error **errp) | ||
{ | ||
DeviceState *dev = DEVICE(obj); | ||
Property *prop = opaque; | ||
NetClientState **ptr = qdev_get_prop_ptr(dev, prop); | ||
Error *local_err = NULL; | ||
int32_t id; | ||
NetClientState *hubport; | ||
|
||
if (dev->state != DEV_STATE_CREATED) { | ||
error_set(errp, QERR_PERMISSION_DENIED); | ||
return; | ||
} | ||
|
||
visit_type_int32(v, &id, name, &local_err); | ||
if (local_err) { | ||
error_propagate(errp, local_err); | ||
return; | ||
} | ||
if (id == -1) { | ||
*ptr = NULL; | ||
return; | ||
} | ||
|
||
hubport = net_hub_port_find(id); | ||
if (!hubport) { | ||
error_set(errp, QERR_INVALID_PARAMETER_VALUE, | ||
name, prop->info->name); | ||
return; | ||
} | ||
*ptr = hubport; | ||
} | ||
|
||
PropertyInfo qdev_prop_vlan = { | ||
.name = "vlan", | ||
.print = print_vlan, | ||
.get = get_vlan, | ||
.set = set_vlan, | ||
}; | ||
|
||
int qdev_prop_set_drive(DeviceState *dev, const char *name, | ||
BlockDriverState *value) | ||
{ | ||
Error *errp = NULL; | ||
const char *bdrv_name = value ? bdrv_get_device_name(value) : ""; | ||
object_property_set_str(OBJECT(dev), bdrv_name, | ||
name, &errp); | ||
if (errp) { | ||
qerror_report_err(errp); | ||
error_free(errp); | ||
return -1; | ||
} | ||
return 0; | ||
} | ||
|
||
void qdev_prop_set_drive_nofail(DeviceState *dev, const char *name, | ||
BlockDriverState *value) | ||
{ | ||
if (qdev_prop_set_drive(dev, name, value) < 0) { | ||
exit(1); | ||
} | ||
} | ||
void qdev_prop_set_chr(DeviceState *dev, const char *name, | ||
CharDriverState *value) | ||
{ | ||
Error *errp = NULL; | ||
assert(!value || value->label); | ||
object_property_set_str(OBJECT(dev), | ||
value ? value->label : "", name, &errp); | ||
assert_no_error(errp); | ||
} | ||
|
||
void qdev_prop_set_netdev(DeviceState *dev, const char *name, | ||
NetClientState *value) | ||
{ | ||
Error *errp = NULL; | ||
assert(!value || value->name); | ||
object_property_set_str(OBJECT(dev), | ||
value ? value->name : "", name, &errp); | ||
assert_no_error(errp); | ||
} | ||
|
||
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", NULL)) { | ||
qdev_prop_set_uint32(dev, "vectors", nd->nvectors); | ||
} | ||
nd->instantiated = 1; | ||
} | ||
|
||
static int qdev_add_one_global(QemuOpts *opts, void *opaque) | ||
{ | ||
GlobalProperty *g; | ||
|
||
g = g_malloc0(sizeof(*g)); | ||
g->driver = qemu_opt_get(opts, "driver"); | ||
g->property = qemu_opt_get(opts, "property"); | ||
g->value = qemu_opt_get(opts, "value"); | ||
qdev_prop_register_global(g); | ||
return 0; | ||
} | ||
|
||
void qemu_add_globals(void) | ||
{ | ||
qemu_opts_foreach(qemu_find_opts("global"), qdev_add_one_global, NULL, 0); | ||
} |
Oops, something went wrong.