全虛擬化hypercall

在全虛擬化中,由於Guest OS 的代碼沒有被修改,因此Guest OS 的特權操作, 比如更新頁表等主要通過VT 技術的VMX 操作來實現。敏感操作也不像半虛擬化那樣通過超級調用來實現,也是通過VMX 的指令來實現。因此全虛擬化下的Guest OS 即HVM 很少使用超級調用。但是有時由於開發的需要,我們需要通過超級調用來實現一些功能。本報告的內容如下:添加一個hypercall, 並讓HVM 像半虛擬化那樣方便的調用該hypercall.


   1. 搭建實驗環境

       機器是Fedora 8 作爲dom0, xen 版本4.0.1, 需要創建一個HVM. 我們這裏用ubuntu 作domU

步驟:

    a. 首先創建一個虛擬硬盤來安裝hvm

    #mkdir hvm
    # dd if=/dev/zero of=/hvm/ubuntu.img bs=1k seek=10000k count=1

    b. 下載ubuntu 鏡像文件ubuntu-9.04-desktop-i386.iso ,放到本地一個目錄下,在這裏我們放到/home/sploving/application/ 下

    c. 創建配置文件ubuntu.cfg ,用來安裝domU, 如下:

           kernel = "/usr/lib/xen/boot/hvmloader"
        builder='hvm'

        memory = 1024
        vcpus=1
        name = "ubuntu"

        disk = [ 'file:/home/sploving/hvm/ubuntu.img,ioemu:hda,w ',

        'file:/home/sploving/application/ubuntu-9.04-desktop- i386.iso,ioemu:hdc:cdrom,r' ]


        vif = [ 'type=ioemu, bridge=virbr0' ]
        usbdevice='tablet'

        on_poweroff = 'destroy'
        on_reboot = 'destroy'
        on_crash = 'destroy'

         boot='d'
         vnc=1
         sdl=0

         vncdisplay=3

         import os, re
         arch = os.uname()[4]
         if re.search('64', arch):
         arch_libdir = 'lib64'
         else:
         arch_libdir = 'lib'
         device_model = '/usr/' + arch_libdir + '/xen/bin/qemu-dm'

    d. 安裝domU

    #cd hvm
    #xm create ubuntu.cfg
    #vncviewer 127.0.0.1:3

    然後像平時安裝操作系統的步驟來安裝domU

    e. 安裝完後,提示重啓時,不要按“重啓”,而是利用xm destroy ubuntu.
    然後修改配置文件ubuntu.cfg ,將boot='d' 該爲boot='c'

    f. 啓動hvm domU

    #xm create ubuntu.cfg
    #vncviewer 127.0.0.1:3


 2. xen 中添加超級調用
    該步驟的添加超級調用的方法和在半虛擬化添加超級調用的方式一樣,具體如下:

    a. 首先註冊一個hypercall 調用號。
    xen/include/public/xen.h
    #define __HYPERVISOR_tmem_op 38
    +#define __HYPERVISOR_create_sim 39

    b. 更新系統調用表
    /xen/arch/x86/x86_32/entry.S
    ENTRY(hypercall_table)
    .long do_tmem_op
    +.long do_create_sim

    ENTRY(hypercall_args_table)

    .byte 1 /* do_tmem_op */
    +.byte 2 /* do_create_sim */

    c. 定義函數頭文件
    /xen/include/asm-x86/hypercall.h

    extern int
    do_kexec(
    unsigned long op, unsigned arg1, XEN_GUEST_HANDLE(void) uarg);
    +extern int

    +do_create_sim(
    + unsigned long paddr, int num);

    d. 定義函數( 函數定義在合適的文件中,這個例子採用mm.c)
    /xen/arch/x86/mm.c
    +int do_create_sim(unsigned long paddr, int num) {
    + printk("creat the sim space using shadow page!/n");
    +}


  3. HVM domU 中使用該 hypercall
    
    a. 首先在xen/arch/x86/hvm/hvm.c 中添加該hypercall

    static hvm_hypercall_t *hvm_hypercall32_table[NR_hypercalls] = {
    [ __HYPERVISOR_memory_op ] = (hvm_hypercall_t *)hvm_memory_op,
    [ __HYPERVISOR_grant_table_op ] = (hvm_hypercall_t *)hvm_grant_table_op,
    [ __HYPERVISOR_vcpu_op ] = (hvm_hypercall_t *)hvm_vcpu_op,
    HYPERCALL(xen_version),
    HYPERCALL(event_channel_op),
    HYPERCALL(sched_op),
    HYPERCALL(set_timer_op),
    HYPERCALL(hvm_op),
    HYPERCALL(tmem_op),
    + HYPERCALL(create_sim)

    };


    b. 在HVM domU 中寫一個模塊sim.c ,來調用該hypercall, 如下:
    #include <linux/module.h>
    #include <linux/kernel.h>
    #include <linux/vmalloc.h>
    #include <linux/mm.h>
    #include <asm/pgtable.h>
    #define __HYPERVISOR_create_sim 39
    
    char *hypercall_stubs;

    /* 下面兩個define 可以參考文件asm/hypercall.h */

    #define _hypercall2(type, name, a1, a2) /
    ({ /
    type __res; /
    long __ign1, __ign2; /
    asm volatile ( /
    HYPERCALL_STR(name) /
    : "=a" (__res), "=D" (__ign1), "=S" (__ign2) /
    : "1" ((long)(a1)), "2" ((long)(a2)) /
    : "memory" ); /
    __res; /
    })

    #define HYPERCALL_STR(name) /
        "mov $("__stringify(__HYPERVISOR_##name)" * 32),%%eax; "/
        "add hypercall_stubs,%%eax; " /
        "call *%%eax"

    static int init_hypercall(void)
    {
        uint32_t eax, ebx, ecx, edx, pages, msr, i;
        char signature[13];
    
    /* 通過cpuid 指令獲取當前操作系統的狀態 */

    cpuid(0x40000000, &eax, &ebx, &ecx, &edx);
    *(uint32_t*)(signature + 0) = ebx;
    *(uint32_t*)(signature + 4) = ecx;
    *(uint32_t*)(signature + 8) = edx;
    signature[12] = 0;

    /* 判斷是不是運行在Xen VMM 上 */
    if (strcmp("XenVMMXenVMM", signature) || (eax < 0x40000002)) {
    printk(KERN_WARNING
        "Detected Xen platform device but not Xen VMM?"
    " (sig %s, eax %x)/n",signature, eax);
    return -EINVAL;
    }

    /* 獲取xen 的版本號 */
    cpuid(0x40000001, &eax, &ebx, &ecx, &edx);
    printk(KERN_INFO "Xen version %d.%d./n", eax >> 16, eax & 0xffff);
    
    /* 獲取超級調用頁的數目pages,( 一般情況下只有一頁) 以及相應的寄存器值msr */
    cpuid(0x40000002, &pages, &msr, &ecx, &edx);
    printk(KERN_INFO"there are %u pages/n", pages);

    /* 分配超級調用頁 */
    hypercall_stubs = __vmalloc(pages * PAGE_SIZE,GFP_KERNEL,
                        __pgprot(__PAGE_KERNEL & ~_PAGE_NX));

    if (hypercall_stubs == NULL)
        return -ENOMEM;


    /* 將超級調用頁的物理頁框號寫到MSR 中 */
    for (i = 0; i < pages; i++) {
        unsigned long pfn;
        pfn = vmalloc_to_pfn((char *)hypercall_stubs + i*PAGE_SIZE);
        wrmsrl(msr, ((u64)pfn << PAGE_SHIFT) + i);
    }

    printk(KERN_INFO "Hypercall area is %u pages./n", pages);
    _hypercall2(unsigned long, create_sim, 1,3);
    return 0;
    }
    module_init(init_hypercall);

    Make 文件如下:

    obj-m := sim.o
    all:
        make -C /lib/modules/`uname -r`/build M=`pwd` modules
    clean:
        make -C /lib/modules/`uname -r`/build M=`pwd` clean

    c. 編譯,加載模塊

    #make

    #insmod sim.ko

    切換到dom 0 中,利用xm dm 查看日誌

    (XEN) creat the sim space using shadow page
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章