【原创】Linux虚拟化KVM-Qemu分析(五)之内存虚拟化 (2)

【原创】Linux虚拟化KVM-Qemu分析(五)之内存虚拟化

首先分配一个新的memslots,并将原来的memslots内容复制到新的memslots中;

如果针对slot的操作是删除或者移动,首先根据旧的slot id号从memslots中找到原来的slot,将该slot设置成不可用状态,再将memslots安装回去。这个安装的意思,就是RCU的assignment操作,不理解这个的,建议去看看之前的RCU系列文章。由于slot不可用了,需要解除stage2的映射;

kvm_arch_prepare_memory_region函数,用于处理新的slot可能跨越多个用户进程VMA区域的问题,如果为设备区域,还需要将该区域映射到Guest IPA中;

update_memslots用于更新整个memslots,memslots基于PFN来进行排序的,添加、删除、移动等操作都是基于这个条件。由于都是有序的,因此可以选择二分法来进行查找操作;

将添加新的slot后的memslots安装回KVM中;

kvfree用于将原来的memslots释放掉;

2.2.2 kvm_delete_memslot

kvm_delete_memslot函数,实际就是调用的kvm_set_memslot函数,只是slot的操作设置成KVM_MR_DELETE而已,不再赘述。

3. HVA->HPA

光有了GPA->HVA,似乎还是跟Hypervisor没有太大关系,到底是怎么去访问物理内存的呢?貌似也没有看到去建立页表映射啊?
跟我走吧,带着问题出发!

之前内存管理相关文章中提到过,用户态程序中分配虚拟地址vma后,实际与物理内存的映射是在page fault时进行的。那么同样的道理,我们可以顺着这个思路去查找是否HVA->HPA的映射也是在异常处理的过程中创建的?答案是显然的。

回顾一下前文《Linux虚拟化KVM-Qemu分析(四)之CPU虚拟化(2)》的一张图片:

【原创】Linux虚拟化KVM-Qemu分析(五)之内存虚拟化

当用户态触发kvm_arch_vcpu_ioctl_run时,会让Guest OS去跑在Hypervisor上,当Guest OS中出现异常退出到Host时,此时handle_exit将对退出的原因进行处理;

异常处理函数arm_exit_handlers如下,具体调用选择哪个处理函数,是根据ESR_EL2, Exception Syndrome Register(EL2)中的值来确定的。

static exit_handle_fn arm_exit_handlers[] = { [0 ... ESR_ELx_EC_MAX] = kvm_handle_unknown_ec, [ESR_ELx_EC_WFx] = kvm_handle_wfx, [ESR_ELx_EC_CP15_32] = kvm_handle_cp15_32, [ESR_ELx_EC_CP15_64] = kvm_handle_cp15_64, [ESR_ELx_EC_CP14_MR] = kvm_handle_cp14_32, [ESR_ELx_EC_CP14_LS] = kvm_handle_cp14_load_store, [ESR_ELx_EC_CP14_64] = kvm_handle_cp14_64, [ESR_ELx_EC_HVC32] = handle_hvc, [ESR_ELx_EC_SMC32] = handle_smc, [ESR_ELx_EC_HVC64] = handle_hvc, [ESR_ELx_EC_SMC64] = handle_smc, [ESR_ELx_EC_SYS64] = kvm_handle_sys_reg, [ESR_ELx_EC_SVE] = handle_sve, [ESR_ELx_EC_IABT_LOW] = kvm_handle_guest_abort, [ESR_ELx_EC_DABT_LOW] = kvm_handle_guest_abort, [ESR_ELx_EC_SOFTSTP_LOW]= kvm_handle_guest_debug, [ESR_ELx_EC_WATCHPT_LOW]= kvm_handle_guest_debug, [ESR_ELx_EC_BREAKPT_LOW]= kvm_handle_guest_debug, [ESR_ELx_EC_BKPT32] = kvm_handle_guest_debug, [ESR_ELx_EC_BRK64] = kvm_handle_guest_debug, [ESR_ELx_EC_FP_ASIMD] = handle_no_fpsimd, [ESR_ELx_EC_PAC] = kvm_handle_ptrauth, };

用你那双水汪汪的大眼睛扫描一下这个函数表,发现ESR_ELx_EC_DABT_LOW和ESR_ELx_EC_IABT_LOW两个异常,这不就是指令异常和数据异常吗,我们大胆的猜测,HVA->HPA映射的建立就在kvm_handle_guest_abort函数中。

3.1 kvm_handle_guest_abort

先来补充点知识点,可以更方便的理解接下里的内容:

Guest OS在执行到敏感指令时,产生EL2异常,CPU切换模式并跳转到EL2的el1_sync(arch/arm64/kvm/hyp/entry-hyp.S)异常入口;

CPU的ESR_EL2寄存器记录了异常产生的原因;

Guest退出到kvm后,kvm根据异常产生的原因进行对应的处理。

简要看一下ESR_EL2寄存器:

【原创】Linux虚拟化KVM-Qemu分析(五)之内存虚拟化

EC:Exception class,异常类,用于标识异常的原因;

ISS:Instruction Specific Syndrome,ISS域定义了更详细的异常细节;

在kvm_handle_guest_abort函数中,多处需要对异常进行判断处理;

kvm_handle_guest_abort函数,处理地址访问异常,可以分为两类:

常规内存访问异常,包括未建立页表映射、读写权限等;

IO内存访问异常,IO的模拟通常需要Qemu来进行模拟;

先看一下kvm_handle_guest_abort函数的注释吧:

/** * kvm_handle_guest_abort - handles all 2nd stage aborts * * Any abort that gets to the host is almost guaranteed to be caused by a * missing second stage translation table entry, which can mean that either the * guest simply needs more memory and we must allocate an appropriate page or it * can mean that the guest tried to access I/O memory, which is emulated by user * space. The distinction is based on the IPA causing the fault and whether this * memory region has been registered as standard RAM by user space. */

内容版权声明:除非注明,否则皆为本站原创文章。

转载注明出处:https://www.heiqu.com/wpxsxj.html