QEMU(1) - QOM

Table of Contents

TypeInfo

根類型TypeInfo

TypeInfo鏈路

初始化TypeInfo -> ModuleEntry

Dump TypeInfo

TypeImpl

初始化 ModuleEntry -> TypeImpl

對象類

基對象類ObjectClass

設備派生對象類

總線派生對象類

接口對象類

內存派生對象類

加速器派生對象類

初始化 TypeImpl -> ObjectClass

type_initialize_interface

class_base_init

class_init

對象

基對象Object

設備派生對象

總線派生對象

初始化 TypeImpl -> Object

object_new_with_type

object_initialize_with_type

調用object_new

qdev_device_add

instance_init

object_instance_init

device_initfn

instance_post_init

device_post_init

屬性

ObjectProperty

get/set callback方法

GlobalProperty

"container"屬性

Reference


QEMU設備模型發展歷史參考下圖。最終QEMU選擇面向對象的QEMU Object Module (QOM)來實現對所有的設備,總線,接口的模擬。

下面先介紹QOM模型的幾個重要概念,然後以hw/misc/Pci-testdev.c中的代碼實現作爲實例介紹整個QOM初始化過程。

QOM模型中幾個重要的概念

  • 類型Type,QOM中所有的模型都是通過類型Type註冊的,具體實現過程就是從類型信息TypeInfo註冊爲類型實現TypeImpl,並把所有類型實現添加到全局的GHashTable表中,hash表以類型Type的名字作爲key鍵值。類型Type有父子的概念,QOM有對象(TYPE_OBJECT)和接口(TYPE_INTERFACE)兩種根父類型。
  • 對象類,ObjectClass是對象類的基類,其餘所有的對象類都是繼承自ObjectClass,每個類型Type在註冊過程中都會實例化對象類。ObjectClass的第一個元素指向類型實現TypeImpl。
  • 對象,Object是對象的基類,其餘所有對象都是繼承自Object,每個類型Type在註冊過程中都會實例化對象。對象Ojbect的第一個元素是指向對象類的指針。
  • 屬性Property,屬性有兩類,一類是對象維護屬性GHashTable表。一類是設備對象的屬性結構數組
  • 接口Interface,對象類維護接口GSList單向隊列。

TypeInfo

每個QOM設備模型類型包括設備,總線,接口等都會定義一個類型信息TypeInfo。類型信息TypeInfo是用於註冊類型實現TypeImpl的結構模板。TypeInfo通過name定義類型名稱,通過parernt指定父類型名稱。

struct TypeInfo
{
    const char *name;    //類型名稱
    const char *parent;  //父類型名稱,TYPE_OBJECT和TYPE_INTERFACE是兩個根類型的名稱

    size_t instance_size;                       //對象實例的大小
    void (*instance_init)(Object *obj);         //對象實例的init函數,父類型的init已被執行過了,只需要負責本類型的
    void (*instance_post_init)(Object *obj);    //對象實例的post_init函數,在init執行完後執行
    void (*instance_finalize)(Object *obj);     //對象實例銷燬時執行,釋放動態資源

    bool abstract;                              //如爲true則是抽象的類型,不能直接實例

    size_t class_size;                                        //類對象實例的大小
    void (*class_init)(ObjectClass *klass, void *data);       //類對象實例的init函數,初始化自己的方法指針,也可覆蓋父類的方法指針
    void (*class_base_init)(ObjectClass *klass, void *data);  //在父類的class_init執行完,自己的class_init執行之前執行,做一些清理工作。
    void (*class_finalize)(ObjectClass *klass, void *data);   //類對象實例銷燬時執行,釋放動態資源
    void *class_data;                                         //傳遞給類對象實例各個方法的數據

    InterfaceInfo *interfaces;                  //類型定義的接口信息名稱數組
};

struct InterfaceInfo {
    const char *type;                          //每個接口類型,也會註冊一個TypeImpl類型,這裏只是提供每個接口的名字
};

TypeInfo一般都是靜態定義的,例如如下的pci_testdev_info。

static const TypeInfo pci_testdev_info = {
    .name          = TYPE_PCI_TEST_DEV,
    .parent        = TYPE_PCI_DEVICE,
    .instance_size = sizeof(PCITestDevState),
    .class_init    = pci_testdev_class_init,
};

有時也會動態生成

static void type_initialize_interface(TypeImpl *ti, TypeImpl *interface_type,
                                      TypeImpl *parent_type)
{
    TypeInfo info = { };
...
    info.parent = parent_type->name;
    info.name = g_strdup_printf("%s::%s", ti->name, interface_type->name);
    info.abstract = true;
...
}

根類型TypeInfo

所有的TypeInfo的parent的根爲TYPE_OBJECT和TYPE_INTERFACE

    static TypeInfo interface_info = {
        .name = TYPE_INTERFACE,
        .class_size = sizeof(InterfaceClass),
        .abstract = true,
    };

    static TypeInfo object_info = {
        .name = TYPE_OBJECT,
        .instance_size = sizeof(Object),
        .instance_init = object_instance_init,
        .abstract = true,
    };

TYPE_OBJECT的一級子類型

name parent
TYPE_ACCEL TYPE_OBJECT
TYPE_IOTHREAD TYPE_OBJECT
TYPE_MEMORY_REGION TYPE_OBJECT
TYPE_QJSON TYPE_OBJECT
TYPE_MEMORY_BACKEND TYPE_OBJECT
TYPE_RNG_BACKEND TYPE_OBJECT
TYPE_TPM_BACKEND TYPE_OBJECT
TYPE_QCRYPTO_TLS_CREDS TYPE_OBJECT
TYPE_IRQ TYPE_OBJECT
TYPE_MACHINE TYPE_OBJECT
TYPE_DEVICE TYPE_OBJECT
TYPE_BUS TYPE_OBJECT
TYPE_XILINX_AXI_DMA_DATA_STREAM TYPE_OBJECT
TYPE_XILINX_AXI_DMA_CONTROL_STREAM TYPE_OBJECT
TYPE_XILINX_AXI_ENET_DATA_STREAM TYPE_OBJECT
TYPE_XILINX_AXI_ENET_CONTROL_STREAM TYPE_OBJECT
TYPE_NETFILTER TYPE_OBJECT
"container" TYPE_OBJECT
TYPE_DIRECT_IMPL TYPE_OBJECT
TYPE_DUMMY TYPE_OBJECT
TYPE_DUMMY_DEV TYPE_OBJECT
TYPE_DUMMY_BUS TYPE_OBJECT
TYPE_DUMMY_BACKEND TYPE_OBJECT
TYPE_NONDEVICE TYPE_OBJECT
TYPE_QEMU_CONSOLE TYPE_OBJECT

TYPE_INTERFACE的一級子類型

name parent
TYPE_ACPI_DEVICE_IF TYPE_INTERFACE
TYPE_ARM_LINUX_BOOT_IF TYPE_INTERFACE
TYPE_FW_PATH_PROVIDER TYPE_INTERFACE
TYPE_HOTPLUG_HANDLER TYPE_INTERFACE
TYPE_NMI TYPE_INTERFACE
TYPE_STREAM_SLAVE TYPE_INTERFACE
TYPE_NVRAM TYPE_INTERFACE
TYPE_USER_CREATABLE TYPE_INTERFACE
TYPE_TEST_IF TYPE_INTERFACE

TypeInfo鏈路

由根TypeInfo出發,可以組成不同的TypeInfo鏈路

  • TYPE_OBJECT -> TYPE_DEVICE -> TYPE_PCI_DEVICE -> TYPE_PCI_TEST_DEV
  • TYPE_OBJECT -> TYPE_DEVICE -> TYPE_SYS_BUS_DEVICE -> TYPE_VIRTIO_PCI
  • TYPE_OBJECT -> TYPE_DEVICE -> TYPE_SYS_BUS_DEVICE -> TYPE_VIRTIO_MMIO
  • TYPE_OBJECT -> TYPE_BUS -> TYPE_PCI_BUS -> TYPE_PCIE_BUS
  • TYPE_OBJECT -> TYPE_BUS -> TYPE_USB_BUS
  • TYPE_OBJECT -> TYPE_BUS -> TYPE_VIRTIO_BUS -> TYPE_VIRTIO_PCI_BUS
  • TYPE_OBJECT -> TYPE_BUS -> TYPE_VIRTIO_BUS -> TYPE_VIRTIO_MMIO_BUS
  • TYPE_OBJECT -> TYPE_MEMORY_BACKEND
  • TYPE_OBJECT -> "container"
  • TYPE_INTERFACE -> TYPE_NMI
  • TYPE_INTERFACE -> TYPE_HOTPLUG_HANDLER -> TYPE_USB_BUS::TYPE_HOTPLUG_HANDLER (動態生成)
  • TYPE_INTERFACE -> TYPE_ACPI_DEVICE_IF
  • TYPE_INTERFACE -> TYPE_USR_CREATABLE -> TYPE_MEMORY_BACKEND::TYPE_USR_CREATABLE (動態生成)

示意圖如下

初始化TypeInfo -> ModuleEntry

每個靜態定義的TypeInfo,通過宏type_init定義的構造函數,在main函數被調用之前生成一個ModuleEntry把typeinfo的註冊函數和類型掛載到全局ModuleTypeList隊列中。

以pci_testdev及其parent爲例,數據結構如下。這邊TypeInfo指向ModuleEntry.init的箭頭表示TypeInfo會作爲ModuleEntry.init註冊函數的輸入形參。

 

Dump TypeInfo

寫個python抓取QEMU代碼中所有的TypeInfo定義,保存到typeinfo.csv文件中。

# -*- coding: utf-8 -*-
import os
import re
import pandas as pd

typeinfos = []
def find_cfile(path):
    g = os.walk(path)  
    cfiles = []
    for path,dir_list,file_list in g:  
        for file_name in file_list:  
            if (file_name.endswith('.c')):
                cfiles.append(os.path.join(path, file_name))
    return cfiles

def find_typeinfo_file(cfiles):
    typeinfo_files = []
    for cfile in cfiles:
        with open(cfile, 'r') as f:
            while True:
                line = f.readline()     # 逐行讀取
                if not line:
                    break
                if (line.find('static const TypeInfo') != -1):
                    typeinfo_files.append(cfile)
                    break
    return typeinfo_files
                    
def find_typeinfos(typeinfo_files):
    for typeinfo_file in typeinfo_files:
        find_typeinfo(typeinfo_file)
    
def find_typeinfo(file):
    with open(file, 'r') as f:
        lines = f.readlines()     # 全部讀取
    i = 0
    while True:
        if i == len(lines):
            break
        if lines[i].find('static const TypeInfo') != -1:
            i = parse_typeinfo(file, lines, i)
        else:
            i += 1

def parse_typeinfo (file, lines, i):
    typeinfo = {}
    typeinfo['file'] = file
    typeinfo['name'] = lines[i].split()[3]
    typeinfos.append(typeinfo)
    i += 1
    i = pasre_typeinfo_tokens(lines, i, typeinfo)    
    return i

def pasre_typeinfo_tokens (lines, i, typeinfo):
    while True:
        if lines[i].find('.interfaces') != -1:
            i = pasre_typeinfo_interface(lines, i, typeinfo)
        elif lines[i].find('};') != -1:
            return i+1
        else:
            l = lines[i].split()
            if l[0].startswith('.'):
                typeinfo[l[0][1:]] = l[2][:-1]
            i += 1
    return i

def pasre_typeinfo_interface (lines, i, typeinfo):
    i += 1
    interface = []
    while True:
        pattern = re.compile(r'{.+}')
        result = pattern.findall(lines[i])
        if len(result) == 0:
            break
        for r in result:
            r = r[1:-1]
            r = r.strip()
            if len(r) == 0:
                continue
            interface.append(r)
        i += 1
    typeinfo['interface'] = interface
    return i

def dump_typeinfo(qemupath):
    cfiles = find_cfile(qemupath)
    typeinfo_files = find_typeinfo_file(cfiles)
    find_typeinfos(typeinfo_files)
    pd.DataFrame(typeinfos).to_csv('typeinfo.csv',encoding='utf_8_sig')
    return typeinfos

def main():
    dump_typeinfo ("C:/qemu")    

if __name__ == "__main__":
    main()

TypeImpl

每個ypInfo都會註冊一個TypeImpl,所有的TypeImpl會以類型名爲鍵值保存在全局的hash表中。

struct TypeImpl
{
    //和TypeInfo一一對應部分 - 開始
    const char *name;

    size_t class_size;

    size_t instance_size;

    void (*class_init)(ObjectClass *klass, void *data);
    void (*class_base_init)(ObjectClass *klass, void *data);
    void (*class_finalize)(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;
    //和TypeInfo一一對應部分 - 結束

    TypeImpl *parent_type;                     //父類型的TypeImpl指針

    ObjectClass *class;                        //類型對應的對象類的基類ObjectClass的指針,因爲基類是派生類的第一個元素,所以派生對象類指針也相同

    int num_interfaces;                        //TypeInfo中定義的接口數組的個數
    InterfaceImpl interfaces[MAX_INTERFACES];  //複製自TypeInfo中InterfaceInfo數組提供的接口名字
};

struct InterfaceImpl
{
    const char *typename;                //InterfaceImpl數組也是提供接口的名字
};

#define MAX_INTERFACES 32                //TypeInfo中沒有個數的限制,TypeImpl中新增加了個數限制,一個類型支持的接口類型最大數目爲32

type_new函數會根據TypeInfo輸入,初始化生成性的TypeImpl結構。

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();
    }

    //從TypeInfo複製數據
    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->class_init = info->class_init;
    ti->class_base_init = info->class_base_init;
    ti->class_finalize = info->class_finalize;
    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;
}

初始化 ModuleEntry -> TypeImpl

QEMU入口main函數的一開始就執行module_call_init(MODULE_INIT_QOM)

int main(int argc, char **argv, char **envp)
{
...
    module_call_init(MODULE_INIT_QOM);
...
}

module_call_init找到之前註冊的所有的init函數,然後執行它

void module_call_init(module_init_type type)
{
    ModuleTypeList *l;
    ModuleEntry *e;

    module_load(type);
    l = find_type(type);

    QTAILQ_FOREACH(e, l, node) {
        e->init();                      //找到之前註冊的init函數,調用執行
    }
}

以pci_testdev爲例,init函數爲pci_testdev_register_types,真正執行註冊的函數爲type_register_internal,調用關係爲pci_testdev_register_types->type_register_static->type_register->type_register_internal。

static void pci_testdev_register_types(void)
{
    //直接調用type_register_static,並以TypeInfo結構地址爲輸入參數
    type_register_static(&pci_testdev_info);
}

TypeImpl *type_register_static(const TypeInfo *info)
{
    //直接調用type_register
    return type_register(info);
}

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

    type_table_add(ti);
    return ti;
}

type_register_internal首先調用type_new創建一個TypeImpl結構,並根據TypeInfo初始化它。隨後調用type_table_add把TypeImpl添加到hash表,鍵值爲TypeImpl.name

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

static GHashTable *type_table_get(void)
{
    static GHashTable *type_table;       //靜態變量,保存TypeImplhash表地址

    //第一次需要初始化
    if (type_table == NULL) {
        type_table = g_hash_table_new(g_str_hash, g_str_equal);
    }

    return type_table;         //返回hash表地址
}

總結如下

對象類

基對象類ObjectClass

struct ObjectClass
{
    /*< private >*/
    Type type;                       //對象類對應的類型TypeImpl的指針
    GSList *interfaces;              //接口類InterfaceClass指針單向列表,函數type_initialize_interface初始化

    const char *object_cast_cache[OBJECT_CLASS_CAST_CACHE];      //debug選項,保存object cast歷史
    const char *class_cast_cache[OBJECT_CLASS_CAST_CACHE];       //debug選項,保存object class的cast歷史

    ObjectUnparent *unparent;
};

設備派生對象類

示例PCIDeviceClass對象類的派生關係是ObjectClass->DeviceClass->PCIDeviceClass。

總線派生對象類

示例PciBusClass的派生順序爲ObjectClass -> BusClass -> PCIBusClass

接口對象類

示例UserCreatableClass的派生順序爲ObjectClass -> InterfacClass -> HotplugHandlerClass

內存派生對象類

示例HostMemoryBackendClass的派生順序爲ObjectClass -> HostMemoryBackendClass

加速器派生對象類

示例AccelClass的派生順序爲ObjectClass -> AccelClass

初始化 TypeImpl -> ObjectClass

對象類的初始化是由TypeImpl.class_init函數完成的。TypeImpl.class_init函數在type_initialize函數中被調用。

type_initialize調用有幾種情況

  • 當要獲取一種類型的所有對象類時主動調用循環初始化每個對象類,object_class_get_list -> object_class_foreach -> object_class_foreach_tramp -> type_initialize
  • 通過對象類查找父對象類,object_class_get_parent -> type_initialize
  • 通過類型名查找對象類,object_new_with_props -> object_new_with_propv -> object_class_by_name -> type_initialize
  • 新建一個對象實例, object_get_root -> object_new -> object_new_with_type -> type_initialize
  • 初始化一個對象實例,object-initialize - > object_initlialize_with_type
  • 初始化對象類的接口,object_intialize_interface -> type_initialize
  • 遞歸調用父對象類,type_intialize -> type_initialize

調用關係如圖示

type_initialize函數完成如下工作。

  • 申請對象類空間
  • 調用type_initialize_interface初始化設備接口
  • 調用parent的class_base_init
  • 調用本類型的class_init
static void type_initialize(TypeImpl *ti)
{
    TypeImpl *parent;

    if (ti->class) {                                         //對象類建立了就表示已經初始化過了,直接返回
        return;
    }

    ti->class_size = type_class_get_size(ti);                //type_new()時初始化過,這裏校正一下
    ti->instance_size = type_object_get_size(ti);            //type_new()時初始化過,這裏校正一下

    ti->class = g_malloc0(ti->class_size);                   //爲對象類申請空間

    parent = type_get_parent(ti);                            //獲取parent的TypeImpl
    if (parent) {
        type_initialize(parent);                             //先初始化parent
        GSList *e;
        int i;

        g_assert_cmpint(parent->class_size, <=, ti->class_size);
        memcpy(ti->class, parent->class, parent->class_size);      //繼承parent的對象類,複製到最前面
        //parent對象類已經註冊的接口
        for (e = parent->class->interfaces; e; e = e->next) {   //loop已經註冊的interface隊列
            InterfaceClass *iface = e->data;     // 接口對象類的data保持的是InterfaceClass的指針
            ObjectClass *klass = OBJECT_CLASS(iface);   //cast到基類
            type_initialize_interface(ti, iface->interface_type, klass->type);  //iface->interface_type和klass->type有區別嗎?
        }

        //TypeImpl中定義的接口
        for (i = 0; i < ti->num_interfaces; i++) {
            TypeImpl *t = type_get_by_name(ti->interfaces[i].typename); //通過接口類型名找到接口通過TypeInfo註冊的TypeImpl的指針
            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->type = ti;                                        //設置TypeImpl指針到對象類的type

    while (parent) {
        if (parent->class_base_init) {
            parent->class_base_init(ti->class, ti->class_data);  //調用所有parent對象類的class_base_init函數
        }
        parent = type_get_parent(parent);
    }

    if (ti->class_init) {
        ti->class_init(ti->class, ti->class_data);               //調用對象類初始化函數class_init
    }
}

type_initialize_interface

首先每個接口類型會定義自己的TypeInfo,然後註冊到TypeImpl全局hash表。例如hotplug_handler_info。在隨後的初始化type_initialize函數中接口類型會生成自己的對象類,我們暫且稱之爲接口對象類。所有接口類型及其父類的TypeInfo都沒有定義instance_size,所以不存在接口對象實例。

static const TypeInfo hotplug_handler_info = {
    .name          = TYPE_HOTPLUG_HANDLER,
    .parent        = TYPE_INTERFACE,
    .class_size = sizeof(HotplugHandlerClass),
};

另一方面,有些QOM設備的TypeInfo定義中,會添加自己支持的接口類型,暫且稱爲設備接口。例如usb_bus_info和pcie_slot_type_info。這些interfaces隨後在TypeImpl註冊過程中,會添加到數組 InterfaceImpl interfaces[MAX_INTERFACES]中。interfaces的數目設置到num_interfaces。

static const TypeInfo usb_bus_info = {
    .name = TYPE_USB_BUS,
    .parent = TYPE_BUS,
    .instance_size = sizeof(USBBus),
    .class_init = usb_bus_class_init,
    .interfaces = (InterfaceInfo[]) {
        { TYPE_HOTPLUG_HANDLER },
        { }
    }
};

static const TypeInfo pcie_slot_type_info = {
    .name = TYPE_PCIE_SLOT,
    .parent = TYPE_PCIE_PORT,
    .instance_size = sizeof(PCIESlot),
    .abstract = true,
    .class_init = pcie_slot_class_init,
    .interfaces = (InterfaceInfo[]) {
        { TYPE_HOTPLUG_HANDLER },
        { }
    }
};

Type_initialize_interface負責初始化設備接口,並把設備接口掛在到對象類的ObjectClass->interfaces隊列上。每個TypeImpl需要初始化有兩類接口

  • 已經掛在到其parent的對象類interfaces隊列上上的接口,需要繼承過來。
  • TypeImpl自己的interfaces數組上的接口。

Type_initialize_interface定義如下

static void type_initialize_interface(TypeImpl *ti, TypeImpl *interface_type,
                                      TypeImpl *parent_type)
{
    InterfaceClass *new_iface;
    TypeInfo info = { };
    TypeImpl *iface_impl;

    //初始化一個新的類型接口TypeInfo,名稱問"設備類型名::接口類型名"
    info.parent = parent_type->name;
    info.name = g_strdup_printf("%s::%s", ti->name, interface_type->name);
    info.abstract = true;

    //根據新初始化的TypeInfo,註冊新的接口TypeImpl,同時會生成新的對象類InterfaceClass
    iface_impl = type_new(&info);                      //初始化新的類型接口TypeImpl
    iface_impl->parent_type = parent_type;
    type_initialize(iface_impl);                       //初始新的類型接口對象類
    g_free((char *)info.name);

    //初始化新生成的對象類InterfaceClass
    new_iface = (InterfaceClass *)iface_impl->class;   //cast到InterfaceClass
    new_iface->concrete_class = ti->class;             //InterfaceClass->concrete_class指向的接口通過Typeinfo註冊TypeImpl時,在type_initialize中申請的對象類。即接口對象類
    new_iface->interface_type = interface_type;        //指向接口TypeImpl類型

    //添加新的接口對象類到類型的對象類的接口單向隊列
    ti->class->interfaces = g_slist_append(ti->class->interfaces,
                                           iface_impl->class);
}

//每個接口
//1,在type_init構造中會註冊全局的接口TypeImpl(名稱"接口類型名")和接口對象類,
//2,在本函數中,註冊一個爲本實例類型服務的設備接口TypeImpl(名稱"設備類型名::接口類型名")和設備接口對象類。

class_base_init

在調用class_init之前,先需要調用父類的class_base_init。

對於pci-testdev設備,只有DeviceClass對象類的class_init爲device_class_init,如下

static void device_class_base_init(ObjectClass *class, void *data)
{
    DeviceClass *klass = DEVICE_CLASS(class);

    /* We explicitly look up properties in the superclasses,
     * so do not propagate them to the subclasses.
     */
    klass->props = NULL;
}

TYPE_MACHINE有定義一個class_base_init,如下

static void machine_class_base_init(ObjectClass *oc, void *data)
{
    if (!object_class_is_abstract(oc)) {
        MachineClass *mc = MACHINE_CLASS(oc);
        const char *cname = object_class_get_name(oc);
        assert(g_str_has_suffix(cname, TYPE_MACHINE_SUFFIX));
        mc->name = g_strndup(cname,
                            strlen(cname) - strlen(TYPE_MACHINE_SUFFIX));
    }
}

class_init

對象類的class_init初始化從基類開始調用

  • ObjectClass基類沒有class_init
  • DeviceClass對象類的class_init爲device_class_init
  • PciDeviceClass對象類的class_init爲pci_device_class_init
  • Pci-testdev沒有定義新的對象類,定義了class_init爲pci_testdev_class_init

device_class_init定義如下

static void device_class_init(ObjectClass *class, void *data)
{
    DeviceClass *dc = DEVICE_CLASS(class);

    class->unparent = device_unparent;
    dc->realize = device_realize;
    dc->unrealize = device_unrealize;

    /* by default all devices were considered as hotpluggable,
     * so with intent to check it in generic qdev_unplug() /
     * device_set_realized() functions make every device
     * hotpluggable. Devices that shouldn't be hotpluggable,
     * should override it in their class_init()
     */
    dc->hotpluggable = true;
}

pci_device_class_init定義如下

static void pci_device_class_init(ObjectClass *klass, void *data)
{
    DeviceClass *k = DEVICE_CLASS(klass);
    PCIDeviceClass *pc = PCI_DEVICE_CLASS(klass);

    k->realize = pci_qdev_realize;
    k->unrealize = pci_qdev_unrealize;
    k->bus_type = TYPE_PCI_BUS;
    k->props = pci_props;
    pc->realize = pci_default_realize;
}

pci_testdev_class_init定義如下

static void pci_testdev_class_init(ObjectClass *klass, void *data)
{
    DeviceClass *dc = DEVICE_CLASS(klass);               //通過對象類基類cast到DeviceClass子類
    PCIDeviceClass *k = PCI_DEVICE_CLASS(klass);         //通過對象類基類cast到PCIDeviceClass子類

    k->realize = pci_testdev_realize;                    //override realize方法
    k->exit = pci_testdev_uninit;
    k->vendor_id = PCI_VENDOR_ID_REDHAT;
    k->device_id = PCI_DEVICE_ID_REDHAT_TEST;
    k->revision = 0x00;
    k->class_id = PCI_CLASS_OTHERS;
    dc->desc = "PCI Test Device";
    set_bit(DEVICE_CATEGORY_MISC, dc->categories);
    dc->reset = qdev_pci_testdev_reset;
}

對象

基對象Object

struct Object
{
    /*< private >*/
    ObjectClass *class;                //指向對象類
    ObjectFree *free;                  //當引用計數爲0時,執行釋放方法
    GHashTable *properties;            //屬性hash表
    uint32_t ref;                      //引用計數
    Object *parent;                    //指向父對象,實現繼承
};

設備派生對象

示例PCITestDevState的派生順序是Object->DeviceState->PCIDevice->PCITestDevState。

總線派生對象

示例PCIBus的派生順序爲Object -> BusState -> PCIBus

初始化 TypeImpl -> Object

對象實例的建立是由函數object_new_with_type完成,也有一些對象的空間是靜態分配的或提前準備好的,無需再次動態申請,對於這些對象只需要直接調用初始化函數。對象實例的初始化由object_initialize_with_type完成。

調用關係如下

 

object_new_with_type

Object *object_new_with_type(Type type)
{
    Object *obj;

    g_assert(type != NULL);
    type_initialize(type);                                        //先確保type已經初始化

    obj = g_malloc(type->instance_size);                          //申請對象實例空間
    object_initialize_with_type(obj, type->instance_size, type);  //初始化對象實例
    obj->free = g_free;                                           //基對象的初始化free方法

    return obj;
}

object_initialize_with_type

void object_initialize_with_type(void *data, size_t size, TypeImpl *type)
{
    Object *obj = data;

    g_assert(type != NULL);
    type_initialize(type);                                     //確保type已經初始化過

    //基本check,確保對象實例的大小,TypeImpl不是抽象類型等
    g_assert_cmpint(type->instance_size, >=, sizeof(Object));
    g_assert(type->abstract == false);
    g_assert_cmpint(size, >=, type->instance_size);

    memset(obj, 0, type->instance_size);                       //對象實例清0
    obj->class = type->class;                                  //填寫對象實例中的對象類地址
    object_ref(obj);                                           //對象基類的ref引用數增加1
    obj->properties = g_hash_table_new_full(g_str_hash, g_str_equal,
                                            NULL, object_property_free);  //建立屬性hash表
    object_init_with_type(obj, type);                          //調用.instance_init
    object_post_init_with_type(obj, type);                     //調用.instance_post_init
}

object_init_with_type定義如下

static void object_init_with_type(Object *obj, TypeImpl *ti)
{
    if (type_has_parent(ti)) {
        object_init_with_type(obj, type_get_parent(ti));    //首先執行parent的instance_init
    }

    if (ti->instance_init) {                               //執行instance_init
        ti->instance_init(obj);
    }
}

object_post_init_with_type定義如下

static void object_post_init_with_type(Object *obj, TypeImpl *ti)
{
    if (ti->instance_post_init) {
        ti->instance_post_init(obj);                           //先執行對象自身的instance_post_init
    }

    if (type_has_parent(ti)) {
        object_post_init_with_type(obj, type_get_parent(ti));  //再執行parent對象的instance_post_init
    }
}

調用object_new

來看看是如何調用到object_new的。每個設備在QEMU啓動時都體現爲命令行中一個‘-device’參數。QEMU main函數會解析每個-device參數,調用device_init_func來完成設備的初始化。

int main(int argc, char **argv, char **envp)
{
...
    /* init generic devices */
    if (qemu_opts_foreach(qemu_find_opts("device"), //對於每個“-devcie”參數,調用device_init_func函數
                          device_init_func, NULL, NULL)) {
        exit(1);
    }
...
}

device_init_func定義如下 

static int device_init_func(void *opaque, QemuOpts *opts, Error **errp)
{
    Error *err = NULL;
    DeviceState *dev;

    dev = qdev_device_add(opts, &err);    //添加設備
    if (!dev) {
        error_report_err(err);
        return -1;
    }
    object_unref(OBJECT(dev));            //對象基類的ref減1,當ref值爲1時調用object_finalize
    return 0;
}

qdev_device_add

來看看qdev_device_add的完整代碼

DeviceState *qdev_device_add(QemuOpts *opts, Error **errp)
{
    DeviceClass *dc;
    const char *driver, *path, *id;
    DeviceState *dev;
    BusState *bus = NULL;
    Error *err = NULL;

    driver = qemu_opt_get(opts, "driver");                         //一定要有driver參數?
    if (!driver) {
        error_setg(errp, QERR_MISSING_PARAMETER, "driver");
        return NULL;
    }

    /* find driver */
    dc = qdev_get_device_class(&driver, errp);                     //通過driver名找到對象類DeviceClass
    if (!dc) {
        return NULL;
    }

    /* find bus */
    path = qemu_opt_get(opts, "bus");                              //bus參數
    if (path != NULL) {                                            //bus參數存在
        bus = qbus_find(path, errp);                               //找到bus的對象類
        if (!bus) {
            return NULL;
        }
        if (!object_dynamic_cast(OBJECT(bus), dc->bus_type)) {     //類型check
            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) {                             //bus參數不存在,對象類的bus_type有值
        bus = qbus_find_recursive(sysbus_get_default(), NULL, dc->bus_type);   //通過對象類的bus_type找到bus對象
        if (!bus || qbus_is_full(bus)) {
            error_setg(errp, "No '%s' bus found for device '%s'",
                       dc->bus_type, driver);
            return NULL;
        }
    }
    if (qdev_hotplug && bus && !qbus_is_hotpluggable(bus)) {
        error_setg(errp, QERR_BUS_NO_HOTPLUG, bus->name);
        return NULL;
    }

    /* create device */
    dev = DEVICE(object_new(driver));                            //建立設備對象實例

    //bus對象實例存在時,添加設備對象實例到bus對象實例的link屬性,bus對象實例的ref增加1,設置設備對象實例的parent_bus
    if (bus) {
        qdev_set_parent_bus(dev, bus);                           
    }

    id = qemu_opts_id(opts);                                     //從命令行參數獲取id
    if (id) {
        dev->id = id;                                            //保持到DeviceState對象中的id
    }

    //添加對象到qdev的peripheral對象的對象屬性中
    if (dev->id) {                        //id有指定
        //"container/machine/peripheral"
        object_property_add_child(qdev_get_peripheral(), dev->id,
                                  OBJECT(dev), NULL);
    } else {                              //id沒有指定
        static int anon_count;
        gchar *name = g_strdup_printf("device[%d]", anon_count++);
        //"container/machine/peripheral"
        object_property_add_child(qdev_get_peripheral_anon(), name,
                                  OBJECT(dev), NULL);
        g_free(name);
    }

    /* set properties */
    if (qemu_opt_foreach(opts, set_property, dev, &err)) {          //解析QEMU命令行參數中的對象屬性,執行set方法
        error_propagate(errp, err);
        object_unparent(OBJECT(dev));
        object_unref(OBJECT(dev));
        return NULL;
    }

    dev->opts = opts;
    object_property_set_bool(OBJECT(dev), true, "realized", &err);   //執行realize對象屬性的set方法
    if (err != NULL) {
        error_propagate(errp, err);
        dev->opts = NULL;
        object_unparent(OBJECT(dev));
        object_unref(OBJECT(dev));
        return NULL;
    }
    return dev;
}

instance_init

instance_init的執行順序是先parent再自己,對於Pci-testdev

  • TYPE_OBJECT的instance_init爲object_instance_init
  • TYPE_DEVICE的instance_init爲device_initfn
  • TYPE_PCI_DEVICE的instance_init定義爲空
  • TYPE_PCI_TEST_DEV的instance_init定義爲空

object_instance_init

static void object_instance_init(Object *obj)
{
    object_property_add_str(obj, "type", qdev_get_type, NULL, NULL);   //添加“type”爲string對象屬性
}

device_initfn

static void device_initfn(Object *obj)
{
    DeviceState *dev = DEVICE(obj);        //通過對象基類cast到DeviceState
    ObjectClass *class;
    Property *prop;

    if (qdev_hotplug) {                    //設置支持hotplug的flag
        dev->hotplugged = 1;
        qdev_hot_added = true;
    }

    dev->instance_id_alias = -1;
    dev->realized = false;

    //添加“realized”,"hotpluggable","hotplugged"等bool對象屬性
    object_property_add_bool(obj, "realized",
                             device_get_realized, device_set_realized, NULL);
    object_property_add_bool(obj, "hotpluggable",
                             device_get_hotpluggable, NULL, NULL);
    object_property_add_bool(obj, "hotplugged",
                             device_get_hotplugged, device_set_hotplugged,
                             &error_abort);

    //把對象類的屬性添加到legacy和static對象屬性中?
    class = object_get_class(OBJECT(dev));
    do {
        for (prop = DEVICE_CLASS(class)->props; prop && prop->name; prop++) {
            qdev_property_add_legacy(dev, prop, &error_abort);
            qdev_property_add_static(dev, prop, &error_abort);
        }
        class = object_class_get_parent(class);
    } while (class != object_class_by_name(TYPE_DEVICE));

    //添加parent_bus到link對象屬性
    object_property_add_link(OBJECT(dev), "parent_bus", TYPE_BUS,
                             (Object **)&dev->parent_bus, NULL, 0,
                             &error_abort);
    //初始化隊列dev->gpios
    QLIST_INIT(&dev->gpios);
}

instance_post_init

instance_post_init的執行順序是先自己再parent,對於Pci-testdev

  • TYPE_OBJECT的instance_post_init定義爲空
  • TYPE_DEVICE的instance_post_init爲device_post_init
  • TYPE_PCI_DEVICE的instance_post_init定義爲空
  • TYPE_PCI_TEST_DEV的instance_post_init定義爲空

device_post_init

static void device_post_init(Object *obj)
{
    qdev_prop_set_globals(DEVICE(obj));
}

void qdev_prop_set_globals(DeviceState *dev)
{
    ObjectClass *class = object_get_class(OBJECT(dev));

    //對象類到父對象類遞歸,得到對象類型名稱
    do {
        qdev_prop_set_globals_for_type(dev, object_class_get_name(class));
        class = object_class_get_parent(class);
    } while (class);
}

static void qdev_prop_set_globals_for_type(DeviceState *dev,
                                const char *typename)
{
    GlobalProperty *prop;

    QTAILQ_FOREACH(prop, &global_props, next) {         //全局的隊列global_props
        Error *err = NULL;

        if (strcmp(typename, prop->driver) != 0) {      //全局隊列global_props中找到driver名字與類型名字一致的項
            continue;
        }
        prop->used = true;
        object_property_parse(OBJECT(dev), prop->value, prop->property, &err);   //解析對象是否存在global_props中的屬性,存在的話執行set方法
        if (err != NULL) {
            assert(prop->user_provided);
            error_report("Warning: global %s.%s=%s ignored (%s)",
                         prop->driver, prop->property, prop->value,
                         error_get_pretty(err));
            error_free(err);
            return;
        }
    }
}

屬性

屬性包含兩類,一類是對象(Object)屬性ObjectProperty,一類是對象類屬性Property

ObjectProperty

對象(Object)包含屬性hash表GHashTable *properties,存儲了 屬性名 到 ObjectProperty 的映射。

typedef struct ObjectProperty
{
    gchar *name;                        //屬性名
    gchar *type;                        //屬性類型
                                        //如"int","uint32","size","uint64","bool","string","child<%s>"等
    gchar *description;
    ObjectPropertyAccessor *get;        //讀取屬性時觸發的get方法
    ObjectPropertyAccessor *set;        //設置屬性時觸發的set方法
    ObjectPropertyResolve *resolve;     //resolve方法
    ObjectPropertyRelease *release;     //release方法
    void *opaque;                       //額外的非透明的信息,
                                        //在object_property_add_bool中添加的"bool"屬性中指向BoolProperty
                                        //在object_property_add_child中添加的"child"屬性中指向child的對象實例
                                        //在object_property_add_link中添加的"link"屬性中指向LinkProperty
                                        //在object_property_add_str中添加的"string"屬性中指向StringProperty
                                        //在object_property_add_enum中添加的"string"屬性中指向EnumProperty
} ObjectProperty;

函數object_property_add負責添加

ObjectProperty *
object_property_add(Object *obj, const char *name, const char *type,
                    ObjectPropertyAccessor *get,
                    ObjectPropertyAccessor *set,
                    ObjectPropertyRelease *release,
                    void *opaque, Error **errp)
{
    ObjectProperty *prop;
...
    prop = g_malloc0(sizeof(*prop));

    prop->name = g_strdup(name);
    prop->type = g_strdup(type);

    prop->get = get;
    prop->set = set;
    prop->release = release;
    prop->opaque = opaque;

    g_hash_table_insert(obj->properties, prop->name, prop);   //添加到Object對象的properties hash表
    return prop;
}

get/set callback方法

ObjectProperty的get/set callback方法的調用,還用到了很多中間概念。

第一個概念是QObject,它是每個ObjectProperty get/set callback方法調用過程中的數據載體的基類。定義的是ObjectProperty的類型代碼,引用次數,以及銷燬函數。

typedef struct QObject {
    const QType *type;                         //QObject的代碼及銷燬函數
    size_t refcnt;                             //引用次數
} QObject;

typedef struct QType {
    qtype_code code;                           //代碼
    void (*destroy)(struct QObject *);         //銷燬函數
} QType;

QOBJECT_INIT用於初始化各個派生類型的基類QObject

#define QOBJECT_INIT(obj, qtype_type)   \
    obj->base.refcnt = 1;               \                  //設置引用refcnt爲1
    obj->base.type   = qtype_type                          //設置type爲qstring_type

QObject是一個基類結構,它可以派生出很多類型。這些派生類會根據每個類型,添加上數據選項。

typedef struct QBool {
    QObject base;
    bool value;                //布爾數值
} QBool;

typedef struct QString {
    QObject base;
    char *string;             //字串
    size_t length;            //字串長度
    size_t capacity;          //字串容量,和長度值相同
} QString;

typedef struct QInt {
    QObject base;
    int64_t value;            //整型數值
} QInt;

typedef struct QDict {
    QObject base;
    size_t size;
    QLIST_HEAD(,QDictEntry) table[QDICT_BUCKET_MAX];    //字典
} QDict;

typedef struct QFloat {
    QObject base;
    double value;             //浮點數值
} QFloat;

typedef struct QList {
    QObject base;
    QTAILQ_HEAD(,QListEntry) head;   //隊列
} QList;

每個派生類型的Qtype定義爲

Qtype
.code .destroy
QTYPE_QSTRING qstring_destroy_obj
QTYPE_QBOOL qbool_destroy_obj
QTYPE_QDICT qdict_destroy_obj
QTYPE_QFLOAT qfloat_destroy_obj
QTYPE_QINT qint_destroy_obj
QTYPE_QLIST qlist_destroy_obj
QTYPE_QNULL qnull_destroy_obj

第二個概念是方法Visitor結構,它定義了訪問ObjectProperty各個數據類型的方法。

struct Visitor
{
    /* Must be set */
    void (*start_struct)(Visitor *v, void **obj, const char *kind,
                         const char *name, size_t size, Error **errp);
    void (*end_struct)(Visitor *v, Error **errp);

    void (*start_implicit_struct)(Visitor *v, void **obj, size_t size,
                                  Error **errp);
    void (*end_implicit_struct)(Visitor *v, Error **errp);

    void (*start_list)(Visitor *v, const char *name, Error **errp);
    GenericList *(*next_list)(Visitor *v, GenericList **list, Error **errp);
    void (*end_list)(Visitor *v, Error **errp);

    void (*type_enum)(Visitor *v, int *obj, const char * const strings[],
                      const char *kind, const char *name, Error **errp);
    void (*get_next_type)(Visitor *v, int *kind, const int *qobjects,
                          const char *name, Error **errp);

    void (*type_int)(Visitor *v, int64_t *obj, const char *name, Error **errp);
    void (*type_bool)(Visitor *v, bool *obj, const char *name, Error **errp);
    void (*type_str)(Visitor *v, char **obj, const char *name, Error **errp);
    void (*type_number)(Visitor *v, double *obj, const char *name,
                        Error **errp);
    void (*type_any)(Visitor *v, QObject **obj, const char *name,
                     Error **errp);

    /* May be NULL */
    void (*optional)(Visitor *v, bool *present, const char *name,
                     Error **errp);

    void (*type_uint8)(Visitor *v, uint8_t *obj, const char *name, Error **errp);
    void (*type_uint16)(Visitor *v, uint16_t *obj, const char *name, Error **errp);
    void (*type_uint32)(Visitor *v, uint32_t *obj, const char *name, Error **errp);
    void (*type_uint64)(Visitor *v, uint64_t *obj, const char *name, Error **errp);
    void (*type_int8)(Visitor *v, int8_t *obj, const char *name, Error **errp);
    void (*type_int16)(Visitor *v, int16_t *obj, const char *name, Error **errp);
    void (*type_int32)(Visitor *v, int32_t *obj, const char *name, Error **errp);
    void (*type_int64)(Visitor *v, int64_t *obj, const char *name, Error **errp);
    /* visit_type_size() falls back to (*type_uint64)() if type_size is unset */
    void (*type_size)(Visitor *v, uint64_t *obj, const char *name, Error **errp);
    bool (*start_union)(Visitor *v, bool data_present, Error **errp);
    void (*end_union)(Visitor *v, bool data_present, Error **errp);
};

對於set方法,QmpInputVisitor定義了Vistor方法,以及一個StackObject數組。每個數組元素StackObject是一個QObject的派生。StackObject還包括一個QObject的隊列,以及一個hash表。

struct QmpInputVisitor
{
    Visitor visitor;                          //Visitor方法數組
    StackObject stack[QIV_STACK_SIZE];        //StackObject數組, 最大1024個
    int nb_stack;
    bool strict;
};
#define QIV_STACK_SIZE 1024

typedef struct StackObject
{
    QObject *obj;                             //指向各個派生類型的QObject基類
    const QListEntry *entry;                  //QObject隊列?
    GHashTable *h;                            //爲字典類型準備的hash表
} StackObject;

typedef struct QListEntry {
    QObject *value;
    QTAILQ_ENTRY(QListEntry) next;
} QListEntry;

對於get方法,QmpOutputVisitor定義了Vistor方法,以及一個QStack隊列頭。隊列元素爲QStackEntry。

struct QmpOutputVisitor
{
    Visitor visitor;        //vistor方法
    QStack stack;           //QStack隊列
};

typedef QTAILQ_HEAD(QStack, QStackEntry) QStack;      

typedef struct QStackEntry      //QStack隊列元素爲QStackEntry
{
    QObject *value;                             //QStackEntry是基類QObject的派生
    bool is_list_head;
    QTAILQ_ENTRY(QStackEntry) node;
} QStackEntry;

QmpImputVisitor負責新建一個QmpInputVisitor,並初始化它。

QmpInputVisitor *qmp_input_visitor_new(QObject *obj)
{
    QmpInputVisitor *v;

    v = g_malloc0(sizeof(*v));                            //申請空間

    //初始化Visitor各個方法
    v->visitor.start_struct = qmp_input_start_struct;
    v->visitor.end_struct = qmp_input_end_struct;
    v->visitor.start_implicit_struct = qmp_input_start_implicit_struct;
    v->visitor.end_implicit_struct = qmp_input_end_implicit_struct;
    v->visitor.start_list = qmp_input_start_list;
    v->visitor.next_list = qmp_input_next_list;
    v->visitor.end_list = qmp_input_end_list;
    v->visitor.type_enum = input_type_enum;
    v->visitor.type_int = qmp_input_type_int;
    v->visitor.type_bool = qmp_input_type_bool;
    v->visitor.type_str = qmp_input_type_str;
    v->visitor.type_number = qmp_input_type_number;
    v->visitor.type_any = qmp_input_type_any;
    v->visitor.optional = qmp_input_optional;
    v->visitor.get_next_type = qmp_input_get_next_type;

    qmp_input_push(v, obj, NULL);             //添加QObject到StackObject->stack
    qobject_incref(obj);                      //該QObecjt的引用次數refcnt增加1

    return v;
}

qmp_input_push初始化QmpInputVisitor中的StackObject->stack

static void qmp_input_push(QmpInputVisitor *qiv, QObject *obj, Error **errp)
{
    GHashTable *h;

    if (qiv->nb_stack >= QIV_STACK_SIZE) {
        error_setg(errp, "An internal buffer overran");
        return;
    }

    qiv->stack[qiv->nb_stack].obj = obj;
    qiv->stack[qiv->nb_stack].entry = NULL;
    qiv->stack[qiv->nb_stack].h = NULL;

    if (qiv->strict && qobject_type(obj) == QTYPE_QDICT) {
        h = g_hash_table_new(g_str_hash, g_str_equal);
        qdict_iter(qobject_to_qdict(obj), qdict_add_key, h);
        qiv->stack[qiv->nb_stack].h = h;
    }

    qiv->nb_stack++;
}

對於get方法,qmp_output_visitor_new負責new一個QmpOutputVisitor,並初始化vistor各個 方法和QStack隊列。
 

QmpOutputVisitor *qmp_output_visitor_new(void)
{
    QmpOutputVisitor *v;

    v = g_malloc0(sizeof(*v));

    v->visitor.start_struct = qmp_output_start_struct;
    v->visitor.end_struct = qmp_output_end_struct;
    v->visitor.start_list = qmp_output_start_list;
    v->visitor.next_list = qmp_output_next_list;
    v->visitor.end_list = qmp_output_end_list;
    v->visitor.type_enum = output_type_enum;
    v->visitor.type_int = qmp_output_type_int;
    v->visitor.type_bool = qmp_output_type_bool;
    v->visitor.type_str = qmp_output_type_str;
    v->visitor.type_number = qmp_output_type_number;
    v->visitor.type_any = qmp_output_type_any;

    QTAILQ_INIT(&v->stack);

    return v;
}

每個ObjectProperty的get/set方法的第二個參數就是vistor結構指針

typedef void (ObjectPropertyAccessor)(Object *obj,
                                      struct Visitor *v,
                                      void *opaque,
                                      const char *name,
                                      Error **errp);

通過這個指針也很容易cast到QmpOutputVisitor結構

static QmpOutputVisitor *to_qov(Visitor *v)
{
    return container_of(v, QmpOutputVisitor, visitor);
}

#define container_of(ptr, type, member) ({                      \
        const typeof(((type *) 0)->member) *__mptr = (ptr);     \
        (type *) ((char *) __mptr - offsetof(type, member));})

下面以string屬性爲類,看看具體的get/set方法

先看set,object_property_set_str函數先把string轉換爲QStraing,並添加到QmpInputVisitor中。隨後的Visitor方法再逆向得到string字串,調用StringProperty->set方法完成最終的設置動作。

再看get,object_property_get_str函數先通過object_property_get_qobject獲取String的QString對象,然後通過qstring_get_str得到最終的string字串,最後還需要將refcnt減1,並判斷是否需要銷燬該QObject。

 

部分ObjectProperty的API接口總結

API type get set release opaque
object_property_add_child "child<%typename%>" object_get_child_property NULL object_finalize_child_property Object
object_property_add_str "string" property_get_str property_set_str property_release_str StringProperty
object_property_add_bool "bool" property_get_bool property_set_bool property_release_bool BoolProperty
object_property_add_link "link<%typename%>" object_get_link_property object_set_link_property object_release_link_property LinkProperty
object_property_add_enum input property_get_enum property_set_enum property_release_enum EnumProperty
object_property_add_uint8_ptr "uint8" property_get_uint8_ptr NULL NULL uint8_t
object_property_add_uint16_ptr "uint16" property_get_uint16_ptr NULL NULL uint16_t
object_property_add_uint32_ptr "uint32" property_get_uint32_ptr NULL NULL uint32_t
object_property_add_uint64_ptr "uint64" property_get_uint64_ptr NULL NULL uint64_t
object_property_add_alias "link<%typename%>"
or %typename%
property_get_alias property_set_alias property_release_alias AliasProperty
object_property_add input input input input input
qdev_property_add_legacy "legacy-%Propertyname%" qdev_get_legacy_property NULL NULL Property
qdev_property_add_static "%Propertyname%" Property.info->get Property.info->set Property.info->release Property

Property

對象類DeviceClass (派生自ObjectClass基類)包含Property列表

typedef struct DeviceClass {
    /*< private >*/
    ObjectClass parent_class;
    /*< public >*/
...
    Property *props;          //屬性列表
...
} DeviceClass;

Property結構定義如下

struct Property {
    const char   *name;
    PropertyInfo *info;
    ptrdiff_t    offset;
    uint8_t      bitnr;
    qtype_code   qtype;
    int64_t      defval;
    int          arrayoffset;
    PropertyInfo *arrayinfo;
    int          arrayfieldsize;
};

struct PropertyInfo {
    const char *name;
    const char *description;
    const char * const *enum_table;
    int (*print)(DeviceState *dev, Property *prop, char *dest, size_t len);
    ObjectPropertyAccessor *get;
    ObjectPropertyAccessor *set;
    ObjectPropertyRelease *release;
};

DeviceClass.props一般在對象類的class_init中執行,屬性列表一般爲靜態的,初始化就是指針賦值動作。

static void host_x86_cpu_class_init(ObjectClass *oc, void *data)
{
    DeviceClass *dc = DEVICE_CLASS(oc);
...
    dc->props = host_x86_cpu_properties;
...
}

//靜態屬性列表
static Property host_x86_cpu_properties[] = {
    DEFINE_PROP_BOOL("migratable", X86CPU, migratable, true),
    DEFINE_PROP_BOOL("host-cache-info", X86CPU, cache_info_passthrough, false),
    DEFINE_PROP_END_OF_LIST()
};

DeviceClass.class_init方法把Property屬性會轉化爲ObjectProperty

static void device_initfn(Object *obj)
{
...
    class = object_get_class(OBJECT(dev));
    do {
        for (prop = DEVICE_CLASS(class)->props; prop && prop->name; prop++) {
            qdev_property_add_legacy(dev, prop, &error_abort);     //名爲"legacy-%接口名%"的字串屬性
            qdev_property_add_static(dev, prop, &error_abort);     //以Property信息爲input
        }
        class = object_class_get_parent(class);
    } while (class != object_class_by_name(TYPE_DEVICE));
...
}

GlobalProperty

GlobalProperty定義如下,它包含QTAIL_ENTRY隊列

typedef struct GlobalProperty {
    const char *driver;
    const char *property;
    const char *value;
    bool user_provided;
    bool used;
    QTAILQ_ENTRY(GlobalProperty) next;
} GlobalProperty;

所有的GlobalProperty是掛載到QTAIL_HEAD隊列頭global_props

static QTAILQ_HEAD(, GlobalProperty) global_props =
        QTAILQ_HEAD_INITIALIZER(global_props);

void qdev_prop_register_global(GlobalProperty *prop)
{
    QTAILQ_INSERT_TAIL(&global_props, prop, next);
}

void qdev_prop_register_global_list(GlobalProperty *props)
{
    int i;

    for (i = 0; props[i].driver != NULL; i++) {
        qdev_prop_register_global(props+i);
    }
}

添加GlobalProperty的幾種方法

  • 命令行參數“global”定義多個GlobalProperty,由函數qemu_add_globals添加到隊列中
  • 特別的一些QEMU命令行參數,例如“rtc_td_hack”,定義一個GlobalProperty entry,通過qdev_prop_register_global_list函數添加
  • 定義machine時,SET_MACHINE_COMPAT宏定義MachineClass.compat_props,隨後通過qdev_prop_register_global_list函數來添加到隊列中

註冊好的GloabalProperty在device_post_init函數中會被使用。device_post_init爲TYPE_DEVICE的instance_post_init。每個QOM設備在初始化過程中都需要調用到該函數。查看每個設備是否包含GloabalProperty定義的屬性,找到的話就調用它的set方法。

device_post_init 
  -> qdev_prop_set_globals 
       -> loop device and its parent 調用 qdev_prop_set_globals_for_type
           -> loop GlobalProperty in global_props隊列,調用object_property_parse
                -> string_input_visitor_new 新建並初始化StringInputVisitor
                -> object_property_set
                     -> ObjectProperty->set
                -> string_input_visitor_cleanup

"container"屬性

Qdev使用“containner"屬性來實現device tree功能。

在qom/container.c中定義了container設備的TypeInfo信息。

static const TypeInfo container_info = {
    .name          = "container",
    .instance_size = sizeof(Object),
    .parent        = TYPE_OBJECT,
};

object_get_root接口生成root 對象。

Object *object_get_root(void)
{
    static Object *root;

    if (!root) {
        root = object_new("container");   //生成“container”對象
    }

    return root;
}

container_get接口用於建立設備的path tree。爲每一級path建立一個“container”對象,然後把每一級path路徑作爲child屬性添加到這一級的“container”對象上。

Object *container_get(Object *root, const char *path)
{
    Object *obj, *child;
    gchar **parts;
    int i;

    parts = g_strsplit(path, "/", 0);
    assert(parts != NULL && parts[0] != NULL && !parts[0][0]);
    obj = root;

    for (i = 1; parts[i] != NULL; i++, obj = child) {
        child = object_resolve_path_component(obj, parts[i]);       //找到路徑的對象
        if (!child) {
            child = object_new("container");                        //沒有找到,新建一個
            object_property_add_child(obj, parts[i], child, NULL);  //添加路徑到對象屬性
        }
    }

    g_strfreev(parts);

    return obj;
}

例如qdev_get_machine會調用container_get,建立/machine路徑。

Object *qdev_get_machine(void)
{
    static Object *dev;

    if (dev == NULL) {
        dev = container_get(object_get_root(), "/machine");
    }

    return dev;
}

 

Reference

https://www.binss.me/blog/qemu-note-of-qemu-object-model/

https://terenceli.github.io/%E6%8A%80%E6%9C%AF/2017/01/08/qom-introduction

http://juniorprincewang.github.io/2018/07/23/qemu%E6%BA%90%E7%A0%81%E6%B7%BB%E5%8A%A0%E8%AE%BE%E5%A4%87/

https://www.linux-kvm.org/images/0/0b/Kvm-forum-2013-Modern-QEMU-devices.pdf

https://translate.googleusercontent.com/translate_c?anno=2&depth=1&hl=zh-CN&rurl=translate.google.com&sl=en&sp=nmt4&tl=zh-CN&u=http://people.redhat.com/~thuth/blog/qemu/2018/09/10/instance-init-realize.html&xid=17259,15700022,15700186,15700190,15700256,15700259,15700262,15700265&usg=ALkJrhjdyz0aUXPhoQIA85_LRDTinfNeoA

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章