QOM

  • OOP (object-oriented programming) system in C
  • Single-inheritance of types
  • Multiple inheritance of interfaces
  • Dynamic type registering(modules)
  • Map internal members to publicly exposed properties (get/set method)

QEMU use c language, and implement a class mechanism.

review OO:

  • encapsulation (Object properties)
  • inheritance
  • static field
  • constructor and destructor
  • polymorphic
    • override (dynamic)
    • overload (static)
  • abstract class (interface)
  • dynamic cast

Base

TypeInfo define one class in QOM

like:

static const TypeInfo x86_base_cpu_type_info = {
	.name = X*^_CPU_TYPE_NAME("base"),
	.parent = TYPE_X86_CPU,
	.class_init = x86_cpu_base_class_init,
};

struct first field implement inheritance

example:

static const TypeInfo x86_cpu_type_info = {
    .name = TYPE_X86_CPU,
    .parent = TYPE_CPU,
		// ...
    .instance_size = sizeof(X86CPU),
};
 
static const TypeInfo cpu_type_info = {
    .name = TYPE_CPU,
    .parent = TYPE_DEVICE,
		// ...
    .instance_size = sizeof(CPUState),
};

In X86CPU contain one CPUState member:

struct X86CPU {
	// private
	CPUState parent_obj;
	// public
	
	CPUNegativeOffsetState neg;
};

static member/method is shared by all object, and non-static member/method that is different in every object

static const TypeInfo x86_cpu_type_info = {
     // ...
    .instance_size = sizeof(X86CPU),
     // ...
    .class_size = sizeof(X86CPUClass),
};

X86CPU is non-static member, and X86CPUClass is static member.

QEMU all objects parent is Object and ObjectClass

Object store the non-static part, ObjectClass store the static part

/* include/qom/object.h: 127 */ 
struct ObjectClass { 
	/* private: */ 
	Type type; 
	GSList *interfaces; 
	
	const char *object_cast_cache[OBJECT_CLASS_CAST_CACHE]; 
	const char *class_cast_cache[OBJECT_CLASS_CAST_CACHE]; 
	
	ObjectUnparent *unparent; 
	GHashTable *properties; 
};
 
struct Object { 
	/* private: */ 
	ObjectClass *class; 
	ObjectFree *free; 
	GHashTable *properties; 
	uint32_t ref; 
	Object *parent; 
};

Object first member is ObjectClass*. C language guarantee the first member is the struct base address, so any derived object first member is base object, we can cast it to Object.

TypeInfo instance_init and class_init is the ctor of class and object

static const TypeInfo x86_cpu_type_info = {
    .instance_init = x86_cpu_initfn,
    .class_init = x86_cpu_common_class_init,
};

Dtor

QOM use ref count mechanism to determine when to call the dtor function to delete object.

TypeInfo’s instance_finalize to define the dtor function.

function pointer in derived class init impl the override

x86_cpu_common_class_init and cpu_class_init are x86_cpu_type_info and cpu_type_info registered ctor ,the parse_features function, x86_cpu_common_class_init register to another function x86_cpu_parser_featurestr.

static void x86_cpu_common_class_init(ObjectClass *oc, void *data)
{
    X86CPUClass *xcc = X86_CPU_CLASS(oc);
    CPUClass *cc = CPU_CLASS(oc);
    DeviceClass *dc = DEVICE_CLASS(oc);
 
    cc->parse_features = x86_cpu_parse_featurestr;
    // ...
}
    
static void cpu_class_init(ObjectClass *klass, void *data)
{
    DeviceClass *dc = DEVICE_CLASS(klass);
    CPUClass *k = CPU_CLASS(klass);
 
    k->parse_features = cpu_common_parse_features;
    // ...
}

QEMU do not support multi-inheritance

Init

  • type_init: register one TypeInfo (define one class)
  • TypeInfo::class_init: init the static member
  • TypeInfo::instance_init: init the non-static member

qdev have qdev_realize to initialize the device. Why we need the realized property.

As the Class init must be successful, so device need realize and unrealize to handle the device create and destroy dynamicly. If device realize successfully, the device would be added to qom-tree, or set Error** pointer.

type_init

static void x86_cpu_register_types(void)
{
		// ...
    type_register_static(&x86_cpu_type_info);
}
 
type_init(x86_cpu_register_types)
 
// type_init is macro and macro expansion is below
static void __attribute__((constructor))
do_qemu_init_x86_cpu_register_types(void) {
  register_module_init(x86_cpu_register_types, MODULE_INIT_QOM);
}
// __attribute__((constructor)) is compiler supported attr, and exec before main function
 
// register_module_init add the new type on one static list: init_type_list[MODULE_INIT_QOM]

Use TypeInfo init the TypeImpl, TypeInfo store the static data, TypeImpl store the runtime data

So, the call chain is

main
	qemu_init
		qemu_init_subsystem
			module_call_init: MODULE_INIT_QOM ctor hook
				type_register_static
					type_register
						type_register_internal
							type_new: use TypeInfo init TypeImpl
							g_hash_table_inisert(type_table_get(), (void*)ti->name, ti): created TypeImpl add type_table

init static part

static members are shared by all objects, so should be inited before all objects created, and the initial only happen once, instance_init called by every object at creating.

- main
  - qemu_init
    - select_machine
      - object_class_get_list
        - object_class_foreach
          - g_hash_table_foreach
            - object_class_foreach_tramp
              - type_initialize
                - type_initialize
                  - x86_cpu_common_class_init

In select_machine, get all TYPE_MACHINE class, by the way call type_initialize all TypeImpl

- object_class_get_list
  - object_class_foreach --> object_class_get_list_tramp (将元素添加到后面) <------------
    - g_hash_table_foreach (对于 type_table 循环) ---> object_class_foreach_tramp       |
                                                          - type_initialize             |
                                                          - object_class_dynamic_cast   |
                                                            - 执行 callback 函数 --------

type_initialize

  • allocate class memory
  • call parent class_init recursively

init non-static part

Call object_new to initial:

  • object_initialize_with_type:
    • init one empty Object::properties
    • object_init_with_type
      • if object has parent, call parent’s object_init_with_type
      • call TypeImpl::instance_init

qdev describe device init that is related the object and objectclass

The class relationship is below:

Property

All property must be accessed by visitor. C language do not support struct private member, so QEMU Object import properties hash table, contains Object can access/modify data and function(ObjectProperty)

struct ObjectProperty { 
	char *name; 
	char *type; 
	char *description; 
	ObjectPropertyAccessor *get; 
	ObjectPropertyAccessor *set; 
	ObjectPropertyResolve *resolve; 
	ObjectPropertyRelease *release; 
	ObjectPropertyInit *init; 
	void *opaque; 
	QObject *defval; 
};

Interface

struct InterfaceClass {
	ObjectClass parent_class;
	
	ObjectClass *concrete_class;
	Type interface_type;
};

One QOM class can implement multiple interface, ObjectClass struct has interfaces list, every element is InterfaceClass*, to find the interface_type that point one TypeImpl.

Cast

As override describe, at runtime, QEMU support dynamic cast to call different function pointer to implement the polymorphic. The feature is

ObjectClass* object_class_dynamic_cast(ObjectClass* class, const char* typename);