Q1: Write a hypercall in KVM
1.1 下载linux相关源代码zip到本地
1.2 修改kernel代码
- 打开
/linux/arch/x86/kvm/x86.c
,在6107与6191行左右的位置插入写好的代码:
这段代码可以用于输出
pid: the corresponding PID of the VCPU thread in KVM host
gp_regs: the values of the general purpose registers for the virtual CPU num_exits: number of vm exits from the VCPU
我在上面这段代码中进行了错误的处理,如果vcpu的值出现错误,则返回一个无效参数的对应字段。
同时你需要在int kvm_emulate_hypercall(struct kvm_vcpu *vcpu)
这段带代码中添加以下代码,用于找到对应的hypercall。
- 修改
/linux/include/uapi/linux/kvm_para.h
下的定义字段,添加以下代码,字段值为9。
- 将代码上传到host机。
1.3 编译刚刚修改的代码
使用这些指令集合即可,注意:由于我们上次课分配的是4核,这里用make -j4。
cp /boot/config-#YOUR_UBUNTU_VERSION .config
yes "" | make oldconfig
sudo apt-get install libssl-dev bc libncurses-dev
make menuconfig
make -j4
make modules_install
make install
运行完之后重启系统,使用uname -r
查看系统为4.10。
Q2: Test your new hypercall
2.1 修改guest机器上的kernel
-
和host机器一样,我们下载好了源码,然后进行修改。
-
修改
/linux/arch/x86/entry/syscalls/syscall_64.tbl
中约341行,添加代号为332的系统
-
修改
/linux/arch/x86/include/asm/syscalls.h
在最后的endif前定义自己的系统调用:
-
在
/linux/arch/x86/kernel/Makefile
中添加刚刚的编译选项:
-
定义自己的hypercall,添加
/linux/arch/x86/kernel/vcpu_info.c
文件,该文件实现了枚举每个在线的vcpu,通过vcpu_info hypercall将CPU ID传递给KVM,用于测试的内容如下:
#include <linux/kvm_para.h>
#include <asm/hypervisor.h>
#include <asm/kvm_guest.h>
#include <asm/syscalls.h>
#include <linux/cpu.h>
int vcpu_info(int vcpu_id)
{
int ret = 0;
ret = kvm_hypercall1(KVM_HC_X86_VCPU_INFO, vcpu_id);
return ret;
}
asmlinkage int sys_vcpu_info(void)
{
int ret = 0;
int i;
for_each_online_cpu(i) {
ret = vcpu_info(i);
if (ret)
return ret;
}
return ret;
}
- 在
/linux/include/uapi/linux/kvm_para.h
中定义#define KVM_HC_X86_VCPU_INFO 9
,用于实现hypercalls。
2.2 编译刚刚修改的linux文件下的代码
使用host机一样的指令,编译代码。
2.3 测试调用
- 在编译好的guest机器上定义一个简单的函数如:
#include <stdio.h>
#include <stdlib.h>
int main() {
syscall(332);
return 0;
}
- 编译并运行刚刚定义的外部的函数。
- 在host机器使用
cat /sys/kernel/debug/tracing/trace
查看显示以下结果
可以看到,host机成功记录了对应的虚拟机信息。 - 可以通过修改这个函数来实现错误的vcpu输入,如下:
- 在guest机器使用前面的
cat /sys/kernel/debug/tracing/trace
命令,发现成功调用;在host机器使用查看显示结果,只有正确的cpuid才会打印出cpu的相关信息,下面为为第一次正确的调用:
- 输入错误的cpuid再调用一次,当函数执行到host机器的时候由于错误的输入直接退出,故在host机器中不会打印出cpu的相关信息,发现host机器的日志文件并没有增加内容,而guest机器增加了。