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.
* @remarks Best to make sure iHostCpuSet shares cache line with idHostCpu! */
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.1.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的值
/** The leaf number. */
uint32_t uLeaf;
/** The sub-leaf number. */
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
{
//用于binary search的index
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.1.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.1.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.1.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;
yangrong的blog
发布了10 篇原创文章 · 获赞 0 · 访问量 222
私信
关注