]> Git Repo - qemu.git/blob - hw/qdev.c
qdev: Add rudimentary help for property value
[qemu.git] / hw / qdev.c
1 /*
2  *  Dynamic device configuration and creation.
3  *
4  *  Copyright (c) 2009 CodeSourcery
5  *
6  * This library is free software; you can redistribute it and/or
7  * modify it under the terms of the GNU Lesser General Public
8  * License as published by the Free Software Foundation; either
9  * version 2 of the License, or (at your option) any later version.
10  *
11  * This library is distributed in the hope that it will be useful,
12  * but WITHOUT ANY WARRANTY; without even the implied warranty of
13  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
14  * Lesser General Public License for more details.
15  *
16  * You should have received a copy of the GNU Lesser General Public
17  * License along with this library; if not, see <http://www.gnu.org/licenses/>.
18  */
19
20 /* The theory here is that it should be possible to create a machine without
21    knowledge of specific devices.  Historically board init routines have
22    passed a bunch of arguments to each device, requiring the board know
23    exactly which device it is dealing with.  This file provides an abstract
24    API for device configuration and initialization.  Devices will generally
25    inherit from a particular bus (e.g. PCI or I2C) rather than
26    this API directly.  */
27
28 #include "net.h"
29 #include "qdev.h"
30 #include "sysemu.h"
31 #include "monitor.h"
32 #include "qerror.h"
33
34 static int qdev_hotplug = 0;
35
36 /* This is a nasty hack to allow passing a NULL bus to qdev_create.  */
37 static BusState *main_system_bus;
38
39 DeviceInfo *device_info_list;
40
41 static BusState *qbus_find_recursive(BusState *bus, const char *name,
42                                      const BusInfo *info);
43 static BusState *qbus_find(const char *path);
44
45 /* Register a new device type.  */
46 void qdev_register(DeviceInfo *info)
47 {
48     assert(info->size >= sizeof(DeviceState));
49     assert(!info->next);
50
51     info->next = device_info_list;
52     device_info_list = info;
53 }
54
55 static DeviceInfo *qdev_find_info(BusInfo *bus_info, const char *name)
56 {
57     DeviceInfo *info;
58
59     /* first check device names */
60     for (info = device_info_list; info != NULL; info = info->next) {
61         if (bus_info && info->bus_info != bus_info)
62             continue;
63         if (strcmp(info->name, name) != 0)
64             continue;
65         return info;
66     }
67
68     /* failing that check the aliases */
69     for (info = device_info_list; info != NULL; info = info->next) {
70         if (bus_info && info->bus_info != bus_info)
71             continue;
72         if (!info->alias)
73             continue;
74         if (strcmp(info->alias, name) != 0)
75             continue;
76         return info;
77     }
78     return NULL;
79 }
80
81 /* Create a new device.  This only initializes the device state structure
82    and allows properties to be set.  qdev_init should be called to
83    initialize the actual device emulation.  */
84 DeviceState *qdev_create(BusState *bus, const char *name)
85 {
86     DeviceInfo *info;
87     DeviceState *dev;
88
89     if (!bus) {
90         if (!main_system_bus) {
91             main_system_bus = qbus_create(&system_bus_info, NULL, "main-system-bus");
92         }
93         bus = main_system_bus;
94     }
95
96     info = qdev_find_info(bus->info, name);
97     if (!info) {
98         hw_error("Unknown device '%s' for bus '%s'\n", name, bus->info->name);
99     }
100
101     dev = qemu_mallocz(info->size);
102     dev->info = info;
103     dev->parent_bus = bus;
104     qdev_prop_set_defaults(dev, dev->info->props);
105     qdev_prop_set_defaults(dev, dev->parent_bus->info->props);
106     qdev_prop_set_globals(dev);
107     QLIST_INSERT_HEAD(&bus->children, dev, sibling);
108     if (qdev_hotplug) {
109         assert(bus->allow_hotplug);
110         dev->hotplugged = 1;
111     }
112     dev->state = DEV_STATE_CREATED;
113     return dev;
114 }
115
116 static int qdev_print_devinfo(DeviceInfo *info, char *dest, int len)
117 {
118     int pos = 0;
119     int ret;
120
121     ret = snprintf(dest+pos, len-pos, "name \"%s\", bus %s",
122                    info->name, info->bus_info->name);
123     pos += MIN(len-pos,ret);
124     if (info->alias) {
125         ret = snprintf(dest+pos, len-pos, ", alias \"%s\"", info->alias);
126         pos += MIN(len-pos,ret);
127     }
128     if (info->desc) {
129         ret = snprintf(dest+pos, len-pos, ", desc \"%s\"", info->desc);
130         pos += MIN(len-pos,ret);
131     }
132     if (info->no_user) {
133         ret = snprintf(dest+pos, len-pos, ", no-user");
134         pos += MIN(len-pos,ret);
135     }
136     return pos;
137 }
138
139 static int set_property(const char *name, const char *value, void *opaque)
140 {
141     DeviceState *dev = opaque;
142
143     if (strcmp(name, "driver") == 0)
144         return 0;
145     if (strcmp(name, "bus") == 0)
146         return 0;
147
148     if (qdev_prop_parse(dev, name, value) == -1) {
149         qemu_error("can't set property \"%s\" to \"%s\" for \"%s\"\n",
150                    name, value, dev->info->name);
151         return -1;
152     }
153     return 0;
154 }
155
156 int qdev_device_help(QemuOpts *opts)
157 {
158     const char *driver;
159     DeviceInfo *info;
160     char msg[256];
161     Property *prop;
162
163     driver = qemu_opt_get(opts, "driver");
164     if (driver && !strcmp(driver, "?")) {
165         for (info = device_info_list; info != NULL; info = info->next) {
166             qdev_print_devinfo(info, msg, sizeof(msg));
167             qemu_error("%s\n", msg);
168         }
169         return 1;
170     }
171
172     if (!qemu_opt_get(opts, "?")) {
173         return 0;
174     }
175
176     info = qdev_find_info(NULL, driver);
177     if (!info) {
178         return 0;
179     }
180
181     for (prop = info->props; prop && prop->name; prop++) {
182         qemu_error("%s.%s=%s\n", info->name, prop->name, prop->info->name);
183     }
184     return 1;
185 }
186
187 DeviceState *qdev_device_add(QemuOpts *opts)
188 {
189     const char *driver, *path, *id;
190     DeviceInfo *info;
191     DeviceState *qdev;
192     BusState *bus;
193
194     driver = qemu_opt_get(opts, "driver");
195     if (!driver) {
196         qemu_error("-device: no driver specified\n");
197         return NULL;
198     }
199
200     /* find driver */
201     info = qdev_find_info(NULL, driver);
202     if (!info) {
203         qemu_error_new(QERR_DEVICE_NOT_FOUND, driver);
204         return NULL;
205     }
206     if (info->no_user) {
207         qemu_error("device \"%s\" can't be added via command line\n",
208                    info->name);
209         return NULL;
210     }
211
212     /* find bus */
213     path = qemu_opt_get(opts, "bus");
214     if (path != NULL) {
215         bus = qbus_find(path);
216     } else {
217         bus = qbus_find_recursive(main_system_bus, NULL, info->bus_info);
218     }
219     if (!bus) {
220         qemu_error("Did not find %s bus for %s\n",
221                    path ? path : info->bus_info->name, info->name);
222         return NULL;
223     }
224     if (qdev_hotplug && !bus->allow_hotplug) {
225         qemu_error("Bus %s does not support hotplugging\n",
226                    bus->name);
227         return NULL;
228     }
229
230     /* create device, set properties */
231     qdev = qdev_create(bus, driver);
232     id = qemu_opts_id(opts);
233     if (id) {
234         qdev->id = id;
235     }
236     if (qemu_opt_foreach(opts, set_property, qdev, 1) != 0) {
237         qdev_free(qdev);
238         return NULL;
239     }
240     if (qdev_init(qdev) < 0) {
241         qemu_error("Error initializing device %s\n", driver);
242         return NULL;
243     }
244     qdev->opts = opts;
245     return qdev;
246 }
247
248 static void qdev_reset(void *opaque)
249 {
250     DeviceState *dev = opaque;
251     if (dev->info->reset)
252         dev->info->reset(dev);
253 }
254
255 /* Initialize a device.  Device properties should be set before calling
256    this function.  IRQs and MMIO regions should be connected/mapped after
257    calling this function.
258    On failure, destroy the device and return negative value.
259    Return 0 on success.  */
260 int qdev_init(DeviceState *dev)
261 {
262     int rc;
263
264     assert(dev->state == DEV_STATE_CREATED);
265     rc = dev->info->init(dev, dev->info);
266     if (rc < 0) {
267         qdev_free(dev);
268         return rc;
269     }
270     qemu_register_reset(qdev_reset, dev);
271     if (dev->info->vmsd)
272         vmstate_register(-1, dev->info->vmsd, dev);
273     dev->state = DEV_STATE_INITIALIZED;
274     return 0;
275 }
276
277 int qdev_unplug(DeviceState *dev)
278 {
279     if (!dev->parent_bus->allow_hotplug) {
280         qemu_error("Bus %s does not support hotplugging\n",
281                    dev->parent_bus->name);
282         return -1;
283     }
284     assert(dev->info->unplug != NULL);
285
286     return dev->info->unplug(dev);
287 }
288
289 /* can be used as ->unplug() callback for the simple cases */
290 int qdev_simple_unplug_cb(DeviceState *dev)
291 {
292     /* just zap it */
293     qdev_free(dev);
294     return 0;
295 }
296
297 /* Like qdev_init(), but terminate program via hw_error() instead of
298    returning an error value.  This is okay during machine creation.
299    Don't use for hotplug, because there callers need to recover from
300    failure.  Exception: if you know the device's init() callback can't
301    fail, then qdev_init_nofail() can't fail either, and is therefore
302    usable even then.  But relying on the device implementation that
303    way is somewhat unclean, and best avoided.  */
304 void qdev_init_nofail(DeviceState *dev)
305 {
306     DeviceInfo *info = dev->info;
307
308     if (qdev_init(dev) < 0)
309         hw_error("Initialization of device %s failed\n", info->name);
310 }
311
312 /* Unlink device from bus and free the structure.  */
313 void qdev_free(DeviceState *dev)
314 {
315     BusState *bus;
316
317     if (dev->state == DEV_STATE_INITIALIZED) {
318         while (dev->num_child_bus) {
319             bus = QLIST_FIRST(&dev->child_bus);
320             qbus_free(bus);
321         }
322         if (dev->info->vmsd)
323             vmstate_unregister(dev->info->vmsd, dev);
324         if (dev->info->exit)
325             dev->info->exit(dev);
326         if (dev->opts)
327             qemu_opts_del(dev->opts);
328     }
329     qemu_unregister_reset(qdev_reset, dev);
330     QLIST_REMOVE(dev, sibling);
331     qemu_free(dev);
332 }
333
334 void qdev_machine_creation_done(void)
335 {
336     /*
337      * ok, initial machine setup is done, starting from now we can
338      * only create hotpluggable devices
339      */
340     qdev_hotplug = 1;
341 }
342
343 /* Get a character (serial) device interface.  */
344 CharDriverState *qdev_init_chardev(DeviceState *dev)
345 {
346     static int next_serial;
347
348     /* FIXME: This function needs to go away: use chardev properties!  */
349     return serial_hds[next_serial++];
350 }
351
352 BusState *qdev_get_parent_bus(DeviceState *dev)
353 {
354     return dev->parent_bus;
355 }
356
357 void qdev_init_gpio_in(DeviceState *dev, qemu_irq_handler handler, int n)
358 {
359     assert(dev->num_gpio_in == 0);
360     dev->num_gpio_in = n;
361     dev->gpio_in = qemu_allocate_irqs(handler, dev, n);
362 }
363
364 void qdev_init_gpio_out(DeviceState *dev, qemu_irq *pins, int n)
365 {
366     assert(dev->num_gpio_out == 0);
367     dev->num_gpio_out = n;
368     dev->gpio_out = pins;
369 }
370
371 qemu_irq qdev_get_gpio_in(DeviceState *dev, int n)
372 {
373     assert(n >= 0 && n < dev->num_gpio_in);
374     return dev->gpio_in[n];
375 }
376
377 void qdev_connect_gpio_out(DeviceState * dev, int n, qemu_irq pin)
378 {
379     assert(n >= 0 && n < dev->num_gpio_out);
380     dev->gpio_out[n] = pin;
381 }
382
383 void qdev_set_nic_properties(DeviceState *dev, NICInfo *nd)
384 {
385     qdev_prop_set_macaddr(dev, "mac", nd->macaddr);
386     if (nd->vlan)
387         qdev_prop_set_vlan(dev, "vlan", nd->vlan);
388     if (nd->netdev)
389         qdev_prop_set_netdev(dev, "netdev", nd->netdev);
390     if (nd->nvectors != NIC_NVECTORS_UNSPECIFIED &&
391         qdev_prop_exists(dev, "vectors")) {
392         qdev_prop_set_uint32(dev, "vectors", nd->nvectors);
393     }
394 }
395
396 static int next_block_unit[IF_COUNT];
397
398 /* Get a block device.  This should only be used for single-drive devices
399    (e.g. SD/Floppy/MTD).  Multi-disk devices (scsi/ide) should use the
400    appropriate bus.  */
401 BlockDriverState *qdev_init_bdrv(DeviceState *dev, BlockInterfaceType type)
402 {
403     int unit = next_block_unit[type]++;
404     DriveInfo *dinfo;
405
406     dinfo = drive_get(type, 0, unit);
407     return dinfo ? dinfo->bdrv : NULL;
408 }
409
410 BusState *qdev_get_child_bus(DeviceState *dev, const char *name)
411 {
412     BusState *bus;
413
414     QLIST_FOREACH(bus, &dev->child_bus, sibling) {
415         if (strcmp(name, bus->name) == 0) {
416             return bus;
417         }
418     }
419     return NULL;
420 }
421
422 static BusState *qbus_find_recursive(BusState *bus, const char *name,
423                                      const BusInfo *info)
424 {
425     DeviceState *dev;
426     BusState *child, *ret;
427     int match = 1;
428
429     if (name && (strcmp(bus->name, name) != 0)) {
430         match = 0;
431     }
432     if (info && (bus->info != info)) {
433         match = 0;
434     }
435     if (match) {
436         return bus;
437     }
438
439     QLIST_FOREACH(dev, &bus->children, sibling) {
440         QLIST_FOREACH(child, &dev->child_bus, sibling) {
441             ret = qbus_find_recursive(child, name, info);
442             if (ret) {
443                 return ret;
444             }
445         }
446     }
447     return NULL;
448 }
449
450 static DeviceState *qdev_find_recursive(BusState *bus, const char *id)
451 {
452     DeviceState *dev, *ret;
453     BusState *child;
454
455     QLIST_FOREACH(dev, &bus->children, sibling) {
456         if (dev->id && strcmp(dev->id, id) == 0)
457             return dev;
458         QLIST_FOREACH(child, &dev->child_bus, sibling) {
459             ret = qdev_find_recursive(child, id);
460             if (ret) {
461                 return ret;
462             }
463         }
464     }
465     return NULL;
466 }
467
468 static void qbus_list_bus(DeviceState *dev, char *dest, int len)
469 {
470     BusState *child;
471     const char *sep = " ";
472     int pos = 0;
473
474     pos += snprintf(dest+pos, len-pos,"child busses at \"%s\":",
475                     dev->id ? dev->id : dev->info->name);
476     QLIST_FOREACH(child, &dev->child_bus, sibling) {
477         pos += snprintf(dest+pos, len-pos, "%s\"%s\"", sep, child->name);
478         sep = ", ";
479     }
480 }
481
482 static void qbus_list_dev(BusState *bus, char *dest, int len)
483 {
484     DeviceState *dev;
485     const char *sep = " ";
486     int pos = 0;
487
488     pos += snprintf(dest+pos, len-pos, "devices at \"%s\":",
489                     bus->name);
490     QLIST_FOREACH(dev, &bus->children, sibling) {
491         pos += snprintf(dest+pos, len-pos, "%s\"%s\"",
492                         sep, dev->info->name);
493         if (dev->id)
494             pos += snprintf(dest+pos, len-pos, "/\"%s\"", dev->id);
495         sep = ", ";
496     }
497 }
498
499 static BusState *qbus_find_bus(DeviceState *dev, char *elem)
500 {
501     BusState *child;
502
503     QLIST_FOREACH(child, &dev->child_bus, sibling) {
504         if (strcmp(child->name, elem) == 0) {
505             return child;
506         }
507     }
508     return NULL;
509 }
510
511 static DeviceState *qbus_find_dev(BusState *bus, char *elem)
512 {
513     DeviceState *dev;
514
515     /*
516      * try to match in order:
517      *   (1) instance id, if present
518      *   (2) driver name
519      *   (3) driver alias, if present
520      */
521     QLIST_FOREACH(dev, &bus->children, sibling) {
522         if (dev->id  &&  strcmp(dev->id, elem) == 0) {
523             return dev;
524         }
525     }
526     QLIST_FOREACH(dev, &bus->children, sibling) {
527         if (strcmp(dev->info->name, elem) == 0) {
528             return dev;
529         }
530     }
531     QLIST_FOREACH(dev, &bus->children, sibling) {
532         if (dev->info->alias && strcmp(dev->info->alias, elem) == 0) {
533             return dev;
534         }
535     }
536     return NULL;
537 }
538
539 static BusState *qbus_find(const char *path)
540 {
541     DeviceState *dev;
542     BusState *bus;
543     char elem[128], msg[256];
544     int pos, len;
545
546     /* find start element */
547     if (path[0] == '/') {
548         bus = main_system_bus;
549         pos = 0;
550     } else {
551         if (sscanf(path, "%127[^/]%n", elem, &len) != 1) {
552             qemu_error("path parse error (\"%s\")\n", path);
553             return NULL;
554         }
555         bus = qbus_find_recursive(main_system_bus, elem, NULL);
556         if (!bus) {
557             qemu_error("bus \"%s\" not found\n", elem);
558             return NULL;
559         }
560         pos = len;
561     }
562
563     for (;;) {
564         if (path[pos] == '\0') {
565             /* we are done */
566             return bus;
567         }
568
569         /* find device */
570         if (sscanf(path+pos, "/%127[^/]%n", elem, &len) != 1) {
571             qemu_error("path parse error (\"%s\" pos %d)\n", path, pos);
572             return NULL;
573         }
574         pos += len;
575         dev = qbus_find_dev(bus, elem);
576         if (!dev) {
577             qbus_list_dev(bus, msg, sizeof(msg));
578             qemu_error("device \"%s\" not found\n%s\n", elem, msg);
579             return NULL;
580         }
581         if (path[pos] == '\0') {
582             /* last specified element is a device.  If it has exactly
583              * one child bus accept it nevertheless */
584             switch (dev->num_child_bus) {
585             case 0:
586                 qemu_error("device has no child bus (%s)\n", path);
587                 return NULL;
588             case 1:
589                 return QLIST_FIRST(&dev->child_bus);
590             default:
591                 qbus_list_bus(dev, msg, sizeof(msg));
592                 qemu_error("device has multiple child busses (%s)\n%s\n",
593                            path, msg);
594                 return NULL;
595             }
596         }
597
598         /* find bus */
599         if (sscanf(path+pos, "/%127[^/]%n", elem, &len) != 1) {
600             qemu_error("path parse error (\"%s\" pos %d)\n", path, pos);
601             return NULL;
602         }
603         pos += len;
604         bus = qbus_find_bus(dev, elem);
605         if (!bus) {
606             qbus_list_bus(dev, msg, sizeof(msg));
607             qemu_error("child bus \"%s\" not found\n%s\n", elem, msg);
608             return NULL;
609         }
610     }
611 }
612
613 void qbus_create_inplace(BusState *bus, BusInfo *info,
614                          DeviceState *parent, const char *name)
615 {
616     char *buf;
617     int i,len;
618
619     bus->info = info;
620     bus->parent = parent;
621
622     if (name) {
623         /* use supplied name */
624         bus->name = qemu_strdup(name);
625     } else if (parent && parent->id) {
626         /* parent device has id -> use it for bus name */
627         len = strlen(parent->id) + 16;
628         buf = qemu_malloc(len);
629         snprintf(buf, len, "%s.%d", parent->id, parent->num_child_bus);
630         bus->name = buf;
631     } else {
632         /* no id -> use lowercase bus type for bus name */
633         len = strlen(info->name) + 16;
634         buf = qemu_malloc(len);
635         len = snprintf(buf, len, "%s.%d", info->name,
636                        parent ? parent->num_child_bus : 0);
637         for (i = 0; i < len; i++)
638             buf[i] = qemu_tolower(buf[i]);
639         bus->name = buf;
640     }
641
642     QLIST_INIT(&bus->children);
643     if (parent) {
644         QLIST_INSERT_HEAD(&parent->child_bus, bus, sibling);
645         parent->num_child_bus++;
646     }
647
648 }
649
650 BusState *qbus_create(BusInfo *info, DeviceState *parent, const char *name)
651 {
652     BusState *bus;
653
654     bus = qemu_mallocz(info->size);
655     bus->qdev_allocated = 1;
656     qbus_create_inplace(bus, info, parent, name);
657     return bus;
658 }
659
660 void qbus_free(BusState *bus)
661 {
662     DeviceState *dev;
663
664     while ((dev = QLIST_FIRST(&bus->children)) != NULL) {
665         qdev_free(dev);
666     }
667     if (bus->parent) {
668         QLIST_REMOVE(bus, sibling);
669         bus->parent->num_child_bus--;
670     }
671     if (bus->qdev_allocated) {
672         qemu_free(bus);
673     }
674 }
675
676 #define qdev_printf(fmt, ...) monitor_printf(mon, "%*s" fmt, indent, "", ## __VA_ARGS__)
677 static void qbus_print(Monitor *mon, BusState *bus, int indent);
678
679 static void qdev_print_props(Monitor *mon, DeviceState *dev, Property *props,
680                              const char *prefix, int indent)
681 {
682     char buf[64];
683
684     if (!props)
685         return;
686     while (props->name) {
687         if (props->info->print) {
688             props->info->print(dev, props, buf, sizeof(buf));
689             qdev_printf("%s-prop: %s = %s\n", prefix, props->name, buf);
690         }
691         props++;
692     }
693 }
694
695 static void qdev_print(Monitor *mon, DeviceState *dev, int indent)
696 {
697     BusState *child;
698     qdev_printf("dev: %s, id \"%s\"\n", dev->info->name,
699                 dev->id ? dev->id : "");
700     indent += 2;
701     if (dev->num_gpio_in) {
702         qdev_printf("gpio-in %d\n", dev->num_gpio_in);
703     }
704     if (dev->num_gpio_out) {
705         qdev_printf("gpio-out %d\n", dev->num_gpio_out);
706     }
707     qdev_print_props(mon, dev, dev->info->props, "dev", indent);
708     qdev_print_props(mon, dev, dev->parent_bus->info->props, "bus", indent);
709     if (dev->parent_bus->info->print_dev)
710         dev->parent_bus->info->print_dev(mon, dev, indent);
711     QLIST_FOREACH(child, &dev->child_bus, sibling) {
712         qbus_print(mon, child, indent);
713     }
714 }
715
716 static void qbus_print(Monitor *mon, BusState *bus, int indent)
717 {
718     struct DeviceState *dev;
719
720     qdev_printf("bus: %s\n", bus->name);
721     indent += 2;
722     qdev_printf("type %s\n", bus->info->name);
723     QLIST_FOREACH(dev, &bus->children, sibling) {
724         qdev_print(mon, dev, indent);
725     }
726 }
727 #undef qdev_printf
728
729 void do_info_qtree(Monitor *mon)
730 {
731     if (main_system_bus)
732         qbus_print(mon, main_system_bus, 0);
733 }
734
735 void do_info_qdm(Monitor *mon)
736 {
737     DeviceInfo *info;
738     char msg[256];
739
740     for (info = device_info_list; info != NULL; info = info->next) {
741         qdev_print_devinfo(info, msg, sizeof(msg));
742         monitor_printf(mon, "%s\n", msg);
743     }
744 }
745
746 void do_device_add(Monitor *mon, const QDict *qdict)
747 {
748     QemuOpts *opts;
749
750     opts = qemu_opts_parse(&qemu_device_opts,
751                            qdict_get_str(qdict, "config"), "driver");
752     if (opts && !qdev_device_help(opts))
753         qdev_device_add(opts);
754 }
755
756 void do_device_del(Monitor *mon, const QDict *qdict)
757 {
758     const char *id = qdict_get_str(qdict, "id");
759     DeviceState *dev;
760
761     dev = qdev_find_recursive(main_system_bus, id);
762     if (NULL == dev) {
763         qemu_error("Device '%s' not found\n", id);
764         return;
765     }
766     qdev_unplug(dev);
767 }
This page took 0.072863 seconds and 4 git commands to generate.