影子页表虚拟化---INVLPG

580阅读 0评论2014-01-10 embeddedlwp
分类:LINUX

影子页表虚拟化---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;
}

上一篇:虚拟MMU---客户机页表遍历
下一篇:EPT学习总结及KVM的处理