虚拟化入门-1-CPU虚拟化
研究组的方向是虚拟化…研一一直在学pwn(都还没入门..惭愧). 刚回所里,有师兄带一下,学一下虚拟化, 接触了一点感觉还不错,挺有意思的, 主要是 操作系统层面的东西. 或许后面可以作为主要方向来做.
下面的内容都是来自《系统虚拟化:原理与实现》,作为虚拟化的概念入门还是不错的感觉
主要是了解了虚拟化基础的概念之后,学习intel的 VT虚拟化技术
第三章 虚拟化概述
虚拟机的三个典型特征:同质、高效、资源受控。
大多数计算机体系结构都有两个及以上的特权级,用来分隔系统软件和应用软件。系统中有一些操作和管理关键系统资源的指令被定位特权指令,只有在最高特权级上能够正确执行。如果在非最高特权级上运行,特权指令会引发一个异常,处理器会陷入最高特权级,交由系统软件来处理。
在虚拟化世界中,有一类指令成为敏感指令,简而言之是操作特权资源的指令。所有的特权指令都是敏感指令,但不是所有的敏感指令都是特权指令。
判断一个结构是否可以虚拟化,核心在于对敏感指令的支持,如果在某些结构上所有敏感指令都是特权指令,则它是可虚拟化的结构,否则,如果它无法支持在所有的敏感指令上触发异常,则不是一个可虚拟化的机构,称其存在“虚拟化漏洞”
通过陷入再模拟指令的执行来实现虚拟机的方法是前提条件的:所有的敏感指令都必须是特权指令。如果不满足的话就会有遗漏,此时需要想办法来填补或者避免这些遗漏。
3.2 处理器虚拟化
处理器虚拟化是VMM中最核心的部分,因为访问内存或者I/O的指令本身就是敏感指令,所以内存虚拟化与I/O虚拟化都依赖于处理器虚拟化的正确实现。
指令的模拟
因为特权级的存在,敏感指令需要陷入到VMM中通过软件的方式进行模拟。
三个概念:虚拟寄存器、上下文和虚拟处理器
当客户机操作系统试图访问关键资源的时候,该请求并不会真正发生在物理寄存器上。相反,VMM会通过准确模拟物理处理器的行为,而将其访问定位到VMM为其设计与物理寄存器对应的“虚拟”的寄存器上。(对VMM来说,这样的虚拟寄存器往往是在内存中。)
一个案例,不论是对CR0的修改还是访问,都会经过处理器抛出异常,由VMM操作对应的虚拟CR0。
在没有虚拟化的环境中,os直接负责物理处理器管理,负责进程间的调度和切换。在VMM接管物理处理器后,客户机操作系统没有管理物理处理器的权利,可以说此时它已经运行在VMM为之设计的虚拟处理器之上,客户机管理虚拟处理器,并在虚拟处理器上负责该虚拟机内进程间调度和切换。
而VMM管理物理处理器,负责虚拟处理器的调度和切换,以保证在给定时间内,每个虚拟处理器上的当前进程可以在物理处理器上运行一段时间。但是,不管是何种调度,必然要涉及到保留现场,这个现场就是上下文状态。
相比进程等上下文,虚拟处理器上下文会更加复杂,因为客户机操作系统本身包含许多敏感指令,会试图访问和修改物理处理器上定义的所有寄存器,但这种访问会和修改会被VMM重定位到虚拟处理器上。所以,对于虚拟处理器,其上下文包括了更多的系统寄存器.当VMM在决定切换虚拟处理器的时候,为了让虚拟机看起来好像从未被中断过一样,VMM需要考虑保存和回复的上下文也更加复杂。
从VMM的角度来说,虚拟处理器是其需要模拟完成的一组功能集合,虚拟处理器的功能可以由物理处理器和VMM共同完成。对于非敏感指令,物理处理器直接解码处理其请求,并将相关的效果直接反应到物理寄存器上,而对于敏感指令,VMM负责陷入再模拟,从程序角度来说就是一组数据结构和相关处理代码的集合。数据结构用于存储虚拟寄存器的内容,而相关处理代码负责按照物理处理器的行为将效果反映到虚拟寄存器上。
上面的概念可以说明,在处理器虚拟化中,不论是定义虚拟寄存器和虚拟处理器还是利用上下文进行虚拟处理器调度切换,其宗旨都是让虚拟机里执行的敏感指令陷入下来以后,能被VMM模拟,而不要直接作用于真实硬件上。
模拟的前提是陷入,需要了解怎么进行陷入,需要陷入的时候,是怎么通知VMM的。概括地说,VMM陷入是利用了处理器的保护机制,利用中断和异常来完成,有以下几种方式:
- 1.基于处理器保护机制触发的异常,例如敏感指令的执行。
- 2.虚拟机主动触发异常,即通常所说的陷阱
- 异步中断,包括处理器内部的中断源和外部的设备中断源
VMM的功能和组成
VMM的主要功能事基于物理资源创建相应的虚拟资源,组成虚拟机,为客户机操作系统提供虚拟的平台。所以,可以推测,VMM基本上可以分为两部分:虚拟环境的管理和物理资源的管理。
虚拟环境的管理
物理资源的管理
其他模块
VMM分类
按虚拟平台分类
完全虚拟化
虚拟出来的平台和现实平台是一样的,客户机操作系统不用做任何修改就可以运行。重点是VMM要能够正确处理所有可能的指令。
在实现方式上,以x86为例,经历了两个阶段:软件辅助的完全虚拟化和硬件辅助的完全虚拟化
软件辅助的完全虚拟化
早期因为一开始肯定没想要要弄这个,所以硬件上也不会专门适配,所以完全虚拟化需要通过软件来实现。一个典型的做法是优先级压缩和二进制代码翻译相结合。
优先级压缩来处理的话,有部分指令不能触发异常,因此不能截获做处理。而二进制代码翻译就是为了解决这部分指令,它的思想是,通过扫描并修改客户机的二进制代码,将这些指令转换成支持虚拟化的指令。
虽然这种方式能够实现完全虚拟化,但是这种类似于打补丁的方式很难在架构上保证其完整性,于是后期,x86厂商就在硬件上加入了对虚拟化的支持。
硬件辅助的完全虚拟化
很符合计算机的抽象层次的逻辑,当这一层事情比较难解决的时候,就给它再抽象出一层来。Intel的VT-x技术是这一方向的代表,它在处理器上引入了一个新的执行模式用于运行虚拟机。当虚拟机运行在这个特殊模式中时,任何特权操作都会被处理器拦截并报告给VMM。
类虚拟化
在源代码级别(操作系统内核的代码)修改指令以回避虚拟化漏洞的方式来使VMM能够对物理资源实现虚拟化。
按VMM实现结构分类
Hypervisor模型、宿主模型、混合模型
第五章 硬件辅助虚拟化
硬件辅助虚拟化,即在CPU、芯片组及I/O设备等硬件中加入专门针对虚拟化的支持,使得系统软件可以更加容易、高效地实现虚拟化功能。本章以intel VT为例。
intel vt分别在CPU、内存、IO虚拟化方面提供了不同的技术,分别对应VT-x、EPT、VT-d。
CPU虚拟化的硬件支持
引入了两种操作模式,统称为VMX模式,每种模式都有0~3的特权级。
- 根操作模式(VMX Root Operation):VMM运行所处的模式
- 非根操作模式(VMX Non-Root Operation):客户机运行所处的模式
非根模式下所有敏感指令的行为都会被重新定义,使得他们能不经过虚拟化就能直接运行或者通过“陷入再模拟”的方式来处理,在根模式下,所有指令的行为和传统IA32一样,因此所有软件都能够正常运行。
非根模式下敏感指令引起的“陷入”被称为VM-Exit。这会导致CPU自动从非根模式切换到根模式。相应的,VM-Entry,该操作由VMM发起,通常是调度某个客户机运行,此时CPU从根模式切换到非根模式。
为了更好地支持CPU虚拟化,VT-x引入了VMCS(virtual-Machine Control Structure 。虚拟机控制结构),VMCS保存虚拟CPU需要的相关状态,例如CPU在两种模式下的特权寄存器的值。VMCS主要供CPU使用,CPU在发生VM-Exit和VM-Entry时都会自动查询和更新VMCS。VMM可以通过指令来配置VMCS,进而影响CPU的行为。
VT-x还引入了一组新的指令,包括VMLAUCH/VMRESUME用于发起VM-Entry,VMREAD/VMWRITE用于配置VMCS等
这里有点没看懂。。。
VMCS
与虚拟寄存器的概念类似,可以看作是虚拟寄存器概念在硬件上的应用。VMCS是保存在内存中的数据结构,包含了虚拟CPU的相关寄存器的内容和虚拟CPU相关的控制信息,每个VMCS对应一个虚拟CPU。(换句话说,个人理解的是,物理CPU和虚拟CPU之间的一个媒介,用于保存和恢复切换时的上下文
VMCS在使用时需要和物理CPU绑定。VMCS与物理CPU是一对一绑定的关系。但在不同时刻可以绑定到不同的物理CPU。这种绑定关系的变化称为VMCS的迁移。
VT-x提供了两条指令用于VMCS的绑定与解除绑定
VMPRTLD<VMCS地址>: 将指定的VMCS与执行该指令的物理CPU绑定
VMCLEAR:将执行该指令的物理CPU与它的VMCS解除绑定。该指令会将物理CPU缓存中的VMCS结构同步到内存中去,从而保证VMCS与新的物理CPU绑定时,内存中的值是最新的。
VMCS格式如下:
主要信息放在数据域里面,VT-x提供两条指令用于访问VMCS
VMREAD<索引>: 读VMCS中索引指定的域
VMWRITE<索引><数据>:写VMCS中索引指定的域
VMCS数据域包括六大类信息
客户机状态域
宿主机状态域
VM-Entry控制域
VM-Execution控制域
VM-Exit控制域
VM-Exit信息域
VMX操作模式
VM-Entry
在发起之前,VMM会设置好VMCS相关域的内容,例如客户机状态域、宿主机状态域等,然后执行VM-Entry指令。
VT-x为VM-Entry提供了两条指令
VMLAUNCH: 用于刚执行过VMCLEAER的VMCS的第一次VM-Entry
VMRESUME:用于执行过VMLAUNCH的VMCS的后续VM-Entry
在VM-Entry进入时,会有很多不同的特性和选择,或者说具体行为,由VM-Entry控制域来规定
注入的事件最终是用客户机自己的IDT里面指定的处理函数来处理的,这样在客户机虚拟CPU看来,这些事件就和没有虚拟化的环境里面对应的事件没有任何区别
VM-Entry的过程
- 执行基本的检查来确保VM-Entry能开始
- 对VMCS中的宿主机状态域的有效性进行检查,以确保下一次VM-Exit时可以正确地从客户机环境切换到VMM环境
- 检查VMCS中客户机状态域的有效性,根据客户机状态域来装载处理器的状态
- 根据VMCS中VM-Entry MSR-load区域装载MSR寄存器
- 根据VMCS中VM-Entry事件注入控制的配置,可能需要注入事件到客户机
如果1-4步的检查没有全部通过,CPU会报告VM-Entry失败,这通常意味着VMCS中某些字段的设置有错误。如果全部通过了,处理器就会把执行环境从VMM切换到客户机环境,开始执行客户机指令。
VM-Exit
指CPU从非根模式切换到根模式,客户机切换到VMM的操作。因为的原因有很多,例如在非根模式下执行了敏感指令、发生了中断等。处理VM-Exit事件是VMM模拟指令、虚拟特权资源的一大任务。
具体过程
- CPU首先将此次VM-Exit的原因信息记录到VMCS相应的信息域中,VM-Entry interruption-information字段的有效位(bit31)被清零。
- CPU状态被保存到VMCS客户机状态域。根据设置也可能将客户机的MSR保存到VM-Exit MSR-store区域
- 根据VMCS中宿主机状态域和VM-Exit控制域中的设计,将宿主机状态加载到CPU相应寄存器。CPU也可能根据VM-Exit MSR-store区域来加载VMM的MSR。
CPU虚拟化的实现
硬件虚拟化用VCPU描述符来描述虚拟CPU,类似os中的进程描述符,其本质是一个结构体,
结构如下:
当VMM创建客户机时,首先要为客户机创建VCPU,整个客户机的运行实际上可以看作是VMM调度不同的VCPU运行。
VMCS的创建与初始化
VCPU的运行
上下文切换
VCPU的上下文分为两部分,所以切换也分为由硬件自动切换(VMCS部分)和VMM软件切换(非VMCS部分)
具体切换步骤:
1.VMM保存自己的上下文,主要是保存VMCS不保存的寄存器,即宿主机状态域以外的部分
2.VMM将保存在VCPU中的由软件切换的上下文加载到物理CPU中
3.VMM执行VMRESUME/VMLAUNCH指令,触发VM-Entry,此时CPU自动将VCPU上下文中VMCS部分加载到物理CPU,CPU切换到非根模式。
惰性保存/恢复: 这个方法是对上下文切换进行的优化,因为上下文切换带来的开销比较大。它的思想是
VCPU的硬件优化
优化的目的是尽可能少地在客户机和VMM之间切换,从而减少上下文切换的开销。Intel VT-x提供两种优化方法。
VCPU的退出
推出的原因可能是执行了特权指令、发生了物理中断等,在VT-x中表现为发生VM-Exit。对VCPU退出的处理是VMM进行CPU虚拟化的核心,例如模拟各种特权指令。
退出的原因大体上有三类:
1.访问了特权资源,对CR和MSR寄存器的访问都属于这一类
2.客户机执行的指令引发了异常,例如缺页错误
3.发生了中断。