转载:http://blog.chinaunix.net/uid-26000137-id-3768752.html
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=4table_gfn【0-3】存放 4级页表项基地址pte_gpa【0-3】 存放4级页表项中index基地址ptes【0-3】 存放4级页表项中index基地址内容pt_access: gfn的访问权限pte_access: gfn的访问权限2:客户机页表的遍历,也是虚拟MMUstatic 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;}