客户机页表的遍历
MMU的功能:虚拟机地址转换为物理地址,下面函数模拟此过程。
1.数据结构
struct guest_walker {int level;
gfn_t table_gfn[PT_MAX_FULL_LEVELS];
pt_element_t ptes[PT_MAX_FULL_LEVELS];
gpa_t pte_gpa[PT_MAX_FULL_LEVELS];
unsigned pt_access;
unsigned pte_access;
gfn_t gfn;
u32 error_code;
};
+-------+
| |
+-------+
| c |
|-------|<-----b
| |
+-------+
|-------|
|-------|
+-------+<-----a
某级页表
a:页表项基地址
b:index的地址
c:index的地址内容
遍历完成后,数据结构内容为
gfn:客户机页表转换后物理地址页框号
假设PT_MAX_FULL_LEVELS=4
table_gfn【0-3】存放 4级页表项基地址
pte_gpa【0-3】 存放4级页表项中index基地址
ptes【0-3】 存放4级页表项中index基地址内容
pt_access: gfn的访问权限
pte_access: gfn的访问权限
2:客户机页表的遍历,也是虚拟MMU
static int FNAME(walk_addr)(struct guest_walker *walker,
struct kvm_vcpu *vcpu, gva_t addr,
int write_fault, int user_fault, int fetch_fault)
walker->level = vcpu->arch.mmu.root_level;//64位客户机系统,页表级数为4.
pte = vcpu->arch.cr3; //页目录基地址
for (;;) { //从64位客户机页目录开始遍历,最后到页表
index = PT_INDEX(addr, walker->level);
table_gfn = gpte_to_gfn(pte);
pte_gpa = gfn_to_gpa(table_gfn);
pte_gpa += index * sizeof(pt_element_t);
walker->table_gfn[walker->level - 1] = table_gfn; //存放页表基地址
walker->pte_gpa[walker->level - 1] = pte_gpa; //存放页表index基地址
if (kvm_read_guest(vcpu->kvm, pte_gpa, &pte, sizeof(pte)))//获取存放页表index基地址页表项
goto not_present;
pte_access = pt_access & FNAME(gpte_access)(vcpu, pte);//获取存放页表index基地址页表项权限
walker->ptes[walker->level - 1] = pte;//存放存放页表index基地址页表项
if ((walker->level == PT_PAGE_TABLE_LEVEL)//页表的最后一级存放客户机物理页地址
{
int lvl = walker->level;
walker->gfn = gpte_to_gfn_lvl(pte, lvl);//转换客户机物理页地址为客户机物理页框号
walker->gfn += (addr & PT_LVL_OFFSET_MASK(lvl))
>> PAGE_SHIFT;
break;
}
pt_access = pte_access;
--walker->level; //遍历下一级页表
}
//页表遍历完成后,获取页表的访问权限,存放到数据结构中
walker->pt_access = pt_access;
walker->pte_access = pte_access;
pgprintk("%s: pte %llx pte_access %x pt_access %x\n",
__func__, (u64)pte, pt_access, pte_access);
return 1;
}