QEMU RISCV TCG 详解六 -- RISCV CPU 的使能(How a RISCV CPU Realized)
当 RISCV CPU 被创建(Created),被初始化(Initialized)后,要使得其开始运行,需要让其使能(Realized)。Realize 过程如下代码片段:
static void cpu_create(MachineState *machine) {MyMachineState *s = RISCV_VIRT_MACHINE(machine);// create clusterobject_initialize_child(OBJECT(machine), "cluster", &s->cluster,TYPE_CPU_CLUSTER);qdev_prop_set_uint32(DEVICE(&s->cluster), "cluster-id", 0);// create hart array and initialize cpusobject_initialize_child(OBJECT(&s->cluster), "cpus", &s->cpus,TYPE_RISCV_HART_ARRAY);object_property_set_str(OBJECT(&s->cpus), "cpu-type", TYPE_RISCV_CPU_BASE,&error_abort);object_property_set_int(OBJECT(&s->cpus), "hartid-base", 0, &error_abort);object_property_set_int(OBJECT(&s->cpus), "num-harts", 1, &error_abort);object_property_set_int(OBJECT(&s->cpus), "resetvec", 0x0,&error_abort);// realize cpus sysbus_realize(SYS_BUS_DEVICE(&s->cpus), &error_fatal);// realize clusterqdev_realize(DEVICE(&s->cluster), NULL, &error_abort);
}
其中,object_initialize_child() 创建及初始化对应的CPUs,后续的 object_property_set_*() 设置对应的属性,然后调用 sysbus_realize() 来使得 CPUs realized.
bool sysbus_realize(SysBusDevice *dev, Error **errp){return qdev_realize(DEVICE(dev), sysbus_get_default(), errp);
}bool qdev_realize(DeviceState *dev, BusState *bus, Error **errp){
...return object_property_set_bool(OBJECT(dev), "realized", true, errp);
}
当 TYPE_RISCV_HART_ARRAY 的 realized property 被设置为 true 的时候,会调用 riscv_harts_realize() -> riscv_hart_realize(),然后再对单个的CPU 使用 qdev_realize(),从而使得 riscv_cpu_realize() 被调用。
这里使用了 realized property 的机制,当该属性被设置值时,会调用其 set() 函数,即 ObjectPropertyAccessor * ObjectProperty::set。然而 realize property 的定义如下:
static void device_class_init(ObjectClass *class, const void *data){
...object_class_property_add_bool(class, "realized",device_get_realized, device_set_realized);
...}static void device_set_realized(Object *obj, bool value, Error **errp) {DeviceState *dev = DEVICE(obj);DeviceClass *dc = DEVICE_GET_CLASS(dev);...if (value && !dev->realized) {...if (dc->realize) {dc->realize(dev, &local_err);......} else if (!value && dev->realized) {...}
...}
因此,再查看对应的 DeviceClass::realize() 是否被设置,如下:
// Harts' Device Class
static void riscv_harts_class_init(ObjectClass *klass, const void *data){DeviceClass *dc = DEVICE_CLASS(klass);device_class_set_props(dc, riscv_harts_props);dc->realize = riscv_harts_realize;
}// RISCV CPU's Device Class
static void riscv_cpu_common_class_init(ObjectClass *c, const void *data){RISCVCPUClass *mcc = RISCV_CPU_CLASS(c);CPUClass *cc = CPU_CLASS(c);DeviceClass *dc = DEVICE_CLASS(c);
...device_class_set_parent_realize(dc, riscv_cpu_realize,&mcc->parent_realize);
...
}void device_class_set_parent_realize(DeviceClass *dc,DeviceRealize dev_realize,DeviceRealize *parent_realize){*parent_realize = dc->realize;dc->realize = dev_realize;
}
这里 RISCV CPU 保留了其父类的 realize() 函数,即 CPUClass::realize(),并在RISCVCPUClass::realize() 调用的最后阶段调用 CPUClass::realize()。代码如下:
//Set CPUClass::realize()
static void cpu_common_class_init(ObjectClass *klass, const void *data){DeviceClass *dc = DEVICE_CLASS(klass);
...dc->realize = cpu_common_realizefn;
...}static void riscv_cpu_realize(DeviceState *dev, Error **errp){CPUState *cs = CPU(dev);
...qemu_init_vcpu(cs);cpu_reset(cs);mcc->parent_realize(dev, errp);
}
当 RISCV CPU 进行 realize 的时候,调用了 qemu_init_vcpu(),用于创建CPU的执行过程,如下:
void qemu_init_vcpu(CPUState *cpu){MachineState *ms = MACHINE(qdev_get_machine());cpu->nr_threads = ms->smp.threads;cpu->stopped = true;cpu->random_seed = qemu_guest_random_seed_thread_part1();if (!cpu->as) {/* If the target cpu hasn't set up any address spaces itself,* give it the default one.*/cpu->num_ases = 1;cpu_address_space_init(cpu, 0, "cpu-memory", cpu->memory);}/* accelerators all implement the AccelOpsClass */g_assert(cpus_accel != NULL && cpus_accel->create_vcpu_thread != NULL);cpus_accel->create_vcpu_thread(cpu);while (!cpu->created) {qemu_cond_wait(&qemu_cpu_cond, &bql);}
}
其中,cpus_accel->create_vcpu_thread(),对应的就是 TCG 的 mttcg_start_vcpu_thread(),该接口函数的设置如下:
static void tcg_accel_ops_init(AccelOpsClass *ops){if (qemu_tcg_mttcg_enabled()) {ops->create_vcpu_thread = mttcg_start_vcpu_thread;ops->kick_vcpu_thread = mttcg_kick_vcpu_thread;ops->handle_interrupt = tcg_handle_interrupt;} else {...}ops->cpu_reset_hold = tcg_cpu_reset_hold;ops->supports_guest_debug = tcg_supports_guest_debug;ops->insert_breakpoint = tcg_insert_breakpoint;ops->remove_breakpoint = tcg_remove_breakpoint;ops->remove_all_breakpoints = tcg_remove_all_breakpoints;
}static void tcg_accel_ops_class_init(ObjectClass *oc, const void *data){AccelOpsClass *ops = ACCEL_OPS_CLASS(oc);ops->ops_init = tcg_accel_ops_init;
}
static const TypeInfo tcg_accel_ops_type = {.name = ACCEL_OPS_NAME("tcg"),.parent = TYPE_ACCEL_OPS,.class_init = tcg_accel_ops_class_init,.abstract = true,
};
module_obj(ACCEL_OPS_NAME("tcg"));
static void tcg_accel_ops_register_types(void){type_register_static(&tcg_accel_ops_type);
}
type_init(tcg_accel_ops_register_types);
其中,TCG 就是其中一种QEMU加速器(Accelerator),另外还有 KVM,XEN,HVF等。这个就不展开来说了。
另外,mttcg_start_vcpu_thread() 就接上了本系列的第一篇文章《QEMU RISCV TCG 详解一 -- 主执行循环(Main Execution Loop)》 了。
回到 qemu_init_vcpu() 函数,其中 有 cpu->stopped = true,那么,在TCG的最外层执行循环中,即 mttcg_cpu_thread_fn() 函数中循环,一直等待 CPU stopped 改为 false,从而进入执行状态,代码如下:
/** In the multi-threaded case each vCPU has its own thread. The TLS* variable current_cpu can be used deep in the code to find the* current CPUState for a given thread.*/
static void *mttcg_cpu_thread_fn(void *arg){ ...do {if (cpu_can_run(cpu)) {int r;...r = tcg_cpu_exec(cpu);...}
...} while (!cpu->unplug || cpu_can_run(cpu));
...}
bool cpu_can_run(CPUState *cpu){if (cpu->stop) {return false;}if (cpu_is_stopped(cpu)) {return false;}return true;
}
bool cpu_is_stopped(CPUState *cpu){return cpu->stopped || !runstate_is_running();
}
当整个 SoC(Machine)都创建完成、初始化完成、使能完成,后,qemu主线程会调用 vm_start() -> resume_all_vcpus() -> cpu_resume(),从而改变 cpu->stop 和 cpu->stopped 为 false,让 cpu 执行线程开始运作。
回到 riscv_cpu_realize() 函数,除了 创建及初始化 CPU 执行线程外,还 重置(reset)了 CPU,即:
void cpu_reset(CPUState *cpu){device_cold_reset(DEVICE(cpu));
...}
从而进一步地让 CPU 处于重置状态,等待最初执行的开始。cpu_reset 会最终调用 riscv_cpu_reset_hold() -> cpu_common_reset_hold() 等一系列的 cpu 重置函数 *reset*()。这里可以看到:
static void cpu_common_reset_hold(Object *obj, ResetType type){CPUState *cpu = CPU(obj);
...cpu->cflags_next_tb = -1;
...}
cflags_next_tb 就是在 TCGTBCPUState::cflags 的初始值。
总结一下:RISCV CPU 的初始过程有:
1. RISCV CPU 类型的注册,基于QEMU类型注册机制, type_init();
2. RISCV CPU 的创建,基于 QOM 机制;
3. RISCV CPU 的初始化,基于QOM机制;
4. RISCV CPU 的使得(realize),基于 realize 属性(property)机制,属于 QOM 的一部分;
5. RISCV CPU 的执行(TCG 执行循环)。