X86_CPU

正如上一节所说,Qemu中对不同类型的CPU做了具体的模拟。而且根据不同的CPU类型和Machine类型其初始化过程又有些不同。

这里我们就来看看X86_CPU。

继承关系

                                    TYPE_DEVICE
                                    +-------------------------------+
                                    |class_size                     | = sizeof(DeviceClass)
                                    |class_init                     | = device_class_init
                                    |                               |
                                    |instance_size                  | = sizeof(Object)
                                    |instance_init                  | = device_initfn
                                    |instance_finalize              | = device_finalize
                                    |                               |
                                    |realize                        | = x86_cpu_realizefn
                                    +-------------------------------+


                                    TYPE_CPU
                                    +-------------------------------+
                                    |class_size                     | = sizeof(CPUClass)
                                    |class_init                     | = cpu_class_init
                                    |                               |
                                    |instance_size                  | = sizeof(CPUState)
                                    |instance_init                  | = cpu_common_initfn
                                    |instance_finalize              | = cpu_common_finalize
                                    +-------------------------------+



                                    TYPE_X86_CPU
                                    +-------------------------------+
                                    |abstract                       | = true
                                    |class_size                     | = sizeof(X86CPUClass)
                                    |class_init                     | = x86_cpu_common_class_init
                                    |                               |
                                    |instance_size                  | = sizeof(X86CPU)
                                    |instance_init                  | = x86_cpu_initfn
                                    |                               |
                                    |parent_realize                 | = cpu_common_realizefn
                                    +-------------------------------+


   base-x86_64-cpu                                                    qemu64-x86_64-cpu                                                   host-x86_64-cpu
   +-------------------------------+                                  +-------------------------------+                                   +-------------------------------+
   |class_size                     |                                  |class_size                     |                                   |class_size                     |
   |class_init                     | = x86_cpu_base_class_init        |class_init                     | = x86_cpu_cpudef_class_init       |class_init                     | = host_x86_cpu_class_init
   |                               |                                  |                               |                                   |                               |
   |instance_size                  |                                  |instance_size                  |                                   |instance_size                  |
   |instance_init                  |                                  |instance_init                  |                                   |instance_init                  |
   +-------------------------------+                                  +-------------------------------+                                   +-------------------------------+

这个继承关系略有点让人烦

  • 首先大家都有一个TYPE_X86_CPU的父类

  • 这个父类下又可以生出好多子类

  • 貌似现在默认使用的是qemu64-x86_64-cpu

类型定义

首先我们要来说说CPU类型定义,不仅使用了常见的方式,还用了一个函数来注册CPU类型。

static void x86_cpu_register_types(void)
{
    int i;

    type_register_static(&x86_cpu_type_info);
    for (i = 0; i < ARRAY_SIZE(builtin_x86_defs); i++) {
        x86_register_cpudef_type(&builtin_x86_defs[i]);
    }
    type_register_static(&max_x86_cpu_type_info);
    type_register_static(&x86_base_cpu_type_info);
#if defined(CONFIG_KVM) || defined(CONFIG_HVF)
    type_register_static(&host_x86_cpu_type_info);
#endif
}

是不是比之前的有点丑?丑的还没有展开呢。

x86_register_cpudef_type()会对builtin_x86_defs数组一次进行注册,里面标明了具体cpu的型号和对应能支持的功能。那里面简直就是。。。当然看着看着也就习惯了。

指定CPU类型

既然定义了这么多的CPU类型,那就可以在启动qemu的时候指定才对。方法如下:

   -cpu MODEL

这里的MODEL就是你可以指定的类型了。具体qemu支持哪些类型,可以通过命令qemu -cpu help来查看。

故事讲到这里就结束本来也是可以的,不过我打算再深入看一下代码中是哪里解析命令行的CPU类型并且找到我们注册的这些类型的。

说起来也不难,就是在main函数中调用了parse_cpu_option这个函数。

 parse_cpu_optioin(), parse option "cpu"
    model_pieces = g_strsplit(cpu_option, ",", 2)
    oc = cpu_class_by_name(CPU_RESOLVING_TYPE, model_pieces)
    cpu_type = object_class_get_name(oc);
        cc->class_by_name(cpu_model) = x86_cpu_class_by_name
    cc->parse_features(cpu_type, ) = x86_cpu_parse_featurestr

看明白了,套路就是这么简单。

最终得到的这个类型将被赋值到current_machine->cpu_type等待这后续初始化过程中被使用。

除此之外,还解析了是否有增加或者减少的feature。比如命令行中如下:

-cpu host,+movdir64b,-movdiri

这部分将在初始化cpu的时候用到,这里知道就行了。

初始化

从创建Machine开始

CPU和Machine看来还是非常紧密联系在一起的。这点我们从创建cpu的流程中可以看出。

在PCMachine上,cpu初始化的一个简单流程如下:

 pc_init1/pc_q35_init
   x86_cpus_init
       x86_cpu_new()
           object_new(MACHINE(x86ms)->cpu_type)

看到了熟悉的cpu_type没有?对了,这个就是之前解析cpu类型时得到的值。由此证明了命令行传入的类型会用来创建cpu。

X86 CPU的初始化细节

CPU的初始化涉及到很多内容,比如支持什么特性,创建vcpu线程,如何接收信号等。在这里我们只是宏观得展开,具体细节有待专门章节讲解。

 object_new(MACHINE(x86ms)->cpu_type)
     cpu_common_initfn
          cpu_exec_initfn()
          x86_cpu_initfn
              x86_cpu_register_feature_bit_props()
              x86_cpu_load_model(xcc->model)
     x86_cpu_realizefn
          x86_cpu_expand_features(), expand cpuid
              plus_features, passed from qemu command line
              minus_features, passed from qemu command line
          x86_cpu_filter_features()
          cpu_exec_realizefn()
              cpu_list_add()
              vmstate_register()
          mce_init()
          qemu_init_vcpu
              qemu_kvm_start_vcpu->qemu_kvm_cpu_thread_fn
          x86_cpu_apic_realize()
              object_property_set_bool(OBJECT(cpu->apic_state), true, "realized",)
              Map APIC MMIO area
          xcc->parent_realize = cpu_common_realizefn

作为DEVICE类型的子类,初始化流程自然包含了两个方面:

  • instance_init的层次调用

  • realize函数的调用

仔细看图的朋友估计已经发现了,初始化过程中的第二点,也就是类型中的realize函数发生了微妙的变化。

  • Device类型的realize变成了 x86_cpu_realizefn

  • TYPE_X86_CPU的realize变成了 cpu_common_realizefn

这个偷梁换柱的工作是 x86_cpu_common_class_init 干的。

所以当一个cpu要实例化的时候,调用的realize函数会走到x86_cpu_realizefn,而不是通常的cpu_common_realizefn。

在整个realize过程中,主要完成了几件事:

  • 整理了feature,按照当前的了解就是哪些cpuid是支持的

  • 启动了vcpu线程

  • realize了apic

因为每个cpu对应一个线程,每个cpu上也有自己的apic,所以每个vcpu都会对应去做这样的操作。

Last updated