Line data Source code
1 : /* $OpenBSD: vmt.c,v 1.15 2018/04/28 15:44:59 jasper Exp $ */
2 :
3 : /*
4 : * Copyright (c) 2007 David Crawshaw <david@zentus.com>
5 : * Copyright (c) 2008 David Gwynne <dlg@openbsd.org>
6 : *
7 : * Permission to use, copy, modify, and distribute this software for any
8 : * purpose with or without fee is hereby granted, provided that the above
9 : * copyright notice and this permission notice appear in all copies.
10 : *
11 : * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
12 : * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
13 : * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
14 : * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
15 : * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
16 : * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
17 : * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
18 : */
19 :
20 : #if !defined(__i386__) && !defined(__amd64__)
21 : #error vmt(4) is only supported on i386 and amd64
22 : #endif
23 :
24 : /*
25 : * Protocol reverse engineered by Ken Kato:
26 : * https://sites.google.com/site/chitchatvmback/backdoor
27 : */
28 :
29 : #include <sys/param.h>
30 : #include <sys/systm.h>
31 : #include <sys/kernel.h>
32 : #include <sys/malloc.h>
33 : #include <sys/timeout.h>
34 : #include <sys/signalvar.h>
35 : #include <sys/syslog.h>
36 : #include <sys/proc.h>
37 : #include <sys/socket.h>
38 : #include <sys/ioctl.h>
39 : #include <sys/mount.h>
40 : #include <sys/task.h>
41 :
42 : #include <net/if.h>
43 : #include <net/if_var.h>
44 : #include <netinet/in.h>
45 :
46 : #include <dev/pv/pvvar.h>
47 : #include <dev/rndvar.h>
48 :
49 : /* "The" magic number, always occupies the EAX register. */
50 : #define VM_MAGIC 0x564D5868
51 :
52 : /* Port numbers, passed on EDX.LOW . */
53 : #define VM_PORT_CMD 0x5658
54 : #define VM_PORT_RPC 0x5659
55 :
56 : /* Commands, passed on ECX.LOW. */
57 : #define VM_CMD_GET_SPEED 0x01
58 : #define VM_CMD_APM 0x02
59 : #define VM_CMD_GET_MOUSEPOS 0x04
60 : #define VM_CMD_SET_MOUSEPOS 0x05
61 : #define VM_CMD_GET_CLIPBOARD_LEN 0x06
62 : #define VM_CMD_GET_CLIPBOARD 0x07
63 : #define VM_CMD_SET_CLIPBOARD_LEN 0x08
64 : #define VM_CMD_SET_CLIPBOARD 0x09
65 : #define VM_CMD_GET_VERSION 0x0a
66 : #define VM_VERSION_UNMANAGED 0x7fffffff
67 : #define VM_CMD_GET_DEVINFO 0x0b
68 : #define VM_CMD_DEV_ADDREMOVE 0x0c
69 : #define VM_CMD_GET_GUI_OPTIONS 0x0d
70 : #define VM_CMD_SET_GUI_OPTIONS 0x0e
71 : #define VM_CMD_GET_SCREEN_SIZE 0x0f
72 : #define VM_CMD_GET_HWVER 0x11
73 : #define VM_CMD_POPUP_OSNOTFOUND 0x12
74 : #define VM_CMD_GET_BIOS_UUID 0x13
75 : #define VM_CMD_GET_MEM_SIZE 0x14
76 : /*#define VM_CMD_GET_TIME 0x17 */ /* deprecated */
77 : #define VM_CMD_RPC 0x1e
78 : #define VM_CMD_GET_TIME_FULL 0x2e
79 :
80 : /* RPC sub-commands, passed on ECX.HIGH. */
81 : #define VM_RPC_OPEN 0x00
82 : #define VM_RPC_SET_LENGTH 0x01
83 : #define VM_RPC_SET_DATA 0x02
84 : #define VM_RPC_GET_LENGTH 0x03
85 : #define VM_RPC_GET_DATA 0x04
86 : #define VM_RPC_GET_END 0x05
87 : #define VM_RPC_CLOSE 0x06
88 :
89 : /* RPC magic numbers, passed on EBX. */
90 : #define VM_RPC_OPEN_RPCI 0x49435052UL /* with VM_RPC_OPEN. */
91 : #define VM_RPC_OPEN_TCLO 0x4F4C4354UL /* with VP_RPC_OPEN. */
92 : #define VM_RPC_ENH_DATA 0x00010000UL /* with enhanced RPC data calls. */
93 :
94 : #define VM_RPC_FLAG_COOKIE 0x80000000UL
95 :
96 : /* RPC reply flags */
97 : #define VM_RPC_REPLY_SUCCESS 0x0001
98 : #define VM_RPC_REPLY_DORECV 0x0002 /* incoming message available */
99 : #define VM_RPC_REPLY_CLOSED 0x0004 /* RPC channel is closed */
100 : #define VM_RPC_REPLY_UNSENT 0x0008 /* incoming message was removed? */
101 : #define VM_RPC_REPLY_CHECKPOINT 0x0010 /* checkpoint occurred -> retry */
102 : #define VM_RPC_REPLY_POWEROFF 0x0020 /* underlying device is powering off */
103 : #define VM_RPC_REPLY_TIMEOUT 0x0040
104 : #define VM_RPC_REPLY_HB 0x0080 /* high-bandwidth tx/rx available */
105 :
106 : /* VM state change IDs */
107 : #define VM_STATE_CHANGE_HALT 1
108 : #define VM_STATE_CHANGE_REBOOT 2
109 : #define VM_STATE_CHANGE_POWERON 3
110 : #define VM_STATE_CHANGE_RESUME 4
111 : #define VM_STATE_CHANGE_SUSPEND 5
112 :
113 : /* VM guest info keys */
114 : #define VM_GUEST_INFO_DNS_NAME 1
115 : #define VM_GUEST_INFO_IP_ADDRESS 2
116 : #define VM_GUEST_INFO_DISK_FREE_SPACE 3
117 : #define VM_GUEST_INFO_BUILD_NUMBER 4
118 : #define VM_GUEST_INFO_OS_NAME_FULL 5
119 : #define VM_GUEST_INFO_OS_NAME 6
120 : #define VM_GUEST_INFO_UPTIME 7
121 : #define VM_GUEST_INFO_MEMORY 8
122 : #define VM_GUEST_INFO_IP_ADDRESS_V2 9
123 :
124 : /* RPC responses */
125 : #define VM_RPC_REPLY_OK "OK "
126 : #define VM_RPC_RESET_REPLY "OK ATR toolbox"
127 : #define VM_RPC_REPLY_ERROR "ERROR Unknown command"
128 : #define VM_RPC_REPLY_ERROR_IP_ADDR "ERROR Unable to find guest IP address"
129 :
130 : /* VM backup error codes */
131 : #define VM_BACKUP_SUCCESS 0
132 : #define VM_BACKUP_SYNC_ERROR 3
133 : #define VM_BACKUP_REMOTE_ABORT 4
134 :
135 : #define VM_BACKUP_TIMEOUT 30 /* seconds */
136 :
137 : /* A register. */
138 : union vm_reg {
139 : struct {
140 : uint16_t low;
141 : uint16_t high;
142 : } part;
143 : uint32_t word;
144 : #ifdef __amd64__
145 : struct {
146 : uint32_t low;
147 : uint32_t high;
148 : } words;
149 : uint64_t quad;
150 : #endif
151 : } __packed;
152 :
153 : /* A register frame. */
154 : struct vm_backdoor {
155 : union vm_reg eax;
156 : union vm_reg ebx;
157 : union vm_reg ecx;
158 : union vm_reg edx;
159 : union vm_reg esi;
160 : union vm_reg edi;
161 : union vm_reg ebp;
162 : } __packed;
163 :
164 : /* RPC context. */
165 : struct vm_rpc {
166 : uint16_t channel;
167 : uint32_t cookie1;
168 : uint32_t cookie2;
169 : };
170 :
171 : struct vmt_softc {
172 : struct device sc_dev;
173 :
174 : struct vm_rpc sc_tclo_rpc;
175 : char *sc_rpc_buf;
176 : int sc_rpc_error;
177 : int sc_tclo_ping;
178 : int sc_set_guest_os;
179 : int sc_quiesce;
180 : struct task sc_quiesce_task;
181 : #define VMT_RPC_BUFLEN 4096
182 :
183 : struct timeout sc_tick;
184 : struct timeout sc_tclo_tick;
185 : struct ksensordev sc_sensordev;
186 : struct ksensor sc_sensor;
187 :
188 : char sc_hostname[MAXHOSTNAMELEN];
189 : };
190 :
191 : #ifdef VMT_DEBUG
192 : #define DPRINTF(_arg...) printf(_arg)
193 : #else
194 : #define DPRINTF(_arg...) do {} while(0)
195 : #endif
196 : #define DEVNAME(_s) ((_s)->sc_dev.dv_xname)
197 :
198 : void vm_cmd(struct vm_backdoor *);
199 : void vm_ins(struct vm_backdoor *);
200 : void vm_outs(struct vm_backdoor *);
201 :
202 : /* Functions for communicating with the VM Host. */
203 : int vm_rpc_open(struct vm_rpc *, uint32_t);
204 : int vm_rpc_close(struct vm_rpc *);
205 : int vm_rpc_send(const struct vm_rpc *, const uint8_t *, uint32_t);
206 : int vm_rpc_send_str(const struct vm_rpc *, const uint8_t *);
207 : int vm_rpc_get_length(const struct vm_rpc *, uint32_t *, uint16_t *);
208 : int vm_rpc_get_data(const struct vm_rpc *, char *, uint32_t, uint16_t);
209 : int vm_rpc_send_rpci_tx_buf(struct vmt_softc *, const uint8_t *, uint32_t);
210 : int vm_rpc_send_rpci_tx(struct vmt_softc *, const char *, ...)
211 : __attribute__((__format__(__kprintf__,2,3)));
212 : int vm_rpci_response_successful(struct vmt_softc *);
213 :
214 : int vmt_kvop(void *, int, char *, char *, size_t);
215 :
216 : void vmt_probe_cmd(struct vm_backdoor *, uint16_t);
217 : void vmt_tclo_state_change_success(struct vmt_softc *, int, char);
218 : void vmt_do_reboot(struct vmt_softc *);
219 : void vmt_do_shutdown(struct vmt_softc *);
220 : void vmt_shutdown(void *);
221 :
222 : void vmt_update_guest_info(struct vmt_softc *);
223 : void vmt_update_guest_uptime(struct vmt_softc *);
224 :
225 : void vmt_tick_hook(struct device *self);
226 : void vmt_tick(void *);
227 : void vmt_resume(void);
228 :
229 : int vmt_match(struct device *, void *, void *);
230 : void vmt_attach(struct device *, struct device *, void *);
231 : int vmt_activate(struct device *, int);
232 :
233 : void vmt_tclo_tick(void *);
234 : int vmt_tclo_process(struct vmt_softc *, const char *);
235 : void vmt_tclo_reset(struct vmt_softc *);
236 : void vmt_tclo_ping(struct vmt_softc *);
237 : void vmt_tclo_halt(struct vmt_softc *);
238 : void vmt_tclo_reboot(struct vmt_softc *);
239 : void vmt_tclo_poweron(struct vmt_softc *);
240 : void vmt_tclo_suspend(struct vmt_softc *);
241 : void vmt_tclo_resume(struct vmt_softc *);
242 : void vmt_tclo_capreg(struct vmt_softc *);
243 : void vmt_tclo_broadcastip(struct vmt_softc *);
244 :
245 : void vmt_set_backup_status(struct vmt_softc *, const char *, int,
246 : const char *);
247 : void vmt_quiesce_task(void *);
248 : void vmt_quiesce_done_task(void *);
249 : void vmt_tclo_abortbackup(struct vmt_softc *);
250 : void vmt_tclo_startbackup(struct vmt_softc *);
251 : void vmt_tclo_backupdone(struct vmt_softc *);
252 :
253 : int vmt_probe(void);
254 :
255 : struct vmt_tclo_rpc {
256 : const char *name;
257 : void (*cb)(struct vmt_softc *);
258 : } vmt_tclo_rpc[] = {
259 : /* Keep sorted by name (case-sensitive) */
260 : { "Capabilities_Register", vmt_tclo_capreg },
261 : { "OS_Halt", vmt_tclo_halt },
262 : { "OS_PowerOn", vmt_tclo_poweron },
263 : { "OS_Reboot", vmt_tclo_reboot },
264 : { "OS_Resume", vmt_tclo_resume },
265 : { "OS_Suspend", vmt_tclo_suspend },
266 : { "Set_Option broadcastIP 1", vmt_tclo_broadcastip },
267 : { "ping", vmt_tclo_ping },
268 : { "reset", vmt_tclo_reset },
269 : { "vmbackup.abort", vmt_tclo_abortbackup },
270 : { "vmbackup.snapshotDone", vmt_tclo_backupdone },
271 : { "vmbackup.start 1", vmt_tclo_startbackup },
272 : { NULL },
273 : #if 0
274 : /* Various unsupported commands */
275 : { "Set_Option autohide 0" },
276 : { "Set_Option copypaste 1" },
277 : { "Set_Option enableDnD 1" },
278 : { "Set_Option enableMessageBusTunnel 0" },
279 : { "Set_Option linkRootHgfsShare 0" },
280 : { "Set_Option mapRootHgfsShare 0" },
281 : { "Set_Option synctime 1" },
282 : { "Set_Option synctime.period 0" },
283 : { "Set_Option time.synchronize.tools.enable 1" },
284 : { "Set_Option time.synchronize.tools.percentCorrection 0" },
285 : { "Set_Option time.synchronize.tools.slewCorrection 1" },
286 : { "Set_Option time.synchronize.tools.startup 1" },
287 : { "Set_Option toolScripts.afterPowerOn 1" },
288 : { "Set_Option toolScripts.afterResume 1" },
289 : { "Set_Option toolScripts.beforePowerOff 1" },
290 : { "Set_Option toolScripts.beforeSuspend 1" },
291 : { "Time_Synchronize 0" },
292 : { "Vix_1_Relayed_Command \"38cdcae40e075d66\"" },
293 : #endif
294 : };
295 :
296 : struct cfattach vmt_ca = {
297 : sizeof(struct vmt_softc),
298 : vmt_match,
299 : vmt_attach,
300 : NULL,
301 : vmt_activate
302 : };
303 :
304 : struct cfdriver vmt_cd = {
305 : NULL,
306 : "vmt",
307 : DV_DULL
308 : };
309 :
310 : extern char hostname[MAXHOSTNAMELEN];
311 :
312 : void
313 0 : vmt_probe_cmd(struct vm_backdoor *frame, uint16_t cmd)
314 : {
315 0 : bzero(frame, sizeof(*frame));
316 :
317 0 : (frame->eax).word = VM_MAGIC;
318 0 : (frame->ebx).word = ~VM_MAGIC;
319 0 : (frame->ecx).part.low = cmd;
320 0 : (frame->ecx).part.high = 0xffff;
321 0 : (frame->edx).part.low = VM_PORT_CMD;
322 0 : (frame->edx).part.high = 0;
323 :
324 0 : vm_cmd(frame);
325 0 : }
326 :
327 : int
328 0 : vmt_probe(void)
329 : {
330 0 : struct vm_backdoor frame;
331 :
332 0 : vmt_probe_cmd(&frame, VM_CMD_GET_VERSION);
333 0 : if (frame.eax.word == 0xffffffff ||
334 0 : frame.ebx.word != VM_MAGIC)
335 0 : return (0);
336 :
337 0 : vmt_probe_cmd(&frame, VM_CMD_GET_SPEED);
338 0 : if (frame.eax.word == VM_MAGIC)
339 0 : return (0);
340 :
341 0 : return (1);
342 0 : }
343 :
344 : int
345 0 : vmt_match(struct device *parent, void *match, void *aux)
346 : {
347 0 : struct pv_attach_args *pva = aux;
348 0 : struct pvbus_hv *hv = &pva->pva_hv[PVBUS_VMWARE];
349 :
350 0 : if (hv->hv_base == 0)
351 0 : return (0);
352 0 : if (!vmt_probe())
353 0 : return (0);
354 :
355 0 : return (1);
356 0 : }
357 :
358 : void
359 0 : vmt_attach(struct device *parent, struct device *self, void *aux)
360 : {
361 0 : struct vmt_softc *sc = (struct vmt_softc *)self;
362 0 : struct pv_attach_args *pva = aux;
363 0 : struct pvbus_hv *hv = &pva->pva_hv[PVBUS_VMWARE];
364 :
365 0 : printf("\n");
366 0 : sc->sc_rpc_buf = malloc(VMT_RPC_BUFLEN, M_DEVBUF, M_NOWAIT);
367 0 : if (sc->sc_rpc_buf == NULL) {
368 0 : printf("%s: unable to allocate buffer for RPC\n",
369 0 : DEVNAME(sc));
370 0 : return;
371 : }
372 :
373 0 : if (vm_rpc_open(&sc->sc_tclo_rpc, VM_RPC_OPEN_TCLO) != 0) {
374 0 : printf("%s: failed to open backdoor RPC channel "
375 0 : "(TCLO protocol)\n", DEVNAME(sc));
376 0 : goto free;
377 : }
378 :
379 : /* don't know if this is important at all yet */
380 0 : if (vm_rpc_send_rpci_tx(sc,
381 0 : "tools.capability.hgfs_server toolbox 1") != 0) {
382 0 : printf(": failed to set HGFS server capability\n");
383 0 : goto free;
384 : }
385 :
386 0 : strlcpy(sc->sc_sensordev.xname, sc->sc_dev.dv_xname,
387 : sizeof(sc->sc_sensordev.xname));
388 :
389 0 : sc->sc_sensor.type = SENSOR_TIMEDELTA;
390 0 : sc->sc_sensor.status = SENSOR_S_UNKNOWN;
391 :
392 0 : sensor_attach(&sc->sc_sensordev, &sc->sc_sensor);
393 0 : sensordev_install(&sc->sc_sensordev);
394 :
395 0 : config_mountroot(self, vmt_tick_hook);
396 :
397 0 : timeout_set(&sc->sc_tclo_tick, vmt_tclo_tick, sc);
398 0 : timeout_add_sec(&sc->sc_tclo_tick, 1);
399 0 : sc->sc_tclo_ping = 1;
400 :
401 : /* pvbus(4) key/value interface */
402 0 : hv->hv_kvop = vmt_kvop;
403 0 : hv->hv_arg = sc;
404 :
405 0 : return;
406 :
407 : free:
408 0 : free(sc->sc_rpc_buf, M_DEVBUF, VMT_RPC_BUFLEN);
409 0 : }
410 :
411 : int
412 0 : vmt_kvop(void *arg, int op, char *key, char *value, size_t valuelen)
413 : {
414 0 : struct vmt_softc *sc = arg;
415 : char *buf = NULL, *ptr;
416 : size_t bufsz;
417 : int error = 0;
418 :
419 : bufsz = VMT_RPC_BUFLEN;
420 0 : buf = malloc(bufsz, M_TEMP|M_ZERO, M_WAITOK);
421 :
422 0 : switch (op) {
423 : case PVBUS_KVWRITE:
424 0 : if ((size_t)snprintf(buf, bufsz, "info-set %s %s",
425 0 : key, value) >= bufsz) {
426 : DPRINTF("%s: write command too long", DEVNAME(sc));
427 : error = EINVAL;
428 0 : goto done;
429 : }
430 : break;
431 : case PVBUS_KVREAD:
432 0 : if ((size_t)snprintf(buf, bufsz, "info-get %s",
433 0 : key) >= bufsz) {
434 : DPRINTF("%s: read command too long", DEVNAME(sc));
435 : error = EINVAL;
436 0 : goto done;
437 : }
438 : break;
439 : default:
440 : error = EOPNOTSUPP;
441 0 : goto done;
442 : }
443 :
444 0 : if (vm_rpc_send_rpci_tx(sc, "%s", buf) != 0) {
445 : DPRINTF("%s: error sending command: %s\n", DEVNAME(sc), buf);
446 0 : sc->sc_rpc_error = 1;
447 : error = EIO;
448 0 : goto done;
449 : }
450 :
451 0 : if (vm_rpci_response_successful(sc) == 0) {
452 : DPRINTF("%s: host rejected command: %s\n", DEVNAME(sc), buf);
453 : error = EINVAL;
454 0 : goto done;
455 : }
456 :
457 : /* skip response that was tested in vm_rpci_response_successful() */
458 0 : ptr = sc->sc_rpc_buf + 2;
459 :
460 : /* might truncat, copy anyway but return error */
461 0 : if (strlcpy(value, ptr, valuelen) >= valuelen)
462 0 : error = ENOMEM;
463 :
464 : done:
465 0 : free(buf, M_TEMP, bufsz);
466 0 : return (error);
467 : }
468 :
469 : void
470 0 : vmt_resume(void)
471 : {
472 0 : struct vm_backdoor frame;
473 : extern void rdrand(void *);
474 :
475 0 : bzero(&frame, sizeof(frame));
476 0 : frame.eax.word = VM_MAGIC;
477 0 : frame.ecx.part.low = VM_CMD_GET_TIME_FULL;
478 0 : frame.edx.part.low = VM_PORT_CMD;
479 0 : vm_cmd(&frame);
480 :
481 0 : rdrand(NULL);
482 0 : enqueue_randomness(frame.eax.word);
483 0 : enqueue_randomness(frame.esi.word);
484 0 : enqueue_randomness(frame.edx.word);
485 0 : enqueue_randomness(frame.ebx.word);
486 0 : resume_randomness(NULL, 0);
487 0 : }
488 :
489 : int
490 0 : vmt_activate(struct device *self, int act)
491 : {
492 : int rv = 0;
493 :
494 0 : switch (act) {
495 : case DVACT_POWERDOWN:
496 0 : vmt_shutdown(self);
497 0 : break;
498 : case DVACT_RESUME:
499 0 : vmt_resume();
500 0 : break;
501 : }
502 0 : return (rv);
503 : }
504 :
505 :
506 : void
507 0 : vmt_update_guest_uptime(struct vmt_softc *sc)
508 : {
509 : /* host wants uptime in hundredths of a second */
510 0 : if (vm_rpc_send_rpci_tx(sc, "SetGuestInfo %d %lld00",
511 0 : VM_GUEST_INFO_UPTIME, (long long)time_uptime) != 0) {
512 : DPRINTF("%s: unable to set guest uptime", DEVNAME(sc));
513 0 : sc->sc_rpc_error = 1;
514 0 : }
515 0 : }
516 :
517 : void
518 0 : vmt_update_guest_info(struct vmt_softc *sc)
519 : {
520 0 : if (strncmp(sc->sc_hostname, hostname, sizeof(sc->sc_hostname)) != 0) {
521 0 : strlcpy(sc->sc_hostname, hostname, sizeof(sc->sc_hostname));
522 :
523 0 : if (vm_rpc_send_rpci_tx(sc, "SetGuestInfo %d %s",
524 0 : VM_GUEST_INFO_DNS_NAME, sc->sc_hostname) != 0) {
525 : DPRINTF("%s: unable to set hostname", DEVNAME(sc));
526 0 : sc->sc_rpc_error = 1;
527 0 : }
528 : }
529 :
530 : /*
531 : * We're supposed to pass the full network address information back
532 : * here, but that involves xdr (sunrpc) data encoding, which seems a
533 : * bit unreasonable.
534 : */
535 :
536 0 : if (sc->sc_set_guest_os == 0) {
537 0 : if (vm_rpc_send_rpci_tx(sc, "SetGuestInfo %d %s %s %s",
538 : VM_GUEST_INFO_OS_NAME_FULL,
539 0 : ostype, osrelease, osversion) != 0) {
540 : DPRINTF("%s: unable to set full guest OS", DEVNAME(sc));
541 0 : sc->sc_rpc_error = 1;
542 0 : }
543 :
544 : /*
545 : * Host doesn't like it if we send an OS name it doesn't
546 : * recognise, so use the closest match, which happens
547 : * to be FreeBSD.
548 : */
549 :
550 0 : if (vm_rpc_send_rpci_tx(sc, "SetGuestInfo %d %s",
551 0 : VM_GUEST_INFO_OS_NAME, "FreeBSD") != 0) {
552 : DPRINTF("%s: unable to set guest OS", DEVNAME(sc));
553 0 : sc->sc_rpc_error = 1;
554 0 : }
555 :
556 0 : sc->sc_set_guest_os = 1;
557 0 : }
558 0 : }
559 :
560 : void
561 0 : vmt_tick_hook(struct device *self)
562 : {
563 0 : struct vmt_softc *sc = (struct vmt_softc *)self;
564 :
565 0 : timeout_set(&sc->sc_tick, vmt_tick, sc);
566 0 : vmt_tick(sc);
567 0 : }
568 :
569 : void
570 0 : vmt_tick(void *xarg)
571 : {
572 0 : struct vmt_softc *sc = xarg;
573 0 : struct vm_backdoor frame;
574 0 : struct timeval *guest = &sc->sc_sensor.tv;
575 : struct timeval host, diff;
576 :
577 0 : microtime(guest);
578 :
579 0 : bzero(&frame, sizeof(frame));
580 0 : frame.eax.word = VM_MAGIC;
581 0 : frame.ecx.part.low = VM_CMD_GET_TIME_FULL;
582 0 : frame.edx.part.low = VM_PORT_CMD;
583 0 : vm_cmd(&frame);
584 :
585 0 : if (frame.eax.word != 0xffffffff) {
586 0 : host.tv_sec = ((uint64_t)frame.esi.word << 32) | frame.edx.word;
587 0 : host.tv_usec = frame.ebx.word;
588 :
589 0 : timersub(guest, &host, &diff);
590 :
591 0 : sc->sc_sensor.value = (u_int64_t)diff.tv_sec * 1000000000LL +
592 0 : (u_int64_t)diff.tv_usec * 1000LL;
593 0 : sc->sc_sensor.status = SENSOR_S_OK;
594 0 : } else {
595 0 : sc->sc_sensor.status = SENSOR_S_UNKNOWN;
596 : }
597 :
598 0 : vmt_update_guest_info(sc);
599 0 : vmt_update_guest_uptime(sc);
600 :
601 0 : timeout_add_sec(&sc->sc_tick, 15);
602 0 : }
603 :
604 : void
605 0 : vmt_tclo_state_change_success(struct vmt_softc *sc, int success, char state)
606 : {
607 0 : if (vm_rpc_send_rpci_tx(sc, "tools.os.statechange.status %d %d",
608 0 : success, state) != 0) {
609 : DPRINTF("%s: unable to send state change result\n",
610 : DEVNAME(sc));
611 0 : sc->sc_rpc_error = 1;
612 0 : }
613 0 : }
614 :
615 : void
616 0 : vmt_do_shutdown(struct vmt_softc *sc)
617 : {
618 0 : vmt_tclo_state_change_success(sc, 1, VM_STATE_CHANGE_HALT);
619 0 : vm_rpc_send_str(&sc->sc_tclo_rpc, VM_RPC_REPLY_OK);
620 0 : pvbus_shutdown(&sc->sc_dev);
621 0 : }
622 :
623 : void
624 0 : vmt_do_reboot(struct vmt_softc *sc)
625 : {
626 0 : vmt_tclo_state_change_success(sc, 1, VM_STATE_CHANGE_REBOOT);
627 0 : vm_rpc_send_str(&sc->sc_tclo_rpc, VM_RPC_REPLY_OK);
628 0 : pvbus_reboot(&sc->sc_dev);
629 0 : }
630 :
631 : void
632 0 : vmt_shutdown(void *arg)
633 : {
634 0 : struct vmt_softc *sc = arg;
635 :
636 0 : if (vm_rpc_send_rpci_tx(sc,
637 : "tools.capability.hgfs_server toolbox 0") != 0) {
638 : DPRINTF("%s: failed to disable hgfs server capability\n",
639 : DEVNAME(sc));
640 : }
641 :
642 0 : if (vm_rpc_send(&sc->sc_tclo_rpc, NULL, 0) != 0) {
643 : DPRINTF("%s: failed to send shutdown ping\n", DEVNAME(sc));
644 : }
645 :
646 0 : vm_rpc_close(&sc->sc_tclo_rpc);
647 0 : }
648 :
649 : void
650 0 : vmt_tclo_reset(struct vmt_softc *sc)
651 : {
652 0 : if (sc->sc_rpc_error != 0) {
653 : DPRINTF("%s: resetting rpc\n", DEVNAME(sc));
654 0 : vm_rpc_close(&sc->sc_tclo_rpc);
655 :
656 : /* reopen and send the reset reply next time around */
657 0 : sc->sc_rpc_error = 1;
658 0 : return;
659 : }
660 :
661 0 : if (vm_rpc_send_str(&sc->sc_tclo_rpc, VM_RPC_RESET_REPLY) != 0) {
662 : DPRINTF("%s: failed to send reset reply\n", DEVNAME(sc));
663 0 : sc->sc_rpc_error = 1;
664 0 : }
665 0 : }
666 :
667 : void
668 0 : vmt_tclo_ping(struct vmt_softc *sc)
669 : {
670 0 : vmt_update_guest_info(sc);
671 :
672 0 : if (vm_rpc_send_str(&sc->sc_tclo_rpc, VM_RPC_REPLY_OK) != 0) {
673 : DPRINTF("%s: error sending ping response\n", DEVNAME(sc));
674 0 : sc->sc_rpc_error = 1;
675 0 : }
676 0 : }
677 :
678 : void
679 0 : vmt_tclo_halt(struct vmt_softc *sc)
680 : {
681 0 : vmt_do_shutdown(sc);
682 0 : }
683 :
684 : void
685 0 : vmt_tclo_reboot(struct vmt_softc *sc)
686 : {
687 0 : vmt_do_reboot(sc);
688 0 : }
689 :
690 : void
691 0 : vmt_tclo_poweron(struct vmt_softc *sc)
692 : {
693 0 : vmt_tclo_state_change_success(sc, 1, VM_STATE_CHANGE_POWERON);
694 :
695 0 : if (vm_rpc_send_str(&sc->sc_tclo_rpc, VM_RPC_REPLY_OK) != 0) {
696 : DPRINTF("%s: error sending poweron response\n", DEVNAME(sc));
697 0 : sc->sc_rpc_error = 1;
698 0 : }
699 0 : }
700 :
701 : void
702 0 : vmt_tclo_suspend(struct vmt_softc *sc)
703 : {
704 0 : log(LOG_KERN | LOG_NOTICE,
705 : "VMware guest entering suspended state\n");
706 :
707 0 : suspend_randomness();
708 :
709 0 : vmt_tclo_state_change_success(sc, 1, VM_STATE_CHANGE_SUSPEND);
710 0 : if (vm_rpc_send_str(&sc->sc_tclo_rpc, VM_RPC_REPLY_OK) != 0) {
711 : DPRINTF("%s: error sending suspend response\n", DEVNAME(sc));
712 0 : sc->sc_rpc_error = 1;
713 0 : }
714 0 : }
715 :
716 : void
717 0 : vmt_tclo_resume(struct vmt_softc *sc)
718 : {
719 0 : log(LOG_KERN | LOG_NOTICE,
720 : "VMware guest resuming from suspended state\n");
721 :
722 : /* force guest info update */
723 0 : sc->sc_hostname[0] = '\0';
724 0 : sc->sc_set_guest_os = 0;
725 0 : vmt_update_guest_info(sc);
726 0 : vmt_resume();
727 :
728 0 : vmt_tclo_state_change_success(sc, 1, VM_STATE_CHANGE_RESUME);
729 0 : if (vm_rpc_send_str(&sc->sc_tclo_rpc, VM_RPC_REPLY_OK) != 0) {
730 : DPRINTF("%s: error sending resume response\n", DEVNAME(sc));
731 0 : sc->sc_rpc_error = 1;
732 0 : }
733 0 : }
734 :
735 : void
736 0 : vmt_tclo_capreg(struct vmt_softc *sc)
737 : {
738 : /* don't know if this is important at all */
739 0 : if (vm_rpc_send_rpci_tx(sc,
740 0 : "vmx.capability.unified_loop toolbox") != 0) {
741 : DPRINTF("%s: unable to set unified loop\n", DEVNAME(sc));
742 0 : sc->sc_rpc_error = 1;
743 0 : }
744 :
745 0 : if (vm_rpci_response_successful(sc) == 0) {
746 : DPRINTF("%s: host rejected unified loop setting\n",
747 : DEVNAME(sc));
748 : }
749 :
750 : /* the trailing space is apparently important here */
751 0 : if (vm_rpc_send_rpci_tx(sc,
752 0 : "tools.capability.statechange ") != 0) {
753 : DPRINTF("%s: unable to send statechange capability\n",
754 : DEVNAME(sc));
755 0 : sc->sc_rpc_error = 1;
756 0 : }
757 :
758 0 : if (vm_rpci_response_successful(sc) == 0) {
759 : DPRINTF("%s: host rejected statechange capability\n",
760 : DEVNAME(sc));
761 : }
762 :
763 0 : if (vm_rpc_send_rpci_tx(sc, "tools.set.version %u",
764 0 : VM_VERSION_UNMANAGED) != 0) {
765 : DPRINTF("%s: unable to set tools version\n",
766 : DEVNAME(sc));
767 0 : sc->sc_rpc_error = 1;
768 0 : }
769 :
770 0 : vmt_update_guest_uptime(sc);
771 :
772 0 : if (vm_rpc_send_str(&sc->sc_tclo_rpc, VM_RPC_REPLY_OK) != 0) {
773 : DPRINTF("%s: error sending capabilities_register"
774 : " response\n", DEVNAME(sc));
775 0 : sc->sc_rpc_error = 1;
776 0 : }
777 0 : }
778 :
779 : void
780 0 : vmt_tclo_broadcastip(struct vmt_softc *sc)
781 : {
782 : struct ifnet *iface;
783 : struct sockaddr_in *guest_ip;
784 :
785 : /* find first available ipv4 address */
786 : guest_ip = NULL;
787 0 : TAILQ_FOREACH(iface, &ifnet, if_list) {
788 : struct ifaddr *iface_addr;
789 :
790 : /* skip loopback */
791 0 : if (strncmp(iface->if_xname, "lo", 2) == 0 &&
792 0 : iface->if_xname[2] >= '0' &&
793 0 : iface->if_xname[2] <= '9') {
794 0 : continue;
795 : }
796 :
797 0 : TAILQ_FOREACH(iface_addr, &iface->if_addrlist,
798 : ifa_list) {
799 0 : if (iface_addr->ifa_addr->sa_family != AF_INET)
800 : continue;
801 :
802 0 : guest_ip = satosin(iface_addr->ifa_addr);
803 0 : break;
804 : }
805 0 : }
806 :
807 0 : if (guest_ip != NULL) {
808 0 : char ip[INET_ADDRSTRLEN];
809 :
810 0 : inet_ntop(AF_INET, &guest_ip->sin_addr, ip, sizeof(ip));
811 0 : if (vm_rpc_send_rpci_tx(sc, "info-set guestinfo.ip %s",
812 0 : ip) != 0) {
813 : DPRINTF("%s: unable to send guest IP address\n",
814 : DEVNAME(sc));
815 0 : sc->sc_rpc_error = 1;
816 0 : }
817 :
818 0 : if (vm_rpc_send_str(&sc->sc_tclo_rpc,
819 0 : VM_RPC_REPLY_OK) != 0) {
820 : DPRINTF("%s: error sending broadcastIP"
821 : " response\n", DEVNAME(sc));
822 0 : sc->sc_rpc_error = 1;
823 0 : }
824 0 : } else {
825 0 : if (vm_rpc_send_str(&sc->sc_tclo_rpc,
826 0 : VM_RPC_REPLY_ERROR_IP_ADDR) != 0) {
827 : DPRINTF("%s: error sending broadcastIP"
828 : " error response\n", DEVNAME(sc));
829 0 : sc->sc_rpc_error = 1;
830 0 : }
831 : }
832 0 : }
833 :
834 : void
835 0 : vmt_set_backup_status(struct vmt_softc *sc, const char *state, int code,
836 : const char *desc)
837 : {
838 0 : if (vm_rpc_send_rpci_tx(sc, "vmbackup.eventSet %s %d %s",
839 : state, code, desc) != 0) {
840 : DPRINTF("%s: setting backup status failed\n", DEVNAME(sc));
841 : }
842 0 : }
843 :
844 : void
845 0 : vmt_quiesce_task(void *data)
846 : {
847 0 : struct vmt_softc *sc = data;
848 : int err;
849 :
850 : DPRINTF("%s: quiescing filesystems for backup\n", DEVNAME(sc));
851 0 : err = vfs_stall(curproc, 1);
852 0 : if (err != 0) {
853 0 : printf("%s: unable to quiesce filesystems\n", DEVNAME(sc));
854 0 : vfs_stall(curproc, 0);
855 :
856 0 : vmt_set_backup_status(sc, "req.aborted", VM_BACKUP_SYNC_ERROR,
857 : "vfs_stall failed");
858 0 : vmt_set_backup_status(sc, "req.done", VM_BACKUP_SUCCESS, "");
859 0 : sc->sc_quiesce = 0;
860 0 : return;
861 : }
862 :
863 : DPRINTF("%s: filesystems quiesced\n", DEVNAME(sc));
864 0 : vmt_set_backup_status(sc, "prov.snapshotCommit", VM_BACKUP_SUCCESS, "");
865 0 : }
866 :
867 : void
868 0 : vmt_quiesce_done_task(void *data)
869 : {
870 0 : struct vmt_softc *sc = data;
871 :
872 0 : vfs_stall(curproc, 0);
873 :
874 0 : if (sc->sc_quiesce == -1)
875 0 : vmt_set_backup_status(sc, "req.aborted", VM_BACKUP_REMOTE_ABORT,
876 : "");
877 :
878 0 : vmt_set_backup_status(sc, "req.done", VM_BACKUP_SUCCESS, "");
879 0 : sc->sc_quiesce = 0;
880 0 : }
881 :
882 : void
883 0 : vmt_tclo_abortbackup(struct vmt_softc *sc)
884 : {
885 : const char *reply = VM_RPC_REPLY_OK;
886 :
887 0 : if (sc->sc_quiesce > 0) {
888 : DPRINTF("%s: aborting backup\n", DEVNAME(sc));
889 0 : sc->sc_quiesce = -1;
890 0 : task_set(&sc->sc_quiesce_task, vmt_quiesce_done_task, sc);
891 0 : task_add(systq, &sc->sc_quiesce_task);
892 0 : } else {
893 : DPRINTF("%s: can't abort, no backup in progress\n",
894 : DEVNAME(sc));
895 : reply = VM_RPC_REPLY_ERROR;
896 : }
897 :
898 0 : if (vm_rpc_send_str(&sc->sc_tclo_rpc, reply) != 0) {
899 : DPRINTF("%s: error sending vmbackup.abort reply\n",
900 : DEVNAME(sc));
901 0 : sc->sc_rpc_error = 1;
902 0 : }
903 0 : }
904 :
905 : void
906 0 : vmt_tclo_startbackup(struct vmt_softc *sc)
907 : {
908 : const char *reply = VM_RPC_REPLY_OK;
909 :
910 0 : if (sc->sc_quiesce == 0) {
911 : DPRINTF("%s: starting quiesce\n", DEVNAME(sc));
912 0 : vmt_set_backup_status(sc, "reset", VM_BACKUP_SUCCESS, "");
913 :
914 0 : task_set(&sc->sc_quiesce_task, vmt_quiesce_task, sc);
915 0 : task_add(systq, &sc->sc_quiesce_task);
916 0 : sc->sc_quiesce = 1;
917 0 : } else {
918 : DPRINTF("%s: can't start backup, already in progress\n",
919 : DEVNAME(sc));
920 : reply = VM_RPC_REPLY_ERROR;
921 : }
922 :
923 0 : if (vm_rpc_send_str(&sc->sc_tclo_rpc, reply) != 0) {
924 : DPRINTF("%s: error sending vmbackup.start reply\n",
925 : DEVNAME(sc));
926 0 : sc->sc_rpc_error = 1;
927 0 : }
928 0 : }
929 :
930 : void
931 0 : vmt_tclo_backupdone(struct vmt_softc *sc)
932 : {
933 : const char *reply = VM_RPC_REPLY_OK;
934 0 : if (sc->sc_quiesce > 0) {
935 : DPRINTF("%s: backup complete\n", DEVNAME(sc));
936 0 : task_set(&sc->sc_quiesce_task, vmt_quiesce_done_task, sc);
937 0 : task_add(systq, &sc->sc_quiesce_task);
938 0 : } else {
939 : DPRINTF("%s: got backup complete, but not doing a backup\n",
940 : DEVNAME(sc));
941 : reply = VM_RPC_REPLY_ERROR;
942 : }
943 :
944 0 : if (vm_rpc_send_str(&sc->sc_tclo_rpc, reply) != 0) {
945 : DPRINTF("%s: error sending vmbackup.snapshotDone reply\n",
946 : DEVNAME(sc));
947 0 : sc->sc_rpc_error = 1;
948 0 : }
949 0 : }
950 :
951 : int
952 0 : vmt_tclo_process(struct vmt_softc *sc, const char *name)
953 : {
954 : int i;
955 :
956 : /* Search for rpc command and call handler */
957 0 : for (i = 0; vmt_tclo_rpc[i].name != NULL; i++) {
958 0 : if (strcmp(vmt_tclo_rpc[i].name, sc->sc_rpc_buf) == 0) {
959 0 : vmt_tclo_rpc[i].cb(sc);
960 0 : return (0);
961 : }
962 : }
963 :
964 : DPRINTF("%s: unknown command: \"%s\"\n", DEVNAME(sc), name);
965 :
966 0 : return (-1);
967 0 : }
968 :
969 : void
970 0 : vmt_tclo_tick(void *xarg)
971 : {
972 0 : struct vmt_softc *sc = xarg;
973 0 : u_int32_t rlen;
974 0 : u_int16_t ack;
975 : int delay;
976 :
977 : /* By default, poll every second for new messages */
978 : delay = 1;
979 :
980 0 : if (sc->sc_quiesce > 0) {
981 : /* abort quiesce if it's taking too long */
982 0 : if (sc->sc_quiesce++ == VM_BACKUP_TIMEOUT) {
983 0 : printf("%s: aborting quiesce\n", DEVNAME(sc));
984 0 : sc->sc_quiesce = -1;
985 0 : task_set(&sc->sc_quiesce_task, vmt_quiesce_done_task,
986 : sc);
987 0 : task_add(systq, &sc->sc_quiesce_task);
988 0 : } else
989 0 : vmt_set_backup_status(sc, "req.keepAlive",
990 : VM_BACKUP_SUCCESS, "");
991 : }
992 :
993 : /* reopen tclo channel if it's currently closed */
994 0 : if (sc->sc_tclo_rpc.channel == 0 &&
995 0 : sc->sc_tclo_rpc.cookie1 == 0 &&
996 0 : sc->sc_tclo_rpc.cookie2 == 0) {
997 0 : if (vm_rpc_open(&sc->sc_tclo_rpc, VM_RPC_OPEN_TCLO) != 0) {
998 : DPRINTF("%s: unable to reopen TCLO channel\n",
999 : DEVNAME(sc));
1000 : delay = 15;
1001 0 : goto out;
1002 : }
1003 :
1004 0 : if (vm_rpc_send_str(&sc->sc_tclo_rpc,
1005 0 : VM_RPC_RESET_REPLY) != 0) {
1006 : DPRINTF("%s: failed to send reset reply\n",
1007 : DEVNAME(sc));
1008 0 : sc->sc_rpc_error = 1;
1009 0 : goto out;
1010 : } else {
1011 0 : sc->sc_rpc_error = 0;
1012 : }
1013 0 : }
1014 :
1015 0 : if (sc->sc_tclo_ping) {
1016 0 : if (vm_rpc_send(&sc->sc_tclo_rpc, NULL, 0) != 0) {
1017 : DPRINTF("%s: failed to send TCLO outgoing ping\n",
1018 : DEVNAME(sc));
1019 0 : sc->sc_rpc_error = 1;
1020 0 : goto out;
1021 : }
1022 : }
1023 :
1024 0 : if (vm_rpc_get_length(&sc->sc_tclo_rpc, &rlen, &ack) != 0) {
1025 : DPRINTF("%s: failed to get length of incoming TCLO data\n",
1026 : DEVNAME(sc));
1027 0 : sc->sc_rpc_error = 1;
1028 0 : goto out;
1029 : }
1030 :
1031 0 : if (rlen == 0) {
1032 0 : sc->sc_tclo_ping = 1;
1033 0 : goto out;
1034 : }
1035 :
1036 0 : if (rlen >= VMT_RPC_BUFLEN) {
1037 0 : rlen = VMT_RPC_BUFLEN - 1;
1038 0 : }
1039 0 : if (vm_rpc_get_data(&sc->sc_tclo_rpc, sc->sc_rpc_buf, rlen, ack) != 0) {
1040 : DPRINTF("%s: failed to get incoming TCLO data\n", DEVNAME(sc));
1041 0 : sc->sc_rpc_error = 1;
1042 0 : goto out;
1043 : }
1044 0 : sc->sc_tclo_ping = 0;
1045 :
1046 : /* The VM host can queue multiple messages; continue without delay */
1047 : delay = 0;
1048 :
1049 0 : if (vmt_tclo_process(sc, sc->sc_rpc_buf) != 0) {
1050 0 : if (vm_rpc_send_str(&sc->sc_tclo_rpc,
1051 0 : VM_RPC_REPLY_ERROR) != 0) {
1052 : DPRINTF("%s: error sending unknown command reply\n",
1053 : DEVNAME(sc));
1054 0 : sc->sc_rpc_error = 1;
1055 0 : }
1056 : }
1057 :
1058 0 : if (sc->sc_rpc_error == 1) {
1059 : /* On error, give time to recover and wait a second */
1060 : delay = 1;
1061 0 : }
1062 :
1063 : out:
1064 0 : timeout_add_sec(&sc->sc_tclo_tick, delay);
1065 0 : }
1066 :
1067 : #define BACKDOOR_OP_I386(op, frame) \
1068 : __asm__ volatile ( \
1069 : "pushal;" \
1070 : "pushl %%eax;" \
1071 : "movl 0x18(%%eax), %%ebp;" \
1072 : "movl 0x14(%%eax), %%edi;" \
1073 : "movl 0x10(%%eax), %%esi;" \
1074 : "movl 0x0c(%%eax), %%edx;" \
1075 : "movl 0x08(%%eax), %%ecx;" \
1076 : "movl 0x04(%%eax), %%ebx;" \
1077 : "movl 0x00(%%eax), %%eax;" \
1078 : op \
1079 : "xchgl %%eax, 0x00(%%esp);" \
1080 : "movl %%ebp, 0x18(%%eax);" \
1081 : "movl %%edi, 0x14(%%eax);" \
1082 : "movl %%esi, 0x10(%%eax);" \
1083 : "movl %%edx, 0x0c(%%eax);" \
1084 : "movl %%ecx, 0x08(%%eax);" \
1085 : "movl %%ebx, 0x04(%%eax);" \
1086 : "popl 0x00(%%eax);" \
1087 : "popal;" \
1088 : ::"a"(frame) \
1089 : )
1090 :
1091 : #define BACKDOOR_OP_AMD64(op, frame) \
1092 : __asm__ volatile ( \
1093 : "pushq %%rbp; \n\t" \
1094 : "pushq %%rax; \n\t" \
1095 : "movq 0x30(%%rax), %%rbp; \n\t" \
1096 : "movq 0x28(%%rax), %%rdi; \n\t" \
1097 : "movq 0x20(%%rax), %%rsi; \n\t" \
1098 : "movq 0x18(%%rax), %%rdx; \n\t" \
1099 : "movq 0x10(%%rax), %%rcx; \n\t" \
1100 : "movq 0x08(%%rax), %%rbx; \n\t" \
1101 : "movq 0x00(%%rax), %%rax; \n\t" \
1102 : op "\n\t" \
1103 : "xchgq %%rax, 0x00(%%rsp); \n\t" \
1104 : "movq %%rbp, 0x30(%%rax); \n\t" \
1105 : "movq %%rdi, 0x28(%%rax); \n\t" \
1106 : "movq %%rsi, 0x20(%%rax); \n\t" \
1107 : "movq %%rdx, 0x18(%%rax); \n\t" \
1108 : "movq %%rcx, 0x10(%%rax); \n\t" \
1109 : "movq %%rbx, 0x08(%%rax); \n\t" \
1110 : "popq 0x00(%%rax); \n\t" \
1111 : "popq %%rbp; \n\t" \
1112 : : /* No outputs. */ : "a" (frame) \
1113 : /* No pushal on amd64 so warn gcc about the clobbered registers. */ \
1114 : : "rbx", "rcx", "rdx", "rdi", "rsi", "cc", "memory" \
1115 : )
1116 :
1117 :
1118 : #ifdef __i386__
1119 : #define BACKDOOR_OP(op, frame) BACKDOOR_OP_I386(op, frame)
1120 : #else
1121 : #define BACKDOOR_OP(op, frame) BACKDOOR_OP_AMD64(op, frame)
1122 : #endif
1123 :
1124 : void
1125 0 : vm_cmd(struct vm_backdoor *frame)
1126 : {
1127 0 : BACKDOOR_OP("inl %%dx, %%eax;", frame);
1128 0 : }
1129 :
1130 : void
1131 0 : vm_ins(struct vm_backdoor *frame)
1132 : {
1133 0 : BACKDOOR_OP("cld;\n\trep insb;", frame);
1134 0 : }
1135 :
1136 : void
1137 0 : vm_outs(struct vm_backdoor *frame)
1138 : {
1139 0 : BACKDOOR_OP("cld;\n\trep outsb;", frame);
1140 0 : }
1141 :
1142 : int
1143 0 : vm_rpc_open(struct vm_rpc *rpc, uint32_t proto)
1144 : {
1145 0 : struct vm_backdoor frame;
1146 :
1147 0 : bzero(&frame, sizeof(frame));
1148 0 : frame.eax.word = VM_MAGIC;
1149 0 : frame.ebx.word = proto | VM_RPC_FLAG_COOKIE;
1150 0 : frame.ecx.part.low = VM_CMD_RPC;
1151 0 : frame.ecx.part.high = VM_RPC_OPEN;
1152 0 : frame.edx.part.low = VM_PORT_CMD;
1153 0 : frame.edx.part.high = 0;
1154 :
1155 0 : vm_cmd(&frame);
1156 :
1157 0 : if (frame.ecx.part.high != 1 || frame.edx.part.low != 0) {
1158 : /* open-vm-tools retries without VM_RPC_FLAG_COOKIE here.. */
1159 : DPRINTF("vmware: open failed, eax=%08x, ecx=%08x, edx=%08x\n",
1160 : frame.eax.word, frame.ecx.word, frame.edx.word);
1161 0 : return EIO;
1162 : }
1163 :
1164 0 : rpc->channel = frame.edx.part.high;
1165 0 : rpc->cookie1 = frame.esi.word;
1166 0 : rpc->cookie2 = frame.edi.word;
1167 :
1168 0 : return 0;
1169 0 : }
1170 :
1171 : int
1172 0 : vm_rpc_close(struct vm_rpc *rpc)
1173 : {
1174 0 : struct vm_backdoor frame;
1175 :
1176 0 : bzero(&frame, sizeof(frame));
1177 0 : frame.eax.word = VM_MAGIC;
1178 0 : frame.ebx.word = 0;
1179 0 : frame.ecx.part.low = VM_CMD_RPC;
1180 0 : frame.ecx.part.high = VM_RPC_CLOSE;
1181 0 : frame.edx.part.low = VM_PORT_CMD;
1182 0 : frame.edx.part.high = rpc->channel;
1183 0 : frame.edi.word = rpc->cookie2;
1184 0 : frame.esi.word = rpc->cookie1;
1185 :
1186 0 : vm_cmd(&frame);
1187 :
1188 0 : if (frame.ecx.part.high == 0 || frame.ecx.part.low != 0) {
1189 : DPRINTF("vmware: close failed, eax=%08x, ecx=%08x\n",
1190 : frame.eax.word, frame.ecx.word);
1191 0 : return EIO;
1192 : }
1193 :
1194 0 : rpc->channel = 0;
1195 0 : rpc->cookie1 = 0;
1196 0 : rpc->cookie2 = 0;
1197 :
1198 0 : return 0;
1199 0 : }
1200 :
1201 : int
1202 0 : vm_rpc_send(const struct vm_rpc *rpc, const uint8_t *buf, uint32_t length)
1203 : {
1204 0 : struct vm_backdoor frame;
1205 :
1206 : /* Send the length of the command. */
1207 0 : bzero(&frame, sizeof(frame));
1208 0 : frame.eax.word = VM_MAGIC;
1209 0 : frame.ebx.word = length;
1210 0 : frame.ecx.part.low = VM_CMD_RPC;
1211 0 : frame.ecx.part.high = VM_RPC_SET_LENGTH;
1212 0 : frame.edx.part.low = VM_PORT_CMD;
1213 0 : frame.edx.part.high = rpc->channel;
1214 0 : frame.esi.word = rpc->cookie1;
1215 0 : frame.edi.word = rpc->cookie2;
1216 :
1217 0 : vm_cmd(&frame);
1218 :
1219 0 : if ((frame.ecx.part.high & VM_RPC_REPLY_SUCCESS) == 0) {
1220 : DPRINTF("vmware: sending length failed, eax=%08x, ecx=%08x\n",
1221 : frame.eax.word, frame.ecx.word);
1222 0 : return EIO;
1223 : }
1224 :
1225 0 : if (length == 0)
1226 0 : return 0; /* Only need to poke once if command is null. */
1227 :
1228 : /* Send the command using enhanced RPC. */
1229 0 : bzero(&frame, sizeof(frame));
1230 0 : frame.eax.word = VM_MAGIC;
1231 0 : frame.ebx.word = VM_RPC_ENH_DATA;
1232 0 : frame.ecx.word = length;
1233 0 : frame.edx.part.low = VM_PORT_RPC;
1234 0 : frame.edx.part.high = rpc->channel;
1235 0 : frame.ebp.word = rpc->cookie1;
1236 0 : frame.edi.word = rpc->cookie2;
1237 : #ifdef __amd64__
1238 0 : frame.esi.quad = (uint64_t)buf;
1239 : #else
1240 : frame.esi.word = (uint32_t)buf;
1241 : #endif
1242 :
1243 0 : vm_outs(&frame);
1244 :
1245 0 : if (frame.ebx.word != VM_RPC_ENH_DATA) {
1246 : /* open-vm-tools retries on VM_RPC_REPLY_CHECKPOINT */
1247 : DPRINTF("vmware: send failed, ebx=%08x\n", frame.ebx.word);
1248 0 : return EIO;
1249 : }
1250 :
1251 0 : return 0;
1252 0 : }
1253 :
1254 : int
1255 0 : vm_rpc_send_str(const struct vm_rpc *rpc, const uint8_t *str)
1256 : {
1257 0 : return vm_rpc_send(rpc, str, strlen(str));
1258 : }
1259 :
1260 : int
1261 0 : vm_rpc_get_data(const struct vm_rpc *rpc, char *data, uint32_t length,
1262 : uint16_t dataid)
1263 : {
1264 0 : struct vm_backdoor frame;
1265 :
1266 : /* Get data using enhanced RPC. */
1267 0 : bzero(&frame, sizeof(frame));
1268 0 : frame.eax.word = VM_MAGIC;
1269 0 : frame.ebx.word = VM_RPC_ENH_DATA;
1270 0 : frame.ecx.word = length;
1271 0 : frame.edx.part.low = VM_PORT_RPC;
1272 0 : frame.edx.part.high = rpc->channel;
1273 0 : frame.esi.word = rpc->cookie1;
1274 : #ifdef __amd64__
1275 0 : frame.edi.quad = (uint64_t)data;
1276 : #else
1277 : frame.edi.word = (uint32_t)data;
1278 : #endif
1279 0 : frame.ebp.word = rpc->cookie2;
1280 :
1281 0 : vm_ins(&frame);
1282 :
1283 : /* NUL-terminate the data */
1284 0 : data[length] = '\0';
1285 :
1286 0 : if (frame.ebx.word != VM_RPC_ENH_DATA) {
1287 : DPRINTF("vmware: get data failed, ebx=%08x\n",
1288 : frame.ebx.word);
1289 0 : return EIO;
1290 : }
1291 :
1292 : /* Acknowledge data received. */
1293 0 : bzero(&frame, sizeof(frame));
1294 0 : frame.eax.word = VM_MAGIC;
1295 0 : frame.ebx.word = dataid;
1296 0 : frame.ecx.part.low = VM_CMD_RPC;
1297 0 : frame.ecx.part.high = VM_RPC_GET_END;
1298 0 : frame.edx.part.low = VM_PORT_CMD;
1299 0 : frame.edx.part.high = rpc->channel;
1300 0 : frame.esi.word = rpc->cookie1;
1301 0 : frame.edi.word = rpc->cookie2;
1302 :
1303 0 : vm_cmd(&frame);
1304 :
1305 0 : if (frame.ecx.part.high == 0) {
1306 : DPRINTF("vmware: ack data failed, eax=%08x, ecx=%08x\n",
1307 : frame.eax.word, frame.ecx.word);
1308 0 : return EIO;
1309 : }
1310 :
1311 0 : return 0;
1312 0 : }
1313 :
1314 : int
1315 0 : vm_rpc_get_length(const struct vm_rpc *rpc, uint32_t *length, uint16_t *dataid)
1316 : {
1317 0 : struct vm_backdoor frame;
1318 :
1319 0 : bzero(&frame, sizeof(frame));
1320 0 : frame.eax.word = VM_MAGIC;
1321 0 : frame.ebx.word = 0;
1322 0 : frame.ecx.part.low = VM_CMD_RPC;
1323 0 : frame.ecx.part.high = VM_RPC_GET_LENGTH;
1324 0 : frame.edx.part.low = VM_PORT_CMD;
1325 0 : frame.edx.part.high = rpc->channel;
1326 0 : frame.esi.word = rpc->cookie1;
1327 0 : frame.edi.word = rpc->cookie2;
1328 :
1329 0 : vm_cmd(&frame);
1330 :
1331 0 : if ((frame.ecx.part.high & VM_RPC_REPLY_SUCCESS) == 0) {
1332 : DPRINTF("vmware: get length failed, eax=%08x, ecx=%08x\n",
1333 : frame.eax.word, frame.ecx.word);
1334 0 : return EIO;
1335 : }
1336 0 : if ((frame.ecx.part.high & VM_RPC_REPLY_DORECV) == 0) {
1337 0 : *length = 0;
1338 0 : *dataid = 0;
1339 0 : } else {
1340 0 : *length = frame.ebx.word;
1341 0 : *dataid = frame.edx.part.high;
1342 : }
1343 :
1344 0 : return 0;
1345 0 : }
1346 :
1347 : int
1348 0 : vm_rpci_response_successful(struct vmt_softc *sc)
1349 : {
1350 0 : return (sc->sc_rpc_buf[0] == '1' && sc->sc_rpc_buf[1] == ' ');
1351 : }
1352 :
1353 : int
1354 0 : vm_rpc_send_rpci_tx_buf(struct vmt_softc *sc, const uint8_t *buf,
1355 : uint32_t length)
1356 : {
1357 0 : struct vm_rpc rpci;
1358 0 : u_int32_t rlen;
1359 0 : u_int16_t ack;
1360 : int result = 0;
1361 :
1362 0 : if (vm_rpc_open(&rpci, VM_RPC_OPEN_RPCI) != 0) {
1363 : DPRINTF("%s: rpci channel open failed\n", DEVNAME(sc));
1364 0 : return EIO;
1365 : }
1366 :
1367 0 : if (vm_rpc_send(&rpci, sc->sc_rpc_buf, length) != 0) {
1368 : DPRINTF("%s: unable to send rpci command\n", DEVNAME(sc));
1369 : result = EIO;
1370 0 : goto out;
1371 : }
1372 :
1373 0 : if (vm_rpc_get_length(&rpci, &rlen, &ack) != 0) {
1374 : DPRINTF("%s: failed to get length of rpci response data\n",
1375 : DEVNAME(sc));
1376 : result = EIO;
1377 0 : goto out;
1378 : }
1379 :
1380 0 : if (rlen > 0) {
1381 0 : if (rlen >= VMT_RPC_BUFLEN) {
1382 0 : rlen = VMT_RPC_BUFLEN - 1;
1383 0 : }
1384 :
1385 0 : if (vm_rpc_get_data(&rpci, sc->sc_rpc_buf, rlen, ack) != 0) {
1386 : DPRINTF("%s: failed to get rpci response data\n",
1387 : DEVNAME(sc));
1388 : result = EIO;
1389 0 : goto out;
1390 : }
1391 : }
1392 :
1393 : out:
1394 0 : if (vm_rpc_close(&rpci) != 0) {
1395 : DPRINTF("%s: unable to close rpci channel\n", DEVNAME(sc));
1396 : }
1397 :
1398 0 : return result;
1399 0 : }
1400 :
1401 : int
1402 0 : vm_rpc_send_rpci_tx(struct vmt_softc *sc, const char *fmt, ...)
1403 : {
1404 0 : va_list args;
1405 : int len;
1406 :
1407 0 : va_start(args, fmt);
1408 0 : len = vsnprintf(sc->sc_rpc_buf, VMT_RPC_BUFLEN, fmt, args);
1409 0 : va_end(args);
1410 :
1411 0 : if (len >= VMT_RPC_BUFLEN) {
1412 : DPRINTF("%s: rpci command didn't fit in buffer\n", DEVNAME(sc));
1413 0 : return EIO;
1414 : }
1415 :
1416 0 : return vm_rpc_send_rpci_tx_buf(sc, sc->sc_rpc_buf, len);
1417 0 : }
1418 :
1419 : #if 0
1420 : struct vm_backdoor frame;
1421 :
1422 : bzero(&frame, sizeof(frame));
1423 :
1424 : frame.eax.word = VM_MAGIC;
1425 : frame.ecx.part.low = VM_CMD_GET_VERSION;
1426 : frame.edx.part.low = VM_PORT_CMD;
1427 :
1428 : printf("\n");
1429 : printf("eax 0x%08x\n", frame.eax.word);
1430 : printf("ebx 0x%08x\n", frame.ebx.word);
1431 : printf("ecx 0x%08x\n", frame.ecx.word);
1432 : printf("edx 0x%08x\n", frame.edx.word);
1433 : printf("ebp 0x%08x\n", frame.ebp.word);
1434 : printf("edi 0x%08x\n", frame.edi.word);
1435 : printf("esi 0x%08x\n", frame.esi.word);
1436 :
1437 : vm_cmd(&frame);
1438 :
1439 : printf("-\n");
1440 : printf("eax 0x%08x\n", frame.eax.word);
1441 : printf("ebx 0x%08x\n", frame.ebx.word);
1442 : printf("ecx 0x%08x\n", frame.ecx.word);
1443 : printf("edx 0x%08x\n", frame.edx.word);
1444 : printf("ebp 0x%08x\n", frame.ebp.word);
1445 : printf("edi 0x%08x\n", frame.edi.word);
1446 : printf("esi 0x%08x\n", frame.esi.word);
1447 : #endif
1448 :
1449 : /*
1450 : * Notes on tracing backdoor activity in vmware-guestd:
1451 : *
1452 : * - Find the addresses of the inl / rep insb / rep outsb
1453 : * instructions used to perform backdoor operations.
1454 : * One way to do this is to disassemble vmware-guestd:
1455 : *
1456 : * $ objdump -S /emul/freebsd/sbin/vmware-guestd > vmware-guestd.S
1457 : *
1458 : * and search for '<tab>in ' in the resulting file. The rep insb and
1459 : * rep outsb code is directly below that.
1460 : *
1461 : * - Run vmware-guestd under gdb, setting up breakpoints as follows:
1462 : * (the addresses shown here are the ones from VMware-server-1.0.10-203137,
1463 : * the last version that actually works in FreeBSD emulation on OpenBSD)
1464 : *
1465 : * break *0x805497b (address of 'in' instruction)
1466 : * commands 1
1467 : * silent
1468 : * echo INOUT\n
1469 : * print/x $ecx
1470 : * print/x $ebx
1471 : * print/x $edx
1472 : * continue
1473 : * end
1474 : * break *0x805497c (address of instruction after 'in')
1475 : * commands 2
1476 : * silent
1477 : * echo ===\n
1478 : * print/x $ecx
1479 : * print/x $ebx
1480 : * print/x $edx
1481 : * echo \n
1482 : * continue
1483 : * end
1484 : * break *0x80549b7 (address of instruction before 'rep insb')
1485 : * commands 3
1486 : * silent
1487 : * set variable $inaddr = $edi
1488 : * set variable $incount = $ecx
1489 : * continue
1490 : * end
1491 : * break *0x80549ba (address of instruction after 'rep insb')
1492 : * commands 4
1493 : * silent
1494 : * echo IN\n
1495 : * print $incount
1496 : * x/s $inaddr
1497 : * echo \n
1498 : * continue
1499 : * end
1500 : * break *0x80549fb (address of instruction before 'rep outsb')
1501 : * commands 5
1502 : * silent
1503 : * echo OUT\n
1504 : * print $ecx
1505 : * x/s $esi
1506 : * echo \n
1507 : * continue
1508 : * end
1509 : *
1510 : * This will produce a log of the backdoor operations, including the
1511 : * data sent and received and the relevant register values. You can then
1512 : * match the register values to the various constants in this file.
1513 : */
|