Section 6.9. Virtual Machine Monitor


6.9. Virtual Machine Monitor

The Mac OS X kernel (PowerPC) implements a virtual machine monitor (VMM) facility, which a user-space program can use to dynamically create and manipulate virtual machine (VM) contexts. Each VM instance has its own processor state and address space, both of which are controlled by the VMM. A program executing in a VM is referred to as a guest. The facility is primarily implemented in the vmachmon.h, vmachmon.c, and vmachmon_asm.s files within the osfmk/ppc/ directory. The files osfmk/ppc/hw_vm.s and osfmk/ppc/hw_exception.s also contain support code for the VMM facility.

Let us revisit Figure 68, in which we saw that a thread's machine-dependent statethe machine_thread structure [osfmk/ppc/thread.h]contains pointers to VMM control table (vmmCntrlTable) and VMM control table entry (vmmCntrlEntry) structures. vmmCntrlTable is non-NULL for a thread that is using the VMM facility. When the thread is running a VM, its vmmCntrlEntry points to the current emulation context but is NULL otherwise. Figure 650 shows these data structures.

Figure 650. Control data structures for the VMM facility

// osfmk/ppc/vmachmon.h #define kVmmMaxContexts 32 ... typedef struct vmmCntrlEntry {     unsigned int vmmFlags;             // Assorted control flags     unsigned int vmmXAFlgs;            // Extended Architecture (XA) flags     // Address of context communication area     vmm_state_page_t *vmmContextKern;  // Kernel virtual address     ppnum_t           vmmContextPhys;  // Physical address     vmm_state_page_t *vmmContextUser;  // User virtual address     facility_context  vmmFacCtx;       // Header for VMX and FP contexts     pmap_t            vmmPmap;         // Last dispatched pmap     uint64_t          vmmTimer;        // Last set timer value (0 if unset)     unsigned int      vmmFAMintercept; // FAM intercepted exceptions } vmmCntrlEntry; typedef struct vmmCntrlTable {     unsigned int  vmmGFlags;           // Global flags     addr64_t      vmmLastMap;          // Last vaddr mapping mode     // An entry for each possible VMM context     vmmCntrlEntry vmmc[kVmmMaxContexts];     pmap_t        vmmAdsp[kVmmMaxContexts]; // Guest address space maps } vmmCntrlTable; ...

For each VM, the VMM allocates a page of memory for holding the VM's context communications area, which is accessed both as a vmm_comm_page_t structure and a vmm_state_page_t structurethe former embeds the latter.

// osfmk/ppc/vmachmon.h typedef struct vmm_comm_page_t {     union {         vmm_state_page_t vmcpState;    // Reserve area for state         unsigned int     vmcpPad[768]; // Reserve state for 3/4 page state area     } vmcpfirst;     unsigned int vmcpComm[256]; // Last 1024 bytes used as a communications                                 // area in a function-specific manner } vmm_comm_page_t; ...


A VM's processor state is stored in a vmm_processor_state_t structure within a vmm_state_page_t structure. A vmm_processor_state_t includes the processor's general-purpose, floating-point, vector, and special-purpose registers. Note that the vmcpComm field of the vmm_comm_page_t structure is used as a general-purpose communications buffer by several VMM functions. For example, the vmm_map_list function, which maps a list of pages into a guest address space, reads the list of pages as a sequence of { host virtual address, guest virtual address } pairs from vmcpComm.

6.9.1. Features

The specific features provided by the VMM facility on a system depend on the host processor and the facility's version. The following are examples of VMM features.

  • kVmmFeature_LittleEndian The VMM supports a VM to be run in little-endian mode. This feature is available only on PowerPC processors that implement the optional little-endian facility. Therefore, it is unavailable on G5-based systems.

  • kVmmFeature_Stop The VMM supports stopping and resuming VMs.

  • kVmmFeature_ExtendedMapping The VMM supports extended protection modes for address space mappings.

  • kVmmFeature_ListMapping The VMM supports mapping a list of pages into and unmapping a list of pages from guest address spaces.

  • kVmmFeature_FastAssist The VMM supports an optimization called fast assist mode (FAM). In this mode, the ultra-fast-path VMM system calls are valid. As noted in Table 615, the calls are handled by vmm_ufp() [osfmk/ppc/vmachmon_asm.s], which ensures that the incoming system call number is within the range of calls designated as FAM calls (kVmmResumeGuest tHRough kVmmSetGuestRegister).

  • kVmmFeature_XA The VMM supports getting and setting extended architecture (XA) flags in the control table entry of each VM context controlled by it.

  • kVmmFeature_SixtyFourBit The VMM provides 64-bit support on 64-bit host processors.

  • kVmmFeature_MultAddrSpace The VMM allows multiple address spaces, with each address space capable of handling the maximum virtual address supported by the host processor. A guest VM may be launched using any one address space.

6.9.2. Using the VMM Facility

The VMM facility exports its functionality through routines such as those for initializing a VM context (vmm_init_context), releasing a VM context (vmm_tear_down_context), and mapping a page from the host address space to a guest address space (vmm_map_page). Most of these routines do not have corresponding stubs in a user-space library; they are accessed using a dispatcher routine (vmm_dispatch), which does have a user-space stub. When the C library is compiled, it picks up assembly stubs for Mach trapsincluding VMM-related trapsfrom header files such as <mach/syscall_sw.h> and <mach/ppc/syscall_sw.h>.

$ nm -oj /usr/lib/libSystem.dylib | grep -i vmm /usr/lib/libSystem.dylib:mach_traps.So:_vmm_dispatch /usr/lib/libSystem.dylib:mach_traps.So:_vmm_get_features /usr/lib/libSystem.dylib:mach_traps.So:_vmm_get_version /usr/lib/libSystem.dylib:mach_traps.So:_vmm_init_context /usr/lib/libSystem.dylib:mach_traps.So:_vmm_stop_vm


The vmm_dispatch() system call allows all exported VMM routines to be invoked from user space. The index of the desired routine is passed as the first argument to vmm_dispatch(), followed by arguments specific to that function. Here is an example of what using the VMM facility looks like.

// Get VMM version version = vmm_dispatch(kVmmGetVersion); ... // Get VMM features to know what we may or may not use features = vmm_dispatch(kVmmGetFeatures); // Allocate page-aligned memory for use with the VMM/VMs kr = vm_allocate(myTask, &vmmCommPage, pageSize, TRUE); ... kr = vm_allocate(...); ... // Initialize a new VM context kr = vmm_dispatch(kVmmInitContext, version, vmmCommPage); // Set up the VM's initial processor state ... // Map pages, or page lists, into the VM's address space // Actual pages to map are in a separate "communication" page kr = vmm_dispatch(kVmmMapList, vmmIndex, nPages, is64bit); // Launch the VM // Mapping a page and setting the VM running can be combined kr = vmm_dispatch(kVmmMapExecute, vmmIndex, aPage, vAddress, protectionBits); // Handle things when control comes back to the VMM ... // Stop and resume the VM ... // Tear down the VM context kr = vmm_dispatch(kVmmTearDownContext, vmmIndex);


6.9.3. Example: Running Code in a Virtual Machine

Let us now look at a real programming example of using the VMM facility. In our program, vmachmon32, we will perform the following sequence of steps.

  • Retrieve the VMM version and feature set supported by the kernel using vmm_get_version() and vmm_get_features(), respectively.

  • Allocate page-aligned memory using Mach's vm_allocate() memory allocation function. Specifically, we will allocate a page for the VM's state, a page for the VM's stack, and a page for the VM's text (code).

  • Set the VM's program counter to the beginning of the text page.

  • Set the VM's stack pointer to the end of the stack page, while taking the Red Zone into account.

  • Populate the text page either with a handcrafted sequence of instructions or with machine code for a function that calculates the factorial of its argument. We will obtain the function's machine code by statically compiling its C source.

  • Map the stack page and the text page into the VM's address space.

  • Set the VM running. When the VM runs out of code to execute, we will ensure that it returns to the VMM by making the final instruction that it executes illegal.

Moreover, we will print the contents of several VM registers at the end of the program, which will allow us to see the result of the code that the VM ran. You can run full-fledged programs (including operating systems)rather than primitive codewithin a VM provided the resources needed for the program to run are made available in the VM's address space.

Figure 651 shows the source for vmachmon32. It is liberally annotated with comments, including further description of using the VMM facility.

Figure 651. A program to run machine code within a VM using the VMM facility

// vmachmon32.c // Mac OS X Virtual Machine Monitor (Vmm) facility demonstration #define PROGNAME "vmachmon32" #include <stdio.h> #include <string.h> #include <stdlib.h> #include <sys/types.h> #include <mach/mach.h> #include <architecture/ppc/cframe.h> #ifndef _VMACHMON32_KLUDGE_ // We need to include xnu/osfmk/ppc/vmachmon.h, which includes several other // kernel headers and is not really meant for inclusion in user programs. // We perform the following kludges to include vmachmon.h to be able to // compile this program: // // 1. Provide dummy forward declarations for data types that vmachmon.h //    needs, but we will not actually use. // 2. Copy vmachmon.h to the current directory from the kernel source tree. // 3. Remove or comment out "#include <ppc/exception.h>" from vmachmon.h. // struct  savearea;             // kludge #1 typedef int ReturnHandler;    // kludge #1 typedef int pmap_t;           // kludge #1 typedef int facility_context; // kludge #1 #include "vmachmon.h"         // kludge #2 #endif #define OUT_ON_MACH_ERROR(msg, retval) \     if (kr != KERN_SUCCESS) { mach_error("*** " msg ":" , kr); goto out; } // vmm_dispatch() is a PowerPC-only system call that allows us to invoke // functions residing in the Vmm dispatch table. In general, Vmm routines // are available to user space, but the C library (or another library) does // not contain stubs to call them. Thus, we must go through vmm_dispatch(), // using the index of the function to call as the first parameter in GPR3. // // Since vmachmon.h contains the kernel prototype of vmm_dispatch(), which // is not what we want, we will declare our own function pointer and set // it to the stub available in the C library. // typedef kern_return_t (* vmm_dispatch_func_t)(int, ...); vmm_dispatch_func_t my_vmm_dispatch; // Convenience data structure for pretty-printing Vmm features struct VmmFeature {     int32_t  mask;     char    *name; } VmmFeatures[] = {     { kVmmFeature_LittleEndian,        "LittleEndian"        },     { kVmmFeature_Stop,                "Stop"                },     { kVmmFeature_ExtendedMapping,     "ExtendedMapping"     },     { kVmmFeature_ListMapping,         "ListMapping"         },     { kVmmFeature_FastAssist,          "FastAssist"          },     { kVmmFeature_XA,                  "XA"                  },     { kVmmFeature_SixtyFourBit,        "SixtyFourBit"        },     { kVmmFeature_MultAddrSpace,       "MultAddrSpace"       },     { kVmmFeature_GuestShadowAssist,   "GuestShadowAssist"   },     { kVmmFeature_GlobalMappingAssist, "GlobalMappingAssist" },     { kVmmFeature_HostShadowAssist,    "HostShadowAssist"    },     { kVmmFeature_MultAddrSpaceAssist, "MultAddrSpaceAssist" },     { -1, NULL }, }; // For Vmm messages that we print #define Printf(fmt, ...) printf("Vmm> " fmt, ## __VA_ARGS__) // PowerPC instruction template: add immediate, D-form typedef struct I_addi_d_form {     u_int32_t OP: 6;  // major opcode     u_int32_t RT: 5;  // target register     u_int32_t RA: 5;  // register operand     u_int32_t SI: 16; // immediate operand } I_addi_d_form; // PowerPC instruction template: unconditional branch, I-form typedef struct branch_i_form {     u_int32_t OP: 6;  // major opcode     u_int32_t LI: 24; // branch target (immediate)     u_int32_t AA: 1;  // absolute or relative     u_int32_t LK: 1;  // link or not } I_branch_i_form; // PowerPC instruction template: add, XO-form typedef struct I_add_xo_form {     u_int32_t OP: 6;  // major opcode     u_int32_t RT: 5;  // target register     u_int32_t RA: 5;  // register operand A     u_int32_t RB: 5;  // register operand B     u_int32_t OE: 1;  // alter SO, OV?     u_int32_t XO: 9;  // extended opcode     u_int32_t Rc: 1;  // alter CR0? } I_add_xo_form; // Print the bits of a 32-bit number void prbits32(u_int32_t u) {     u_int32_t i = 32;     for (; i > 16 && i--; putchar(u & 1 << i ? '1' : '0'))         ;     printf(" ");     for (; i--; putchar(u & 1 << i ? '1' : '0'))         ;     printf("\n"); } // Function to initialize a memory buffer with some machine code void initGuestText_Dummy(u_int32_t    *text,                     vm_address_t  guestTextAddress,                     vmm_regs32_t *ppcRegs32) {     // We will execute a stream of a few instructions in the virtual machine     // through the Vmm (that is, us). I0 and I1 will load integer values into     // registers GPR10 and GPR11. I3 will be an illegal instruction. I2 will     // jump over I3 by unconditionally branching to I4, which will sum GPR10     // and GPR11, placing their sum in GPR12.     //     // We will allow I5 to either be illegal, in which case control will     // return to the Vmm, or, be a branch to itself: an infinite     // loop. One Infinite Loop.     //     I_addi_d_form   *I0;     I_addi_d_form   *I1;     I_branch_i_form *I2;     // I3 is illegal     I_add_xo_form   *I4;     I_branch_i_form *I5;     // Guest will run the following instructions     I0 = (I_addi_d_form   *)(text + 0);     I1 = (I_addi_d_form   *)(text + 1);     I2 = (I_branch_i_form *)(text + 2);     text[3] = 0xdeadbeef; // illegal     I4 = (I_add_xo_form   *)(text + 4);     // Possibly overridden by an illegal instruction below     I5 = (I_branch_i_form *)(text + 5);     // Use an illegal instruction to be the last inserted instruction (I5)     // in the guest's instruction stream     text[5] = 0xfeedface;     // Fill the instruction templates     // addi r10,0,4     ; I0     I0->OP = 14;     I0->RT = 10;     I0->RA = 0;     I0->SI = 4; // load the value '4' in r10     // addi r11,0,5     ; I1     I1->OP = 14;     I1->RT = 11;     I1->RA = 0;     I1->SI = 5; // load the value '5' in r11     // ba               ; I2     // We want to branch to the absolute address of the 5th instruction,     // where the first instruction is at guestTextAddress. Note the shifting.     //     I2->OP = 18;     I2->LI = (guestTextAddress + (4 * 4)) >> 2;     I2->AA = 1;     I2->LK = 0;     // I3 is illegal; already populated in the stream     // add  r12,r10,r11 ; I4     I4->OP = 31;     I4->RT = 12;     I4->RA = 10;     I4->RB = 11;     I4->OE = 0;     I4->XO = 266;     I4->Rc = 0;     // I5 is illegal or an infinite loop; already populated in the stream     Printf("Fabricated instructions for executing "            "in the guest virtual machine\n"); } // Function to initialize a memory buffer with some machine code void initGuestText_Factorial(u_int32_t    *text,                         vm_address_t  guestTextAddress,                         vmm_regs32_t *ppcRegs32) {     // Machine code for the following function:     //     // int     // factorial(int n)     // {     //     if (n <= 0)     //         return 1;     //     else     //         return n * factorial(n - 1);     // }     //     // You can obtain this from the function's C source using a command-line     // sequence like the following:     //     // $ gcc -static -c factorial.c     // $ otool -tX factorial.o     // ...     //     u_int32_t factorial_ppc32[] = {         0x7c0802a6, 0xbfc1fff8, 0x90010008, 0x9421ffa0,         0x7c3e0b78, 0x907e0078, 0x801e0078, 0x2f800000,         0x419d0010, 0x38000001, 0x901e0040, 0x48000024,         0x805e0078, 0x3802ffff, 0x7c030378, 0x4bffffc5,         0x7c621b78, 0x801e0078, 0x7c0201d6, 0x901e0040,         0x807e0040, 0x80210000, 0x80010008, 0x7c0803a6,         0xbbc1fff8, 0x4e800020,     };     memcpy(text, factorial_ppc32, sizeof(factorial_ppc32)/sizeof(u_int8_t));     // This demo takes an argument in GPR3: the number whose factorial is to     // be computed. The result is returned in GPR3.     //     ppcRegs32->ppcGPRs[3] = 10; // factorial(10)     // Set the LR to the end of the text in the guest's virtual address space.     // Our demo will only use the LR for returning to the Vmm by placing an     // illegal instruction's address in it.     //     ppcRegs32->ppcLR = guestTextAddress + vm_page_size - 4;     Printf("Injected factorial instructions for executing "            "in the guest virtual machine\n"); } // Some modularity... these are the demos our program supports typedef void (* initGuestText_Func)(u_int32_t *, vm_address_t, vmm_regs32_t *); typedef struct {     const char         *name;     initGuestText_Func  textfiller; } Demo; Demo SupportedDemos[] = {     {         "executes a few hand-crafted instructions in a VM",         initGuestText_Dummy,     },     {         "executes a recursive factorial function in a VM",         initGuestText_Factorial,     }, }; #define MAX_DEMO_ID (sizeof(SupportedDemos)/sizeof(Demo)) static int demo_id = -1; void usage(int argc, char **argv) {     int i;     if (argc != 2)         goto OUT;     demo_id = atoi(argv[1]);     if ((demo_id >= 0) && (demo_id < MAX_DEMO_ID))         return; OUT:     fprintf(stderr, "usage: %s <demo ID>\nSupported demos:\n"             "  ID\tDescription\n", PROGNAME);     for (i = 0; i < MAX_DEMO_ID; i++)         fprintf(stderr, "  %d\t%s\n", i, SupportedDemos[i].name);     exit(1); } int main(int argc, char **argv) {     int i, j;     kern_return_t       kr;     mach_port_t         myTask;     unsigned long      *return_params32;     vmm_features_t      features;     vmm_regs32_t       *ppcRegs32;     vmm_version_t       version;     vmm_thread_index_t  vmmIndex;             // The VM's index     vm_address_t        vmmUStatePage = 0;    // Page for VM's user state     vmm_state_page_t   *vmmUState;            // It's a vmm_comm_page_t too     vm_address_t        guestTextPage = 0;    // Page for guest's text     vm_address_t        guestStackPage = 0;   // Page for guest's stack     vm_address_t        guestTextAddress = 0;     vm_address_t        guestStackAddress = 0;     my_vmm_dispatch = (vmm_dispatch_func_t)vmm_dispatch;     // Ensure that the user chose a demo     usage(argc, argv);     // Get Vmm version implemented by this kernel     version = my_vmm_dispatch(kVmmGetVersion);     Printf("Mac OS X virtual machine monitor (version %lu.%lu)\n",            (version >> 16), (version & 0xFFFF));     // Get features supported by this Vmm implementation     features = my_vmm_dispatch(kVmmvGetFeatures);     Printf("Vmm features:\n");     for (i = 0; VmmFeatures[i].mask != -1; i++)         printf("  %-20s = %s\n", VmmFeatures[i].name,                (features & VmmFeatures[i].mask) ?  "Yes" : "No");     Printf("Page size is %u bytes\n", vm_page_size);     myTask = mach_task_self(); // to save some characters (sure)     // Allocate chunks of page-sized page-aligned memory     // VM user state     kr = vm_allocate(myTask, &vmmUStatePage, vm_page_size, VM_FLAGS_ANYWHERE);     OUT_ON_MACH_ERROR("vm_allocate", kr);     Printf("Allocated page-aligned memory for virtual machine user state\n");     vmmUState = (vmm_state_page_t *)vmmUStatePage;     // Guest's text     kr = vm_allocate(myTask, &guestTextPage, vm_page_size, VM_FLAGS_ANYWHERE);     OUT_ON_MACH_ERROR("vm_allocate", kr);     Printf("Allocated page-aligned memory for guest's " "text\n");     // Guest's stack     kr = vm_allocate(myTask, &guestStackPage, vm_page_size, VM_FLAGS_ANYWHERE);     OUT_ON_MACH_ERROR("vm_allocate", kr);     Printf("Allocated page-aligned memory for guest's stack\n");     // We will lay out the text and stack pages adjacent to one another in     // the guest's virtual address space.     //     // Virtual addresses increase -->     // 0              4K             8K             12K     // +--------------------------------------------+     // | __PAGEZERO   |  GUEST_TEXT  | GUEST_STACK  |     // +--------------------------------------------+     //     // We put the text page at virtual offset vm_page_size and the stack     // page at virtual offset (2 * vm_page_size).     //     guestTextAddress = vm_page_size;     guestStackAddress = 2 * vm_page_size;     // Initialize a new virtual machine context     kr = my_vmm_dispatch(kVmmInitContext, version, vmmUState);     OUT_ON_MACH_ERROR("vmm_init_context", kr);     // Fetch the index returned by vmm_init_context()     vmmIndex = vmmUState->thread_index;     Printf("New virtual machine context initialized, index = %lu\n", vmmIndex);     // Set a convenience pointer to the VM's registers     ppcRegs32 = &(vmmUState->vmm_proc_state.ppcRegs.ppcRegs32);     // Set the program counter to the beginning of the text in the guest's     // virtual address space     ppcRegs32->ppcPC = guestTextAddress;     Printf("Guest virtual machine PC set to %p\n", (void *)guestTextAddress);     // Set the stack pointer (GPR1), taking the Red Zone into account     #define PAGE2SP(x) ((void *)((x) + vm_page_size - C_RED_ZONE))     ppcRegs32->ppcGPRs[1] = (u_int32_t)PAGE2SP(guestStackAddress); // 32-bit     Printf("Guest virtual machine SP set to %p\n", PAGE2SP(guestStackAddress));     // Map the stack page into the guest's address space     kr = my_vmm_dispatch(kVmmMapPage, vmmIndex, guestStackPage,                       guestStackAddress, VM_PROT_ALL);     Printf("Mapping guest stack page\n");     // Call the chosen demo's instruction populator     (SupportedDemos[demo_id].textfiller)((u_int32_t *)guestTextPage,                                          guestTextAddress, ppcRegs32);     // Finally, map the text page into the guest's address space, and set the     // VM running     //     Printf("Mapping guest text page and switching to guest virtual machine\n");     kr = my_vmm_dispatch(kVmmMapExecute, vmmIndex, guestTextPage,                       guestTextAddress, VM_PROT_ALL);     // Our demo ensures that the last instruction in the guest's text is     // either an infinite loop or illegal. The monitor will "hang" in the case     // of an infinite loop. It will have to be interupted (^C) to gain control.     // In the case of an illegal instruction, the monitor will gain control at     // this point, and the following code will be executed. Depending on the     // exact illegal instruction, Mach's error messages may be different.     //     if (kr != KERN_SUCCESS)         mach_error("*** vmm_map_execute32:", kr);     Printf("Returned to vmm\n");     Printf("Processor state:\n");     printf("  Distance from origin = %lu instructions\n",            (ppcRegs32->ppcPC - vm_page_size) >> 2);     printf("  PC                   = %p (%lu)\n",            (void *)ppcRegs32->ppcPC, ppcRegs32->ppcPC);     printf("  Instruction at PC    = %#08x\n",         ((u_int32_t *)(guestTextPage))[(ppcRegs32->ppcPC - vm_page_size) >> 2]);     printf("  CR                   = %#08lx\n"            "                         ", ppcRegs32->ppcCR);     prbits32(ppcRegs32->ppcCR);     printf("  LR                   = %#08lx (%lu)\n",            ppcRegs32->ppcLR, ppcRegs32->ppcLR);     printf("  MSR                  = %#08lx\n"            "                         ", ppcRegs32->ppcMSR);     prbits32(ppcRegs32->ppcMSR);     printf("  return_code          = %#08lx (%lu)\n",            vmmUState->return_code, vmmUState->return_code);     return_params32 = vmmUState->vmmRet.vmmrp32.return_params;     for (i = 0; i < 4; i++)         printf("  return_params32[%d]   = 0x%08lx (%lu)\n", i,                return_params32[i], return_params32[i]);     printf("  GPRs:\n");     for (j = 0; j < 16; j++) {         printf("  ");         for (i = 0; i < 2; i++) {             printf("r%-2d = %#08lx ", j * 2 + i,                    ppcRegs32->ppcGPRs[j * 2 + i]);         }         printf("\n");     }     // Tear down the virtual machine ... that's all for now     kr = my_vmm_dispatch(kVmmTearDownContext, vmmIndex);     OUT_ON_MACH_ERROR("vmm_init_context", kr);     Printf("Virtual machine context torn down\n"); out:     if (vmmUStatePage)         (void)vm_deallocate(myTask, vmmUStatePage, vm_page_size);     if (guestTextPage)         (void)vm_deallocate(myTask, guestTextPage, vm_page_size);     if (guestStackPage)         (void)vm_deallocate(myTask, guestStackPage, vm_page_size);     exit(kr); }

Virtual PC

The Virtual PC software for Mac OS X is implemented using the VMM facility. In the xnu kernel source corresponding to Mac OS X 10.0, the VMM source is copyrighted to Connectix Corporation, the developer of Virtual PC. Connectix was later acquired by Microsoft.


The code in Figure 651 provides two demonstrations, demo 0 and demo 1. Demo 0 calls initGuestText_Dummy() to populate the VM's text page with a contrived sequence of instructions terminated by an illegal instruction. The first few words of the page are as follows, assuming the page starts at virtual address addr:

addr+00 addi    r10,0,4      ; load the value '4' in r10 addr+04 addi    r11,0,5      ; load the value '5' in r11 addr+08                      ; branch to addr+16 addr+12 0xdeadbeef           ; no such instruction addr+16 add     r12,r11,r11  ; place (r10 + r11) in r12 addr+20 0xfeedface           ; no such instruction


The VM bypasses the first illegal instruction in the sequence by branching over it. When execution reaches the second illegal instruction, control returns back to our host program. Alternatively, you can make the last instruction be an infinite loop, in which case the VM will run until interrupted.

When vmachmon32 finishes running demo 0, the VM's GPR10, GPR11, and GPR12 should contain the values 4, 5, and 9, respectively. Moreover, the program counter should contain addr+20, where addr is the starting address of the guest's text page. Figure 652 shows the result of running demo 0.

Figure 652. Result of running a sequence of machine instructions in a VM using the vmachmon32 program

$ gcc -Wall -o vmachmon32 vmachmon32.c $ ./vmachmon32 usage: vmachmon32 <demo ID> Supported demos:   ID    Description   0     executes a few hand-crafted instructions in a VM   1     executes a recursive factorial function in a VM $ ./vmachmon32 0 Vmm> Mac OS X virtual machine monitor (version 1.7) Vmm> Vmm features:   LittleEndian         = Yes   Stop                 = Yes   ExtendedMapping      = Yes   ListMapping          = Yes   FastAssist           = Yes   XA                   = Yes   SixtyFourBit         = No   MultAddrSpace        = No   GuestShadowAssist    = Yes   GlobalMappingAssist  = No   HostShadowAssist     = No   MultAddrSpaceAssist  = No Vmm> Page size is 4096 bytes Vmm> Allocated page-aligned memory for virtual machine user state Vmm> Allocated page-aligned memory for guest's text Vmm> Allocated page-aligned memory for guest's stack Vmm> New virtual machine context initialized, index = 1 Vmm> Guest virtual machine PC set to 0x00001000 Vmm> Guest virtual machine SP set to 0x00002f20 Vmm> Mapping guest stack page Vmm> Fabricated instructions for executing in the guest virtual machine Vmm> Mapping guest text page and switching to guest virtual machine *** vmm_map_execute32 (os/kern) not receiver Vmm> Returned to vmm Vmm> Processor state:   Distance from origin = 5 instructions   PC                   = 0x00001014 (4116)   Instruction at PC    = 0x00000060   CR                   = 0x00000000                          0000000000000000 0000000000000000   LR                   = 0x00000000 (0)   MSR                  = 0x0008d030                          0000000000001000 1101000000110000   return_code          = 0x00000007 (7)   return_params32[0]   = 0x00001000 (4096)   return_params32[1]   = 0x40000000 (1073741824)   return_params32[2]   = 0x00000000 (0)   return_params32[3]   = 0x00000000 (0)   GPRs:   r0  = 0x00000000 r1  = 0x00002f20   r2  = 0x00000000 r3  = 0x00000000   r4  = 0x00000000 r5  = 0x00000000   r6  = 0x00000000 r7  = 0x00000000   r8  = 0x00000000 r9  = 0x00000000   r10 = 0x00000004 r11 = 0x00000005   r12 = 0x00000009 r13 = 0x00000000   r14 = 0x00000000 r15 = 0x00000000   r16 = 0x00000000 r17 = 0x00000000   r18 = 0x00000000 r19 = 0x00000000   r20 = 0x00000000 r21 = 0x00000000   r22 = 0x00000000 r23 = 0x00000000   r24 = 0x00000000 r25 = 0x00000000   r26 = 0x00000000 r27 = 0x00000000   r28 = 0x00000000 r29 = 0x00000000   r30 = 0x00000000 r31 = 0x00000000 Vmm> Virtual machine context torn down

As noted in the program's comments in Figure 651, compiling vmachmon32.c requires that you copy osfmk/ppc/vmachmon.h from the kernel source tree to the current directory (with respect to vmachmon32.c). Additionally, the source line that includes <ppc/exception.h> must be commented out in vmachmon.h.


Demo 1 populates the guest's text page by calling initGuestText_Factorial(), which copies machine instructions for a recursive factorial function into the page and populates the LR so that the function returns to an address containing an illegal instruction. The function takes a single argument in GPR3: the number whose factorial to compute. It returns the computed factorial in GPR3. Again, we will verify the program's working by examining the register dump at the end of the program's execution. Figure 653 shows the result of running demo 1. The vmachmon32 code shown in Figure 651 passes 10 as an argument to the factorial function. Correspondingly, GPR3 should contain 0x00375f00 as the result.

Figure 653. Result of running a recursive factorial function within a VM using the vmachmon32 program

$ ./vmachmon32 1 Vmm> Mac OS X virtual machine monitor (version 1.7) ... Vmm> Returned to vmm Vmm> Processor state:   Distance from origin = 1023 instructions   PC                   = 0x00001ffc (8188)   Instruction at PC    = 0x00000000   CR                   = 0x00000002                          0000000000000000 0000000000000010   LR                   = 0x00001ffc (8188)   MSR                  = 0x0008d030                          0000000000001000 1101000000110000   return_code          = 0x00000007 (7)   return_params32[0]   = 0x00001000 (4096)   return_params32[1]   = 0x40000000 (1073741824)   return_params32[2]   = 0x00000000 (0)   return_params32[3]   = 0x00000000 (0)   GPRs:   r0  = 0x00001ffc r1  = 0x00002f20   r2  = 0x00058980 r3  = 0x00375f00   r4  = 0x00000000 r5  = 0x00000000 ... Vmm> Virtual machine context torn down




Mac OS X Internals. A Systems Approach
Mac OS X Internals: A Systems Approach
ISBN: 0321278542
EAN: 2147483647
Year: 2006
Pages: 161
Authors: Amit Singh

flylib.com © 2008-2017.
If you may any questions please contact us: flylib@qtcs.net