Commit 8ffb1bcf56a4b62d80c8dbefa715cd16394255e0
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 | |
... | ... |