Booting Chickadee on Chickadee: Extending Kernel-based Virtual Machine Capabilities to Chickadee OS
Harrison Zhang
Context

Work done as final project for CS 161 (Operating Systems) at Harvard University.

tl;dr
We build kernel-based virtual machine (KVM) capabilities from scratch on the Chickadee Operating System (Chickadee OS) using Intel VT-x virtualization extensions according to the Intel 64 and IA-32 Architectures Software Developer's Manual (Volume 3C). We develop VM control structures, guest Extended Pagetable (EPT) address translation, exception handling, instruction trapping, virtual CPU (vCPU) abstraction for 64-bit guests, and subsequently boot Chickadee on Chickadee.
Introduction

This project develops KVM capabilities on Chickadee OS, affording hypervisor role, and boots a second Chickadee OS image as a guest VM. We use a Microsoft Azure D4sv3 machine running Ubuntu 22.04 with Intel VT-x virtualization extension technology. Chickadee is subsequently emulated on QEMU within a Docker container. Within the Chickadee emulation, we boot another image of Chickadee as a guest virtual machine (VM).

VM Overview

If Intel VT-x virtualization is supported and enabled, the system proceeds with VM initialization. Otherwise, the calling process is terminated. Upon successful processor control field setup and memory allocation for VM control structures and guest stack, the VM is launched, Chickadee is booted, the initial user process is set up, and virtual CPU (vCPU) structures are initialized and mapped to real CPUs. Finally, the stack pointer is adjusted, and the initial process is scheduled on a vCPU. During a $\texttt{vmexit}$, whether triggered by a privileged instruction or an error, control transitions to the hypervisor. The hypervisor manages the situation by handling the error, emulating, or skipping the instruction, and then invoking $\texttt{vmresume}$ to continue execution. In cases where the $\texttt{vmexit}$ condition is unhandled or the issue is critical (e.g., a triple fault), the system enters an emergency spin state to prevent further errors.

Execution Control Field

On VM initialization, we set up VM control structure (VMCS) region, set the revision ID from model-specific registers (MSRs), and $\texttt{vmptrld}$ the physical VM region. We initialize key control fields in the VMCS, configuring the execution environment including pin-based and processor-based execution controls, support for Extended Page Tables (EPT) if available, and exception handling via exception bitmap to determine which exceptions cause VM exits. We manage page fault handling and set VM entry and exit controls to support 64-bit guest and host modes. Additionally, we initialize control register masks and read shadows to regulate access to CPU control registers in the guest context. These steps ensure efficient and isolated operation and controlled interaction with hardware resources.

Hypervisor Initialization

The next step is to initialize the host-state region of the VMCS, which configures the system state to transition back to the host upon a VM exit event. First, we clear the lower three bits of the segment selectors (ES, CS, SS, DS, FS, GS, and TR) according to Intel specification. Depending on the VM exit controls, we conditionally write MSR values for CPU registers IA32_PAT (Page Attribute Table), IA32_EFER (Extended Feature Enable Register), and IA32_PERF_GLOBAL_CTRL (Performance Monitoring Control) to the host-state area. Next, we write to the VMCS control register (cr0, cr3, cr4) values and FS and GS base addresses, as well as global descriptor table (GDT), interrupt descriptor table (IDT), and task state segment (TSS) base addresses with their respective descriptors. We set up system call-related MSRs IA32_SYSENTER_ESP, IA32_SYSENTER_EIP, and IA32_SYSENTER_CS. To ensure seamless transition to host environment after VM exit event, the host instruction pointer is set to the VMM (virtual machine monitor) or hypervisor entry point.

Guest Initialization

The last step before VM boot configuration is initializing the guest-state area of the VMCS, which includes mirroring key segment selectors, control registers, and MSRs from the host state, configuring segment limits and access rights, and setting up the GDT, IDT, and TSS base addresses. We allocate guest stack, set the guest instruction pointer and stack pointer, and configures system call MSRs. This step finalizes the guest control and execution context. Afterward, we immediately jump to the Chickadee boot sequence.

Chickadee on Chickadee

To boot Chickadee on Chickadee, the kernel's ELF header is verified by reading the first page from disk and checking its magic number for validity. Next, we invoke the custom bootloader (as Chickadee does not follow the El Torito standard) and set up the guest stack pointer to the top of CPU state page for the first vCPU. We launch the VM kernel, which continues vCPU initialization by enabling interrupt requests and further initializing vCPU state. Finally, we create guest page directory and pagetable hierarchy, set the EPT pointer to enable VM-VMM pagetable mapping, and enqueue the first VM process on a vCPU, which is mapped to and executed on a real CPU.

Applications

One of the best applications is in teaching virtualization concepts, OS and kernel development, and VM-VMM interaction. Moreover, as a lightweight prototype, this project can even be applied in research for creating lightweight virtualized environments, enhancing security through isolation, exploring new hypervisor features, and developing efficient virtualization frameworks.

Related Readings
Linux Kernel-based Virtual Machine [Website]
Intel 64 and IA-32 Architectures Software Developer Manual [Website]
Linux Virtual Machine Control Structure Layout [Code]
VM-execution Control Fields [Excerpt]