目录
- qemu源码解析【04】qom实例
- 1. type_init()宏
- 2. type_register_static()宏
- 3. arm_sbcon_i2c_init()何时被qemu系统调用
qemu源码解析【04】qom实例
-
qemu源码解析【总目录】
-
继续分析arm_sbcon_i2c实例,代码从行尾往上逐步分析
#include "qemu/osdep.h"
#include "hw/i2c/arm_sbcon_i2c.h"
#include "hw/registerfields.h"
#include "qemu/log.h"
#include "qemu/module.h"
#include "qom/object.h"REG32(CONTROL_GET, 0)
REG32(CONTROL_SET, 0)
REG32(CONTROL_CLR, 4)#define SCL BIT(0)
#define SDA BIT(1)static uint64_t arm_sbcon_i2c_read(void *opaque, hwaddr offset,unsigned size)
{ArmSbconI2CState *s = opaque;switch (offset) {case A_CONTROL_SET:return (s->out & 1) | (s->in << 1);default:qemu_log_mask(LOG_GUEST_ERROR,"%s: Bad offset 0x%x\n", __func__, (int)offset);return -1;}
}static void arm_sbcon_i2c_write(void *opaque, hwaddr offset,uint64_t value, unsigned size)
{ArmSbconI2CState *s = opaque;switch (offset) {case A_CONTROL_SET:s->out |= value & 3;break;case A_CONTROL_CLR:s->out &= ~value;break;default:qemu_log_mask(LOG_GUEST_ERROR,"%s: Bad offset 0x%x\n", __func__, (int)offset);}bitbang_i2c_set(&s->bitbang, BITBANG_I2C_SCL, (s->out & SCL) != 0);s->in = bitbang_i2c_set(&s->bitbang, BITBANG_I2C_SDA, (s->out & SDA) != 0);
}static const MemoryRegionOps arm_sbcon_i2c_ops = {.read = arm_sbcon_i2c_read,.write = arm_sbcon_i2c_write,.endianness = DEVICE_NATIVE_ENDIAN,
};static void arm_sbcon_i2c_init(Object *obj)
{DeviceState *dev = DEVICE(obj);ArmSbconI2CState *s = ARM_SBCON_I2C(obj);SysBusDevice *sbd = SYS_BUS_DEVICE(obj);I2CBus *bus;bus = i2c_init_bus(dev, "i2c");bitbang_i2c_init(&s->bitbang, bus);memory_region_init_io(&s->iomem, obj, &arm_sbcon_i2c_ops, s,"arm_sbcon_i2c", 0x1000);sysbus_init_mmio(sbd, &s->iomem);
}static const TypeInfo arm_sbcon_i2c_info = {.name = TYPE_ARM_SBCON_I2C,.parent = TYPE_SYS_BUS_DEVICE,.instance_size = sizeof(ArmSbconI2CState),.instance_init = arm_sbcon_i2c_init,
};static void arm_sbcon_i2c_register_types(void)
{type_register_static(&arm_sbcon_i2c_info);
}type_init(arm_sbcon_i2c_register_types)
1. type_init()宏
- 注意到上述代码最后一行,有个type_init()宏,这个宏用来注册qemu代码中,所有的qom类型
- 将这个宏简单展开,可以得到如下函数:
static ModuleTypeList *find_type(module_init_type type)
{init_lists();return &init_type_list[type];
}void register_module_init(void (*fn)(void), MODULE_INIT_QOM)
{ModuleEntry *e;ModuleTypeList *l;e = g_malloc0(sizeof(*e));e->init = fn;e->type = MODULE_INIT_QOM;l = find_type(type);QTAILQ_INSERT_TAIL(l, e, node);
}
- 不难看出,qemu中维护了一个全局列表ModuleTypeList * init_type_list[8],用来存放所有类型的初始化接口。qom类型位于init_type_list[MODULE_INIT_QOM]这个节点上
- 所有qom类型经过注册之后,可以得到以下的链表:
- qemu系统启动时,对于qom类型,会逐个执行init_type_list[MODULE_INIT_QOM]列表上,所有的init入口函数,从而完成所有qom类型的初始化
- 对于arm_sbcon_i2c这个实例,也就是执行arm_sbcon_i2c_register_types()入口函数
2. type_register_static()宏
- 对于arm_sbcon_i2c这个实例,它的初始化入口函数arm_sbcon_i2c_register_types(),只有一行代码:
static void arm_sbcon_i2c_register_types(void)
{type_register_static(&arm_sbcon_i2c_info);
}
- 将这个宏定义简单展开,可以得到
static TypeImpl *type_register_internal(const TypeInfo *info)
{TypeImpl *ti;// 检测一下成员name是否合法if (!type_name_is_valid(info->name)) {fprintf(stderr, "Registering '%s' with illegal type name\n", info->name);abort();}// 根据传入的TypeInfo类型,生成对应的TypeImpl类型ti = type_new(info);// 将新生成的TypeImpl类型,添加到全局的hash表中assert(!enumerating_types);g_hash_table_insert(type_table_get(), (void *)ti->name, ti);return ti;
}
- 上述的代码注释已经写清楚了,我们在arm_sbcon_i2c.c中生成了一个TypeInfo类型,然后用这个宏生成对应的TypeImpl类型,基本上两个结构体的成员差不多。然后将这个TypeImpl数据添加到全局的hash表中
3. arm_sbcon_i2c_init()何时被qemu系统调用
- 简单分析了一下,针对这个arm_sbcon_i2c实例,arm_sbcon_i2c_init()的调用层级如以下所示:
- system/main.c,main()函数
- system/vl.c,qemu_init()函数
- system/vl.c,qemu_create_machine()函数
- qom/object.c,object_new_with_class()函数
- qom/object.c,object_new_with_type()函数
- qom/object.c,object_initialize_with_type()函数
- qom/object.c,object_init_with_type()函数,这个函数正式调用每个hash表节点的初始化函数,并且还带了一个递归逻辑,循环初始化父类型:
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);}
}