问题描述
在Xen源码中,有一个函数longdo_memory_op(unsignedlongcmd,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这个结构体是怎么赋值的?拜谢!!
解决方案
解决方案二:
我来尝试回答下内存地址是怎么传递的?或者说,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]=(unsignedlong)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源代码)staticlongprivcmd_ioctl(structfile*file,unsignedintcmd,unsignedlongdata){intret=-ENOSYS;void__user*udata=(void__user*)data;switch(cmd){caseIOCTL_PRIVCMD_HYPERCALL:ret=privcmd_ioctl_hypercall(udata);break;}继续进入privcmd_ioctl_hypercall(udata)staticlongprivcmd_ioctl_hypercall(void__user*udata){structprivcmd_hypercallhypercall;longret;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);asmvolatile("call*%[call]":__HYPERCALL_5PARAM:[call]"a"(&hypercall_page[call]):__HYPERCALL_CLOBBER5);虽然我看得很迷糊....但通过宏可以猜测到op在eax,arg0-45个参数分别放在了ebx,ecx,edx,esi,edi(x8632)里,然后直接根据hypercallop号调用对应接口(linux3.0代码里直接去调用了??没有通过int0x82???hypercall_page应该在建立Dom0(linux)时候映射的)(如果有高手,请指教....)再回到xen代码ENTRY(compat_hypercall_table).quadcompat_set_trap_table/*0*/.quaddo_mmu_update.quadcompat_set_gdt.quaddo_stack_switch.quadcompat_set_callbacks.quaddo_fpu_taskswitch/*5*/.quaddo_sched_op_compat.quadcompat_platform_op.quaddo_set_debugreg.quaddo_get_debugreg.quadcompat_update_descriptor/*10*/.quadcompat_ni_hypercall.quadcompat_memory_op/*__HYPERVISOR_memory_op=12*/longdo_memory_op(unsignedlongcmd,XEN_GUEST_HANDLE_PARAM(void)arg)想不明白,为啥这里的两个参数可以从寄存器ebx,ecx中取得...即cmd=>arg[0]=>ebx,arg=>arg[1]=>ecx...上面
解决方案三:
如果是通过int0x82来调用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);statichvm_hypercall_t*consthvm_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)};
解决方案四:
上面说错了,不是int0x82,是通过vmcall指令.....
解决方案五:
xen的源码哪里有呀
解决方案六:
还是自己来解答自己的疑问Dom0Linuxhypercalltable里存放的不是具体的hypercallcode,而是staticvoidvmx_init_hypercall_page(structdomain*d,void*hypercall_page){char*p;inti;for(i=0;i<(PAGE_SIZE/32);i++){if(i==__HYPERVISOR_iret)continue;p=(char*)(hypercall_page+(i*32));*(u8*)(p+0)=0xb8;/*movimm32,%eax*/*(u32*)(p+1)=i;*(u8*)(p+5)=0x0f;/*vmcall*/*(u8*)(p+6)=0x01;*(u8*)(p+7)=0xc1;*(u8*)(p+8)=0xc3;/*ret*/}/*Don'tsupportHYPERVISOR_iretatthemoment*/*(u16*)(hypercall_page+(__HYPERVISOR_iret*32))=0x0b0f;/*ud2*/}最终通过vmcall进入vmx,就回到了我之前说的逻辑