在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這個結構體是怎么賦值的?
拜謝!!
uj5u.com熱心網友回復:
我來嘗試回答下 記憶體地址是怎么傳遞的?或者說,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...
上面
uj5u.com熱心網友回復:
如果是通過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)
};
uj5u.com熱心網友回復:
上面說錯了,不是int 0x82, 是通過vmcall指令.....uj5u.com熱心網友回復:
xen的原始碼哪里有呀uj5u.com熱心網友回復:
還是自己來解答自己的疑問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, 就回到了我之前說的邏輯
轉載請註明出處,本文鏈接:https://www.uj5u.com/qita/104272.html
標籤:虛擬化
