KVM Shadow Page Table Walking
看起来 Direct Page Fault 和 Shadow MMU 都使用了这一套框架。
struct kvm_shadow_walk_iterator
struct kvm_shadow_walk_iterator {
// GVA CR2(normal 情况) 或者是 GPA(EPT)
// 很容易理解,普通的情况,shadow PT 需要完成 GVA->HPA 的转换,所以应该是 GVA
// EPT 下,shadow PT 需要完成 GPA->HPA 的转换,所以应该是 GPA
u64 addr;
// 表示当前 PTE 所映射到的 HPA
hpa_t shadow_addr;
// 指向 PTE
u64 *sptep;
// 当前 PTE 所在的 level
int level;
// 这个 entry 是所在 page 的 512 entries 里的第几个
unsigned index;
};
shadow_walk_next()
/ __shadow_walk_next()
/ KVM
static void __shadow_walk_next(struct kvm_shadow_walk_iterator *iterator, u64 spte)
{
// Not present: 4K page 里 512 entries 里的这一个 entry 是 0,表示压根就没有这个 entry,查找失败
// 如果存在,但是是最后一级 spte
if (!is_shadow_present_pte(spte) || is_last_spte(spte, iterator->level)) {
iterator->level = 0;
return;
}
// 如果都不是,按 DFS 找到下一个 level,根据 SPTE 的内容找到下一个 PSE 的 HPA 来继续查找
iterator->shadow_addr = spte & SPTE_BASE_ADDR_MASK;
--iterator->level;
}
shadow_walk_okay()
/ KVM
static bool shadow_walk_okay(struct kvm_shadow_walk_iterator *iterator)
{
// 这种情况那就只能是 PG_LEVEL_NONE 了
// 在 next() 函数里会置为 iterator->level = 0;
// 这样的话就应该退出了
if (iterator->level < PG_LEVEL_4K)
return false;
iterator->index = SPTE_INDEX(iterator->addr, iterator->level);
// 继续下一步:根据这个 PSE 所映射到的下一级的 HPA (shadow_addr) 找到 HVA,
// 这个值指向了 4K 大小的一个 page,包含了 512 个 entries,所以还需要加上
// iterator->index 来定位到对应的 entry(PSE)。
iterator->sptep = ((u64 *)__va(iterator->shadow_addr)) + iterator->index;
// 我们还没有结束,继续找!
return true;
}
shadow_walk_init_using_root()
/ shadow_walk_init()
/ KVM
static void shadow_walk_init_using_root(struct kvm_shadow_walk_iterator *iterator,
struct kvm_vcpu *vcpu, hpa_t root,
u64 addr)
{
iterator->addr = addr; // GPA?
iterator->shadow_addr = root; // SPT 树的 root page 所在的 HPA
// 初始化为 SPT root page 的 level,表示是根节点。
iterator->level = vcpu->arch.mmu->root_role.level;
// 下面一直在捣鼓的是 iterator->level 应该是什么
// 有一些 corner cases,暂时先不管了。
}
for_each_shadow_entry()
KVM
从 root PT 当中遍历直到找到映射 _addr
的 PTE,返回每一级的 PSE。
#define for_each_shadow_entry(_vcpu, _addr, _walker) \
for (shadow_walk_init(&(_walker), _vcpu, _addr); \
shadow_walk_okay(&(_walker)); \
shadow_walk_next(&(_walker)))