sim-card
/
qemu
Archived
10
0
Fork 0

qdev: bus walker + qdev_device_add()

This patch implements a parser and qdev tree walker for bus paths and
adds qdev_device_add on top of this.

A bus path can be:
  (1) full path, i.e. /i440FX-pcihost/pci.0/lsi/scsi.0
  (2) bus name, i.e. "scsi.0".  Best used together with id= to make
      sure this is unique.
  (3) relative path starting with a bus name, i.e. "pci.0/lsi/scsi.0"

For the (common) case of a single child bus being attached to a device
it is enougth to specify the device only, i.e. "pci.0/lsi" will be
accepted too.

qdev_device_add() adds devices and accepts bus= parameters to find the
bus the device should be attached to.  Without bus= being specified it
takes the first bus it finds where the device can be attached to (i.e.
first pci bus for pci devices, ...).

Signed-off-by: Gerd Hoffmann <kraxel@redhat.com>
Signed-off-by: Anthony Liguori <aliguori@us.ibm.com>
This commit is contained in:
Gerd Hoffmann 2009-07-15 13:59:25 +02:00 committed by Anthony Liguori
parent d271de9f1b
commit 8ffb1bcf56
2 changed files with 252 additions and 0 deletions

251
hw/qdev.c
View File

@ -35,6 +35,10 @@ static BusState *main_system_bus;
static DeviceInfo *device_info_list;
static BusState *qbus_find_recursive(BusState *bus, const char *name,
const BusInfo *info);
static BusState *qbus_find(const char *path);
/* Register a new device type. */
void qdev_register(DeviceInfo *info)
{
@ -101,6 +105,80 @@ DeviceState *qdev_create(BusState *bus, const char *name)
return dev;
}
DeviceState *qdev_device_add(const char *cmdline)
{
DeviceInfo *info;
DeviceState *qdev;
BusState *bus;
char driver[32], path[128] = "";
char tag[32], value[256];
const char *params = NULL;
int n = 0;
if (1 != sscanf(cmdline, "%32[^,],%n", driver, &n)) {
fprintf(stderr, "device parse error: \"%s\"\n", cmdline);
return NULL;
}
if (strcmp(driver, "?") == 0) {
for (info = device_info_list; info != NULL; info = info->next) {
fprintf(stderr, "name \"%s\", bus %s\n", info->name, info->bus_info->name);
}
return NULL;
}
if (n) {
params = cmdline + n;
get_param_value(path, sizeof(path), "bus", params);
}
info = qdev_find_info(NULL, driver);
if (!info) {
fprintf(stderr, "Device \"%s\" not found. Try -device '?' for a list.\n",
driver);
return NULL;
}
if (info->no_user) {
fprintf(stderr, "device \"%s\" can't be added via command line\n",
info->name);
return NULL;
}
if (strlen(path)) {
bus = qbus_find(path);
if (!bus)
return NULL;
qdev = qdev_create(bus, driver);
} else {
bus = qbus_find_recursive(main_system_bus, NULL, info->bus_info);
if (!bus)
return NULL;
qdev = qdev_create(bus, driver);
}
if (params) {
while (params[0]) {
if (2 != sscanf(params, "%31[^=]=%255[^,]%n", tag, value, &n)) {
fprintf(stderr, "parse error at \"%s\"\n", params);
break;
}
params += n;
if (params[0] == ',')
params++;
if (strcmp(tag, "bus") == 0)
continue;
if (strcmp(tag, "id") == 0) {
qdev->id = qemu_strdup(value);
continue;
}
if (-1 == qdev_prop_parse(qdev, tag, value)) {
fprintf(stderr, "can't set property \"%s\" to \"%s\" for \"%s\"\n",
tag, value, driver);
}
}
}
qdev_init(qdev);
return qdev;
}
/* Initialize a device. Device properties should be set before calling
this function. IRQs and MMIO regions should be connected/mapped after
calling this function. */
@ -229,6 +307,179 @@ void scsi_bus_new(DeviceState *host, SCSIAttachFn attach)
}
}
static BusState *qbus_find_recursive(BusState *bus, const char *name,
const BusInfo *info)
{
DeviceState *dev;
BusState *child, *ret;
int match = 1;
if (name && (strcmp(bus->name, name) != 0)) {
match = 0;
}
if (info && (bus->info != info)) {
match = 0;
}
if (match) {
return bus;
}
LIST_FOREACH(dev, &bus->children, sibling) {
LIST_FOREACH(child, &dev->child_bus, sibling) {
ret = qbus_find_recursive(child, name, info);
if (ret) {
return ret;
}
}
}
return NULL;
}
static void qbus_list_bus(DeviceState *dev, char *dest, int len)
{
BusState *child;
const char *sep = " ";
int pos = 0;
pos += snprintf(dest+pos, len-pos,"child busses at \"%s\":",
dev->id ? dev->id : dev->info->name);
LIST_FOREACH(child, &dev->child_bus, sibling) {
pos += snprintf(dest+pos, len-pos, "%s\"%s\"", sep, child->name);
sep = ", ";
}
}
static void qbus_list_dev(BusState *bus, char *dest, int len)
{
DeviceState *dev;
const char *sep = " ";
int pos = 0;
pos += snprintf(dest+pos, len-pos, "devices at \"%s\":",
bus->name);
LIST_FOREACH(dev, &bus->children, sibling) {
pos += snprintf(dest+pos, len-pos, "%s\"%s\"",
sep, dev->info->name);
if (dev->id)
pos += snprintf(dest+pos, len-pos, "/\"%s\"", dev->id);
sep = ", ";
}
}
static BusState *qbus_find_bus(DeviceState *dev, char *elem)
{
BusState *child;
LIST_FOREACH(child, &dev->child_bus, sibling) {
if (strcmp(child->name, elem) == 0) {
return child;
}
}
return NULL;
}
static DeviceState *qbus_find_dev(BusState *bus, char *elem)
{
DeviceState *dev;
/*
* try to match in order:
* (1) instance id, if present
* (2) driver name
* (3) driver alias, if present
*/
LIST_FOREACH(dev, &bus->children, sibling) {
if (dev->id && strcmp(dev->id, elem) == 0) {
return dev;
}
}
LIST_FOREACH(dev, &bus->children, sibling) {
if (strcmp(dev->info->name, elem) == 0) {
return dev;
}
}
LIST_FOREACH(dev, &bus->children, sibling) {
if (dev->info->alias && strcmp(dev->info->alias, elem) == 0) {
return dev;
}
}
return NULL;
}
static BusState *qbus_find(const char *path)
{
DeviceState *dev;
BusState *bus;
char elem[128], msg[256];
int pos, len;
/* find start element */
if (path[0] == '/') {
bus = main_system_bus;
pos = 0;
} else {
if (sscanf(path, "%127[^/]%n", elem, &len) != 1) {
fprintf(stderr, "path parse error (\"%s\")\n", path);
return NULL;
}
bus = qbus_find_recursive(main_system_bus, elem, NULL);
if (!bus) {
fprintf(stderr, "bus \"%s\" not found\n", elem);
return NULL;
}
pos = len;
}
for (;;) {
if (path[pos] == '\0') {
/* we are done */
return bus;
}
/* find device */
if (sscanf(path+pos, "/%127[^/]%n", elem, &len) != 1) {
fprintf(stderr, "path parse error (\"%s\" pos %d)\n", path, pos);
return NULL;
}
pos += len;
dev = qbus_find_dev(bus, elem);
if (!dev) {
qbus_list_dev(bus, msg, sizeof(msg));
fprintf(stderr, "device \"%s\" not found\n%s\n", elem, msg);
return NULL;
}
if (path[pos] == '\0') {
/* last specified element is a device. If it has exactly
* one child bus accept it nevertheless */
switch (dev->num_child_bus) {
case 0:
fprintf(stderr, "device has no child bus (%s)\n", path);
return NULL;
case 1:
return LIST_FIRST(&dev->child_bus);
default:
qbus_list_bus(dev, msg, sizeof(msg));
fprintf(stderr, "device has multiple child busses (%s)\n%s\n",
path, msg);
return NULL;
}
}
/* find bus */
if (sscanf(path+pos, "/%127[^/]%n", elem, &len) != 1) {
fprintf(stderr, "path parse error (\"%s\" pos %d)\n", path, pos);
return NULL;
}
pos += len;
bus = qbus_find_bus(dev, elem);
if (!bus) {
qbus_list_bus(dev, msg, sizeof(msg));
fprintf(stderr, "child bus \"%s\" not found\n%s\n", elem, msg);
return NULL;
}
}
}
BusState *qbus_create(BusInfo *info, DeviceState *parent, const char *name)
{
BusState *bus;

View File

@ -82,6 +82,7 @@ struct CompatProperty {
/*** Board API. This should go away once we have a machine config file. ***/
DeviceState *qdev_create(BusState *bus, const char *name);
DeviceState *qdev_device_add(const char *cmdline);
void qdev_init(DeviceState *dev);
void qdev_free(DeviceState *dev);