Generally, LWN runs top quality articles. I always read them with pleasure and they are good enough to make me a paid subscriber. Every now and then though, they would publish something pretty great even by their standards. I read this and was amazed. I had not realized that it is this easy to create and run a simple virtual machine. I typed the code in and played with it for a couple of hours. You can get the file that actually compiles (C++14) and runs here.
1//------------------------------------------------------------------------------
2// Based on: https://lwn.net/Articles/658511/
3//------------------------------------------------------------------------------
4
5#include <iostream>
6#include <iomanip>
7#include <cstdint>
8#include <cstring>
9#include <cerrno>
10#include <sys/stat.h>
11#include <fcntl.h>
12#include <unistd.h>
13#include <sys/ioctl.h>
14#include <sys/mman.h>
15#include <linux/kvm.h>
16
17//------------------------------------------------------------------------------
18// Error handling macro
19//------------------------------------------------------------------------------
20#define RUN(var, command) \
21var = command; \
22if (var == -1) { \
23 std::cerr << #command ": " << strerror(errno) << std::endl; \
24 return 1; \
25}
26
27//------------------------------------------------------------------------------
28// The code to be run inside of the virtual machine
29//------------------------------------------------------------------------------
30const uint8_t code[] = {
31 0xba, 0xf8, 0x03, /* mov $0x3f8, %dx */
32 0x00, 0xd8, /* add %bl, %al */
33 0x04, '0', /* add $'0', %al */
34 0xee, /* out %al, (%dx) */
35 0xb0, '\n', /* mov $'\n', %al */
36 0xee, /* out %al, (%dx) */
37 0xf4 /* hlt */
38};
39
40int main(int argc, char **argv)
41{
42 using namespace std;
43
44 //----------------------------------------------------------------------------
45 // Open KVM
46 //----------------------------------------------------------------------------
47 int kvm = open("/dev/kvm", O_RDWR | O_CLOEXEC);
48 if (kvm == -1) {
49 cerr << "Unable to open /dev/kvm: " << strerror(errno) << endl;
50 return 1;
51 }
52
53 //----------------------------------------------------------------------------
54 // Check the version of the API, we need 12
55 //----------------------------------------------------------------------------
56 int ret;
57 RUN(ret, ioctl(kvm, KVM_GET_API_VERSION, 0));
58
59 if (ret != 12) {
60 cerr << "KVM_GET_API_VERSION " << ret << ", expected 12" << endl;
61 return 1;
62 }
63
64 //----------------------------------------------------------------------------
65 // Check if the extension required to set up guest memory is present
66 //----------------------------------------------------------------------------
67 RUN(ret, ioctl(kvm, KVM_CHECK_EXTENSION, KVM_CAP_USER_MEMORY));
68
69 if (!ret) {
70 cerr << "KVM_CAP_USER_MEM extension is not available" << endl;
71 return 1;
72 }
73
74 //----------------------------------------------------------------------------
75 // Set up a virtual machine
76 //----------------------------------------------------------------------------
77 int vmfd;
78 RUN(vmfd, ioctl(kvm, KVM_CREATE_VM, (unsigned long)0));
79
80 //----------------------------------------------------------------------------
81 // Get some page-aligned memory and copy the code to it
82 //----------------------------------------------------------------------------
83 void *mem = mmap(0, 0x1000, PROT_READ | PROT_WRITE, MAP_SHARED | MAP_ANONYMOUS,
84 -1, 0);
85 if (mem == MAP_FAILED) {
86 cerr << "Failed to get a page of memory: " << strerror(errno) << endl;
87 return 1;
88 }
89 memcpy(mem, code, sizeof(code));
90
91 //----------------------------------------------------------------------------
92 // Tell the virtual machine about this region
93 //----------------------------------------------------------------------------
94 kvm_userspace_memory_region region;
95 memset(®ion, 0, sizeof(region));
96 region.slot = 0;
97 region.guest_phys_addr = 0x1000;
98 region.memory_size = 0x1000;
99 region.userspace_addr = (uint64_t)mem;
100
101 RUN(ret, ioctl(vmfd, KVM_SET_USER_MEMORY_REGION, ®ion));
102
103 //----------------------------------------------------------------------------
104 // Create a virtual CPU #0
105 //----------------------------------------------------------------------------
106 int vcpufd;
107 RUN(vcpufd, ioctl(vmfd, KVM_CREATE_VCPU, (unsigned long)0));
108
109 //----------------------------------------------------------------------------
110 // Allocate memory for kvm_run data structure
111 //----------------------------------------------------------------------------
112 int vcpu_run_size;
113 RUN(vcpu_run_size, ioctl(kvm, KVM_GET_VCPU_MMAP_SIZE, 0));
114
115 kvm_run *run;
116 run = (kvm_run *)mmap(0, vcpu_run_size, PROT_READ | PROT_WRITE, MAP_SHARED,
117 vcpufd, 0);
118 if (run == MAP_FAILED) {
119 cerr << "Allocating VCPU run struct failed: " << strerror(errno) << endl;
120 return 1;
121 }
122
123 //----------------------------------------------------------------------------
124 // Set up the special registers of the VCPU #0
125 //----------------------------------------------------------------------------
126 kvm_sregs sregs;
127 RUN(ret, ioctl(vcpufd, KVM_GET_SREGS, &sregs));
128 sregs.cs.base = 0;
129 sregs.cs.selector = 0;
130 RUN(ret, ioctl(vcpufd, KVM_SET_SREGS, &sregs));
131
132 //----------------------------------------------------------------------------
133 // Set up the standard registers
134 //----------------------------------------------------------------------------
135 kvm_regs regs;
136 memset(®s, 0, sizeof(regs));
137 regs.rip = 0x1000;
138 regs.rax = 2;
139 regs.rbx = 2;
140 regs.rflags = 0x2; // starting the VM will fail with this not set, x86
141 // architecture requirement
142 RUN(ret, ioctl(vcpufd, KVM_SET_REGS, ®s));
143
144 //----------------------------------------------------------------------------
145 // Run the VCPU #0
146 //----------------------------------------------------------------------------
147 while (1) {
148 RUN(ret, ioctl(vcpufd, KVM_RUN, 0));
149 switch (run->exit_reason) {
150
151 //------------------------------------------------------------------------
152 // HLT instruction - we're done
153 //------------------------------------------------------------------------
154 case KVM_EXIT_HLT:
155 cerr << "KVM_EXIT_HLT" << endl;
156 return 0;
157
158 //------------------------------------------------------------------------
159 // Simulate an IO port at 0x3f8
160 //------------------------------------------------------------------------
161 case KVM_EXIT_IO:
162 if (run->io.direction == KVM_EXIT_IO_OUT &&
163 run->io.size == 1 &&
164 run->io.port == 0x3f8 &&
165 run->io.count == 1)
166 cout << *(((char *)run) + run->io.data_offset) << flush;
167 else
168 cerr << "Unhandled KVM_EXIT_IO" << endl;
169 break;
170
171 //------------------------------------------------------------------------
172 // Underlying virtualization mechanism can't start the VM
173 //------------------------------------------------------------------------
174 case KVM_EXIT_FAIL_ENTRY:
175 cerr << "KVM_EXIT_FAIL_ENTRY: hardware_entry_failure_reason = 0x";
176 cerr << hex;
177 cerr << (unsigned long long)run->fail_entry.hardware_entry_failure_reason;
178 cerr << endl;
179 return 1;
180
181 //------------------------------------------------------------------------
182 // Error from the KVM subsystem
183 //------------------------------------------------------------------------
184 case KVM_EXIT_INTERNAL_ERROR:
185 cerr << "KVM_EXIT_INTERNAL_ERROR: suberror = 0x" << hex;
186 cerr << run->internal.suberror << endl;
187 return 1;
188 }
189 }
190
191 //----------------------------------------------------------------------------
192 // Cleanup
193 //----------------------------------------------------------------------------
194 munmap(mem, 0x1000);
195 munmap(run, vcpu_run_size);
196 close(vcpufd);
197 close(vmfd);
198 close(kvm);
199
200 return 0;
201}