Full Analysis of QEMU Source Code 23 - Introduction to QOM (12)

Continued from the previous article: Full Analysis of QEMU Source Code 22 —— Introduction to QOM (11)

References for the content of this article:

"Interesting Talk about Linux Operating System " —— Liu Chao, Geek Time

" QEMU /KVM" source code analysis and application - Li Qiang, Machinery Industry Press

Thank you very much!

The last time we analyzed the QEMU object construction and initialization function call process, this time we will introduce and analyze this process in depth with examples.

Still take the TypeInfo of edu above as an example. Post the edu-related code again as follows (in hw/misc/edu.c):

static void pci_edu_register_types(void)
{
    static InterfaceInfo interfaces[] = {
        { INTERFACE_CONVENTIONAL_PCI_DEVICE },
        { },
    };
    static const TypeInfo edu_info = {
        .name          = TYPE_PCI_EDU_DEVICE,
        .parent        = TYPE_PCI_DEVICE,
        .instance_size = sizeof(EduState),
        .instance_init = edu_instance_init,
        .class_init    = edu_class_init,
        .interfaces = interfaces,
    };

    type_register_static(&edu_info);
}
type_init(pci_edu_register_types)

The object size of edu is sizeof(EduState), so in fact, an object of type edu is an EduState structure, and each object will have a XXXState corresponding to it, which records the relevant information of the object. If edu is a PCI device, there will be some information about this device in EduState, such as: interrupt information, device status, memory area corresponding to MMIO and PIO used, etc.

It can be seen in the object_init_with_type function that the parameter of the call is an Object, but it can always call the initialization function of the parent type. Not surprisingly, there will be a hierarchical relationship here. For ease of understanding, the relevant code is posted again:

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

Take a look at the EduState structure specifically, which is defined in hw/misc/edu.c, as follows:

struct EduState {
    PCIDevice pdev;
    MemoryRegion mmio;

    QemuThread thread;
    QemuMutex thr_mutex;
    QemuCond thr_cond;
    bool stopping;

    uint32_t addr4;
    uint32_t fact;
#define EDU_STATUS_COMPUTING    0x01
#define EDU_STATUS_IRQFACT      0x80
    uint32_t status;

    uint32_t irq_status;

#define EDU_DMA_RUN             0x1
#define EDU_DMA_DIR(cmd)        (((cmd) & 0x2) >> 1)
# define EDU_DMA_FROM_PCI       0
# define EDU_DMA_TO_PCI         1
#define EDU_DMA_IRQ             0x4
    struct dma_state {
        dma_addr_t src;
        dma_addr_t dst;
        dma_addr_t cnt;
        dma_addr_t cmd;
    } dma;
    QEMUTimer dma_timer;
    char dma_buf[DMA_SIZE];
    uint64_t dma_mask;
};

Focus on the type of the first member: PCIDevice structure. It corresponds to pci_device_type_info, the code is as follows (in hw/pci/pci.c):

static const TypeInfo pci_device_type_info = {
    .name = TYPE_PCI_DEVICE,
    .parent = TYPE_DEVICE,
    .instance_size = sizeof(PCIDevice),
    .abstract = true,
    .class_size = sizeof(PCIDeviceClass),
    .class_init = pci_device_class_init,
    .class_base_init = pci_device_class_base_init,
};

struct PCIDevice is defined in include/hw/pci/pci.h, the code is as follows:

struct PCIDevice {
    DeviceState qdev;
    bool partially_hotplugged;
    bool has_power;

    /* PCI config space */
    uint8_t *config;

    /* Used to enable config checks on load. Note that writable bits are
     * never checked even if set in cmask. */
    uint8_t *cmask;

    /* Used to implement R/W bytes */
    uint8_t *wmask;

    /* Used to implement RW1C(Write 1 to Clear) bytes */
    uint8_t *w1cmask;

    /* Used to allocate config space for capabilities. */
    uint8_t *used;

    /* the following fields are read only */
    int32_t devfn;
    /* Cached device to fetch requester ID from, to avoid the PCI
     * tree walking every time we invoke PCI request (e.g.,
     * MSI). For conventional PCI root complex, this field is
     * meaningless. */
    PCIReqIDCache requester_id_cache;
    char name[64];
    PCIIORegion io_regions[PCI_NUM_REGIONS];
    AddressSpace bus_master_as;
    MemoryRegion bus_master_container_region;
    MemoryRegion bus_master_enable_region;

    /* do not access the following fields */
    PCIConfigReadFunc *config_read;
    PCIConfigWriteFunc *config_write;

    /* Legacy PCI VGA regions */
    MemoryRegion *vga_regions[QEMU_PCI_VGA_NUM_REGIONS];
    bool has_vga;

    /* Current IRQ levels.  Used internally by the generic PCI code.  */
    uint8_t irq_state;

    /* Capability bits */
    uint32_t cap_present;

    /* Offset of MSI-X capability in config space */
    uint8_t msix_cap;

    /* MSI-X entries */
    int msix_entries_nr;

    /* Space to store MSIX table & pending bit array */
    uint8_t *msix_table;
    uint8_t *msix_pba;

    /* May be used by INTx or MSI during interrupt notification */
    void *irq_opaque;

    MSITriggerFunc *msi_trigger;
    MSIPrepareMessageFunc *msi_prepare_message;
    MSIxPrepareMessageFunc *msix_prepare_message;

    /* MemoryRegion container for msix exclusive BAR setup */
    MemoryRegion msix_exclusive_bar;
    /* Memory Regions for MSIX table and pending bit entries. */
    MemoryRegion msix_table_mmio;
    MemoryRegion msix_pba_mmio;
    /* Reference-count for entries actually in use by driver. */
    unsigned *msix_entry_used;
    /* MSIX function mask set or MSIX disabled */
    bool msix_function_masked;
    /* Version id needed for VMState */
    int32_t version_id;

    /* Offset of MSI capability in config space */
    uint8_t msi_cap;

    /* PCI Express */
    PCIExpressDevice exp;

    /* SHPC */
    SHPCDevice *shpc;

    /* Location of option rom */
    char *romfile;
    uint32_t romsize;
    bool has_rom;
    MemoryRegion rom;
    uint32_t rom_bar;

    /* INTx routing notifier */
    PCIINTxRoutingNotifier intx_routing_notifier;

    /* MSI-X notifiers */
    MSIVectorUseNotifier msix_vector_use_notifier;
    MSIVectorReleaseNotifier msix_vector_release_notifier;
    MSIVectorPollNotifier msix_vector_poll_notifier;

    /* ID of standby device in net_failover pair */
    char *failover_pair_id;
    uint32_t acpi_index;
};

Also pay attention to the type of the first member: DeviceState structure. It corresponds to device_type_info, the code is as follows (hw/core/qdev.c):

static const TypeInfo device_type_info = {
    .name = TYPE_DEVICE,
    .parent = TYPE_OBJECT,
    .instance_size = sizeof(DeviceState),
    .instance_init = device_initfn,
    .instance_post_init = device_post_init,
    .instance_finalize = device_finalize,
    .class_base_init = device_class_base_init,
    .class_init = device_class_init,
    .abstract = true,
    .class_size = sizeof(DeviceClass),
    .interfaces = (InterfaceInfo[]) {
        { TYPE_VMSTATE_IF },
        { TYPE_RESETTABLE_INTERFACE },
        { }
    }
};

struct DeviceState is defined in include/hw/qdev-core.h, the code is as follows:

/**
 * DeviceState:
 * @realized: Indicates whether the device has been fully constructed.
 *            When accessed outside big qemu lock, must be accessed with
 *            qatomic_load_acquire()
 * @reset: ResettableState for the device; handled by Resettable interface.
 *
 * This structure should not be accessed directly.  We declare it here
 * so that it can be embedded in individual device state structures.
 */
struct DeviceState {
    /*< private >*/
    Object parent_obj;
    /*< public >*/

    char *id;
    char *canonical_path;
    bool realized;
    bool pending_deleted_event;
    int64_t pending_deleted_expires_ms;
    QDict *opts;
    int hotplugged;
    bool allow_unplug_during_migration;
    BusState *parent_bus;
    QLIST_HEAD(, NamedGPIOList) gpios;
    QLIST_HEAD(, NamedClockList) clocks;
    QLIST_HEAD(, BusState) child_bus;
    int num_child_bus;
    int instance_id_alias;
    int alias_required_for_version;
    ResettableState reset;
    GSList *unplug_blockers;
};

From the above definitions of edu_info, pci_device_type_info, device_type_info and the corresponding EduState, PCIDevice, and DeviceState, it can be seen that there is actually a parent object and child object relationship between objects. Like types, objects in QOM can also use macros to convert a pointer to an Object object into a pointer to a subclass object. The conversion process is similar to the type ObjectClass and will not be described here.

It can be seen here that, unlike type information and types, objects are created according to needs, and objects will only be created after a device is specified on the command line or a device is hot-swapped. Types and objects are connected through the Class field of Object. This is achieved by obj->class = type->class in the object_initialize_with_type function. The corresponding code is posted again (in qom/object.c):

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

Based on the above (starting from the full analysis of the QEMU source code - QOM introduction (1)), the QOM object can be constructed into three parts: the first part is the type construction, and a TypeImpl hash table is constructed through TypeInfo, which is in the main function completed before main; the second part is the initialization of the type, both of which are global, that is, as long as the compiled QOM object will be called; the third part is the construction of the class object, which is to construct a specific object instance, only The object is only created if the corresponding device is specified on the command line.

Analogous to the reflection mechanism in Java: In Java, for a class, first of all, when writing the code, you need to write a definition of class xxx, and put it in the .class file after compiling, which is in the state of paper. This part corresponds to the first part above - the construction of the type; then, Java will have a Class object for reading and representing the class xxx on this paper. This part corresponds to the second part above - the initialization of the type; finally, Java generates the real object. This section corresponds to the third section above - object construction.

Combined with the actual process to explain: the class_init function will generate XXXClass, which is equivalent to the Class object in Java; TypeImpl will also have an instance_init function, which is equivalent to a constructor, which is used to generate Object according to XXXClass, which is equivalent to the final object in Java reflection created object.

If you want to know what happened next, let's see the next chapter.

Guess you like

Origin blog.csdn.net/phmatthaus/article/details/132033046
Recommended