小男孩‘自慰网亚洲一区二区,亚洲一级在线播放毛片,亚洲中文字幕av每天更新,黄aⅴ永久免费无码,91成人午夜在线精品,色网站免费在线观看,亚洲欧洲wwwww在线观看

分享

【原創(chuàng)】Linux虛擬化KVM-Qemu分析(三)之KVM源碼(1)

 小仙女本仙人 2022-06-04 發(fā)布于北京

背景

  • Read the fucking source code! --By 魯迅
  • A picture is worth a thousand words. --By 高爾基

說明:

  1. KVM版本:5.9.1
  2. QEMU版本:5.0.0
  3. 工具:Source Insight 3.5, Visio
  4. 文章同步在博客園:https://www.cnblogs.com/LoyenWang/

1. 概述

  • 從本文開始將開始source code的系列分析了;
  • KVM作為內(nèi)核模塊,可以認(rèn)為是一個中間層,向上對接用戶的控制,向下對接不同架構(gòu)的硬件虛擬化支持;
  • 本文主要介紹體系架構(gòu)初始化部分,以及向上的框架;

2. KVM初始化

  • 貝多芬曾經(jīng)說過,一旦你找到了代碼的入口,你就扼住了軟件的咽喉;
  • 我們的故事,從module_init(arm_init)開始,代碼路徑:arch/arm64/kvm/arm.c

老規(guī)矩,先來一張圖(圖片中涉及到的紅色框函數(shù),都是會展開描述的):

  • 內(nèi)核的功能模塊,基本上的套路就是:1)完成模塊初始化,向系統(tǒng)注冊;2)響應(yīng)各類請求,這種請求可能來自用戶態(tài),也可能來自異常響應(yīng)等;
  • kvm的初始化,在kvm_init中完成,既包含了體系結(jié)構(gòu)相關(guān)的初始化設(shè)置,也包含了各類回調(diào)函數(shù)的設(shè)置,資源分配,以及設(shè)備注冊等,只有當(dāng)初始化完成后,才能響應(yīng)各類請求,比如創(chuàng)建虛擬機等;
    1. 回調(diào)函數(shù)設(shè)置:cpuhp_setup_state_nocall與CPU的熱插拔相關(guān),register_reboot_notifer與系統(tǒng)的重啟相關(guān),register_syscore_ops與系統(tǒng)的休眠喚醒相關(guān),而這幾個模塊的回調(diào)函數(shù),最終都會去調(diào)用體系結(jié)構(gòu)相關(guān)的函數(shù)去打開或關(guān)閉Hypervisor;
    2. 資源分配:kmem_cache_create_usercopykvm_async_pf_init都是創(chuàng)建slab緩存,用于內(nèi)核對象的分配;
    3. kvm_vfio_ops_initVFIO是一個可以安全將設(shè)備I/O、中斷、DMA導(dǎo)出到用戶空間的框架,后續(xù)在將IO虛擬化時再深入分析;
  • 圖片中紅色的兩個函數(shù),是本文分析的內(nèi)容,其中kvm_arch_init與前文ARMv8硬件虛擬化支持緊密相關(guān),而misc_register與上層操作緊密相關(guān);

2.1 kvm_arch_init

  • It's a big topic, I'll try to put it in a nutshell.
  • 這部分內(nèi)容,設(shè)計ARMv8體系結(jié)構(gòu),建議先閱讀《Linux虛擬化KVM-Qemu分析(二)之ARMv8虛擬化》;
  • 紅色框的函數(shù)是需要進(jìn)一步展開講述的;

  • is_hyp_mode_available用于判斷ARMv8的Hyp模式是否可用,實際是通過判斷__boot_cpu_mode的值來完成,該值是在arch/arm64/kernel/head.S中定義,在啟動階段會設(shè)置該值:

  • is_kernel_in_hyp_mode,通過讀取ARMv8的CurrentEL,判斷是否為CurrentEL_EL2;
  • ARM架構(gòu)中,SVE的實現(xiàn)要求VHE也要實現(xiàn),這個可以從arch/arm64/Kconfig中看到,SVE的模塊編譯:depends on !KVM || ARM64_VHESVE(scalable vector extension),是AArch64下一代的SIMD(single instruction multiple data)指令集,用于加速高性能計算。其中SIMD如下:

  • init_common_resources,用于設(shè)置IPA的地址范圍,將其限制在系統(tǒng)能支持的物理地址范圍之內(nèi)。stage 2頁表依賴于stage 1頁表代碼,需要遵循一個條件:Stage 1的頁表級數(shù) >= Stage 2的頁表級數(shù);

2.1.1 init_hyp_mode

  • 放眼望去,init_hyp_mode解決的問題就是各種映射,最終都會調(diào)用到__create_hyp_mappings,先來解決這個映射問題:

  • 看過之前內(nèi)存管理子系統(tǒng)的同學(xué),應(yīng)該熟悉這個頁表映射建立的過程,基本的流程是給定一個虛擬地址區(qū)間和物理地址,然后從pgd開始逐級往下去建立映射。ARMv8架構(gòu)在實際映射過程中,P4D這一級頁表并沒有使用。

讓我們繼續(xù)回到init_hyp_mode的正題上來,這個函數(shù)完成了PGD頁表的分配,完成了IDMAP代碼段的映射,完成了其他各種段的映射,完成了異常向量表的映射,等等。此外,再補充幾點內(nèi)容:

  1. ARMv8異常向量表

  • ARMv8架構(gòu)的AArch64執(zhí)行態(tài)中,每種EL都有16個entry,分為四類:Synchronous,IRQ,F(xiàn)IQ,SError。以系統(tǒng)啟動時設(shè)置hypervisor的異常向量表__hyp_stub_vectors為例:

  • 當(dāng)從不同的Exception Level觸發(fā)異常時,根據(jù)執(zhí)行狀態(tài),去選擇對應(yīng)的handler處理,比如上圖中只有el1_sync有效,也就是在EL1狀態(tài)觸發(fā)EL2時跳轉(zhuǎn)到該函數(shù);
  1. pushsection/popsection
  • init_hyp_mode函數(shù)中,完成各種段的映射,段的定義放置在vmlinux.lds.S中,比如hyp.idmap.text

  • 可以通過pushsection/popsection來在目標(biāo)文件中來添加一個段,并指定段的屬性,比如"ax"代表可分配和可執(zhí)行,這個在匯編代碼中經(jīng)常用到,比如hyp-init.S中,會將代碼都放置在hyp.idmap.text中:

  • 除了pushsection/popsection外,通過#define __hyp_text __section(.hyp.text) notrace __noscs的形式也能將代碼放置在指定的段中;
  1. Hypervisor相關(guān)寄存器
  • 講幾個關(guān)鍵的相關(guān)寄存器:
    1)sctlr_el2(System Control Register):可以用于控制EL2的MMU和Cache相關(guān)操作;
    2)ttbr0_el2(Translation Table Base Register 0):用于存放頁表的基地址,上文中提到分配的hyp_pgd就需要設(shè)置到該寄存器中;
    3)vbar_el2(Vector Base Address Register):用于存放異常向量表的基地址;

我們需要先明確幾點:

  1. Hyp模式下要執(zhí)行的代碼,需要先建立起映射;
  2. 映射IDMAP代碼段和其他代碼段,明確這些段中都有哪些函數(shù),這個可以通過pushsection/popsection以及__hyp_text宏可以看出來;
  3. 最終的目標(biāo)是需要建立好頁表映射,并安裝好異常向量表;

貌似內(nèi)容比較零碎,最終的串聯(lián)與謎題留在下一小節(jié)來解答。

2.1.2 init_subsystems

先看一下函數(shù)的調(diào)用流程:

  • VGIC,timer,以及電源管理相關(guān)模塊在本文中暫且不深入分析了,本節(jié)主要關(guān)心cpu_hyp_reinit的功能;
  • 綠色框中的函數(shù),會陷入到EL2進(jìn)行執(zhí)行;

看圖中有好幾次異常向量表的設(shè)置,此外,還有頁表基地址、棧頁的獲取與設(shè)置等,結(jié)合上一小節(jié)的各類映射,是不是已經(jīng)有點迷糊了,下邊這張圖會將這些內(nèi)容串聯(lián)起來:

  • 在整個異常向量表創(chuàng)建的過程中,涉及到三個向量表:__hyp_stub_vectors,__kvm_hyp_init__kvm_call_hyp,這些代碼都是匯編實現(xiàn);
  • 在系統(tǒng)啟動過程中(arch/arm64/kernel/head.S),調(diào)用到el2_setup函數(shù),在該函數(shù)中設(shè)置了一個臨時的異常向量表,也就是先打一個樁,這個從名字也可以看出來,該異常向量表中僅實現(xiàn)了el2_synchandler處理函數(shù),可以應(yīng)對兩種異常:1)設(shè)置新的異常向量表;2)重置異常向量表,也就是設(shè)置回__hyp_stub_vectors;
  • kvm初始化時,調(diào)用了__hyp_set_vectors來設(shè)置新的異常向量表:__kvm_hyp_init。這個向量表中只實現(xiàn)了__do_hyp_init的處理函數(shù),也就是只能用來對Hyp模式進(jìn)行初始化。上文提到過idmap段,這個代碼就放置在idmap段,以前分析內(nèi)存管理子系統(tǒng)時也提到過idmap,為什么需要這個呢?idmap: identity map,也就是物理地址和虛擬地址是一一映射的,防止MMU在使能前后代碼不能執(zhí)行;
  • __kvm_call_hyp函數(shù),用于在Hyp模式下執(zhí)行指定的函數(shù),在cpu_hyp_reinit函數(shù)中調(diào)用了該函數(shù),傳遞的參數(shù)包括了新的異常向量表地址,頁表基地址,Hyp的棧地址,per-CPU偏移等,最終會調(diào)用__do_hyp_init函數(shù)完成相應(yīng)的設(shè)置。

到此,頁表和異常向量表的設(shè)置算是完成了。

2.2 misc_register

misc_register用于注冊字符設(shè)備驅(qū)動,在kvm_init函數(shù)中調(diào)用此函數(shù)完成注冊,以便上層應(yīng)用程序來使用kvm模塊

  • 字符設(shè)備的注冊分為三級,分別代表kvm, vm, vcpu,上層最終使用底層的服務(wù)都是通過ioctl函數(shù)來操作;
  • kvm:代表kvm內(nèi)核模塊,可以通過kvm_dev_ioctl來管理kvm版本信息,以及vm的創(chuàng)建等;
  • vm:虛擬機實例,可以通過kvm_vm_ioctl函數(shù)來創(chuàng)建vcpu,設(shè)置內(nèi)存區(qū)間,分配中斷等;
  • vcpu:代表虛擬的CPU,可以通過kvm_vcpu_ioctl來啟動或暫停CPU的運行,設(shè)置vcpu的寄存器等;

Qemu的使用為例:

  1. 打開/dev/kvm設(shè)備文件;
  2. ioctl(xx, KVM_CREATE_VM, xx)創(chuàng)建虛擬機對象;
  3. ioctl(xx, KVM_CREATE_VCPU, xx)為虛擬機創(chuàng)建vcpu對象;
  4. ioctl(xx, KVM_RUN, xx)讓vcpu運行起來;

3. 總結(jié)

本文主要從兩個方向來介紹了kvm_init

  1. 底層的體系結(jié)構(gòu)相關(guān)的初始化,主要涉及的就是EL2的相關(guān)設(shè)置,比如各個段的映射,異常向量表的安裝,頁表基地址的設(shè)置等,當(dāng)把這些準(zhǔn)備工作做完后,才能在硬件上去支持虛擬化的服務(wù)請求;
  2. 字符設(shè)備注冊,設(shè)置好各類ioctl的函數(shù),上層應(yīng)用程序可以通過字符設(shè)備文件,來操作底層的kvm模塊。這部分內(nèi)容深入的分析,留到后續(xù)的文章再展開了;

實際在看代碼過程中,一度為很多細(xì)節(jié)絞盡乳汁,對不起,是絞盡腦汁,每有會意,便欣然忘食,一文也無法覆蓋所有內(nèi)容,草率了。

歡迎關(guān)注個人公眾號,不定期更新技術(shù)文章。

    本站是提供個人知識管理的網(wǎng)絡(luò)存儲空間,所有內(nèi)容均由用戶發(fā)布,不代表本站觀點。請注意甄別內(nèi)容中的聯(lián)系方式、誘導(dǎo)購買等信息,謹(jǐn)防詐騙。如發(fā)現(xiàn)有害或侵權(quán)內(nèi)容,請點擊一鍵舉報。
    轉(zhuǎn)藏 分享 獻(xiàn)花(0

    0條評論

    發(fā)表

    請遵守用戶 評論公約

    類似文章 更多