Content tagged lwn

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) \
 21 var = command;       \
 22 if (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 //------------------------------------------------------------------------------
 30 const 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 
 40 int 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(&region, 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, &region));
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(&regs, 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, &regs));
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 }

I like reading LWN like a newspaper on my Kindle but find it painful to click through the calibre GUI every time a new issue appears. I also find it useful to be able to download and make ebooks out of the historical issues. Unfortunately there was no really good way to do this, so I have made a couple of hacks to facilitate these two things. Have a look here, if you're interested.

Just put your credentials in the username and password files, or delete these files altogether, if you only want to download the free version. Put 0 in the issue file for the most recent issue (paid version) or 1 or above for any of the later issues (free version). Then add to your cron:

30 7 * * 4 /home/your_account/calibre-hacks/recipes/download-and-send.sh calibre-hacks/recipes/lwn your_account@free.kindle.com

to have a LWN issue delivered to you every Thursday morning.