在Xen源码中,有一个函数long do_memory_op(unsigned long cmd, XEN_GUEST_HANDLE(void) arg),里面调用了rc = memory_exchange(guest_handle_cast(arg, xen_memory_exchange_t)),我不明白的是XEN_GUEST_HANDLE和guest_handle_cast这是什么?我看了源码,前者只有一个宏定义,后者虽然有具体内容,但是看不懂。
另外,内存地址是怎么传递的?或者说,xen_memory_exchange_t这个结构体是怎么赋值的?
拜谢!!

解决方案 »

  1.   

    我来尝试回答下 内存地址是怎么传递的?或者说,xen_memory_exchange_t这个结构体是怎么赋值的?这个问题把.这个hypercall大多应该是在Dom0的控制台出发的, 调用了libxc的接口do_memory_op(xch, XENMEM_exchange, &exchange, sizeof(exchange));
    在do_memory_op中设置hypercall的值, 再调用do_xen_hypercall, 代码如下
        hypercall.op     = __HYPERVISOR_memory_op;
        hypercall.arg[0] = (unsigned long) cmd;
        hypercall.arg[1] = HYPERCALL_BUFFER_AS_ARG(arg);
        ret = do_xen_hypercall(xch, &hypercall);对于支持Xen的linux中, do_xen_hypercall最终调用了linux_privcmd_hypercall,这个函数其实就是对/dev/xen/privcmd设备做了个ioctl(fd, IOCTL_PRIVCMD_HYPERCALL, hypercall)操作(这里其实就可以知道, 为啥要给linux 内核打Xen补丁才能支持Xen, 支持Xen的Linux通过对/dev/xen/privcmd来执行hypercall)
    再来看/dev/xen/privcmd这个设备驱动中提供的ioctl接口(这里要跳转到linux 内核代码xen下, 不是xen源代码)
    static long privcmd_ioctl(struct file *file,
      unsigned int cmd, unsigned long data)
    {
    int ret = -ENOSYS;
    void __user *udata = (void __user *) data; switch (cmd) {
    case IOCTL_PRIVCMD_HYPERCALL:
    ret = privcmd_ioctl_hypercall(udata);
    break;
    }
    继续进入privcmd_ioctl_hypercall(udata)
    static long privcmd_ioctl_hypercall(void __user *udata)
    {
    struct privcmd_hypercall hypercall;
    long ret; if (copy_from_user(&hypercall, udata, sizeof(hypercall)))
    return -EFAULT; ret = privcmd_call(hypercall.op,
       hypercall.arg[0], hypercall.arg[1],
       hypercall.arg[2], hypercall.arg[3],
       hypercall.arg[4]);
    }
    hypercall的内容已经被拷贝到内核空间, 然后继续跟进 privcmd_call
    这个函数中调用了一段内联汇编
    __HYPERCALL_DECLS;
    __HYPERCALL_5ARG(a1, a2, a3, a4, a5);
    asm volatile("call *%[call]"
         : __HYPERCALL_5PARAM
         : [call] "a" (&hypercall_page[call])
         : __HYPERCALL_CLOBBER5);
    虽然我看得很迷糊....但通过宏可以猜测到op在eax,arg0-4 5个参数分别放在了ebx,ecx,edx,esi,edi(x86 32)里,然后直接根据hypercall op号调用对应接口(linux 3.0代码里直接去调用了??没有通过int 0x82??? hypercall_page应该在建立 Dom0(linux)时候映射的) (如果有高手,请指教....)再回到xen代码
    ENTRY(compat_hypercall_table)
            .quad compat_set_trap_table     /*  0 */
            .quad do_mmu_update
            .quad compat_set_gdt
            .quad do_stack_switch
            .quad compat_set_callbacks
            .quad do_fpu_taskswitch         /*  5 */
            .quad do_sched_op_compat
            .quad compat_platform_op
            .quad do_set_debugreg
            .quad do_get_debugreg
            .quad compat_update_descriptor  /* 10 */
            .quad compat_ni_hypercall
            .quad compat_memory_op /*__HYPERVISOR_memory_op=12*/
    long do_memory_op(unsigned long cmd, XEN_GUEST_HANDLE_PARAM(void) arg)
    想不明白, 为啥这里的两个参数可以从寄存器ebx, ecx中取得...即cmd=>arg[0]=>ebx, arg=>arg[1]=>ecx...上面
      

  2.   

    如果是通过int 0x82来调用hypercall比较好理解, 从vm退出进入vmx, 调用
    vmx_asm_vmexit_handler,
    将寄存器压栈, 
    然后调用vmx_asm_vmexit_handler, 根据退出vm的原因
    应该为EXIT_REASON_VMCALL, 继而调用
    rc = hvm_do_hypercall(regs); reg指向的内容就是就是刚才压栈的那些寄存器
    最后调用:
            regs->eax = hvm_hypercall32_table[eax]((uint32_t)regs->ebx,
                                                   (uint32_t)regs->ecx,
                                                   (uint32_t)regs->edx,
                                                   (uint32_t)regs->esi,
                                                   (uint32_t)regs->edi,
                                                   (uint32_t)regs->ebp);
    static hvm_hypercall_t *const hvm_hypercall32_table[NR_hypercalls] = {
        [ __HYPERVISOR_memory_op ] = (hvm_hypercall_t *)hvm_memory_op_compat32,
        [ __HYPERVISOR_grant_table_op ] = (hvm_hypercall_t *)hvm_grant_table_op_compat32,
        [ __HYPERVISOR_vcpu_op ] = (hvm_hypercall_t *)hvm_vcpu_op_compat32,
        [ __HYPERVISOR_physdev_op ] = (hvm_hypercall_t *)hvm_physdev_op_compat32,
        COMPAT_CALL(xen_version),
        HYPERCALL(console_io),
        HYPERCALL(event_channel_op),
        COMPAT_CALL(sched_op),
        COMPAT_CALL(set_timer_op),
        HYPERCALL(xsm_op),
        HYPERCALL(hvm_op),
        HYPERCALL(sysctl),
        HYPERCALL(domctl),
        HYPERCALL(tmem_op)
    };
      

  3.   

    上面说错了,不是int 0x82, 是通过vmcall指令.....
      

  4.   

    xen的源码哪里有呀
      

  5.   

    还是自己来解答自己的疑问
    Dom0 Linux hypercall table里存放的不是具体的hypercallcode, 而是
    static void vmx_init_hypercall_page(struct domain *d, void *hypercall_page)
    {
        char *p;
        int i;    for ( i = 0; i < (PAGE_SIZE / 32); i++ )
        {
            if ( i == __HYPERVISOR_iret )
                continue;        p = (char *)(hypercall_page + (i * 32));
            *(u8  *)(p + 0) = 0xb8; /* mov imm32, %eax */
            *(u32 *)(p + 1) = i;
            *(u8  *)(p + 5) = 0x0f; /* vmcall */
            *(u8  *)(p + 6) = 0x01;
            *(u8  *)(p + 7) = 0xc1;
            *(u8  *)(p + 8) = 0xc3; /* ret */
        }    /* Don't support HYPERVISOR_iret at the moment */
        *(u16 *)(hypercall_page + (__HYPERVISOR_iret * 32)) = 0x0b0f; /* ud2 */
    }最终通过vmcall进入vmx, 就回到了我之前说的逻辑