How to create bridge device, bus, and pci device.
As we all know, the "device and bus" architecture in qemu is that, a pci
device should attach to a pci bus, and a pci bus should add to a bridge
device, and the bridge device should link to main-system-bus.
To realize this, four important functions are introduced:
1. qdev_bridge_create() to create a bridge device named "s390-bridge-dev".
2. qbus_create() to create a pci bus named "s390-pci-bus" and attach
"s390-pci-bus" to "s390-bridge-dev".
3. s390_pci_bus_init() is used by coder to assign a bridge device and a pci bus,
it will call qdev_bridge_create() and qbus_create().
4. qdev_device_add() to create a pci device and attach it to designated bus.
The following codes are telling how to realize this in detail.
I. Define TypeInfo in test_pci.c file.
static void s390_pci_dev_class_init(ObjectClass *kclass, void *data)
{
DeviceClass *dc = DEVICE_CLASS(kclass);
dc->bus_type = TYPE_S390_PCI_BUS;
}
static const TypeInfo s390_bridge_dev_info = {
.name = TYPE_S390_BRIDGE_DEV,
.parent = TYPE_SYS_BUS_DEVICE,
.class_init = s390_bridge_dev_class_init,
}
static const TypeInfo s390_pci_bus_info = {
.name = TYPE_S390_PCI_BUS,
.parent = TYPE_BUS,
}
static const TypeInfo s390_pci_dev_info = {
.name = TYPE_S390_PCI_DEV,
.parent = TYPE_DEVICE,
.class_init = s390_pci_dev_class_init,
};
II. Define macro in test_pci.h file.
#define TYPE_S390_BRIDGE_DEV "s390-bridge-dev"
#define TYPE_SYS_BUS_DEVICE "sys-bus-device"
typedef struct S390PCIBus {
BusState parent_obj;
} S390PCIBus
#define TYPE_S390_PCI_BUS "s390-pci-bus"
#define S390_PCI_BUS(obj) \
OBJECT_CHECK(S390PCIBus, (obj)TYPE_S390_PCI_BUS)
#define TYPE_BUS "bus"
#define TYPE_S390_PCI_DEV "s390-pci-dev"
#define TYPE_DEVICE "device"
III. How to create s390-bridge-dev and s390-pci-bus.
/**
* object_class_by_name to initialize ObjectClass.
*
* Will find corresponding TypeImpl by given typename, like "s390-bridge-dev".
* Then call type_initialize() to initialize device's ObjectClass.
*
* Actually, in our code, all devices' ObjectClass initialization is already
* realized by object_class_foreach_tramp() in find_default_machine().
*
* Returns: one device's ObjectClass according to its TypeInfo's name.
*/
static ObjectClass *object_class_by_name(const char *typename)
{
TypeImpl *type = type_get_by_name(typename);
if (!type) {
return NULL;
}
type_initialize(type);
return type->class;
}
/**
* To create a bridge device, named "s390-bridge-dev".
* After initialization, the new created "s390-bridge-dev" device
* should attach to main_system_bus as its parent bus.
*/
DeviceState *qdev_bridge_create(BusState *bus, const char *typename)
{
DeviceState *dev;
/* Initialize ObjectClass. */
if (object_class_by_name(typename) == NULL) {
return NULL;
}
/* Initialize Object to create a device. */
dev = DEVICE(object_new(typename));
if (!dev) {
return NULL;
}
/* to get main_system_bus */
if (!bus) {
bus = sysbus_get_default();
}
/* add s390-bridge-dev's link to main_system_bus */
qdev_set_parent_bus(dev, bus);
/* add s390-bridge-dev to main_system_bus as a child */
object_property_add_child(qdev_get_machine, typename,
OBJECT(dev), NULL);
object_unref(OBJECT(dev));
return dev;
}
/**
* To create a pci bus named "s390-pci-bus".
* After initialization, the new created "s390-pci-bus" should add
* to its upper bridge device named "s390-bridge-dev".
*/
BusState *qbus_create(const char *typename,
DeviceState *parent, const char *name)
{
BusState *bus;
/* Initialize s390-pci-bus */
bus = BUS(object_new(typename));
/* Assign name to s390-pci-bus, and add s390-pci-bus to s390-bridge-dev */
qbus_realize(bus, parent, name);
return bus;
}
/**
* To create a bridge device named "s390-bridge-dev", create a pci bus named
* "s390-pci-bus", and attach "s390-pci-bus" to "s390-bridge-dev".
*
* There are two methods to create a device:
* -- define in command line, like "-device, s390-bridge-dev"
* -- assign name directly in file like ccw_init called by main()
* Here we adopt the second method.
*/
S390PCIBus *s390_pci_bus_init(void)
{
S390PCIBus *pbus;
BusState *bus;
DeviceState *dev;
/* Create s390-bridge-dev */
dev = qdev_bridge_create(NULL, TYPE_S390_BRIDGE_DEV);
/* Create s390-pci-bus which should attach to s390-bridge-dev */
bus = qbus_create(TYPE_S390_PCI_BUS, dev, NULL);
pbus = S390_PCI_BUS(bus);
return pbus;
}
IV. How to create s390-pci-dev.
/**
* To create a pci device named "s390-pci-dev", then find its upper bus and
* attach "s390-pci-dev" to the bus.
*
* There are two methods to designate a pci device's upper bus.
* -- define bus in command line, like "-device s390-pci-dev, bus=pci.1"
* -- designate its bus_type when initializing ObjectClass
*
* Here we adopt the second method, this means code will find s390-pci-dev's
* bus by its bus_type "s390-pci-bus".
*/
DeviceState *qdev_device_add(QemuOpts *opts, Error **errp)
{
DeviceClass *dc;
const char *driver, *path;
DeviceState *dev;
BusState *bus = NULL;
Error *err = NULL;
/* the value of driver will be returned as "s390-pci-dev" */
driver = qemu_opt_get(opts, "driver");
if (!driver) {
error_setg(errp, QERR_MISSING_PARAMETER, "driver");
return NULL;
}
/* Initialize ObjectClass by typename */
dc = qdev_get_device_class(&driver, errp);
if (!dc) {
return NULL;
}
/* will return NULL as we did not define "bus=..." in command line */
path = qemu_opt_get(opts, "bus");
if (path != NULL) {
bus = qbus_find(path, errp);
if (!bus) {
return NULL;
}
if (!object_dynamic_cast(OBJECT(bus), dc->bus_type)) {
error_setg(errp, "Device '%s' can't go on %s bus",
driver, object_get_typename(OBJECT(bus)));
return NULL;
}
} else if (dc->bus_type != NULL) {
/* this is a recursion function that will return the matched bus only
* if the first parameter's busname equals to dc->bus_type.
*/
bus = qbus_find_recursive(sysbus_get_default(), NULL, dc->bus_type);
if (!bus || qbus_is_full(bus)) {
error_setg(errp, "No '%s' bus found for device '%s'",
dc->bus_type, driver);
return NULL;
}
}
/* Initialize Object to create device */
dev = DEVICE(object_new(driver));
/* add s390-pci-dev's link to s390-pci-bus */
if (bus) {
qdev_set_parent_bus(dev, bus);
}
/* add s390-pci-dev to s390-pci-bus as a child */
if (dev->id) {
object_property_add_child(qdev_get_peripheral(), dev->id,
OBJECT(dev), NULL);
} else {
static int anon_count;
gchar *name = g_strdup_printf("device[%d]", anon_count++);
object_property_add_child(qdev_get_peripheral_anon(), name,
OBJECT(dev), NULL);
g_free(name);
}
return dev;
}