Linux X86_64 内核态缺页处理

 Linux X86针对meltdown漏洞开启了页表隔离(PTI)功能,PTI使用两组PGD来表示整个进程空间.在进程陷入内核态时,CR3会相应的切换到进程内核态的PGD页表.

假如进程调用了vmalloc分配内存,此时会进行页表映射,但是vmalloc是把这块内存映射到了内核的虚拟地址空间(init_mm),并没有映射到当前进程的内核态页表.

vmalloc->__vmalloc_node_range->__vmalloc_area_node->map_vm_area->vmap_page_range->vmap_page_range_noflush->pgd_offset_k //建立内核空间页表

#define pgd_offset_k(address) pgd_offset(&init_mm, (address))

所以当ring3进程陷入内核态,并访问vmalloc分配的内存时,这里会产生内核态缺页异常.

处理流程:

do_page_fault->__do_page_fault->do_kern_addr_fault->vmalloc_fault

在vmalloc_fault中,会复制init_mm的页表到当前CR3指向的页表。

static noinline int vmalloc_fault(unsigned long address)
{
	pgd_t *pgd, *pgd_k;
	p4d_t *p4d, *p4d_k;
	pud_t *pud;
	pmd_t *pmd;
	pte_t *pte;

	/* Make sure we are in vmalloc area: */
	if (!(address >= VMALLOC_START && address < VMALLOC_END))
		return -1;

	WARN_ON_ONCE(in_nmi());

	/*
	 * Copy kernel mappings over when needed. This can also
	 * happen within a race in page table update. In the later
	 * case just flush:
	 */
        /*读取当前进程内核态PGD */
	pgd = (pgd_t *)__va(read_cr3_pa()) + pgd_index(address);
	/*pgd_k为vmalloc建立的内核页表 */
        pgd_k = pgd_offset_k(address);
	if (pgd_none(*pgd_k))
		return -1;

        /*下面就是依次复制内核页表的P4D->PUD->PMD->PTE */

	/* With 4-level paging, copying happens on the p4d level. */
	p4d = p4d_offset(pgd, address);
	p4d_k = p4d_offset(pgd_k, address);
	if (p4d_none(*p4d_k))
		return -1;

	if (p4d_none(*p4d) && !pgtable_l5_enabled()) {
		set_p4d(p4d, *p4d_k);
		arch_flush_lazy_mmu_mode();
	} else {
		BUG_ON(p4d_pfn(*p4d) != p4d_pfn(*p4d_k));
	}

	
	pud = pud_offset(p4d, address);
	if (pud_none(*pud))
		return -1;

	pmd = pmd_offset(pud, address);
	if (pmd_none(*pmd))
		return -1;

	pte = pte_offset_kernel(pmd, address);
	if (!pte_present(*pte))
		return -1;

	return 0;
}

 

你可能感兴趣的:(内存管理)