Why oh why is grub eating CPU resources in a VirtualBox VM?

While reviewing the performance on my desktop today, I noticed that one of my VirtualBox virtual machines was consuming 100% of one CPU:

Swap:  2097144k total,    59504k used,  2037640k free,  4004568k cached

  PID USER      PR  NI  VIRT  RES  SHR S %CPU %MEM    TIME+  COMMAND            
 1895 matty     20   0 2037m 1.0g  57m S 96.2 13.0 831:13.06 VirtualBox    

This was somewhat perplexing, given that the host in question was sitting at a grub> prompt (I’m cleaning up my grub notes to post to my website, so stay tuned!). When I strace’ed the VirtualBox process (having a system encapsulated in a userland process makes debugging these types of issues super easy), I noticed that the process was issuing poll()s and read()s in a tight loop:

[pid  1895] poll([{fd=15, events=POLLIN}, {fd=22, events=POLLIN|POLLPRI}, {fd=24, events=POLLIN|POLLPRI}, {fd=25, events=POLLIN|POLLPRI}, {fd=26, events=POLLIN|POLLPRI}, {fd=27, events=POLLIN}, {fd=28, events=POLLIN}, {fd=14, events=POLLIN}, {fd=29, events=POLLIN}, {fd=34, events=POLLIN}], 10, 0) = 0 (Timeout)
[pid  1895] read(14, 0x1492424, 4096)   = -1 EAGAIN (Resource temporarily unavailable)
[pid  1895] read(14, 0x1492424, 4096)   = -1 EAGAIN (Resource temporarily unavailable)
[pid  1895] read(27, 0x14fda94, 4096)   = -1 EAGAIN (Resource temporarily unavailable)

At first I was perplexed by this, but upon further reflection this makes complete sense. The grub interpeter is executing a loop that polls the keyboard IO port for data, and continues to do so over and over again. Since most systems don’t stay at the grub prompt for extended periods of time, the grub developers didn’t use the HLT instruction to idle the CPUs when no actual work was being performed. For some reason this made me extremely curious about microprocessor implementations, so I started reading through the AMD64 Architecture Programmer’s Manual volume 1. So far this is a fantastic read, and I wish I would have read through it years ago!