Commit 8ffb1bcf56a4b62d80c8dbefa715cd16394255e0

Authored by Gerd Hoffmann
Committed by Anthony Liguori
1 parent d271de9f

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>
Showing 2 changed files with 252 additions and 0 deletions
hw/qdev.c
... ... @@ -35,6 +35,10 @@ static BusState *main_system_bus;
35 35  
36 36 static DeviceInfo *device_info_list;
37 37  
  38 +static BusState *qbus_find_recursive(BusState *bus, const char *name,
  39 + const BusInfo *info);
  40 +static BusState *qbus_find(const char *path);
  41 +
38 42 /* Register a new device type. */
39 43 void qdev_register(DeviceInfo *info)
40 44 {
... ... @@ -101,6 +105,80 @@ DeviceState *qdev_create(BusState *bus, const char *name)
101 105 return dev;
102 106 }
103 107  
  108 +DeviceState *qdev_device_add(const char *cmdline)
  109 +{
  110 + DeviceInfo *info;
  111 + DeviceState *qdev;
  112 + BusState *bus;
  113 + char driver[32], path[128] = "";
  114 + char tag[32], value[256];
  115 + const char *params = NULL;
  116 + int n = 0;
  117 +
  118 + if (1 != sscanf(cmdline, "%32[^,],%n", driver, &n)) {
  119 + fprintf(stderr, "device parse error: \"%s\"\n", cmdline);
  120 + return NULL;
  121 + }
  122 + if (strcmp(driver, "?") == 0) {
  123 + for (info = device_info_list; info != NULL; info = info->next) {
  124 + fprintf(stderr, "name \"%s\", bus %s\n", info->name, info->bus_info->name);
  125 + }
  126 + return NULL;
  127 + }
  128 + if (n) {
  129 + params = cmdline + n;
  130 + get_param_value(path, sizeof(path), "bus", params);
  131 + }
  132 + info = qdev_find_info(NULL, driver);
  133 + if (!info) {
  134 + fprintf(stderr, "Device \"%s\" not found. Try -device '?' for a list.\n",
  135 + driver);
  136 + return NULL;
  137 + }
  138 + if (info->no_user) {
  139 + fprintf(stderr, "device \"%s\" can't be added via command line\n",
  140 + info->name);
  141 + return NULL;
  142 + }
  143 +
  144 + if (strlen(path)) {
  145 + bus = qbus_find(path);
  146 + if (!bus)
  147 + return NULL;
  148 + qdev = qdev_create(bus, driver);
  149 + } else {
  150 + bus = qbus_find_recursive(main_system_bus, NULL, info->bus_info);
  151 + if (!bus)
  152 + return NULL;
  153 + qdev = qdev_create(bus, driver);
  154 + }
  155 +
  156 + if (params) {
  157 + while (params[0]) {
  158 + if (2 != sscanf(params, "%31[^=]=%255[^,]%n", tag, value, &n)) {
  159 + fprintf(stderr, "parse error at \"%s\"\n", params);
  160 + break;
  161 + }
  162 + params += n;
  163 + if (params[0] == ',')
  164 + params++;
  165 + if (strcmp(tag, "bus") == 0)
  166 + continue;
  167 + if (strcmp(tag, "id") == 0) {
  168 + qdev->id = qemu_strdup(value);
  169 + continue;
  170 + }
  171 + if (-1 == qdev_prop_parse(qdev, tag, value)) {
  172 + fprintf(stderr, "can't set property \"%s\" to \"%s\" for \"%s\"\n",
  173 + tag, value, driver);
  174 + }
  175 + }
  176 + }
  177 +
  178 + qdev_init(qdev);
  179 + return qdev;
  180 +}
  181 +
104 182 /* Initialize a device. Device properties should be set before calling
105 183 this function. IRQs and MMIO regions should be connected/mapped after
106 184 calling this function. */
... ... @@ -229,6 +307,179 @@ void scsi_bus_new(DeviceState *host, SCSIAttachFn attach)
229 307 }
230 308 }
231 309  
  310 +static BusState *qbus_find_recursive(BusState *bus, const char *name,
  311 + const BusInfo *info)
  312 +{
  313 + DeviceState *dev;
  314 + BusState *child, *ret;
  315 + int match = 1;
  316 +
  317 + if (name && (strcmp(bus->name, name) != 0)) {
  318 + match = 0;
  319 + }
  320 + if (info && (bus->info != info)) {
  321 + match = 0;
  322 + }
  323 + if (match) {
  324 + return bus;
  325 + }
  326 +
  327 + LIST_FOREACH(dev, &bus->children, sibling) {
  328 + LIST_FOREACH(child, &dev->child_bus, sibling) {
  329 + ret = qbus_find_recursive(child, name, info);
  330 + if (ret) {
  331 + return ret;
  332 + }
  333 + }
  334 + }
  335 + return NULL;
  336 +}
  337 +
  338 +static void qbus_list_bus(DeviceState *dev, char *dest, int len)
  339 +{
  340 + BusState *child;
  341 + const char *sep = " ";
  342 + int pos = 0;
  343 +
  344 + pos += snprintf(dest+pos, len-pos,"child busses at \"%s\":",
  345 + dev->id ? dev->id : dev->info->name);
  346 + LIST_FOREACH(child, &dev->child_bus, sibling) {
  347 + pos += snprintf(dest+pos, len-pos, "%s\"%s\"", sep, child->name);
  348 + sep = ", ";
  349 + }
  350 +}
  351 +
  352 +static void qbus_list_dev(BusState *bus, char *dest, int len)
  353 +{
  354 + DeviceState *dev;
  355 + const char *sep = " ";
  356 + int pos = 0;
  357 +
  358 + pos += snprintf(dest+pos, len-pos, "devices at \"%s\":",
  359 + bus->name);
  360 + LIST_FOREACH(dev, &bus->children, sibling) {
  361 + pos += snprintf(dest+pos, len-pos, "%s\"%s\"",
  362 + sep, dev->info->name);
  363 + if (dev->id)
  364 + pos += snprintf(dest+pos, len-pos, "/\"%s\"", dev->id);
  365 + sep = ", ";
  366 + }
  367 +}
  368 +
  369 +static BusState *qbus_find_bus(DeviceState *dev, char *elem)
  370 +{
  371 + BusState *child;
  372 +
  373 + LIST_FOREACH(child, &dev->child_bus, sibling) {
  374 + if (strcmp(child->name, elem) == 0) {
  375 + return child;
  376 + }
  377 + }
  378 + return NULL;
  379 +}
  380 +
  381 +static DeviceState *qbus_find_dev(BusState *bus, char *elem)
  382 +{
  383 + DeviceState *dev;
  384 +
  385 + /*
  386 + * try to match in order:
  387 + * (1) instance id, if present
  388 + * (2) driver name
  389 + * (3) driver alias, if present
  390 + */
  391 + LIST_FOREACH(dev, &bus->children, sibling) {
  392 + if (dev->id && strcmp(dev->id, elem) == 0) {
  393 + return dev;
  394 + }
  395 + }
  396 + LIST_FOREACH(dev, &bus->children, sibling) {
  397 + if (strcmp(dev->info->name, elem) == 0) {
  398 + return dev;
  399 + }
  400 + }
  401 + LIST_FOREACH(dev, &bus->children, sibling) {
  402 + if (dev->info->alias && strcmp(dev->info->alias, elem) == 0) {
  403 + return dev;
  404 + }
  405 + }
  406 + return NULL;
  407 +}
  408 +
  409 +static BusState *qbus_find(const char *path)
  410 +{
  411 + DeviceState *dev;
  412 + BusState *bus;
  413 + char elem[128], msg[256];
  414 + int pos, len;
  415 +
  416 + /* find start element */
  417 + if (path[0] == '/') {
  418 + bus = main_system_bus;
  419 + pos = 0;
  420 + } else {
  421 + if (sscanf(path, "%127[^/]%n", elem, &len) != 1) {
  422 + fprintf(stderr, "path parse error (\"%s\")\n", path);
  423 + return NULL;
  424 + }
  425 + bus = qbus_find_recursive(main_system_bus, elem, NULL);
  426 + if (!bus) {
  427 + fprintf(stderr, "bus \"%s\" not found\n", elem);
  428 + return NULL;
  429 + }
  430 + pos = len;
  431 + }
  432 +
  433 + for (;;) {
  434 + if (path[pos] == '\0') {
  435 + /* we are done */
  436 + return bus;
  437 + }
  438 +
  439 + /* find device */
  440 + if (sscanf(path+pos, "/%127[^/]%n", elem, &len) != 1) {
  441 + fprintf(stderr, "path parse error (\"%s\" pos %d)\n", path, pos);
  442 + return NULL;
  443 + }
  444 + pos += len;
  445 + dev = qbus_find_dev(bus, elem);
  446 + if (!dev) {
  447 + qbus_list_dev(bus, msg, sizeof(msg));
  448 + fprintf(stderr, "device \"%s\" not found\n%s\n", elem, msg);
  449 + return NULL;
  450 + }
  451 + if (path[pos] == '\0') {
  452 + /* last specified element is a device. If it has exactly
  453 + * one child bus accept it nevertheless */
  454 + switch (dev->num_child_bus) {
  455 + case 0:
  456 + fprintf(stderr, "device has no child bus (%s)\n", path);
  457 + return NULL;
  458 + case 1:
  459 + return LIST_FIRST(&dev->child_bus);
  460 + default:
  461 + qbus_list_bus(dev, msg, sizeof(msg));
  462 + fprintf(stderr, "device has multiple child busses (%s)\n%s\n",
  463 + path, msg);
  464 + return NULL;
  465 + }
  466 + }
  467 +
  468 + /* find bus */
  469 + if (sscanf(path+pos, "/%127[^/]%n", elem, &len) != 1) {
  470 + fprintf(stderr, "path parse error (\"%s\" pos %d)\n", path, pos);
  471 + return NULL;
  472 + }
  473 + pos += len;
  474 + bus = qbus_find_bus(dev, elem);
  475 + if (!bus) {
  476 + qbus_list_bus(dev, msg, sizeof(msg));
  477 + fprintf(stderr, "child bus \"%s\" not found\n%s\n", elem, msg);
  478 + return NULL;
  479 + }
  480 + }
  481 +}
  482 +
232 483 BusState *qbus_create(BusInfo *info, DeviceState *parent, const char *name)
233 484 {
234 485 BusState *bus;
... ...
hw/qdev.h
... ... @@ -82,6 +82,7 @@ struct CompatProperty {
82 82 /*** Board API. This should go away once we have a machine config file. ***/
83 83  
84 84 DeviceState *qdev_create(BusState *bus, const char *name);
  85 +DeviceState *qdev_device_add(const char *cmdline);
85 86 void qdev_init(DeviceState *dev);
86 87 void qdev_free(DeviceState *dev);
87 88  
... ...