xref: /DragonOS/user/apps/test_kvm/main.c (revision 338f6903262c5031abad3c8e361813355a27fcdb)
1 /**
2  * @file main.c
3  * @author xiaoyez (xiaoyez@zju.edu.cn)
4  * @brief 测试kvm的程序
5  * @version 0.1
6  * @date 2023-07-13
7  *
8  * @copyright Copyright (c) 2023
9  *
10  */
11 
12 /**
13  * 测试kvm命令的方法:
14  * 1.在DragonOS的控制台输入 exec bin/test_kvm.elf
15  *
16  */
17 #include <fcntl.h>
18 #include <stdint.h>
19 #include <stdio.h>
20 #include <sys/ioctl.h>
21 #include <unistd.h>
22 
23 #define KVM_CREATE_VCPU 0x00
24 #define KVM_SET_USER_MEMORY_REGION 0x01
25 
26 #define KVM_RUN 0x00
27 #define KVM_GET_REGS 0x01
28 #define KVM_SET_REGS 0x02
29 
30 struct kvm_userspace_memory_region {
31     uint32_t slot; // 要在哪个slot上注册内存区间
32     // flags有两个取值,KVM_MEM_LOG_DIRTY_PAGES和KVM_MEM_READONLY,用来指示kvm针对这段内存应该做的事情。
33     // KVM_MEM_LOG_DIRTY_PAGES用来开启内存脏页,KVM_MEM_READONLY用来开启内存只读。
34     uint32_t flags;
35     uint64_t guest_phys_addr; // 虚机内存区间起始物理地址
36     uint64_t memory_size;     // 虚机内存区间大小
37     uint64_t userspace_addr;  // 虚机内存区间对应的主机虚拟地址
38 };
39 
40 struct kvm_regs {
41 	/* out (KVM_GET_REGS) / in (KVM_SET_REGS) */
42 	uint64_t rax, rbx, rcx, rdx;
43 	uint64_t rsi, rdi, rsp, rbp;
44 	uint64_t r8,  r9,  r10, r11;
45 	uint64_t r12, r13, r14, r15;
46 	uint64_t rip, rflags;
47 };
48 
49 int guest_code(){
50     while (1)
51     {
52         // printf("guest code\n");
53         __asm__ __volatile__ (
54             "mov %rax, 0\n\t"
55             "mov %rcx, 0\n\t"
56             "cpuid\n\t"
57         );
58     }
59     return 0;
60 }
61 
62 int main()
63 {
64     printf("Test kvm running...\n");
65     printf("Open /dev/kvm\n");
66     int kvm_fd = open("/dev/kvm", O_RDWR|O_CLOEXEC);
67     int vmfd = ioctl(kvm_fd, 0x01, 0);
68     printf("vmfd=%d\n", vmfd);
69 
70     /*
71          __asm__ __volatile__ (
72             "mov %rax, 0\n\t"
73             "mov %rcx, 0\n\t"
74             "cpuid\n\t"
75         );
76     */
77     const uint8_t code[] = {
78         0xba, 0xf8, 0x03, /* mov $0x3f8, %dx */
79         0x00, 0xd8,       /* add %bl, %al */
80         0x04, '0',        /* add $'0', %al */
81         0xee,             /* out %al, (%dx) */
82         0xb0, '\n',       /* mov $'\n', %al */
83         0xee,             /* out %al, (%dx) */
84         0xf4,             /* hlt */
85     };
86 
87     size_t mem_size = 0x4000; // size of user memory you want to assign
88     printf("code=%p\n", code);
89     // void *mem = mmap(0, mem_size, 0x7, -1, 0);
90     // memcpy(mem, code, sizeof(code));
91     struct kvm_userspace_memory_region region = {
92         .slot = 0,
93         .flags = 0,
94         .guest_phys_addr = 0,
95         .memory_size = mem_size,
96         .userspace_addr = (size_t)code
97     };
98     ioctl(vmfd, KVM_SET_USER_MEMORY_REGION, &region);
99 
100     int vcpufd = ioctl(vmfd, KVM_CREATE_VCPU, 0);
101     printf("vcpufd=%d\n", vcpufd);
102     int user_entry = 0x0;
103 
104     struct kvm_regs regs = {0};
105     regs.rip = user_entry;
106     regs.rsp = 0x3000; // stack address
107     regs.rflags = 0x2; // in x86 the 0x2 bit should always be set
108     ioctl(vcpufd, KVM_SET_REGS, &regs); // set registers
109 
110     ioctl(vcpufd, KVM_RUN, 0);
111 
112     return 0;
113 }
114 
115 
116