在Xen中,hypercall(超调用)是一个比较重要的概念;hypercall类似于system call,hypercall之于xen hypervisor就像系统调用之于Linux kernel.
在x86平台上,Xen中的hypercall是通过软中端(中断号0x82)来实现的。
在linux系统中添加新的系统调用,一般需要三个步骤:
1. 注册新的系统调用号
2. 更新系统调用表
3. 添加新函数
在xen中添加一个hypercall,也类似于在linux中添加一个系统调。下面简单介绍一下,如何创建一个自己的hypercall。
(添加一个hypercall的完整的patch在这里:add_hypercall.patch,可以用在最新的xen-unstable tree上。)
0.准备工作:获取xen源代码,以最新的upstream xen为例
1 2 |
hg clone http://xenbits.xen.org/xen-unstable.hg cd xen-unstable.hg |
也可以到如下网页去下载Xen hypervisor的tarball文件:
http://www.xen.org/products/xen_source.html
1. 注册一个hypercall调用号:xen/include/public/xen.h
1 2 3 |
#define __HYPERVISOR_tmem_op 38 #define __HYPERVISOR_xc_reserved_op 39 /* reserved for XenClient */ +#define __HYPERVISOR_hello_hypercall 39 /* add this temporarily; 39 is not used before */ |
2. 更新系统调用表:xen/arch/x86/x86_64/entry.S (若是32bit Intel平台,则是x86_32/entry.S)。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 |
@@ -697,6 +697,7 @@ .quad do_domctl .quad do_kexec_op .quad do_tmem_op + .quad do_hello_hypercall .rept __HYPERVISOR_arch_0-((.-hypercall_table)/8) .quad do_ni_hypercall .endr @@ -745,6 +746,7 @@ .byte 1 /* do_domctl */ .byte 2 /* do_kexec */ .byte 1 /* do_tmem_op */ + .byte 1 /* do_hello_hypercall */ .rept __HYPERVISOR_arch_0-(.-hypercall_args_table) .byte 0 /* do_ni_hypercall */ .endr |
3. 定义hypercall处理函数的头文件:xen/include/asm-x86/hypercall.h
1 2 3 4 5 6 7 8 |
@@ -94,6 +94,10 @@ do_kexec( unsigned long op, unsigned arg1, XEN_GUEST_HANDLE(void) uarg); +extern int +do_hello_hypercall( + char* str); + |
4. 定义函数在适当的地方(本例在:xen/arch/x86/traps.c中)
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 |
@@ -3850,6 +3850,20 @@ return -EINVAL; } +int do_hello_hypercall(char* str) +{ + printk("------------------------------\n"); + printk("Begin: hello hypercall.\n"); + if(str != NULL) + printk("Hello, %s.\n", str); + else + printk("Hello, the unkown.\n"); + printk("End: hello hypercall.\n"); + printk("----demoed by Jay.----\n"); + printk("------------------------------\n"); + return 0; +} + /* * Local variables: * mode: C |
到这里,添加hypercall的代码就基本完成了,然后需要编译xen hypervisor:
make xen
将生成的xen.gz更新到启动时用的xen.gz,重启系统。
下面写一个测试这个hypercall的简单代码:
xen提供了/proc/xen/privcmd这个虚拟文件,从而在ring 3(用户空间)可以利用ioctl来调用hypercall。
(注意:需要保证/usr/include/xen/xen.h中有你添加的__HYPERVISOR_hello_hypercall的宏定义,否则会编译报错,找不到新定义的hypercall符号。)
详细代码见:test_hypercall.c
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 |
#include <stdio.h> #include <stdlib.h> #include <errno.h> #include <sys/ioctl.h> #include <linux/types.h> #include <fcntl.h> #include <string.h> #include <xenctrl.h> #include <xen/sys/privcmd.h> int main(int argc, char *argv[]) { int fd, ret; char * message; if (argc != 2) { printf("please input one parameter!\n"); return -1; } message = (char *) malloc(sizeof(char) * (strlen(argv[1])+1)); strcpy(message, argv[1]); privcmd_hypercall_t my_hypercall = { __HYPERVISOR_hello_hypercall, {(__u64)message, 0, 0, 0, 0} }; fd = open("/proc/xen/privcmd", O_RDWR); if (fd < 0) { perror("can't open /proc/xen/privcmd"); exit(1); } else printf("privcmd's fd = %d\n", fd); ret = ioctl(fd, IOCTL_PRIVCMD_HYPERCALL, &my_hypercall); printf("ret = %d\n", ret); } |
注意测试代码中,这里有个强制类型转换:(__u64)message,是为了适应结构体privcmd_hypercall_t的定义,否则会出现编译时warning:
test_hypercall.c:23: warning: initialization makes integer from pointer without a cast
因为需要将指针变量转化为__u64整型的变量(__u64类型在linux/types.h中定义),应该显式强制类型转换,详见:/usr/include/xen/sys/privcmd.h
1 2 3 4 5 6 7 8 9 10 11 |
#include <linux/types.h> #ifndef __user #define __user #endif typedef struct privcmd_hypercall { __u64 op; __u64 arg[5]; } privcmd_hypercall_t; |
编译测试程序:gcc test_hypercall.c -o test_hypercall
运行测试程序:
1 2 3 |
[root@vt-snb7 jay]# ./test_hypercall world privcmd's fd = 3 ret = 0 |
查看测试结果:xl dmesg | less
1 2 3 4 5 6 |
(XEN) ------------------------------ (XEN) Begin: hello hypercall. (XEN) Hello, world. (XEN) End: hello hypercall. (XEN) ----demoed by Jay.---- (XEN) ------------------------------ |
参考资料:
在DomU中调用hypercall见:http://blog.csdn.net/sploving/article/details/5990507
http://wiki.xen.org/wiki/Hypercall
http://hi.baidu.com/mars208/blog/item/847dbc2200c628198b82a146.html
您好,看了你这篇文章,我在xen3.2中照着实现了一遍,但是ret=-1,在xm dmesg命令中也没有输出的消息,请问是为什么呢?
你好,谢谢关注。首先,我是在最新的xen上做的,不过,应该在xen3.2上也适用(小声说,你这本版本也太老了。。)然后,打开这个接口文件“/proc/xen/privcmd”有出错吗? 其次,你可以检查一下你用的是32bit的还是64bit的xen,注意看下我第2步系统调用表,他们分别是修改不同的文件。另外,你需要确定重新编译好了你修改过的xen.gz,并且用它重启了系统哦。
hi,你好,我在xen 4.3按照上面实现了一遍,也是在64位下的机器,可是ret=-1,而且xl dmesg没有上面的输出,请问老师什么原因?谢谢
根据你的信息看不出什么问题,只能说是肯定没成功。你可以看下我示例的patch:https://github.com/smilejay/c-cpp/blob/master/c2012/add_hypercall.patch
和测试代码:https://github.com/smilejay/c-cpp/blob/master/c2012/test_hypercall.c
当时应该是在Xen 4.2做的实验,不过Xen4.3也应该是相同的。你也可以考虑切换到Xen较老点的代码,然后用我的patch来试试.