在上一篇的介绍的main()函数中,其实QEMU并没有很多真正具体化的实质性的初始化,在main()函数中做的最多的事情就是注册——各种类型、回调函数等。当然大部分注册是在constructor中完成的,main()中注册的是与当前配置有关的一些内容。


当要开启一个QEMU实例的时候,最主要的初始化在

machine->init()中,而这个machine是根据启动的qemu及其参数觉得的。相信用过的都知道哪些参数会影响这个machine最后的值,即最终使用哪一个machine类型。

这里以ARM CPU的vexpress-a9这个板子为例。

它的信息被写在vexpress_a9_machine这个结构体中,也是在constructor完成注册,在QEMU初始化时就根据这个结构体完成针对这个板子的初始化。

所以在main()中执行machine->init()所执行的函数其实就是这个结构体中描述的vexpress_a9_init()。

vexpress_a9_init()这个函数里面只调用了一个函数,一个为vexpress系列设计的通用初始化函数,但它的第一参数表明了最终初始化为-a9的开发板信息,而在开始其他通用初始化之前也会先调用a9_daughterboard结构体中描述的a9子板的初始化函数。所以我们先从这个函数开始讲起。(好像前面话讲的略多。。)


一个完整的系统主要包括CPU,memory,以及BUS级连接在BUS上的periphery。

而上面说的这三大部分每一块都有很多可以讲。这篇中自然不可能把他们全部展开。

所以如果想要从本文中了解其中一项的具体信息的话,可能就要让你失望了。(抱歉。又啰嗦了一下。)


所以我们要讨论的这个初始化函数也是按这个节凑来进行的:

CPU->memory->bus and device。


CPU:

如果用户不指定具体CPU类型,此板默认使用ARM cortex-a9的CPU。

QEMU把所有的支持的CPU类型都在程序的constructor部分注册了一遍,所以现在要做的就是根据这个cortex-a9的名字找到对应的CPU信息。然后根据这个信息初始化用户指定个数的CPU实例即可。

对于每一个CPU来说,先调用函数cpu_arm_init()初始化这个CPU实例:

首先先通过已注册的实例初始化函数创建一个ARMCPU类型的结构体,改结构体包含了被创建CPU的所有信息。

主要通过arm_cpu_finalizefn和cortex_a9_initfn来做初始化,前者是所有ARM类型CPU(arm_cpu_type_info)的共用函数,有点像父类的初始化函数,而后者则是(arm_cpus[]) cortex-a9的初始化函数。

前者是初始化CPU直接的联系信息,如把所有CPU连接在以first_cpu为首的CPU链表中、为CPU编号设置NUMA ID等。

后者则是具化到本CPU型号的各种特征以及一些常数寄存器的值。这里要特别提到的是每个ARMCPU结构体中都包含一个叫CPUARMState的结构体,这个结构体在整个CPU模拟中起到至关重要的作用,它包含了CPU几乎所有的寄存器信息,CPU特性等。与他对应的变量名通常叫env,至于这个结构体中具体成员的含义,请参见处理器spec。

完成上述结构体初始化后,在arm_cpu_realize()函数中根据根据之前在env->feature中设置的各种特性位设置不同的配置,具体的配置信息也就不在此多加介绍,因为这个和具体CPU有的spec直接相关,要对着ARM的spec才行。


完成这一步后,接下来就是初始化CPU模拟部分最重要的TCG了:

在arm_translate_init()中,将env即CPU state map到TCG_AREG0这个index的TCG寄存器中,然后将运行时较常访问的通用寄存器flag等都map到以TCG_AREG0所存地址经过便宜后的memory中。这一步是为后续QEMU进入code cache后执行target代码时能存取CPU state做准备。

在完成上面的这些初始化后,就reset CPU,然后进入CPU线程创建和初始化的内容了。

在函数qemu_init_vcpu()中,里面包含了线程创建,TCG的初始化,TB管理等CPU模拟的主要内容,由于内容较多,所以这里不加详细介绍。之后如果有机会就在后续文章中来总结介绍好了。

在为每个CPU做完实例的初始化之后,还有给他们没人配上一个中断控制器,arm_pic_init_cpu(),这个函数比较简单,详询源代码。

CPU初始化就介绍到这里。



由于暂时没有时间了。。后面未完待续。。。