Virtualbox源码分析9 CPU manager1

Virtualbox源码分析9 CPU manager1

接下来3篇文章,介绍CPU虚拟化管理部分: CPUM

每个VM 都有一个或者多个VCPU,VCPU是VM运行的单位,类似于操作系统中的进程和线程的概念,VM是进程,VCPU是线程,一个host里可以有多个VM。

每个VCPU都需要全局变量保存相关信息,比如VCPU进入/退出 GuestOS都需要保存相关上下文信息。第一篇介绍一些重要的结构体。

9.1 vm.h里关于CPU的相关结构体

每个VCPU,都有一个对应的VMCPU结构体,保存每个VCPU对应的信息

VMCPUSTATE
//VCPU的状态
typedef enum VMCPUSTATE
{
    /** The customary invalid zero. */
    VMCPUSTATE_INVALID = 0,
    /** Virtual CPU has not yet been started.  */
    VMCPUSTATE_STOPPED,
    /** CPU started. */
    VMCPUSTATE_STARTED,
    /** CPU started in HM context. */
    VMCPUSTATE_STARTED_HM,
    /** Executing guest code and can be poked (RC or STI bits of HM). */
    VMCPUSTATE_STARTED_EXEC,
    /** Executing guest code in the recompiler. */
    VMCPUSTATE_STARTED_EXEC_REM,
    /** Executing guest code using NEM. */
    VMCPUSTATE_STARTED_EXEC_NEM,
    VMCPUSTATE_STARTED_EXEC_NEM_WAIT,
    VMCPUSTATE_STARTED_EXEC_NEM_CANCELED,
    /** Halted. */
    VMCPUSTATE_STARTED_HALTED,
} VMCPUSTATE;
VMCPU : 每个VCPU一个VMCPU结构体
typedef struct VMCPU
{
    //VCPU的状态
	  VMCPUSTATE volatile     enmState;
    //如果VCPU在运行Guest代码,保存这个VCPU是跑在哪个Guest上的
    RTCPUID volatile        idHostCpu;
    /* The CPU set index corresponding to idHostCpu, UINT32_MAX if not valid.*/
    uint32_t volatile       iHostCpuSet;
    //VCPU id
    VMCPUID                 idCpu;
     /** Raw-mode Context VM Pointer. */
    PVMRC                   pVMRC;
    //VM结构体R3指针
    PVMR3                   pVMR3;
    //VM结构体R0指针
    PVMR0                   pVMR0;
    //PUVMCPU结构体R3指针
    PUVMCPU                 pUVCpu;
    //Emulate Thread handle, R3
    RTNATIVETHREAD          hNativeThread;
    //Emulate Thread handle, R0
    RTNATIVETHREAD          hNativeThreadR0;
    
    //各种manager保存的CPU信息
     /** HM part. */
    union VMCPUUNIONHM
    {
#ifdef VMM_INCLUDED_SRC_include_HMInternal_h
        struct HMCPU    s;
#endif
        uint8_t             padding[5888];      /* multiple of 64 */
    } hm;
    ...
      
    //CPUM
    union VMCPUUNIONCPUM
    {
#ifdef VMM_INCLUDED_SRC_include_CPUMInternal_h
        struct CPUMCPU      s;
#endif
#ifdef VMCPU_INCL_CPUM_GST_CTX
        CPUMCTX             GstCtx;
#endif
        uint8_t             padding[4096];      /* multiple of 4096 */
    } cpum;
    ...
}

9.2 cpumctx.h里的结构体

CPUMCTX

保存了所有VCPU寄存器相关的信息

typedef struct CPUMCTX
{
   //寄存器
  	union
    {
        uint16_t        ip;
        uint32_t        eip;
        uint64_t        rip;
    } CPUM_UNION_NM(rip);
  ...
  //gdtr等
  VBOXGDTR        gdtr;
  ....
  //msr寄存器
  uint64_t        msrEFER;
  uint64_t        msrSTAR;          
  uint64_t        msrPAT;
  ...
  //vmx相关寄存器
  struct
  {
    RTGCPHYS                GCPhysVmxon;
    RTGCPHYS                GCPhysVmcs;
    ...
  }vmx;
}

9.3 cpum.h里相关的结构体

CPUMFEATURES
//保存CPU特性相关的结构体
typedef struct CPUMFEATURES
{
    //CPU型号相关的信息
    uint8_t         enmCpuVendor;
    uint8_t         uFamily;
    uint8_t         uModel;
    //CPU特性相关信息
    uint32_t        fMsr : 1;
    ...
    //是否支持AVX指令等
    uint32_t        fAvx : 1;
    ....
    //vmx支持相关指令
    uint32_t        fVmxExtIntExit : 1;
    ...
}
CPUMCPUIDLEAF

保存每个CPUID项返回值

typedef struct CPUMCPUIDLEAF
{
    //保存输入的EAX的值(Leaf)
    uint32_t    uLeaf;
    //保存输入的ECX的值 (subleaf)
    uint32_t    uSubLeaf;
    /** Sub-leaf mask.  This is 0 when sub-leaves aren't used. */
    uint32_t    fSubLeafMask;

  	//四个寄存器的值,对应的CPUID指令的返回值
    /** The EAX value. */
    uint32_t    uEax;
    /** The EBX value. */
    uint32_t    uEbx;
    /** The ECX value. */
    uint32_t    uEcx;
    /** The EDX value. */
    uint32_t    uEdx;

    /** Flags. */
    uint32_t    fFlags;
} CPUMCPUIDLEAF;
CPUMCPUID

CPUID返回值结构体

typedef struct CPUMCPUID
{
    uint32_t uEax;
    uint32_t uEbx;
    uint32_t uEcx;
    uint32_t uEdx;
} CPUMCPUID;
CPUMMSRRANGE

保存每个MSR寄存器到对应值

typedef struct CPUMMSRRANGE
{
    //MSRID, 大部分情况下uFirst = uLast,部分MSR会出现多个MSRID共用一个range,比如IA32_PMCn
    uint32_t    uFirst;
    uint32_t    uLast;
    //这个MSR的读取函数
    uint16_t    enmRdFn;
    //写入函数
    uint16_t    enmWrFn;
    //相对于CPUMCPU的偏移,如果没有使用,默认是UINT16_MAX
    uint16_t    offCpumCpu;
    uint16_t    fReserved;
	//初始值
    uint64_t    uValue;
    //某些bit位ignore写操作,比如MSR_IA32_APICBASE的msr,ignore MSR_IA32_APICBASE_EXTD位的写入
    uint64_t    fWrIgnMask;
    //某些bit位写入的时候发生GP
    uint64_t    fWrGpMask,比如MSR_IA32_APICBASE的msr,写入MSR_IA32_APICBASE_EXTD位的时候发生GP
    //MSR寄存器名字
    char        szName[56];
} CPUMMSRRANGE;
CPUMMSRRDFN/CPUMMSRWRFN

定义了一系列函数列表,GuestOS读取相对应的MSR寄存器时,根据msrid调用向对应的函数

实现函数在VMMAll\CPUMAllMsrs.cpp函数里

//msr寄存器读取的函数列表
typedef enum CPUMMSRRDFN
{
    kCpumMsrRdFn_Ia32P5McAddr,
    kCpumMsrRdFn_Ia32P5McType,
    ....
}
//msr寄存器写入的函数列表
typedef enum CPUMMSRWRFN
{
    kCpumMsrWrFn_Ia32P5McAddr,
    kCpumMsrWrFn_Ia32P5McType,
    ....
}

9.4 CPUMInternal.h里的结构体

CPUMINFO
//Guest 模拟CPUID的时候,会吧每个输入的EAX对应的返回值保存到一个CPUMCPUIDLEAF的结构体里
//并分配一块内存保存这些结构体,并映射到R3和R0虚拟地址
//模拟MSR寄存器也是同样
typedef struct CPUMINFO
{
   //保存的msr数组个数
    uint32_t                    cMsrRanges;
    uint32_t                    fMsrMask;
    uint32_t                    fMxCsrMask;
   //保存的cpuid数组个数
    uint32_t                    cCpuIdLeaves;
    //cpuid数组里extended CPUID leaf第一个元素
    uint32_t                    iFirstExtCpuIdLeaf;
    //4种不同应对未知CPUID输入的方法
    //CPUMUNKNOWNCPUID_DEFAULTS / CPUMUNKNOWNCPUID_LAST_STD_LEAF 
    // CPUMUNKNOWNCPUID_LAST_STD_LEAF_WITH_ECX / CPUMUNKNOWNCPUID_PASSTHRU
    CPUMUNKNOWNCPUID            enmUnknownCpuIdMethod;
    //默认CPUID的返回值,如果在CPUID数组里找不到对应项,返回这个默认值
    CPUMCPUID                   DefCpuId;
    uint64_t                    uScalableBusFreq;
    //msr数组的R0指针
    R0PTRTYPE(PCPUMMSRRANGE)    paMsrRangesR0;
    //CPUID数组的R0指针
    R0PTRTYPE(PCPUMCPUIDLEAF)   paCpuIdLeavesR0;
    //msr数组的R3指针
    R3PTRTYPE(PCPUMMSRRANGE)    paMsrRangesR3;
    //CPUID数组的R3指针
    R3PTRTYPE(PCPUMCPUIDLEAF)   paCpuIdLeavesR3;   
} CPUMINFO;
CPUMHOSTCTX
//保存host cpu信息
typedef struct CPUMHOSTCTX
{
  //host寄存器
  uint64_t        rbx;
  ...
  //ss寄存器
  RTSEL           ss;
  //msr等
  CPUMSYSENTER    SysEnter;
  uint64_t        FSbase;
  ...
}
CPUM : 每个VCPU都有一个向对应的CPUM结构体
 struct CPUM
 {
   //Host CPU 标记 CPUM_USE_SYSENTER/CPUM_USE_SYSCALL等
   uint32_t                fHostUseFlags;
   CPUMHOSTCTX             Host;
   CPUMCTX                 Hyper;
   //保存Guest CPU AVX系列指令支持
   uint64_t                fXStateGuestMask;
   //保存hostCPU AVX系列指令支持 (xgetbv)
    uint64_t                fXStateHostMask;

   //host cpu相关feature信息,(见CPUMFEATURES解释)
   CPUMFEATURES            HostFeatures;
   //Guest cpu相关featrue信息
   CPUMFEATURES            GuestFeatures;
   //Guest CPU Info
   CPUMINFO                GuestInfo;
 }
CPUMCPU
struct CPUMCPU
{
    //Guest的CPU信息
    CPUMCTX                 Guest;
    CPUMCTXMSRS             GuestMsrs;
    //嵌套VMX使用的timer内存
    PTMTIMERR0              pNestedVmxPreemptTimerR0;
    PTMTIMERR3              pNestedVmxPreemptTimerR3;
    //标记这个CPUMCPU是否支持某些模式
    //CPUM_USED_FPU_HOST/CPUM_USED_FPU_GUEST/CPUM_USED_DEBUG_REGS_GUEST/...
    uint32_t                fUseFlags;
    //标记Guest寄存器哪些内容被修改
    uint32_t                fChanged;
    //32-64交换模式的temp变量
    uint32_t                u32RetCode;
    uint32_t                fApicDisVectors;
    //如果开启APIC,VCPU的APIC内存地址
    RTHCPTR                 pvApicBase;
    //CPU是否开启xAPIC
    bool                    fX2Apic;
    //是否进入recompiler
    bool                    fRemEntered;
    //CPUID里关于APIC的bit位是否被可获取到,比如IntelCPU,CPUID(1)返回edx的第9位是否可以是1
    bool                    fCpuIdApicFeatureVisible;
    //host CPU信息
    CPUMHOSTCTX             Host;
    //hypervisor context CPU 状态 (Drx寄存器和cr3)
    CPUMHYPERCTX            Hyper;
#ifdef VBOX_WITH_CRASHDUMP_MAGIC
    //crash dump里的标记
    uint8_t                 aMagic[56];
    uint64_t                uMagic;
#endif
}

9.5 CPUMR3Db.cpp 里的结构体

VBox代码里保存了很多常见CPU系列的名字和相关信息,在VMMR3\cpus里,比如Intel 80x86系列,core-ix系列,AMD athlon系列等。

每个CPU系列都有一个database/CPUID range/ MSR range

CPUMDBENTRY
 //CPU database
 struct CPUMDBENTRY
 {
    //CPU名字 : 比如  "Intel 80486",,
    const char     *pszName;
    //CPU全名 : 比如 "Intel(R) 80486DX2",
    const char     *pszFullName;
    //CPU vendor : AMD/ intel
    uint8_t         enmVendor;
    //标示这个CPU类型的id,每个CPU类型都不同
    uint8_t         uFamily;
    uint8_t         uModel;
    /** The CPU stepping. */
    uint8_t         uStepping;
    /** The microarchitecture. */
    CPUMMICROARCH   enmMicroarch;
    //CPU bus频率?
    uint64_t        uScalableBusFreq;
    /** Flags - CPUDB_F_XXX. */
    uint32_t        fFlags;
    //物理地址有效位数
    uint8_t         cMaxPhysAddrWidth;
    /** The MXCSR mask. */
    uint32_t        fMxCsrMask;
    //自定义CPUID Range数组指针
    PCCPUMCPUIDLEAF paCpuIdLeaves;
    //自定义CPUID Range个数
    uint32_t        cCpuIdLeaves;
    //处理未知CPUID的方法
    CPUMUNKNOWNCPUID enmUnknownCpuId;
    //未知CPUID input的返回值
    CPUMCPUID       DefUnknownCpuId;
   	//部分CPUingore ecx的高位,默认是UINT32_MAX
    uint32_t        fMsrMask;
    //msr个数,如果当前CPU没有自定义msr列表,填0即可
    uint32_t        cMsrRanges;
    //自定义MSRRange
    PCCPUMMSRRANGE  paMsrRanges;
}

9.6 CPUM3Cpuid.cpp里的结构体

CPUMCPUIDCONFIG
//从config里获取的config
typedef struct CPUMCPUIDCONFIG
{
    //这一项开启之后,CPU只支持传入的eax值小于等于3
    bool            fNt4LeafLimit;
    //是否开启Invariant TSC, CPU有3种TSC行为(Variant TSC/Constant TSC/Invariant TSC)
    bool            fInvariantTsc;
    //AMD_Zen_Ryzen/Hygon_Dhyana CPU是否支持zen vme
    bool            fForceVme;
    //是否支持嵌套VT
    bool            fNestedHWVirt;
  	//是否开启对应的功能
    CPUMISAEXTCFG   enmCmpXchg16b;
    CPUMISAEXTCFG   enmMonitor;
    CPUMISAEXTCFG   enmMWaitExtensions;
    CPUMISAEXTCFG   enmSse41;
    CPUMISAEXTCFG   enmSse42;
    CPUMISAEXTCFG   enmAvx;
    CPUMISAEXTCFG   enmAvx2;
    CPUMISAEXTCFG   enmXSave;
    CPUMISAEXTCFG   enmAesNi;
    CPUMISAEXTCFG   enmPClMul;
    CPUMISAEXTCFG   enmPopCnt;
    CPUMISAEXTCFG   enmMovBe;
    CPUMISAEXTCFG   enmRdRand;
    CPUMISAEXTCFG   enmRdSeed;
    CPUMISAEXTCFG   enmCLFlushOpt;
    CPUMISAEXTCFG   enmFsGsBase;
    CPUMISAEXTCFG   enmPcid;
    CPUMISAEXTCFG   enmInvpcid;
    CPUMISAEXTCFG   enmFlushCmdMsr;
    CPUMISAEXTCFG   enmMdsClear;
    CPUMISAEXTCFG   enmArchCapMsr;
    CPUMISAEXTCFG   enmAbm;
    CPUMISAEXTCFG   enmSse4A;
    CPUMISAEXTCFG   enmMisAlnSse;
    CPUMISAEXTCFG   enm3dNowPrf;
    CPUMISAEXTCFG   enmAmdExtMmx;
	
  	//定义CPUID输入的EAX最大值
    //EAX高位是0的最大值
    uint32_t        uMaxStdLeaf;
    //EXA是0x80000000开头的最大值
    uint32_t        uMaxExtLeaf;
    //EAX是0xC0000000开头的最大值
    uint32_t        uMaxCentaurLeaf;
    uint32_t        uMaxIntelFamilyModelStep;
    //模拟的CPU名字
    char            szCpuName[128];
} CPUMCPUIDCONFIG;
  • 1
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
### 回答1: VirtualBox 是一款自由开源的虚拟化软件,可以在各种不同的操作系统上创建和运行虚拟机。如果想要编译 VirtualBox源码,可以按照以下步骤进行操作: 1. 下载源码:首先在 VirtualBox 的官方网站或者代码托管平台上下载最新版本的源码压缩包。 2. 安装依赖:在编译之前,需要先安装一些必要的依赖项。这些依赖项包括编译器、构建工具、各种开发库等。请根据操作系统的不同,使用相应的包管理工具进行安装。 3. 配置编译环境:解压源码压缩包后,进入源码目录运行配置命令。这个命令会自动检测系统环境并生成相应的配置文件。 4. 编译源码:配置完成后,运行编译命令开始编译源码。编译过程可能会持续一段时间,取决于系统的性能和源码的大小。 5. 安装软件:编译完成后,在指定的目录会生成可执行文件。将这些文件拷贝到系统的特定位置,并执行安装命令,完成软件的安装。 编译 VirtualBox源码并不是一件简单的任务,需要有一定的编程和系统知识。同时,由于 VirtualBox 是一个庞大而复杂的项目,编译过程中可能会遇到各种问题和错误。在遇到问题时,可以参考官方文档、社区论坛或者开发者的指导,进行排查和解决。 最后,编译 VirtualBox源码可以让你深入了解这款虚拟化软件的内部工作原理,同时也可以自定义功能或进行二次开发。但对于一般用户而言,并不需要编译源码,直接下载官方发布的预编译版本即可使用。 ### 回答2: 要编译VirtualBox的源代码,首先需要准备开发环境。VirtualBox的源代码是使用C++语言编写的,所以您需要安装相关的编译器和工具,如GCC、make、automake、autoconf等。 接下来,您需要从VirtualBox官方网站下载源代码。VirtualBox的源代码遵循开源许可协议,可以免费获取和使用。下载完成后,解压源代码包到您选择的目录中。 然后,打开终端并导航到源代码的目录中。执行以下命令以生成配置文件: ./configure 此命令将扫描您的系统并检查必要的依赖项是否满足。如果没有安装某个依赖项,您将需要手动安装它们。 完成配置后,您可以执行以下命令来编译VirtualBox的源代码: make 这将使用已安装的编译器将源代码编译成可执行文件。该过程可能需要一些时间,具体取决于您的系统性能和源代码的大小。 编译完成后,您可以通过运行以下命令来安装VirtualBox: make install 此命令将复制编译生成的可执行文件和其他必要的文件到系统路径中。 完成安装后,您就可以使用VirtualBox了。您可以启动VirtualBox并创建和管理虚拟机。 总之,编译VirtualBox的源代码需要准备开发环境、下载源代码、生成配置文件、编译源代码和安装生成的可执行文件。这个过程需要一些技术知识和耐心,但一旦完成,您就可以自定义和修改VirtualBox以满足您的需求。 ### 回答3: VirtualBox 是一款开源的虚拟化软件,它运行在主机操作系统上,并提供了一个虚拟环境,使得用户可以在该环境中运行其他操作系统。 要从源码编译 VirtualBox,首先需要获取源代码。VirtualBox 的源代码可以从官方网站上下载,下载完成后解压到本地。 在进行源码编译之前,需要确保系统中已安装了一些必要的软件和开发工具,如 GCC 编译器、GNU Make 工具、剪贴板管理工具等。同时还需要安装一些开发库和依赖包,如 X11 开发库、SDL 开发库等,这些包的安装可以通过包管理器来进行。对于不同的操作系统,可能需要安装的软件和库略有不同。 接下来,需要配置编译环境。在源代码目录中,执行 "./configure" 命令,该命令会检查系统中的软件和库是否满足编译要求,并生成相应的编译配置。可以通过命令行参数来修改默认配置,如指定编译器、库的路径等。 配置完成后,执行 "make" 命令即可开始编译。编译过程可能需要一段时间,取决于系统的性能和代码的大小。期间会生成许多中间文件和目标文件。如果编译过程中出现错误,可以根据错误提示进行修复,并重新执行 "make" 命令。 编译完成后,可以执行 "make install" 命令将编译好的程序安装到指定位置。安装完成后,就可以使用 VirtualBox 了。 需要注意的是,虚拟化软件对硬件资源要求较高,所以在进行源码编译之前,需要确保计算机硬件满足要求,如支持虚拟化技术的 CPU、足够的内存和磁盘空间等。 总之,通过下载源码、配置编译环境、执行编译命令和安装程序,就可以从源码编译出 VirtualBox,并在主机操作系统上运行虚拟环境了。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值