CPU也是一种设备,因此CPU类继承自Device类。CPU这种设备相比其他设备来说种类非常繁杂。首先,CPU有着不同的架构,而对于每一种架构的CPU来说,随着时间的推移,CPU厂商也会给该架构的CPU不断地增加新特性和更新换代,这种更新换代造成该架构的CPU也有了各种不同的CPU模型。

以x86 CPU为例,QEMU中可以支持的CPU的模型就包括以下几种,我们可以通过qemu-system-x86_64 -cpu ?命令查看QEMU支持的X86 CPU类型。其中一些CPU模型是QEMU自己定义的,另外一些类型则是直接来自厂商。其中host类型是指将物理机上的CPU特性全部暴露给虚拟机,这样可以全面发挥CPU的性能,但是另一方面,由于全部特性被暴露给虚拟机,会造成具有不同特性的CPU架构或CPU模型的物理机之间动态迁移虚拟机会变得不可靠。

如此众多的CPU架构、CPU模型,而CPU架构、CPU模型天然具有父类和子类的关系。使用面向对象编程模型对它们进行设计是再合适不过了。但是目前对于X86CPU中不同的CPU模型(其他的架构没有仔细看),QEMU只是使用一个描述X86CPU定义的数据结构的数组来表示的。QEMU的代码注释中说明,未来要将所有的CPU模型最终定义为X86CPU的子类。

本文档向大家介绍QEMU中CPU类的设计,以及具体的X86CPU数据结构的实现。

x86           qemu64  QEMU Virtual CPU version 2.5+
x86           phenom  AMD Phenom(tm) 9550 Quad-Core Processor
x86         core2duo  Intel(R) Core(TM)2 Duo CPU     T7700  @ 2.40GHz
x86            kvm64  Common KVM processor
x86           qemu32  QEMU Virtual CPU version 2.5+
x86            kvm32  Common 32-bit KVM processor
x86          coreduo  Genuine Intel(R) CPU           T2600  @ 2.16GHz
x86              486
x86          pentium
x86         pentium2
x86         pentium3
x86           athlon  QEMU Virtual CPU version 2.5+
x86             n270  Intel(R) Atom(TM) CPU N270   @ 1.60GHz
x86           Conroe  Intel Celeron_4x0 (Conroe/Merom Class Core 2)
x86           Penryn  Intel Core 2 Duo P9xxx (Penryn Class Core 2)
x86          Nehalem  Intel Core i7 9xx (Nehalem Class Core i7)
x86         Westmere  Westmere E56xx/L56xx/X56xx (Nehalem-C)
x86      SandyBridge  Intel Xeon E312xx (Sandy Bridge)
x86        IvyBridge  Intel Xeon E3-12xx v2 (Ivy Bridge)
x86    Haswell-noTSX  Intel Core Processor (Haswell, no TSX)
x86          Haswell  Intel Core Processor (Haswell)
x86  Broadwell-noTSX  Intel Core Processor (Broadwell, no TSX)
x86        Broadwell  Intel Core Processor (Broadwell)
x86       Opteron_G1  AMD Opteron 240 (Gen 1 Class Opteron)
x86       Opteron_G2  AMD Opteron 22xx (Gen 2 Class Opteron)
x86       Opteron_G3  AMD Opteron 23xx (Gen 3 Class Opteron)
x86       Opteron_G4  AMD Opteron 62xx class CPU
x86       Opteron_G5  AMD Opteron 63xx class CPU
x86             host  KVM processor with all supported host features (only available in KVM mode)1.2.3.4.5.6.7.8.9.10.11.12.13.14.15.16.17.18.19.20.21.22.23.24.25.26.27.28.29.

所有CPU的基类

要定义所有CPU的基类,需要定义CPU的类的数据结构和CPU的对象的数据结构,然后给对应的TypeInfo中的函数指针赋值即可。其中CPU类的数据结构名为CPUClass、CPU对象的数据结构名为CPUState,它们被定义在include/qom/cpu.h中,而对应的TypeInfo的赋值工作则在qom/cpu.c中进行。这里只说明CPUClass、CPUState数据结构。

CPUClass

CPUClass的数据结构如下,其中最主要的内容,是与CPU相关的大量的回调函数,通过给这些回调函数的指针赋值,对应的CPUState就可以调用这些函数实现相应的功能。

typedef struct CPUClass {
    /*< private >*/
    DeviceClass parent_class;   //父类是Device类
    /*< public >*/

    //回调函数
    ObjectClass *(*class_by_name)(const char *cpu_model);
    void (*parse_features)(CPUState *cpu, char *str, Error **errp);

    void (*reset)(CPUState *cpu);
    int reset_dump_flags;
    bool (*has_work)(CPUState *cpu);
    void (*do_interrupt)(CPUState *cpu);
    CPUUnassignedAccess do_unassigned_access;
    void (*do_unaligned_access)(CPUState *cpu, vaddr addr,
                                int is_write, int is_user, uintptr_t retaddr);
    bool (*virtio_is_big_endian)(CPUState *cpu);
    int (*memory_rw_debug)(CPUState *cpu, vaddr addr,
                           uint8_t *buf, int len, bool is_write);
    void (*dump_state)(CPUState *cpu, FILE *f, fprintf_function cpu_fprintf,
                       int flags);
    void (*dump_statistics)(CPUState *cpu, FILE *f,
                            fprintf_function cpu_fprintf, int flags);
    int64_t (*get_arch_id)(CPUState *cpu);
    bool (*get_paging_enabled)(const CPUState *cpu);
    void (*get_memory_mapping)(CPUState *cpu, MemoryMappingList *list,
                               Error **errp);
    void (*set_pc)(CPUState *cpu, vaddr value);
    void (*synchronize_from_tb)(CPUState *cpu, struct TranslationBlock *tb); //该函数与tcg相关,不必看
    int (*handle_mmu_fault)(CPUState *cpu, vaddr address, int rw,
                            int mmu_index);
    hwaddr (*get_phys_page_debug)(CPUState *cpu, vaddr addr);
    int (*gdb_read_register)(CPUState *cpu, uint8_t *buf, int reg);
    int (*gdb_write_register)(CPUState *cpu, uint8_t *buf, int reg);
    void (*debug_excp_handler)(CPUState *cpu);

    int (*write_elf64_note)(WriteCoreDumpFunction f, CPUState *cpu,
                            int cpuid, void *opaque);
    int (*write_elf64_qemunote)(WriteCoreDumpFunction f, CPUState *cpu,
                                void *opaque);
    int (*write_elf32_note)(WriteCoreDumpFunction f, CPUState *cpu,
                            int cpuid, void *opaque);
    int (*write_elf32_qemunote)(WriteCoreDumpFunction f, CPUState *cpu,
                                void *opaque);

    const struct VMStateDescription *vmsd;  //该数据结构记录CPU中的重要数据,在热迁移过程中对CPU重要数据进行传输
    int gdb_num_core_regs;
    const char *gdb_core_xml_file;
    bool gdb_stop_before_watchpoint;

    void (*cpu_exec_enter)(CPUState *cpu);
    void (*cpu_exec_exit)(CPUState *cpu);
    bool (*cpu_exec_interrupt)(CPUState *cpu, int interrupt_request);

    void (*disas_set_info)(CPUState *cpu, disassemble_info *info);
} CPUClass;1.2.3.4.5.6.7.8.9.10.11.12.13.14.15.16.17.18.19.20.21.22.23.24.25.26.27.28.29.30.31.32.33.34.35.36.37.38.39.40.41.42.43.44.45.46.47.48.49.50.51.52.53.54.55.56.

CPUState

CPUState是CPU对象的数据结构,一个CPUState就表示一个虚拟机的CPU。在QEMU中,任何CPU的操作的大部分都是对以CPUState形式出现的CPU来进行的。

struct CPUState {
    /*< private >*/
    DeviceState parent_obj;    //继承自Device对象
    /*< public >*/

    int nr_cores;             //CPU的核数
    int nr_threads;           //CPU每核的线程数
    int numa_node;            //CPU有几个numa node

    struct QemuThread *thread;   //该CPU对应的线程
#ifdef _WIN32
    HANDLE hThread;
#endif
    int thread_id;               //线程id
    uint32_t host_tid;
    bool running;               //CPU是否正在运行
    struct QemuCond *halt_cond;   //cpu停止运行所使用的条件变量,用于通知该CPU
    bool thread_kicked;          
    bool created;      
    bool stop;
    bool stopped;
    bool crash_occurred;
    bool exit_request;        
    uint32_t interrupt_request;
    int singlestep_enabled;
    int64_t icount_extra;
    sigjmp_buf jmp_env;

    QemuMutex work_mutex;
    struct qemu_work_item *queued_work_first, *queued_work_last;

    //CPU对应的地址空间
    CPUAddressSpace *cpu_ases;
    AddressSpace *as;

    //这个数据结构保存该CPU的段寄存器、FPU等信息,x86对应的数据结构是CPUX86State,读者可在target-i386/cpu.h中看到
    void *env_ptr; /* CPUArchState */

    //以下变量与TCG相关,不必看    
    struct TranslationBlock *current_tb;
    struct TranslationBlock *tb_jmp_cache[TB_JMP_CACHE_SIZE];

    //用于调试的变量
    struct GDBRegisterState *gdb_regs;
    int gdb_num_regs;
    int gdb_num_g_regs;
    QTAILQ_ENTRY(CPUState) node;

    /* ice debug support */
    QTAILQ_HEAD(breakpoints_head, CPUBreakpoint) breakpoints;

    QTAILQ_HEAD(watchpoints_head, CPUWatchpoint) watchpoints;
    CPUWatchpoint *watchpoint_hit;

    void *opaque;

    /* In order to avoid passing too many arguments to the MMIO helpers,
     * we store some rarely used information in the CPU context.
     */
    uintptr_t mem_io_pc;
    vaddr mem_io_vaddr;

    //与kvm相关的变量
    int kvm_fd;    //kvm在创建CPU时,给每个CPU分配eventfd,该变量用于保存这个文件描述符
    bool kvm_vcpu_dirty;
    struct KVMState *kvm_state;
    struct kvm_run *kvm_run;
    /* TODO Move common fields from CPUArchState here. */
    int cpu_index; /* used by alpha TCG */
    uint32_t halted; /* used by alpha, cris, ppc TCG */
    union {
        uint32_t u32;
        icount_decr_u16 u16;
    } icount_decr;
    uint32_t can_do_io;
    int32_t exception_index; /* used by m68k TCG */

    /* Used to keep track of an outstanding cpu throttle thread for migration
     * autoconverge
     */
    bool throttle_thread_scheduled;  //跟踪CPU运行,当热迁移时可以自动减慢CPU运行速度,保证热迁移顺利完成。

    /* Note that this is accessed at the start of every TB via a negative
       offset from AREG0.  Leave this field at the end so as to make the
       (absolute value) offset as small as possible.  This reduces code
       size, especially for hosts without large memory offsets.  */
    uint32_t tcg_exit_req;
};1.2.3.4.5.6.7.8.9.10.11.12.13.14.15.16.17.18.19.20.21.22.23.24.25.26.27.28.29.30.31.32.33.34.35.36.37.38.39.40.41.42.43.44.45.46.47.48.49.50.51.52.53.54.55.56.57.58.59.60.61.62.63.64.65.66.67.68.69.70.71.72.73.74.75.76.77.78.79.80.81.82.83.84.85.86.87.88.

与CPU相关的全局变量

与CPU相关的重要全局变量,一个是全局cpus,它的定义如下。它保存了虚拟机中所有的CPUState的指针,用于对CPU的集中管理。代码在exec.c中。

QTAILQ_HEAD(CPUTailQ, CPUState);
struct CPUTailQ cpus = QTAILQ_HEAD_INITIALIZER(cpus);1.2.

另一个是在线程内的全局变量,它的定义如下:

__thread CPUState *current_cpu;1.

它用于给CPU线程指示当前的CPUState的指针。方便调用CPUClass中的相关回调函数对当前的CPUState进行管理。

X86 CPU的设计

作为一种架构的CPU,X86 CPU类继承自CPU的基类,对应的类和对象的数据结构,分别是X86CPUClass和X86CPUState。

X86CPUClass

X86CPUClass是X86CPU类的数据结构,定义在target-i386/cpu-qom.h中

typedef struct X86CPUClass {
    /*< private >*/
    CPUClass parent_class; //继承自CPU类
    /*< public >*/

    /* Should be eventually replaced by subclass-specific property defaults. */
    X86CPUDefinition *cpu_def;  //具体的CPU模型

    bool kvm_required;

    DeviceRealize parent_realize;  //保存父类realize回调函数,这样可以修改parent_class中的realize回调函数
    void (*parent_reset)(CPUState *cpu);
} X86CPUClass;

其中X86CPUDefinition的定义如下:(代码在target-i386/cpu.c中)
struct X86CPUDefinition {
    const char *name;
    uint32_t level;
    uint32_t xlevel;
    uint32_t xlevel2;
    /* vendor is zero-terminated, 12 character ASCII string */
    char vendor[CPUID_VENDOR_SZ + 1];
    int family;
    int model;
    int stepping;
    FeatureWordArray features;
    char model_id[48];
};1.2.3.4.5.6.7.8.9.10.11.12.13.14.15.16.17.18.19.20.21.22.23.24.25.26.27.28.

在target-i386/cpu.c中定义了X86CPUDefinition类型的数组,其中包含了包括qemu64、phenom等不同的X86CPU模型。读者可以自行查看。

X86CPU

X86CPU是x86架构CPU的对象的数据结构,定义在target-i386/cpu-qom.h中

typedef struct X86CPU {
    /*< private >*/
    CPUState parent_obj;
    /*< public >*/

    CPUX86State env;  //保存CPU相关信息的数据结构

    bool hyperv_vapic;
    bool hyperv_relaxed_timing;
    int hyperv_spinlock_attempts;
    char *hyperv_vendor_id;
    bool hyperv_time;
    bool hyperv_crash;
    bool hyperv_reset;
    bool hyperv_vpindex;
    bool hyperv_runtime;
    bool check_cpuid;
    bool enforce_cpuid;
    bool expose_kvm;
    bool migratable;
    bool host_features;
    int64_t apic_id;

    /* if true the CPUID code directly forward host cache leaves to the guest */
    bool cache_info_passthrough;

    /* Features that were filtered out because of missing host capabilities */
    uint32_t filtered_features[FEATURE_WORDS];

    /* Enable PMU CPUID bits. This can't be enabled by default yet because
     * it doesn't have ABI stability guarantees, as it passes all PMU CPUID
     * bits returned by GET_SUPPORTED_CPUID (that depend on host CPU and kernel
     * capabilities) directly to the guest.
     */
    bool enable_pmu;

    /* in order to simplify APIC support, we leave this pointer to the
       user */
    struct DeviceState *apic_state;  //CPU对应的APIC设备
    struct MemoryRegion *cpu_as_root, *cpu_as_mem, *smram;
    Notifier machine_done;
} X86CPU;1.2.3.4.5.6.7.8.9.10.11.12.13.14.15.16.17.18.19.20.21.22.23.24.25.26.27.28.29.30.31.32.33.34.35.36.37.38.39.40.41.42.

CPUX86State数据结构保存了X86CPU中所有寄存器的状态,也包括段寄存器、通用寄存器、标志寄存器等,也包括FPU等浮点寄存器,以及与KVM状态相关的信息。该数据结构很长,这里不再列出,读者可以在target-i386/cpu.h中自行查看。