EPT PTE和宿主PTE条目有什么关系?

What is the relation between EPT PTE and host PTE entry?

我想弄清楚 Linux 系统中 EPT PTE 和主机 PTE 之间的关系,在 X86 主机中进行虚拟化。
例如,当 hypervisor 通过提供主机内存页面设置 EPT 条目时,当访客在访客中写入该页面时会发生什么?
在上述情况下,EPT 条目是 'dirty',该主机页面的主机 PTE 条目是否仍然是脏的?

我为 Linux 编写了一个简单的管理程序,它支持 EPT。我发现当客人写一个页面时,它在EPT条目中设置了脏位,但是通过检查主机PTE条目,我没有发现设置了脏位。

在 EPT 违规处理程序中,我调用 kmalloc 来为来宾获取主机页面。然后我使用以下代码检查该页面的主机 PTE 条目。

void pgtable_walk(unsigned long addr)
{
    pgd_t *pgd;
    pud_t *pud;
    pmd_t *pmd;
    pte_t *pte;
    pte_t  cpte;
    unsigned long page_mask;
    unsigned int level;
    phys_addr_t phys_addr;
    unsigned long offset;

    pgd = pgd_offset(current->mm, addr);
    printk(KERN_ALERT "pgd is : %lx\n", (unsigned long)pgd->pgd);
    printk(KERN_ALERT "pgd index: %lx\n", (unsigned long)pgd_index(addr));
    pud = pud_offset(pgd, addr);
    printk(KERN_ALERT "pud is : %lx\n", (unsigned long)pud->pud);
    printk(KERN_ALERT "pud index: %lx\n", (unsigned long)pud_index(addr));
    pmd = pmd_offset(pud, addr);
    printk(KERN_ALERT "pmd is : %lx\n", (unsigned long)pmd->pmd);
    printk(KERN_ALERT "pmd index: %lx\n", (unsigned long)pmd_index(addr));
    if(!pmd_large(*pmd)) {
        pte = pte_offset_kernel(pmd, addr);
        printk(KERN_ALERT "pte is : %lx\n", (unsigned long)pte->pte);
        printk(KERN_ALERT "pte index: %lx\n", (unsigned long)pte_index(addr));
        level = 2;
    } else {
        pte = (pte_t *)pmd;
        level = 1;
    }
    page_mask = page_level_mask(level);
    phys_addr = pte_pfn(*pte) << PAGE_SHIFT;
    offset    = addr & ~page_mask;

    printk("Final Phys Addr: %lx, dirty=%x, pte=%lx\n",
            (unsigned long)(phys_addr | offset), pte_dirty(*pte), pte_val(*pte));
}

如果是这样,Linux 怎么知道哪个页面是脏的?

处理器只能在执行写入时在用于转换虚拟地址的PTE 中设置脏位。因此,当来宾写入页面时,处理器会在来宾 PTE 和 EPT 中设置脏位。*在来宾中发生写入时,处理器没有指向主机页表的指针,也没有它是否知道页面是否映射到任何主机页表中。所以宿主软件如果想知道页面是否脏了,就必须看EPT。

* 仅当可选的 EPT A/D 功能可用并且通过设置 VMCS 中 EPTP 字段中的第 6 位启用时,才会设置 EPT 脏位。 (请参阅英特尔 SDM 的第 28.2.5 节。)