首页 > 其他分享 >armv8虚拟化原理笔记

armv8虚拟化原理笔记

时间:2024-01-13 14:12:28浏览次数:35  
标签:KVM run .. 虚拟化 kvm 笔记 entry armv8 cpu

随便记记,没有章法。

VTTBR_EL2和TTBR1_EL2有啥区别?

VTTBR_EL2是内存虚拟化中stage2页表的基地址存放的寄存器,高16位存放了VMID,用于提高VM TLB性能;

TTBR1_EL2,是指在VHE开启的情况下host OS可以在EL2运行,这时候内核使用的页表基地址就存放在这里;

设备模拟分为软件模拟和直接assign。前者通过stage 2 entry 设置为fault由hypervisor完成模拟,后者由hypervisor将物理设备内存直接映射到VM。

当stage 2 fault产生时hypervisor需要知道触发fault的地址,读or写,属于哪个设备,哪个寄存器,size大小。HPFAR_EL2负责保存触发fault的IPA,ESR_EL2存放了出错的详细信息。问题:怎么知道是哪个设备触发的fault?

有些系统寄存器经常访问,如果每次都trap会影响性能,所以arm提供了相应的virtual寄存器,比如VPIDR_EL2,是MIDR_EL1的virtual版;VMPIDR_EL2是VPIDR_EL1的virtual版。

HFGRTR_EL2, Hypervisor Fine-Grained Read Trap Register,这是一个非常重要的寄存器,负责控制guest在读系统寄存器的时候的行为,是否trap。

ARM DDI 0487中D19.2以H开头的系列寄存器可以控制在guest读写执行某些敏感指令的行为。

enable HCR_EL2.IMO中断会route到EL2。还由FMO,AMObit也有同样的作用。

HCR_EL2的E2H控制vhe的行为,如果打开则由TGE决定当前是VM还是host,1是host,表面host kernel在EL2,也就不需要virtual interrupt了。所以在VM中E2H=1, TGE=0, IMO=FMO=AMO=1,VM=1

中断虚拟化有两种方式,HCR模拟,VI,VF,VSE可以发送虚拟中断;使用vgic;

中断控制器的模拟

GIC的虚拟化只支持cpu interface,对GICD, GICR, GITS的模拟只能依靠MMIO通过data abort来软件模拟,一般是KVM会使用in-kernel的方式模拟。

kvm_arm_gicv3_realize会真正实现gic。通过ioctl KVM_DEV_ARM_VGIC_GRP_ADDR注册gicd gicr的地址到kvm。

kvm_set_irq通过ioctl KVM_IRQ_LINE向VM注入中断。KVM_SIGNAL_MSI可以注入msi中断。KVM_SET_GSI_ROUTING可以设置gsi 路由

创建虚拟机

打开/dev/kvm得到kvm_fd,然后使用ioctl create vm;创建一个vm实例。

创建vcpu

qemu在cpu的realizefn(arm_cpu_realizefn)里面会初始化vcpu。初始化vcpu主要在qemu_init_vcpu里面。最重要的是创建一个vcpu thread,运行kvm_vcpu_thread_fn,在这个函数中,在VMfd上调用ioctl KVM_CREATE_VCPU创建vcpu,kvm会初始化对应vcpu,接着线程进入while循环,

 do {
        if (cpu_can_run(cpu)) {
            r = kvm_cpu_exec(cpu);
            if (r == EXCP_DEBUG) {
                cpu_handle_guest_debug(cpu);
            }
        }
        qemu_wait_io_event(cpu);
    } while (!cpu->unplug || cpu_can_run(cpu));

如果现在还处于stop状态就等着,如果可以运行就执行kvm_cpu_exec。在这个函数中会使用ioctl KVM_RUN真正是虚拟机进入guest运行。

 

qemu在arm64虚拟机上创建内存的调用链:

#0  ram_block_add (new_block=0x555556a64d00, errp=0x7fffffffdd40) at ../softmmu/physmem.c:1975
#1  0x0000555555e3a61b in qemu_ram_alloc_internal
     (size=size@entry=2147483648, max_size=max_size@entry=2147483648, resized=resized@entry=0x0, host=host@entry=0x0, ram_flags=ram_flags@entry=0, mr=mr@entry=0x555556c926e0, errp=0x7fffffffddb0) at ../softmmu/physmem.c:2180
#2  0x0000555555e3d307 in qemu_ram_alloc (size=size@entry=2147483648, ram_flags=ram_flags@entry=0, mr=mr@entry=0x555556c926e0, errp=errp@entry=0x7fffffffddb0) at ../softmmu/physmem.c:2200
#3  0x0000555555e33cad in memory_region_init_ram_flags_nomigrate
    (mr=mr@entry=0x555556c926e0, owner=owner@entry=0x555556c92680, name=name@entry=0x5555569bd720 "mach-virt.ram", size=2147483648, ram_flags=0, errp=errp@entry=0x7fffffffde20) at ../softmmu/memory.c:1563
#4  0x0000555555af6b0e in ram_backend_memory_alloc (errp=0x7fffffffde20, backend=0x555556c92680) at ../backends/hostmem-ram.c:33
#5  ram_backend_memory_alloc (backend=0x555556c92680, errp=0x7fffffffde20) at ../backends/hostmem-ram.c:20
#6  0x0000555555af6c20 in host_memory_backend_memory_complete (uc=<optimized out>, errp=0x7fffffffde70) at ../backends/hostmem.c:336
#7  0x0000555555ed60d7 in user_creatable_complete (uc=0x555556c92680, errp=errp@entry=0x5555569b2670 <error_fatal>) at ../qom/object_interfaces.c:28
#8  0x00005555558f7cb7 in create_default_memdev (errp=0x5555569b2670 <error_fatal>, path=<optimized out>, ms=0x555556bf8400) at ../hw/core/machine.c:1287
#9  machine_run_board_init (machine=0x555556bf8400, mem_path=<optimized out>, errp=0x5555569b2670 <error_fatal>) at ../hw/core/machine.c:1326
#10 0x0000555555aef986 in qemu_init_board () at ../softmmu/vl.c:2493
#11 qmp_x_exit_preconfig (errp=<optimized out>) at ../softmmu/vl.c:2589
#12 0x0000555555af325a in qmp_x_exit_preconfig (errp=<optimized out>) at ../softmmu/vl.c:2584
#13 qemu_init (argc=<optimized out>, argv=<optimized out>, envp=<optimized out>) at ../softmmu/vl.c:3586
#14 0x000055555588f33b in qemu_main (argc=<optimized out>, argv=<optimized out>, envp=<optimized out>) at ../softmmu/main.c:37
#15 0x00007ffff7629d90 in __libc_start_call_main (main=main@entry=0x555555888e30 <main>, argc=argc@entry=20, argv=argv@entry=0x7fffffffe248) at ../sysdeps/nptl/libc_start_call_main.h:58
#16 0x00007ffff7629e40 in __libc_start_main_impl (main=0x555555888e30 <main>, argc=20, argv=0x7fffffffe248, init=<optimized out>, fini=<optimized out>, rtld_fini=<optimized out>, stack_end=0x7fffffffe238)
    at ../csu/libc-start.c:392
#17 0x000055555588f265 in _start ()

 创建arm64虚拟机的命令行:

/root/qemu/build/qemu-system-aarch64 \
        -m 2048 \
        -cpu cortex-a57 \
        -smp 2 \
        -M virt \
        -nographic \
        -serial mon:stdio \
        -device virtio-scsi-device \
        -drive if=none,file=jammy-server-cloudimg-arm64.img,id=hd0 \
        -device virtio-blk-device,drive=hd0 \
        -bios /root/qemu/pc-bios/edk2-aarch64-code.fd \

 memory分配之后需要通知kvm,还有后续的内存更改通知,这由MemeoryListener来完成。

对于kvm有:

void kvm_memory_listener_register(KVMState *s, KVMMemoryListener *kml,
                                  AddressSpace *as, int as_id, const char *name)
{
   。。。

    kml->listener.region_add = kvm_region_add;
    kml->listener.region_del = kvm_region_del;
    kml->listener.commit = kvm_region_commit;
    kml->listener.log_start = kvm_log_start;
    kml->listener.log_stop = kvm_log_stop;
    kml->listener.priority = MEMORY_LISTENER_PRIORITY_ACCEL;
    kml->listener.name = name;

向kvm注册mmeory region只会添加hva和ipa的联系,并不会让kvm操作stage2的页表,更不会分配内存。此时stage2的页表只有一个pgd,只有在发生stage2的page fault的时候才会route到kvm,kvm这个时候才会去创建s2的页表。

kvm_handle_guest_abort负责解决s2页表缺失的问题。调用链是:

建立页表最重要的是找到ipa和对应的phy_addr。ipa是出错信息中拿到的,物理地址是通过hva拿到的,但是复杂的情况是这个时候可能还没有分配物理内存,所以根本不存在物理地址。hva_to_pfn可以解决这个问题,首先使用快速路径,在内存已经分配的情况下,可以直接拿到这个地址,如果没有分配就进入慢速路径,使用get_user_pages函数分配内存,返回物理地址。如果这样还不够后面还有进一步分操作。拿到物理地址之后就可以使用kvm_pgtable_stage2_map去建立页表了。

 对于mmio就简单很多,凡是没有注册过的ipa,一旦被访问就视为IO区域,一般由userspace处理。

int io_mem_abort(struct kvm_vcpu *vcpu, phys_addr_t fault_ipa)
{
    。。。
    /* Now prepare kvm_run for the potential return to userland. */
    run->mmio.is_write    = is_write;
    run->mmio.phys_addr    = fault_ipa;
    run->mmio.len        = len;
    vcpu->mmio_needed    = 1;

    。。。

    if (is_write)
        memcpy(run->mmio.data, data_buf, len);
    vcpu->stat.mmio_exit_user++;
    run->exit_reason    = KVM_EXIT_MMIO;
    return 0;
}

忽略in-kernel处理的情况,一般mmio会由VMM去处理。

int kvm_cpu_exec(CPUState *cpu)
{
。。。 switch (run->exit_reason) { case KVM_EXIT_IO: /* Called outside BQL */ kvm_handle_io(run->io.port, attrs, (uint8_t *)run + run->io.data_offset, run->io.direction, run->io.size, run->io.count); ret = 0; break; case KVM_EXIT_MMIO: /* Called outside BQL */ address_space_rw(&address_space_memory, run->mmio.phys_addr, attrs, run->mmio.data, run->mmio.len, run->mmio.is_write); ret = 0; break; case KVM_EXIT_IRQ_WINDOW_OPEN: ret = EXCP_INTERRUPT; break; case KVM_EXIT_SHUTDOWN: qemu_system_reset_request(SHUTDOWN_CAUSE_GUEST_RESET); ret = EXCP_INTERRUPT; break; case KVM_EXIT_UNKNOWN: fprintf(stderr, "KVM: unknown exit, hardware reason %" PRIx64 "\n", 。。。

qemu会监控VM trap,如果需要处理就从kvm->run中获取信息,主要包括退出原因,处理完之后再次调用KVM_RUN ioctl进入guest。

 

标签:KVM,run,..,虚拟化,kvm,笔记,entry,armv8,cpu
From: https://www.cnblogs.com/banshanjushi/p/17944080

相关文章

  • RK3568 学习笔记 : 开机上电与串口波特率
    前言开发板:【正点原子】ATK-DLRK3568开发板,包装什么的看上去有点高大上,也有点贵。。开发板资料的Linux-SDK编译通过了,想尝试第一次上电开机,不过,开始出了一点状况,串口信息是乱码,难道【调试串口】数据线有问题?波特率115200bps不正确?调试串口波特率开发板默认有镜像,因此先上电研......
  • RK3568 学习笔记 : 解决 linux_sdk 编译 python 版本报错问题
    前言最近买了【正点原子】的RK3568开发板,下载了开发板的资料,包括LinuxSDK,这个LinuxSDK占用的空间比较大,扩展了一下VM虚拟机ubuntu20.04的硬盘空间,编译才正常通过。编译RK3568LinuxSDK时,遇到python版本的问题,这里做个记录【正点原子】rk3568开发板资料与Lin......
  • haproxy笔记
    文章目录场景haproxy配置文档地址场景还得先从场景说起。生产环境redis检查,发现配置的redis地址不对。redis有3个节点。192.168.0.1192.168.0.2192.168.0.3但是配置的是192.168.0.9端口是16379。好奇怪有没有,是不是配错了?问了下部署大神,才确认部署的没问题。说是走的h......
  • Iot学习笔记记录
    前言2024.1.13沙青图书馆甚至一开始打成了2023年。各位新年快乐。有时间会写下2023的年度总结。不过在此要提前开一个博客,记录一下接下来学习Iot安全的记录了。实在是再不学就要被学弟学妹追上了啊!此时此刻我却还要复习公钥和马原还有python,啊!感叹。想从黑自己的小米手环开始,......
  • 《Java编程思想第四版》学习笔记53--关于UDP
    1、TCP和UDP端口是相互独立的。也就是说,可以在端口8080同时运行一个TCP和UDP服务程序,两者之间不会产生冲突。P.547//:Dgram.java//Autilityclasstoconvertbackandforth//BetweenStringsandDataGramPackets.importjava.net.*;publicclassDgram{publ......
  • 小程序开发:笔记详情显示图片以及可以富文本编辑
    上文说到:把笔记列表的下拉刷新、上拉加载更多功能完成了。本文主要完成的功能项:页面显示图片、编辑时富文本编辑。现在的详情页是这样的: 图片还是个url。刚抽空把首页列表的无数据时展示提示的功能做了,大概样式如下: 而现在的编辑页面是这样的: 只是简单的文字编辑功能......
  • 计算机组成原理 复习笔记
    蒽,谁说不是速成指南呢。目录11Intro12-13指令系统计算机程序与指令系统语言高级语言/算法语言汇编语言机器语言冯诺依曼结构计算机指令和指令系统RISC-V指令系统架构特点特权模式14数据表示及检错纠错数据表示逻辑型数据表示字符的表示数值数据:整数、浮点数数值范围和数......
  • 秦九韶算法学习笔记
    快速求多项式——秦九韶算法计算\(\sum^n_i{a_i\timesx^i}\)的值。1.朴素算法算出每一项的值再相加,总共要进行\(\frac{n(n+1)}{2}\)次乘法,\(n\)次加法。2.秦九韶算法\(a_0+a_1x+a_2x^2+\dotsa_nx^n=(((a_nx+a_{n-1})x+a_{n-2}\dots)x+a_1)x......
  • 读元宇宙改变一切笔记06_虚拟世界引擎
    1. 一棵虚拟的树在虚拟森林里倒下了!1.1. 它们都是数据和代码1.2. 数据可以描述虚拟对象的属性1.2.1. 尺寸或颜色1.3. 为了让我们的树由CPU处理并由GPU渲染,这些数据需要通过代码运行1.4. 该代码必须是运行虚拟世界的更广泛代码框架的一部分2. 现实世界2.1. 现实世......
  • 【opencv学习笔记】028之模板匹配——matchTemplate函数详解
    目录​ ​一、前言​​​ ​二、模板匹配​​​ ​1、模板匹配是个啥​​​ ​2、常用匹配算法​​​​ ​3、API​​​ ​4、代码展示​​​ ​5、执行结果​​一、前言遭遇了点突发情况,所以今天更新的有点晚,也不知道能不能等到今天发出去了。终于可以从模板匹......