Skip to the content.

QOM

0. QOM 概念

QOM(QEMU Object Model) 是 QEMU 中对象的抽象层。QEMU 需要模拟很多设备,每个设备天然的具有继承关系,如模拟 CPU,需要模拟不同架构的 CPU,如 intel, arm, sparc 等等,虽然架构不同,但是所有的 CPU 还是有一些共性的东西,如核数,线程数等等。而虽然是同一架构的 CPU,不同的版本也有细微不同,不可能对每一种 CPU 都全部实现一次,所有采用 C++中面对对象的概念来实现模拟。QOM 就是在 QEMU 中用 C 实现的面向对象思想。

QOM 整个结构分为 3 个部分:类型的注册、类型的初始化以及对象的初始化。


     / +------------+     type_init
     | |  类型的注册  | -> register_module_init
     | +------------+     type_register
     | +------------+
 QOM | | 类型的初始化 | -> type_initialize
     | +------------+
     | +------------+     object_new
     | | 对象的初始化 | -> object_initialize
     \ +------------+     object_initialize_with_type

1. 面向对象

面向对象的基本知识:

下面分析 QEMU 是怎样实现这些特性。

2. 类型的注册

在 QEMU 中用 TypeInfo 表示类

struct TypeInfo
{
    const char *name;
    const char *parent;

    size_t instance_size;
    size_t instance_align;
    void (*instance_init)(Object *obj);
    void (*instance_post_init)(Object *obj);
    void (*instance_finalize)(Object *obj);

    bool abstract;
    size_t class_size;

    void (*class_init)(ObjectClass *klass, void *data);
    void (*class_base_init)(ObjectClass *klass, void *data);
    void *class_data;

    InterfaceInfo *interfaces;
};

如下面的 x86_cpu_type_info 就是声明了一个 x86_cpu 类。

static const TypeInfo x86_cpu_type_info = {
    .name = TYPE_X86_CPU,                     // 类名
    .parent = TYPE_CPU,                       // 父类
    .instance_size = sizeof(X86CPU),          // 实例大小
    .instance_init = x86_cpu_initfn,          // 初始化实例,可以理解为c++中的构造函数
    .abstract = true,
    .class_size = sizeof(X86CPUClass),        // 类大小
    .class_init = x86_cpu_common_class_init,  // 类初始化函数
};

可以看到有 parent 变量,即 x86_cpu_type_info 的父类是 TYPE_CPU 类型,而 TYPE_CPU 又有父类。也就是说 QOM 通过结构体来实现继承

这里静态成员是所有的对象共享的,而非静态的每一个对象都有一份,这里 X86CPU 就是包含的就是非静态成员,而 X86CPUClass 描述的是静态的成员。

类通过 type_init 添加到一个哈希表中,之后就可以根据类名获取具体的类信息。

type_init(x86_cpu_register_types)

type_init 是通过 modult_init 宏实现的

#define type_init(function) module_init(function, MODULE_INIT_QOM)

#define module_init(function, type)                                         \
static void __attribute__((constructor)) do_qemu_init_ ## function(void)    \
{                                                                           \
    register_module_init(function, type);                               \
}

即各个类最终通过函数 register_module_init 注册到系统,其中 function 是每个类型都要实现的初始化函数,如上面的 x86_cpu_register_types。这里的 constructor 是编译器属性,带有这个属性的函数会早于 main 函数执行,也就是说所有的 QOM 类型注册在 main 之前就已经完成了。这里对 register_module_init 不做详细分析,只需要知道它把所有的类都加载到一个 init_type_list 表中,然后进行初始化。

我们着重分析每个类的注册函数,这里分析上面的 x86_cpu_register_types ,它会调用 type_register_static 注册所有的 x86_cpu 类。

static void x86_cpu_register_types(void)
{
    int i;

    type_register_static(&x86_cpu_type_info);
    for (i = 0; i < ARRAY_SIZE(builtin_x86_defs); i++) {
        x86_register_cpudef_types(&builtin_x86_defs[i]);
    }
    type_register_static(&max_x86_cpu_type_info);
    type_register_static(&x86_base_cpu_type_info);
#if defined(CONFIG_KVM) || defined(CONFIG_HVF)
    type_register_static(&host_x86_cpu_type_info);
#endif
}

builtin_x86_defs 是 QEMU 定义的一系列的 CPU 类型,其类型是 X86CPUDefinition

typedef struct X86CPUDefinition {
    const char *name;                   // cpu 名字
    uint32_t level;                     // CPUID 支持的最大功能号
    uint32_t xlevel;                    // CPUID 扩展质量支持的最大功能号
    /* vendor is zero-terminated, 12 character ASCII string */
    char vendor[CPUID_VENDOR_SZ + 1];
    int family;
    int model;
    int stepping;
    FeatureWordArray features;
    const char *model_id;
    CPUCaches *cache_info;
    /*
     * Definitions for alternative versions of CPU model.
     * List is terminated by item with version == 0.
     * If NULL, version 1 will be registered automatically.
     */
    const X86CPUVersionDefinition *versions;
    const char *deprecation_note;
} X86CPUDefinition;

type_register_static 最终调用到 type_register_internal ,它的功能比较简单, type_new 通过 TypeInfo 构建出 TypeImpl,这两者包含的变量基本相同,其区别是 TypeInfo 保存静态注册的数据,而 TypeImpl 保存运行时数据。之后通过 type_table_addTypeImpl 添加到一个哈希表中。

static void type_table_add(TypeImpl *ti)
{
    assert(!enumerating_types);
    g_hash_table_insert(type_table_get(), (void *)ti->name, ti);
}

static TypeImpl *type_table_lookup(const char *name)
{
    return g_hash_table_lookup(type_table_get(), name);
}

static TypeImpl *type_new(const TypeInfo *info)
{
    TypeImpl *ti = g_malloc0(sizeof(*ti));
    int i;

    g_assert(info->name != NULL);

    if (type_table_lookup(info->name) != NULL) {
        fprintf(stderr, "Registering `%s' which already exists\n", info->name);
        abort();
    }

    ti->name = g_strdup(info->name);
    ti->parent = g_strdup(info->parent);

    ti->class_size = info->class_size;
    ti->instance_size = info->instance_size;
    ti->instance_align = info->instance_align;

    ti->class_init = info->class_init;
    ti->class_base_init = info->class_base_init;
    ti->class_data = info->class_data;

    ti->instance_init = info->instance_init;
    ti->instance_post_init = info->instance_post_init;
    ti->instance_finalize = info->instance_finalize;

    ti->abstract = info->abstract;

    for (i = 0; info->interfaces && info->interfaces[i].type; i++) {
        ti->interfaces[i].typename = g_strdup(info->interfaces[i].type);
    }
    ti->num_interfaces = i;

    return ti;
}

static TypeImpl *type_register_internal(const TypeInfo *info)
{
    TypeImpl *ti;
    ti = type_new(info);

    type_table_add(ti);
    return ti;
}

上面说到 TypeImplTypeInfo 的结构基本相同,这里我们看一下 TypeImpl 的结构

struct TypeImpl
{
    const char *name;

    size_t class_size;

    size_t instance_size;
    size_t instance_align;

    void (*class_init)(ObjectClass *klass, void *data);
    void (*class_base_init)(ObjectClass *klass, void *data);

    void *class_data;

    void (*instance_init)(Object *obj);
    void (*instance_post_init)(Object *obj);
    void (*instance_finalize)(Object *obj);

    bool abstract;

    const char *parent;
    TypeImpl *parent_type;

    ObjectClass *class;

    int num_interfaces;
    InterfaceImpl interfaces[MAX_INTERFACES];
};

其他的变量都很好理解,在阅读源码的过程中,我发现 ObjectClass *class 变量很有用,之后很多地方都会通过这个变量直接获取到 ObjectClass ,后面会讲到它是所有类的父类。

到目前为止,QEMU 已经在一个哈希表中保存了所有类信息 TypeImpl ,之后就需要对这些类进行初始化。

3. 类型与对象的初始化

类的初始化是通过 type_initialize 实现的。

static void type_initialize(TypeImpl *ti)
{
    TypeImpl *parent;

    if (ti->class) {
        return;
    }

    ti->class_size = type_class_get_size(ti);
    ti->instance_size = type_object_get_size(ti);
    /* Any type with zero instance_size is implicitly abstract.
     * This means interface types are all abstract.
     */
    if (ti->instance_size == 0) {
        ti->abstract = true;
    }
    if (type_is_ancestor(ti, type_interface)) {
        assert(ti->instance_size == 0);
        assert(ti->abstract);
        assert(!ti->instance_init);
        assert(!ti->instance_post_init);
        assert(!ti->instance_finalize);
        assert(!ti->num_interfaces);
    }
    ti->class = g_malloc0(ti->class_size);

    parent = type_get_parent(ti);
    if (parent) {
        type_initialize(parent);
        GSList *e;
        int i;

        g_assert(parent->class_size <= ti->class_size);
        g_assert(parent->instance_size <= ti->instance_size);
        memcpy(ti->class, parent->class, parent->class_size);
        ti->class->interfaces = NULL;

        for (e = parent->class->interfaces; e; e = e->next) {
            InterfaceClass *iface = e->data;
            ObjectClass *klass = OBJECT_CLASS(iface);

            type_initialize_interface(ti, iface->interface_type, klass->type);
        }

        for (i = 0; i < ti->num_interfaces; i++) {
            TypeImpl *t = type_get_by_name(ti->interfaces[i].typename);
            if (!t) {
                error_report("missing interface '%s' for object '%s'",
                             ti->interfaces[i].typename, parent->name);
                abort();
            }
            for (e = ti->class->interfaces; e; e = e->next) {
                TypeImpl *target_type = OBJECT_CLASS(e->data)->type;

                if (type_is_ancestor(target_type, t)) {
                    break;
                }
            }

            if (e) {
                continue;
            }

            type_initialize_interface(ti, t, t);
        }
    }

    ti->class->properties = g_hash_table_new_full(g_str_hash, g_str_equal, NULL,
                                                  object_property_free);

    ti->class->type = ti;

    while (parent) {
        if (parent->class_base_init) {
            parent->class_base_init(ti->class, ti->class_data);
        }
        parent = type_get_parent(parent);
    }

    if (ti->class_init) {
        ti->class_init(ti->class, ti->class_data);
    }
}

这里基本是对 TypeImpl 结构的逐一初始化,值的注意的是也要对所有的父类进行初始化。

光看代码理解不深刻,我们来看看执行过程,还是以前面的 x86_cpu_type_info 类为例子,每个类有两个初始化函数,分别是非静态变量 x86_cpu_initfn 和静态变量 x86_cpu_common_class_init。静态成员是所有的对象公用的,其初始化显然要发生在所有的对象初始化之前。

#0  x86_cpu_common_class_init (oc=0x555556767080, data=0x0) at ../target/i386/cpu.c:7396
#1  0x0000555555de15ae in type_initialize (ti=0x555556730e70) at ../qom/object.c:364
#2  0x0000555555de1319 in type_initialize (ti=0x555556735e50) at ../qom/object.c:312
#3  0x0000555555de2dbc in object_class_foreach_tramp (key=0x555556735fd0, value=0x555556735e50, opaque=0x7fffffffda90) at ../qom/object.c:1069
#4  0x00007ffff7d7e1b8 in g_hash_table_foreach () from /usr/lib/x86_64-linux-gnu/libglib-2.0.so.0
#5  0x0000555555de2e9f in object_class_foreach (fn=0x555555de3008 <object_class_get_list_tramp>, implements_type=0x55555602f9a5 "machine", include_abstract=false, opaque=0x7fffffffdae0)
    at ../qom/object.c:1091
#6  0x0000555555de308b in object_class_get_list (implements_type=0x55555602f9a5 "machine", include_abstract=false) at ../qom/object.c:1148
#7  0x0000555555c759d2 in select_machine () at ../softmmu/vl.c:1616
#8  0x0000555555c7a2d3 in qemu_init (argc=5, argv=0x7fffffffdd98, envp=0x7fffffffddc8) at ../softmmu/vl.c:3545
#9  0x0000555555818f05 in main (argc=5, argv=0x7fffffffdd98, envp=0x7fffffffddc8) at ../softmmu/main.c:49

QEMU 在 qemu_init 时会调用 select_machine 来选择 machine 类型,然后调用 object_class_get_list 来获得所有该 TYPE_MACHINE 类型组成的链表,最终对链表中的每个元素都调用 type_initialize ,然后 type_initialize 根据不同类的 class_init 进行调用,如这里的 x86_cpu_common_class_init 。也就是说在开始确定 machine 类型的时候就会完成所有类型的初始化。注意,第 2 部分是注册所有的类型,是初始化某一个类型下所有的静态变量。

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);
    FeatureWord w;

    device_class_set_parent_realize(dc, x86_cpu_realizefn,
                                    &xcc->parent_realize);
    device_class_set_parent_unrealize(dc, x86_cpu_unrealizefn,
                                      &xcc->parent_unrealize);
    device_class_set_props(dc, x86_cpu_properties);

    device_class_set_parent_reset(dc, x86_cpu_reset, &xcc->parent_reset);
    cc->reset_dump_flags = CPU_DUMP_FPU | CPU_DUMP_CCOP;

    cc->class_by_name = x86_cpu_class_by_name;
    cc->parse_features = x86_cpu_parse_featurestr;
    cc->has_work = x86_cpu_has_work;

#ifdef CONFIG_TCG
    tcg_cpu_common_class_init(cc);
#endif /* CONFIG_TCG */

    cc->dump_state = x86_cpu_dump_state;
    cc->set_pc = x86_cpu_set_pc;
    cc->gdb_read_register = x86_cpu_gdb_read_register;
    cc->gdb_write_register = x86_cpu_gdb_write_register;
    cc->get_arch_id = x86_cpu_get_arch_id;
    cc->get_paging_enabled = x86_cpu_get_paging_enabled;

    ...

    /*
     * The "unavailable-features" property has the same semantics as
     * CpuDefinitionInfo.unavailable-features on the "query-cpu-definitions"
     * QMP command: they list the features that would have prevented the
     * CPU from running if the "enforce" flag was set.
     */
    object_class_property_add(oc, "unavailable-features", "strList",
                              x86_cpu_get_unavailable_features,
                              NULL, NULL, NULL);

#if !defined(CONFIG_USER_ONLY)
    object_class_property_add(oc, "crash-information", "GuestPanicInformation",
                              x86_cpu_get_crash_info_qom, NULL, NULL, NULL);
#endif

    for (w = 0; w < FEATURE_WORDS; w++) {
        int bitnr;
        for (bitnr = 0; bitnr < 64; bitnr++) {
            x86_cpu_register_feature_bit_props(xcc, w, bitnr);
        }
    }
}

之后是初始化非静态变量,即某个类私有的变量。

#0  x86_cpu_initfn (obj=0x55555699d430) at ../target/i386/cpu.c:7107
#1  0x0000555555de1618 in object_init_with_type (obj=0x55555699d430, ti=0x555556730e70) at ../qom/object.c:375
#2  0x0000555555de15fa in object_init_with_type (obj=0x55555699d430, ti=0x555556731080) at ../qom/object.c:371
#3  0x0000555555de1b73 in object_initialize_with_type (obj=0x55555699d430, size=42928, type=0x555556731080) at ../qom/object.c:517
#4  0x0000555555de22a8 in object_new_with_type (type=0x555556731080) at ../qom/object.c:732
#5  0x0000555555de2307 in object_new (typename=0x555556000a5f "qemu64-x86_64-cpu") at ../qom/object.c:747
#6  0x0000555555b27028 in x86_cpu_new (x86ms=0x5555568b9de0, apic_id=0, errp=0x5555566e9a98 <error_fatal>) at ../hw/i386/x86.c:106
#7  0x0000555555b27144 in x86_cpus_init (x86ms=0x5555568b9de0, default_cpu_version=1) at ../hw/i386/x86.c:138
#8  0x0000555555b2047a in pc_init1 (machine=0x5555568b9de0, host_type=0x555556008544 "i440FX-pcihost", pci_type=0x55555600853d "i440FX") at ../hw/i386/pc_piix.c:159
#9  0x0000555555b20efa in pc_init_v6_0 (machine=0x5555568b9de0) at ../hw/i386/pc_piix.c:427
#10 0x0000555555a8c6e9 in machine_run_board_init (machine=0x5555568b9de0) at ../hw/core/machine.c:1232
#11 0x0000555555c77b73 in qemu_init_board () at ../softmmu/vl.c:2514
#12 0x0000555555c77d52 in qmp_x_exit_preconfig (errp=0x5555566e9a98 <error_fatal>) at ../softmmu/vl.c:2588
#13 0x0000555555c7a45f in qemu_init (argc=5, argv=0x7fffffffdd98, envp=0x7fffffffddc8) at ../softmmu/vl.c:3611
#14 0x0000555555818f05 in main (argc=5, argv=0x7fffffffdd98, envp=0x7fffffffddc8) at ../softmmu/main.c:49

在 smp 结构中初始化所有的 CPU。

void x86_cpus_init(X86MachineState *x86ms, int default_cpu_version)
{
    int i;
    const CPUArchIdList *possible_cpus;
    MachineState *ms = MACHINE(x86ms);
    MachineClass *mc = MACHINE_GET_CLASS(x86ms);

    x86_cpu_set_default_version(default_cpu_version);

    /*
     * Calculates the limit to CPU APIC ID values
     *
     * Limit for the APIC ID value, so that all
     * CPU APIC IDs are < x86ms->apic_id_limit.
     *
     * This is used for FW_CFG_MAX_CPUS. See comments on fw_cfg_arch_create().
     */
    x86ms->apic_id_limit = x86_cpu_apic_id_from_index(x86ms,
                                                      ms->smp.max_cpus - 1) + 1;
    possible_cpus = mc->possible_cpu_arch_ids(ms);
    for (i = 0; i < ms->smp.cpus; i++) {
        x86_cpu_new(x86ms, possible_cpus->cpus[i].arch_id, &error_fatal);
    }
}

这里由 cpu_type 这个 char * 变量就可以找到 TypeImpl 类名。

void x86_cpu_new(X86MachineState *x86ms, int64_t apic_id, Error **errp)
{
    Object *cpu = object_new(MACHINE(x86ms)->cpu_type);

    if (!object_property_set_uint(cpu, "apic-id", apic_id, errp)) {
        goto out;
    }
    qdev_realize(DEVICE(cpu), NULL, errp);

out:
    object_unref(cpu);
}
Object *object_new(const char *typename)
{
    TypeImpl *ti = type_get_by_name(typename);

    return object_new_with_type(ti);
}
static Object *object_new_with_type(Type type)
{
    Object *obj;
    size_t size, align;
    void (*obj_free)(void *);

    g_assert(type != NULL);
    type_initialize(type);

    size = type->instance_size;
    align = type->instance_align;

    /*
     * Do not use qemu_memalign unless required.  Depending on the
     * implementation, extra alignment implies extra overhead.
     */
    if (likely(align <= __alignof__(qemu_max_align_t))) {
        obj = g_malloc(size);
        obj_free = g_free;
    } else {
        obj = qemu_memalign(align, size);
        obj_free = qemu_vfree;
    }

    object_initialize_with_type(obj, size, type);
    obj->free = obj_free;

    return obj;
}
static void object_initialize_with_type(Object *obj, size_t size, TypeImpl *type)
{
    type_initialize(type);

    g_assert(type->instance_size >= sizeof(Object));
    g_assert(type->abstract == false);
    g_assert(size >= type->instance_size);

    memset(obj, 0, type->instance_size);
    obj->class = type->class;
    object_ref(obj);
    object_class_property_init_all(obj);
    obj->properties = g_hash_table_new_full(g_str_hash, g_str_equal,
                                            NULL, object_property_free);
    object_init_with_type(obj, type);
    object_post_init_with_type(obj, type);
}

这里还要初始化父类,如果有的话。instance_init 就是 x86_cpu_initfn

static void object_init_with_type(Object *obj, TypeImpl *ti)
{
    if (type_has_parent(ti)) {
        object_init_with_type(obj, type_get_parent(ti));
    }

    if (ti->instance_init) {
        ti->instance_init(obj);
    }
}
static void x86_cpu_initfn(Object *obj)
{
    X86CPU *cpu = X86_CPU(obj);
    X86CPUClass *xcc = X86_CPU_GET_CLASS(obj);
    CPUX86State *env = &cpu->env;

    env->nr_dies = 1;
    cpu_set_cpustate_pointers(cpu);

    object_property_add(obj, "feature-words", "X86CPUFeatureWordInfo",
                        x86_cpu_get_feature_words,
                        NULL, NULL, (void *)env->features);
    object_property_add(obj, "filtered-features", "X86CPUFeatureWordInfo",
                        x86_cpu_get_feature_words,
                        NULL, NULL, (void *)cpu->filtered_features);

    object_property_add_alias(obj, "sse3", obj, "pni");
    object_property_add_alias(obj, "pclmuldq", obj, "pclmulqdq");
    object_property_add_alias(obj, "sse4-1", obj, "sse4.1");
    object_property_add_alias(obj, "sse4-2", obj, "sse4.2");
    object_property_add_alias(obj, "xd", obj, "nx");
    object_property_add_alias(obj, "ffxsr", obj, "fxsr-opt");
    object_property_add_alias(obj, "i64", obj, "lm");

    ...

    if (xcc->model) {
        x86_cpu_load_model(cpu, xcc->model);
    }
}

经过这样一系列的各种父类,子类的注册,初始化,才能最终向上层提供完整的 x86_cpu 的模拟。

4. 类型的层次结构

QEMU 的继承关系如下:

struct X86CPUClass {
    /*< private >*/
    CPUClass parent_class;
    /*< public >*/
};
struct CPUClass {
    /*< private >*/
    DeviceClass parent_class;
    /*< public >*/
};
struct DeviceClass {
    /*< private >*/
    ObjectClass parent_class;
    /*< public >*/
};
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;
};

QEMU 中所有的对象的 parent 是 Object 和 ObjectClass。Object 存储 Non-static 部分,而 ObjectClass 存储 static 部分。

QEMU 中的类型转换是通过 object_class_dynamic_cast 完成的。如上面的 x86_cpu_initfn ,它的形参是 Object ,但它要初始化的是 X86CPU

ObjectClass *object_class_dynamic_cast(ObjectClass *class,
                                       const char *typename)
{
    ObjectClass *ret = NULL;
    TypeImpl *target_type;
    TypeImpl *type;

    if (!class) {
        return NULL;
    }

    /* A simple fast path that can trigger a lot for leaf classes.  */
    type = class->type;
    if (type->name == typename) {
        return class;
    }

    target_type = type_get_by_name(typename);
    if (!target_type) {
        /* target class type unknown, so fail the cast */
        return NULL;
    }

    if (type->class->interfaces &&
            type_is_ancestor(target_type, type_interface)) {
        int found = 0;
        GSList *i;

        for (i = class->interfaces; i; i = i->next) {
            ObjectClass *target_class = i->data;

            if (type_is_ancestor(target_class->type, target_type)) {
                ret = target_class;
                found++;
            }
         }

        /* The match was ambiguous, don't allow a cast */
        if (found > 1) {
            ret = NULL;
        }
    } else if (type_is_ancestor(type, target_type)) {
        ret = class;
    }

    return ret;
}

目前为止,QEMU 完成了类型的注册,即为每个类型指定一个 TypeInfo 注册到系统中,然后将 TypeInfo 转化成 TypeImpl 放到一个哈希表中。之后系统会通过 type_initialize 对这个哈希表进行初始化,主要是设置 TypeImpl 的一些域以及调用类型的 class_init 函数,如上面的 x86_cpu_common_class_initx86_cpu_initfn 。这里要理清不同的函数是初始化哪个数据结构的。

x86_cpu_common_class_initx86_cpu_initfn 就是具体的对象初始化,执行流程上面已经给出了。

5. 属性

前面几步构造出了具体的设备,但是设备中的数据没有初始化,这里通过将 realized 属性设置为 true 来填充数据。下面分析是怎样填充的。

对于属性,目前还没有完全搞懂,只知道每个类中都会设置属性,如在 x86_cpu_common_class_init 中添加属性,

object_class_property_add(oc, "family", "int",
                              x86_cpuid_version_get_family,
                              x86_cpuid_version_set_family, NULL, NULL);
    object_class_property_add(oc, "model", "int",
                              x86_cpuid_version_get_model,
                              x86_cpuid_version_set_model, NULL, NULL);
    object_class_property_add(oc, "stepping", "int",
                              x86_cpuid_version_get_stepping,
                              x86_cpuid_version_set_stepping, NULL, NULL);
    object_class_property_add_str(oc, "vendor",
                                  x86_cpuid_get_vendor,
                                  x86_cpuid_set_vendor);
    object_class_property_add_str(oc, "model-id",
                                  x86_cpuid_get_model_id,
                                  x86_cpuid_set_model_id);
    object_class_property_add(oc, "tsc-frequency", "int",
                              x86_cpuid_get_tsc_freq,
                              x86_cpuid_set_tsc_freq, NULL, NULL);
    /*
     * The "unavailable-features" property has the same semantics as
     * CpuDefinitionInfo.unavailable-features on the "query-cpu-definitions"
     * QMP command: they list the features that would have prevented the
     * CPU from running if the "enforce" flag was set.
     */
    object_class_property_add(oc, "unavailable-features", "strList",
                              x86_cpu_get_unavailable_features,
                              NULL, NULL, NULL);

#if !defined(CONFIG_USER_ONLY)
    object_class_property_add(oc, "crash-information", "GuestPanicInformation",
                              x86_cpu_get_crash_info_qom, NULL, NULL, NULL);
#endif

但具体怎么用的还不知道,在接下来的源码阅读中如果有遇到再进行分析。

QOM 和 Qdev

Qdev 是 qemu 管理总线和设备的方式。如下所示,所有的设备都是挂载在 bus 上的。

/machine (pc-i440fx-6.1-machine)
  /fw_cfg (fw_cfg_io)
    /\x2from@etc\x2facpi\x2frsdp[0] (memory-region)
    /\x2from@etc\x2facpi\x2ftables[0] (memory-region)
    /\x2from@etc\x2ftable-loader[0] (memory-region)
    /fwcfg.dma[0] (memory-region)
    /fwcfg[0] (memory-region)
  /i440fx (i440FX-pcihost)
    /ioapic (ioapic)
      /ioapic[0] (memory-region)
      /unnamed-gpio-in[0] (irq)
  /unattached (container)
    /device[0] (qemu64-x86_64-cpu)
      /lapic (apic)
        /apic-msi[0] (memory-region)
      /memory[0] (memory-region)
      /memory[1] (memory-region)
      /smram[0] (memory-region)

QOM 则是通过面对对象的方法来实现这种管理(个人理解)。

reference

[1] QEMU/KVM 源码解析与应用

[2] https://martins3.github.io/qemu/qom.html