vmware userland helper
1/*
2 * Copyright (c) 2007 David Crawshaw <david@zentus.com>
3 * Copyright (c) 2008 David Gwynne <dlg@openbsd.org>
4 * Copyright (c) 2010 joshua stein <jcs@jcs.org>
5 *
6 * Permission to use, copy, modify, and distribute this software for any
7 * purpose with or without fee is hereby granted, provided that the above
8 * copyright notice and this permission notice appear in all copies.
9 *
10 * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
11 * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
12 * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
13 * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
14 * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
15 * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
16 * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
17 */
18
19#include <stdint.h>
20#include <stdlib.h>
21#include <stdio.h>
22#include <string.h>
23#include <err.h>
24#include <vis.h>
25
26#include "vmwh.h"
27
28int16_t host_mouse_x;
29int16_t host_mouse_y;
30int mouse_grabbed = 0;
31
32/* "The" magic number, always occupies the EAX register. */
33#define VM_MAGIC 0x564D5868
34
35/* Port numbers, passed on EDX.LOW . */
36#define VM_PORT_CMD 0x5658
37#define VM_PORT_RPC 0x5659
38
39/* Commands, passed on ECX.LOW. */
40#define VM_CMD_GET_MOUSEPOS 0x04
41#define VM_MOUSE_UNGRABBED_POS -100
42#define VM_CMD_SET_MOUSEPOS 0x05
43#define VM_CMD_GET_CLIPBOARD_LEN 0x06
44#define VM_CMD_GET_CLIPBOARD 0x07
45#define VM_CMD_SET_CLIPBOARD_LEN 0x08
46#define VM_CMD_SET_CLIPBOARD 0x09
47#define VM_CMD_GET_VERSION 0x0a
48#define VM_VERSION_UNMANAGED 0x7fffffff
49
50/* A register. */
51union vm_reg {
52 struct {
53 uint16_t low;
54 uint16_t high;
55 } part;
56 uint32_t word;
57#ifdef __amd64__
58 struct {
59 uint32_t low;
60 uint32_t high;
61 } words;
62 uint64_t quad;
63#endif
64} __attribute__((__packed__));
65
66/* A register frame. */
67struct vm_backdoor {
68 union vm_reg eax;
69 union vm_reg ebx;
70 union vm_reg ecx;
71 union vm_reg edx;
72 union vm_reg esi;
73 union vm_reg edi;
74 union vm_reg ebp;
75} __attribute__((__packed__));
76
77void vm_cmd(struct vm_backdoor *);
78void vm_ins(struct vm_backdoor *);
79void vm_outs(struct vm_backdoor *);
80
81#define BACKDOOR_OP_I386(op, frame) \
82 __asm__ __volatile__ ( \
83 "pushal;" \
84 "pushl %%eax;" \
85 "movl 0x18(%%eax), %%ebp;" \
86 "movl 0x14(%%eax), %%edi;" \
87 "movl 0x10(%%eax), %%esi;" \
88 "movl 0x0c(%%eax), %%edx;" \
89 "movl 0x08(%%eax), %%ecx;" \
90 "movl 0x04(%%eax), %%ebx;" \
91 "movl 0x00(%%eax), %%eax;" \
92 op \
93 "xchgl %%eax, 0x00(%%esp);" \
94 "movl %%ebp, 0x18(%%eax);" \
95 "movl %%edi, 0x14(%%eax);" \
96 "movl %%esi, 0x10(%%eax);" \
97 "movl %%edx, 0x0c(%%eax);" \
98 "movl %%ecx, 0x08(%%eax);" \
99 "movl %%ebx, 0x04(%%eax);" \
100 "popl 0x00(%%eax);" \
101 "popal;" \
102 ::"a"(frame) \
103 )
104
105#define BACKDOOR_OP_AMD64(op, frame) \
106 __asm__ __volatile__ ( \
107 "pushq %%rbp; \n\t" \
108 "pushq %%rax; \n\t" \
109 "movq 0x30(%%rax), %%rbp; \n\t" \
110 "movq 0x28(%%rax), %%rdi; \n\t" \
111 "movq 0x20(%%rax), %%rsi; \n\t" \
112 "movq 0x18(%%rax), %%rdx; \n\t" \
113 "movq 0x10(%%rax), %%rcx; \n\t" \
114 "movq 0x08(%%rax), %%rbx; \n\t" \
115 "movq 0x00(%%rax), %%rax; \n\t" \
116 op "\n\t" \
117 "xchgq %%rax, 0x00(%%rsp); \n\t" \
118 "movq %%rbp, 0x30(%%rax); \n\t" \
119 "movq %%rdi, 0x28(%%rax); \n\t" \
120 "movq %%rsi, 0x20(%%rax); \n\t" \
121 "movq %%rdx, 0x18(%%rax); \n\t" \
122 "movq %%rcx, 0x10(%%rax); \n\t" \
123 "movq %%rbx, 0x08(%%rax); \n\t" \
124 "popq 0x00(%%rax); \n\t" \
125 "popq %%rbp; \n\t" \
126 : /* No outputs. */ : "a" (frame) \
127 /* No pushal on amd64 so warn gcc about the clobbered registers. */ \
128 : "rbx", "rcx", "rdx", "rdi", "rsi", "cc", "memory" \
129 )
130
131
132#ifdef __i386__
133#define BACKDOOR_OP(op, frame) BACKDOOR_OP_I386(op, frame)
134#else
135#define BACKDOOR_OP(op, frame) BACKDOOR_OP_AMD64(op, frame)
136#endif
137
138void
139vm_cmd(struct vm_backdoor *frame)
140{
141 BACKDOOR_OP("inl %%dx, %%eax;", frame);
142}
143
144void
145vm_ins(struct vm_backdoor *frame)
146{
147 BACKDOOR_OP("cld;\n\trep insb;", frame);
148}
149
150void
151vm_outs(struct vm_backdoor *frame)
152{
153 BACKDOOR_OP("cld;\n\trep outsb;", frame);
154}
155
156void
157vmware_check_version(void)
158{
159 struct vm_backdoor frame;
160
161 bzero(&frame, sizeof(frame));
162
163 frame.eax.word = VM_MAGIC;
164 frame.ebx.word = ~VM_MAGIC;
165 frame.ecx.part.low = VM_CMD_GET_VERSION;
166 frame.ecx.part.high = 0xffff;
167 frame.edx.part.low = VM_PORT_CMD;
168 frame.edx.part.high = 0;
169
170 vm_cmd(&frame);
171
172 if (debug)
173 printf("vmware_check_version: received version 0x%x\n",
174 frame.eax.word);
175
176 if (frame.eax.word == 0xffffffff ||
177 frame.ebx.word != VM_MAGIC)
178 errx(1, "not running under vmware\n");
179}
180
181void
182vmware_get_mouse_position(void)
183{
184 struct vm_backdoor frame;
185 int last_x = host_mouse_x;
186 int last_y = host_mouse_y;
187 int was_grabbed = mouse_grabbed;
188
189 bzero(&frame, sizeof(frame));
190
191 frame.eax.word = VM_MAGIC;
192 frame.ecx.part.low = VM_CMD_GET_MOUSEPOS;
193 frame.ecx.part.high = 0xffff;
194 frame.edx.part.low = VM_PORT_CMD;
195 frame.edx.part.high = 0;
196
197 vm_cmd(&frame);
198
199 host_mouse_x = frame.eax.word >> 16;
200 host_mouse_y = frame.eax.word & 0xffff;
201
202 if (host_mouse_x == VM_MOUSE_UNGRABBED_POS)
203 mouse_grabbed = 0;
204 else
205 mouse_grabbed = 1;
206
207 if (debug && ((last_x != host_mouse_x) || (last_y != host_mouse_y) ||
208 (was_grabbed != mouse_grabbed)))
209 printf("vmware_get_mouse_position: host cursor is at %d, %d (%s)\n",
210 host_mouse_x, host_mouse_y, (mouse_grabbed ? "grabbed"
211 : "ungrabbed"));
212}
213
214void
215vmware_set_mouse_position(int x, int y)
216{
217 struct vm_backdoor frame;
218
219 if (debug)
220 printf("vmware_set_mouse_position: setting to %d, %d\n", x, y);
221
222 bzero(&frame, sizeof(frame));
223
224 frame.eax.word = VM_MAGIC;
225 frame.ecx.part.low = VM_CMD_SET_MOUSEPOS;
226 frame.ecx.part.high = 0xffff;
227 frame.edx.part.low = VM_PORT_CMD;
228 frame.edx.part.high = 0;
229 frame.ebx.word = ((uint32_t)x << 16) | y;
230
231 vm_cmd(&frame);
232}
233
234int
235vmware_get_clipboard(char **buf)
236{
237 struct vm_backdoor frame;
238 uint32_t total, left;
239
240 bzero(&frame, sizeof(frame));
241
242 frame.eax.word = VM_MAGIC;
243 frame.ecx.part.low = VM_CMD_GET_CLIPBOARD_LEN;
244 frame.ecx.part.high = 0xffff;
245 frame.edx.part.low = VM_PORT_CMD;
246 frame.edx.part.high = 0;
247
248 vm_cmd(&frame);
249
250 total = left = frame.eax.word;
251
252 if (total == 0 || total > 0xffff) {
253 if (debug)
254 printf("vmware_get_clipboard: nothing there\n");
255
256 return (0);
257 }
258
259 if (debug)
260 printf("vmware_get_clipboard: have %d byte%s to read\n",
261 total, (total == 1 ? "" : "s"));
262
263 if ((*buf = malloc(total + 1)) == NULL)
264 err(1, "malloc");
265
266 for (;;) {
267 bzero(&frame, sizeof(frame));
268
269 frame.eax.word = VM_MAGIC;
270 frame.ecx.part.low = VM_CMD_GET_CLIPBOARD;
271 frame.ecx.part.high = 0xffff;
272 frame.edx.part.low = VM_PORT_CMD;
273 frame.edx.part.high = 0;
274
275 vm_cmd(&frame);
276
277 memcpy(*buf + (total - left), &frame.eax.word,
278 left > 4 ? 4 : left);
279
280 if (left <= 4) {
281 buf[0][total] = '\0';
282 break;
283 } else
284 left -= 4;
285 }
286
287 if (debug) {
288 char visbuf[strlen(*buf) * 4];
289 strnvis(visbuf, *buf, sizeof(visbuf), VIS_TAB | VIS_NL | VIS_CSTYLE);
290 printf("vmware_get_clipboard: \"%s\"\n", visbuf);
291 }
292
293 return (1);
294}
295
296void
297vmware_set_clipboard(char *buf)
298{
299 struct vm_backdoor frame;
300 uint32_t total, left;
301
302 if (debug) {
303 char visbuf[strlen(buf) * 4];
304 strnvis(visbuf, buf, sizeof(visbuf), VIS_TAB | VIS_NL | VIS_CSTYLE);
305 printf("vmware_set_clipboard: \"%s\"\n", visbuf);
306 }
307
308 if (!strlen(buf))
309 return;
310
311 bzero(&frame, sizeof(frame));
312
313 frame.eax.word = VM_MAGIC;
314 frame.ecx.part.low = VM_CMD_SET_CLIPBOARD_LEN;
315 frame.ecx.part.high = 0xffff;
316 frame.edx.part.low = VM_PORT_CMD;
317 frame.edx.part.high = 0;
318 frame.ebx.word = (uint32_t)strlen(buf);
319
320 vm_cmd(&frame);
321
322 total = left = strlen(buf);
323
324 for (;;) {
325 bzero(&frame, sizeof(frame));
326
327 frame.eax.word = VM_MAGIC;
328 frame.ecx.part.low = VM_CMD_SET_CLIPBOARD;
329 frame.ecx.part.high = 0xffff;
330 frame.edx.part.low = VM_PORT_CMD;
331 frame.edx.part.high = 0;
332
333 memcpy(&frame.ebx.word, buf + (total - left),
334 left > 4 ? 4 : left);
335
336 vm_cmd(&frame);
337
338 if (left <= 4)
339 break;
340 else
341 left -= 4;
342 }
343
344 return;
345}