影子页表虚拟化---INVLPG
当客户机修改客户机页表的表项,客户机执行INVLPG敏感指令刷新TLB部分内容。
正常流程为:VMM截获这一操作,并对影子页表项进行相应修改,设置不存在,刷新影子页表部分内容。
代码分析:
static void FNAME(invlpg)(struct kvm_vcpu *vcpu, gva_t gva)
{
struct kvm_shadow_walk_iterator iterator;
int level;
u64 *sptep;
int need_flush = 0;
spin_lock(&vcpu->kvm->mmu_lock);
for_each_shadow_entry(vcpu, gva, iterator) {
level = iterator.level;
sptep = iterator.sptep;
/* FIXME: properly handle invlpg on large guest pages */
if (level == PT_PAGE_TABLE_LEVEL ) { //只设置影子页表最后一级,大页不考虑。
if (is_shadow_present_pte(*sptep)) {//影子页表项内容不为空,也就是存在转换
rmap_remove(vcpu->kvm, sptep); //取消反向映射,即通过gfn无法知道sptep,rmap_remove()函数,参考上一篇文档说明。
need_flush = 1;
}
__set_spte(sptep, shadow_trap_nonpresent_pte);//设置影子页表项为不存在
break;
}
if (!is_shadow_present_pte(*sptep))
break;
}
if (need_flush)
kvm_flush_remote_tlbs(vcpu->kvm); //影子页表无操作,主要针对硬件ept页表的。
spin_unlock(&vcpu->kvm->mmu_lock);
}
假设64位宿主机,那么影子页表为4级,相当与pgd,pud,pmd,pte.
另外64位客户机,客户机虚拟地址如下:有效为48位
+---+---+---+---+--------+
| 9 | 9 | 9 | 9 | 12 |
+---+---+---+---+--------+
影子页表遍历代码如下:
#define for_each_shadow_entry(_vcpu, _addr, _walker) \
for (shadow_walk_init(&(_walker), _vcpu, _addr); \
shadow_walk_okay(&(_walker)); \
shadow_walk_next(&(_walker)))
上述代码相当于:
for(level=4,level<1;level--)
初始化页表访问迭代器,设置客户机虚拟地址,第一级影子页表的指针,设置影子页表的级数。对于PAE不考虑,if判断不说明了。
static void shadow_walk_init(struct kvm_shadow_walk_iterator *iterator,
struct kvm_vcpu *vcpu, u64 addr)
{
iterator->addr = addr;//设置客户机物理地址
iterator->shadow_addr = vcpu->arch.mmu.root_hpa;//设置第一级影子页表的指针,相当与pgd的基地址
iterator->level = vcpu->arch.mmu.shadow_root_level;//设置影子页表的级数:4级
}
页表项遍历的结束条件:
false:遍历结束,level为0
true: 继续遍历, 通过客户机虚拟地址找到相应影子页表项索引,然后影子页表加上索引,得到影子页表索引项地址
static bool shadow_walk_okay(struct kvm_shadow_walk_iterator *iterator)
{
if (iterator->level < PT_PAGE_TABLE_LEVEL)//false:遍历结束,level为0
return false;
iterator->index = SHADOW_PT_INDEX(iterator->addr, iterator->level);//对应应页表项索引
iterator->sptep = ((u64 *)__va(iterator->shadow_addr)) + iterator->index;//对应影子页表索引项地址
return true;
}
设置下一级影子页表存放相当与pud,pmd,pte的基地址
static void shadow_walk_next(struct kvm_shadow_walk_iterator *iterator)
{
iterator->shadow_addr = *iterator->sptep & PT64_BASE_ADDR_MASK;
--iterator->level;
}