VMware tools

From OSDev.wiki
Revision as of 01:04, 29 September 2018 by osdev>Klange
Jump to navigation Jump to search

VMware's various virtualization products implement a backdoor which provides some useful functionality to the guest operating system, similar to the VirtualBox Guest Additions. In addition to VMware, QEMU also implements some of the functionality of the VMware backdoor, including support for absolute mouse positioning which can be very useful if you lack a USB stack and want to be able to productively use QEMU's integrated VNC server.

Sample code in this article is provided from ToaruOS and is based on research from the VMware SVGA developer kit and open-vm-tools.

VMware Backdoor

To communicate with the host, VMware uses a port-and-register based backdoor. Magic values, commands, and arguments are stored in the eax, ebx, ecx, and edx registers, a "magic" port operation is executed, and then the returned values are stored in these registers. There are three ways to access the backdoor, which uses two different magic port values. Most backdoor commands use the single port "in" version, while longer messages are read and written through the "high bandwidth" "rep out/in" version.

First, let's define a structure to store the register values and also provide some helpful aliases through unions. I am using unnamed unions here, but you may wish to name them depending on your compiler configuration:

typedef struct {
	union {
		uint32_t ax;
		uint32_t magic;
	};
	union {
		uint32_t bx;
		size_t size;
	};
	union {
		uint32_t cx;
		uint16_t command;
	};
	union {
		uint32_t dx;
		uint16_t port;
	};
	uint32_t si;
	uint32_t di;
} vmware_cmd;

The union values explain the purpose of some of the registers in the single port "in" version of the backdoor. DX will contain the port number used for the port read, EAX will store a magic value, EBX may contain a size argument, and CX will indicate the command we are requesting from the VM. You will not we also have values for SI and DI, which are used by the "high bandwidth" version of the backdoor.

Now let's set up some functions to perform the various backdoor calls. GCC's inline assembly makes this very easy, as we can just pass the values from our struct into a short assembly call as the registers we want to set. We'll have three different methods available: one for the single-port backdoor and both an "in" and "out" method for the high bandwidth version. We'll also set the magic values and ports appropriately before calling the assembly instructions:

#define VMWARE_MAGIC  0x564D5868
#define VMWARE_PORT   0x5658
#define VMWARE_PORTHB 0x5659

void vmware_send(vmware_cmd * cmd) {
	cmd->magic = VMWARE_MAGIC;
	cmd->port = VMWARE_PORT;
	asm volatile("in %%dx, %0" : "+a"(cmd->ax), "+b"(cmd->bx), "+c"(cmd->cx), "+d"(cmd->dx), "+S"(cmd->si), "+D"(cmd->di));
}

static void vmware_send_hb(vmware_cmd * cmd) {
	cmd->magic = VMWARE_MAGIC;
	cmd->port = VMWARE_PORTHB;
	asm volatile("cld; rep; outsb" : "+a"(cmd->ax), "+b"(cmd->bx), "+c"(cmd->cx), "+d"(cmd->dx), "+S"(cmd->si), "+D"(cmd->di));
}

static void vmware_get_hb(vmware_cmd * cmd) {
	cmd->magic = VMWARE_MAGIC;
	cmd->port = VMWARE_PORTHB;
	asm volatile("cld; rep; insb" : "+a"(cmd->ax), "+b"(cmd->bx), "+c"(cmd->cx), "+d"(cmd->dx), "+S"(cmd->si), "+D"(cmd->di));
}


Absolute Mouse Coordinates

The backdoor allows us to disable the PS/2 mouse and instead receive absolute mouse coordinates. This means we don't need to "capture" the mouse pointer in the VM. As an extra bonus, QEMU implements this functionality as well.

When the absolute mouse is enabled, the PS/2 mouse status bit indicates not that the PS/2 port should be read, but that the backdoor should be used to read mouse data instead, so you should route PS/2 mouse interrupts to your VMware backdoor handler where appropriate.

We'll provide two functions, one to enable the absolute mouse, and one to disable it.

Our enable function will perform 4 backdoor calls: One to enable the mouse functionality, two to request status information, and one to switch to absolute mode. Let's define some magic values for these commands:

#define CMD_ABSPOINTER_DATA    39
#define CMD_ABSPOINTER_STATUS  40
#define CMD_ABSPOINTER_COMMAND 41

#define ABSPOINTER_ENABLE   0x45414552 /* Q E A E */
#define ABSPOINTER_RELATIVE 0xF5
#define ABSPOINTER_ABSOLUTE 0x53424152 /* R A B S */

Now we can enable the absolute mouse like this:

void mouse_absolute(void) {
	vmware_cmd cmd;

	/* Enable */
	cmd.bx = ABSPOINTER_ENABLE;
	cmd.command = CMD_ABSPOINTER_COMMAND;
	vmware_send(&cmd);

	/* Status */
	cmd.bx = 0;
	cmd.command = CMD_ABSPOINTER_STATUS;
	vmware_send(&cmd);

	/* Read data (1) */
	cmd.bx = 1;
	cmd.command = CMD_ABSPOINTER_DATA;
	vmware_send(&cmd);

	/* Enable absolute */
	cmd.bx = ABSPOINTER_ABSOLUTE;
	cmd.command = CMD_ABSPOINTER_COMMAND;
	vmware_send(&cmd);
}

Disabling the mouse is a bit easier as it is a single backdoor command:

void mouse_relative(void) {
	vmware_cmd cmd;
	cmd.bx = ABSPOINTER_RELATIVE;
	cmd.command = CMD_ABSPOINTER_COMMAND;
	vmware_send(&cmd);
}

With these two functions, we can toggle the absolute mouse pointer on and off, which is useful if users want to play games in our OS that require a relative mouse pointer (like Quake), so we'll want to provide a user-accessible method to toggling the pointer mode.

Automatically Setting the Display Resolution