From 8ffb1bcf56a4b62d80c8dbefa715cd16394255e0 Mon Sep 17 00:00:00 2001 From: Gerd Hoffmann Date: Wed, 15 Jul 2009 13:59:25 +0200 Subject: [PATCH] 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 Signed-off-by: Anthony Liguori --- hw/qdev.c | 251 ++++++++++++++++++++++++++++++++++++++++++++++++++++++ hw/qdev.h | 1 + 2 files changed, 252 insertions(+) diff --git a/hw/qdev.c b/hw/qdev.c index 3be321ad57..97a9665424 100644 --- a/hw/qdev.c +++ b/hw/qdev.c @@ -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; diff --git a/hw/qdev.h b/hw/qdev.h index d9c78eba60..af2ee0f8b9 100644 --- a/hw/qdev.h +++ b/hw/qdev.h @@ -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); -- GitLab