本文展示的代码中,Linux内核版本为3.14.69

在看KVM源码的过程中,涉及EPT机制时,经常看到struct kvm_mmu这一结构体。
下面,先看看这一结构体与其他结构体的关系。

从上图可以看到,struct kvm_mmu是struct kvm_vcpu_arch的一个字段。
kvm_mmu结构体具体内容如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
/*
* x86 supports 3 paging modes (4-level 64-bit, 3-level 64-bit, and 2-level
* 32-bit). The kvm_mmu structure abstracts the details of the current mmu
* mode.
*/
struct kvm_mmu {
void (*set_cr3)(struct kvm_vcpu *vcpu, unsigned long root);
unsigned long (*get_cr3)(struct kvm_vcpu *vcpu);
u64 (*get_pdptr)(struct kvm_vcpu *vcpu, int index);
int (*page_fault)(struct kvm_vcpu *vcpu, gva_t gva, u32 err,
bool prefault);
void (*inject_page_fault)(struct kvm_vcpu *vcpu,
struct x86_exception *fault);
gpa_t (*gva_to_gpa)(struct kvm_vcpu *vcpu, gva_t gva, u32 access,
struct x86_exception *exception);
gpa_t (*translate_gpa)(struct kvm_vcpu *vcpu, gpa_t gpa, u32 access);
int (*sync_page)(struct kvm_vcpu *vcpu,
struct kvm_mmu_page *sp);
void (*invlpg)(struct kvm_vcpu *vcpu, gva_t gva);
void (*update_pte)(struct kvm_vcpu *vcpu, struct kvm_mmu_page *sp,
u64 *spte, const void *pte);
hpa_t root_hpa;
int root_level;
int shadow_root_level;
union kvm_mmu_page_role base_role;
bool direct_map;
/*
* Bitmap; bit set = permission fault
* Byte index: page fault error code [4:1]
* Bit index: pte permissions in ACC_* format
*/
u8 permissions[16];
u64 *pae_root;
u64 *lm_root;
u64 rsvd_bits_mask[2][4];
u64 bad_mt_xwr;
/*
* Bitmap: bit set = last pte in walk
* index[0:1]: level (zero-based)
* index[2]: pte.ps
*/
u8 last_pte_bitmap;
bool nx;
u64 pdptrs[4]; /* pae */
};

在这里看到函数指针时你可能会感到困惑,下面将解决这些困惑。

  • 通过lsmod | grep kvm指令,看是否加载了kvm及kvm_intel模块

  • vmx_init执行初始化时,会执行kvm_enable_tdp,将tdp_enabled变量置为true。

  • init_kvm_mmu时,会根据tdp_enabled的情况选择使用哪种地址翻译模式。
    当开启ept时,在init_kvm_mmu函数中会执行init_kvm_tdp_mmu函数。该函数具体内容如下:
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    37
    38
    static void init_kvm_tdp_mmu(struct kvm_vcpu *vcpu)
    {
    struct kvm_mmu *context = vcpu->arch.walk_mmu;
    context->base_role.word = 0;
    context->page_fault = tdp_page_fault;
    context->sync_page = nonpaging_sync_page;
    context->invlpg = nonpaging_invlpg;
    context->update_pte = nonpaging_update_pte;
    context->shadow_root_level = kvm_x86_ops->get_tdp_level();
    context->root_hpa = INVALID_PAGE;
    context->direct_map = true;
    context->set_cr3 = kvm_x86_ops->set_tdp_cr3;
    context->get_cr3 = get_cr3;
    context->get_pdptr = kvm_pdptr_read;
    context->inject_page_fault = kvm_inject_page_fault;
    if (!is_paging(vcpu)) {
    context->nx = false;
    context->gva_to_gpa = nonpaging_gva_to_gpa;
    context->root_level = 0;
    } else if (is_long_mode(vcpu)) {
    context->nx = is_nx(vcpu);
    context->root_level = PT64_ROOT_LEVEL;
    reset_rsvds_bits_mask(vcpu, context);
    context->gva_to_gpa = paging64_gva_to_gpa;
    } else if (is_pae(vcpu)) {
    context->nx = is_nx(vcpu);
    context->root_level = PT32E_ROOT_LEVEL;
    reset_rsvds_bits_mask(vcpu, context);
    context->gva_to_gpa = paging64_gva_to_gpa;
    } else {
    context->nx = false;
    context->root_level = PT32_ROOT_LEVEL;
    reset_rsvds_bits_mask(vcpu, context);
    context->gva_to_gpa = paging32_gva_to_gpa;
    }
    update_permission_bitmask(vcpu, context, false);
    update_last_pte_bitmap(vcpu, context);
    }

可以看到kvm_mmu中的函数指针最终指向的函数在init_kvm_tdp_mmu中被解析。

也许读者不太清楚上述函数的使用环境,下面我将给出函数调用图方便大家了解。