概述:当虚拟机的Guest OS需要执行一些更高权限的操作(如:页表的更新、对物理资源的访问等)时,由于自身在非特权域无法完成这些操作,于是便通过调用Hypercall交给Xen Hypervisor来完成这些操作。与系统调用类似,Xen启用130号中断向量端口作为超调用的中断号。这一中断向量的DPL被设置为1,类型为中断门。超调用能够由处于特权级1的客户机操作系统发起,而不能从用户态发起。
fd=open("/proc/xen/privcmd",O_RDWR);
if(fd<0){
perror("can not open /proc/xen/privcmd");
exit(1);
}
ret=ioctl(fd,IOCTL_PRIVCMD_HYPERCALL,&my_hypercall);
在Xen3.0版本前,通过在每个调用的封装例程中直接执行中断汇编指令(INT 0x82)来完成超级调用;而在Xen3.0版本之后,采用了调用函数的方式,通过建立超级调用页来实现超级调用的间接使用。超调用页在虚拟机启动前被初始化(HVM虚拟域的初始化、Ring0对应域的初始化、Ring1对应域的初始化),在虚拟机内核启动时,这个页被映射到在客户机操作系统的一个固定的虚拟地址上。一个超级调用页被分为128块,每块长度为32字节。在初始化时,每一块都会写入一段发起超级调用的汇编代码。第i块的汇编代码会将编号i作为参数来发起超调用。在需要发起超调用时,客户机操作系统将超调用页中每块汇编片段当作一个函数,需要调用第i号超调用就调用第i块代码片段。
Hypercall的代码实现:
首先超级调用页中的代码通过_hypercall0()等函数进行调用,在函数中宏HYPERCALL_STR(name)即为CALL指令,用于调用超级调用页中代码。
“call hypercall_page + ("__stringfy(__HYPERVISOR_##name)" * 32)”
hypercall_page的值为超级调用页在Guest OS地址空间内的虚拟地址,与由超级调用号决定的页内位移相加的结果即为对应代码地址。
自定义Hypercall的方法
1、注册一个新的系统调用号
xen/include/public/xen.h
#define __HYPERVISOR_print_op 39
2、更新系统调用号
/xen/arch/x86/x86_64/entry.S
ENTRY (hypercall_table)
.long do_tmem_op
.long do_print_string
3、定义新函数
/xen/include/asm-x86/hypercall.h中定义新函数
extern int
do_print_string(
char* message);
/xen/arch/x86/XX.c中实现do_print_string
int do_print_string(char *message) {...}
4、重新编译代码即可
应用程序使用Hypercall的方法
由于3环的应用程序无权申请超级调用,即将超级调用请求先传递给1环的Guest OS内核,再由Guest OS内核申请超级调用,Xen系统提供连一个特殊的内核驱动程序privcmd来完成这个工作,privcmd的作用是将位于用户空间的超级调用请求传递到Guest OS内核中。
1、定义privcmd系统调用结构体
privcmd_hypercall_t my_hypercall={
__HYPERVISOR_read_dom_mem,{domainId,(unsigned long long)&readBuffer,10,0,0}
};
__HYPERVISOR_read_dom_mem,{domainId,(unsigned long long)&readBuffer,10,0,0}
};
2、调用privcmd驱动程序
在内核驱动程序privcmd中涉及的操作都是通过file_operation数据结构中的函数指针ioctl来提供的, fd=open("/proc/xen/privcmd",O_RDWR);
if(fd<0){
perror("can not open /proc/xen/privcmd");
exit(1);
}
ret=ioctl(fd,IOCTL_PRIVCMD_HYPERCALL,&my_hypercall);