在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这个结构体是怎么赋值的?
拜谢!!
另外,内存地址是怎么传递的?或者说,xen_memory_exchange_t这个结构体是怎么赋值的?
拜谢!!
解决方案 »
- 【每日译帖】Auth GET failed: http://10.103.0.6:5000/tokens 300 Multiple Choices
- OpenStack兼容的华为云计算解决方案
- 安装OpenStack neutron时出现的问题,已经折磨我很久了,望大家帮忙
- 云计算过热,或需调整
- 免费流量和超多好礼相送!Azure社区问题征集活动启动啦!
- 完全虚拟化和部分虚拟化的IO流程
- Spark-shell运行命令出现:java.lang.OutOfMemoryError: Java heap space
- 阿里云外网zookeeper搭建出错
- ambari 安装的spark 怎么使用Standalone方式提交任务
- 求助:远程连接Spark on Yarn读取Mongodb数据报错
- aws新手提问:自动分配的ip都是新泽西的吗?
- Linux下eclipse进行hadoop开发
在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...上面
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)
};
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, 就回到了我之前说的逻辑