Content from 2015-10

I had read the first chapter and thought: "Gosh that's gonna be a politically correct manual for social justice warriors." I am glad I continued because, despite the dismissal of Ain Rand's ideas without any argument, it was not. The book is a pretty captivating story of Harry trying to use the scientific method to dissect the world of magic and having quite a bit of fun in the process of doing so. It's all about thinking, cognitive biases, decision theory, game theory and such. It's a fan-fiction, but I enjoyed it more than the original.

Some quotes:

Father had told Draco that to fathom a strange plot, one technique was to look at what ended up happening, assume it was the intended result, and ask who benefited.

"Undersstood," hissed the snake. "But remember thiss, boy, other eventss proceed whthout you. Hessitation iss alwayss eassy, rarely usseful."

So far as Harry was concerned, the five stages of grief were Rage, Remorse, Resolve, Research, and Resurrection.

"Oh, for Merlin’s sake - yes, he was trying to kill you. Get used to it. Only boring people never have that experience."

Free download here.

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(&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}