vmware.c 12 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515
  1. /* vim: tabstop=4 shiftwidth=4 noexpandtab
  2. * This file is part of ToaruOS and is released under the terms
  3. * of the NCSA / University of Illinois License - see LICENSE.md
  4. * Copyright (C) 2017-2018 K. Lange
  5. *
  6. * VMWare backdoor driver.
  7. *
  8. * Supports absolute mouse cursor and resolution setting.
  9. *
  10. * Mouse:
  11. * Toggle off / on with ioctl 1 and 2 respectively to /dev/vmmouse.
  12. * Supports mouse buttons, unlike the one in VirtualBox.
  13. * This device is also available by default in QEMU.
  14. *
  15. * Resolution setting:
  16. * Enabled when the "vmware" LFB driver is active. Automatically
  17. * resizes the display when the window size changes.
  18. */
  19. #include <kernel/system.h>
  20. #include <kernel/fs.h>
  21. #include <kernel/printf.h>
  22. #include <kernel/types.h>
  23. #include <kernel/logging.h>
  24. #include <kernel/module.h>
  25. #include <kernel/video.h>
  26. #include <kernel/pipe.h>
  27. #include <kernel/mouse.h>
  28. #include <kernel/args.h>
  29. #define VMWARE_MAGIC 0x564D5868 /* hXMV */
  30. #define VMWARE_PORT 0x5658
  31. #define VMWARE_PORTHB 0x5659
  32. #define PACKETS_IN_PIPE 1024
  33. #define DISCARD_POINT 32
  34. #define CMD_GETVERSION 10
  35. #define CMD_MESSAGE 30
  36. #define CMD_ABSPOINTER_DATA 39
  37. #define CMD_ABSPOINTER_STATUS 40
  38. #define CMD_ABSPOINTER_COMMAND 41
  39. #define ABSPOINTER_ENABLE 0x45414552 /* Q E A E */
  40. #define ABSPOINTER_RELATIVE 0xF5
  41. #define ABSPOINTER_ABSOLUTE 0x53424152 /* R A B S */
  42. #define MESSAGE_RPCI 0x49435052 /* R P C I */
  43. #define MESSAGE_TCLO 0x4f4c4354 /* T C L O */
  44. /* -Wpedantic complains about unnamed unions */
  45. #pragma GCC diagnostic ignored "-Wpedantic"
  46. extern void (*ps2_mouse_alternate)(void); /* modules/mouse.c */
  47. static fs_node_t * mouse_pipe;
  48. typedef struct {
  49. union {
  50. uint32_t ax;
  51. uint32_t magic;
  52. };
  53. union {
  54. uint32_t bx;
  55. size_t size;
  56. };
  57. union {
  58. uint32_t cx;
  59. uint16_t command;
  60. };
  61. union {
  62. uint32_t dx;
  63. uint16_t port;
  64. };
  65. uint32_t si;
  66. uint32_t di;
  67. } vmware_cmd;
  68. /** Low bandwidth backdoor */
  69. static void vmware_send(vmware_cmd * cmd) {
  70. cmd->magic = VMWARE_MAGIC;
  71. cmd->port = VMWARE_PORT;
  72. asm volatile("in %%dx, %0" : "+a"(cmd->ax), "+b"(cmd->bx), "+c"(cmd->cx), "+d"(cmd->dx), "+S"(cmd->si), "+D"(cmd->di));
  73. }
  74. /** Output to high bandwidth backdoor */
  75. static void vmware_send_hb(vmware_cmd * cmd) {
  76. cmd->magic = VMWARE_MAGIC;
  77. cmd->port = VMWARE_PORTHB;
  78. asm volatile("cld; rep; outsb" : "+a"(cmd->ax), "+b"(cmd->bx), "+c"(cmd->cx), "+d"(cmd->dx), "+S"(cmd->si), "+D"(cmd->di));
  79. }
  80. /** Input from high bandwidth backdoor */
  81. static void vmware_get_hb(vmware_cmd * cmd) {
  82. cmd->magic = VMWARE_MAGIC;
  83. cmd->port = VMWARE_PORTHB;
  84. asm volatile("cld; rep; insb" : "+a"(cmd->ax), "+b"(cmd->bx), "+c"(cmd->cx), "+d"(cmd->dx), "+S"(cmd->si), "+D"(cmd->di));
  85. }
  86. static void mouse_off(void) {
  87. /* Disable the absolute mouse */
  88. vmware_cmd cmd;
  89. cmd.bx = ABSPOINTER_RELATIVE;
  90. cmd.command = CMD_ABSPOINTER_COMMAND;
  91. vmware_send(&cmd);
  92. }
  93. static void mouse_absolute(void) {
  94. /*
  95. * Set the mouse to absolute.
  96. *
  97. * You can also set a relative mode, but there's not
  98. * a lot of use in that as disabling the device just
  99. * falls back to the PS/2 (or USB, I guess) device anyway,
  100. * so instead of using that we just... turn it off.
  101. */
  102. vmware_cmd cmd;
  103. /* Enable */
  104. cmd.bx = ABSPOINTER_ENABLE;
  105. cmd.command = CMD_ABSPOINTER_COMMAND;
  106. vmware_send(&cmd);
  107. /* Status */
  108. cmd.bx = 0;
  109. cmd.command = CMD_ABSPOINTER_STATUS;
  110. vmware_send(&cmd);
  111. /* Read data (1) */
  112. cmd.bx = 1;
  113. cmd.command = CMD_ABSPOINTER_DATA;
  114. vmware_send(&cmd);
  115. /* Enable absolute */
  116. cmd.bx = ABSPOINTER_ABSOLUTE;
  117. cmd.command = CMD_ABSPOINTER_COMMAND;
  118. vmware_send(&cmd);
  119. }
  120. volatile int8_t vmware_mouse_byte;
  121. static void vmware_mouse(void) {
  122. /* unused, but we need to read the fake mouse event bytes from the PS/2 device. */
  123. vmware_mouse_byte = inportb(0x60);
  124. /* Read status byte. */
  125. vmware_cmd cmd;
  126. cmd.bx = 0;
  127. cmd.command = CMD_ABSPOINTER_STATUS;
  128. vmware_send(&cmd);
  129. if (cmd.ax == 0xffff0000) {
  130. /* Device error; turn it off and back on again. */
  131. mouse_off();
  132. mouse_absolute();
  133. return;
  134. }
  135. int words = cmd.ax & 0xFFFF;
  136. if (!words || words % 4) {
  137. /* If we don't have data, or for some reason data isn't a multiple of 4... bail */
  138. return;
  139. }
  140. /* Read 4 bytes of data */
  141. cmd.bx = 4; /* how many */
  142. cmd.command = CMD_ABSPOINTER_DATA; /* read */
  143. vmware_send(&cmd);
  144. /*
  145. * I guess the flags tell you if this was relative or absolute, so if we
  146. * actually used the relative mode, we'd want to check that, but...
  147. */
  148. int flags = (cmd.ax & 0xFFFF0000) >> 16;
  149. int buttons = (cmd.ax & 0x0000FFFF);
  150. debug_print(INFO, "flags=%4x buttons=%4x", flags, buttons);
  151. debug_print(INFO, "x=%x y=%x z=%x", cmd.bx, cmd.cx, cmd.dx);
  152. unsigned int x = 0;
  153. unsigned int y = 0;
  154. if (lfb_vid_memory && lfb_resolution_x && lfb_resolution_y) {
  155. /*
  156. * Just like the virtualbox stuff, this is based on a mapping
  157. * to the display resolution, independently scaled in
  158. * each dimension...
  159. */
  160. x = ((unsigned int)cmd.bx * lfb_resolution_x) / 0xFFFF;
  161. y = ((unsigned int)cmd.cx * lfb_resolution_y) / 0xFFFF;
  162. } else {
  163. x = cmd.bx;
  164. y = cmd.cx;
  165. }
  166. mouse_device_packet_t packet;
  167. packet.magic = MOUSE_MAGIC;
  168. packet.x_difference = x;
  169. packet.y_difference = y;
  170. packet.buttons = 0;
  171. /* The particular bits for the buttons seem weird, but okay... */
  172. if (buttons & 0x20) {
  173. packet.buttons |= LEFT_CLICK;
  174. }
  175. if (buttons & 0x10) {
  176. packet.buttons |= RIGHT_CLICK;
  177. }
  178. if (buttons & 0x08) {
  179. packet.buttons |= MIDDLE_CLICK;
  180. }
  181. /* dx = z = scroll amount */
  182. if ((int8_t)cmd.dx > 0) {
  183. packet.buttons |= MOUSE_SCROLL_DOWN;
  184. } else if ((int8_t)cmd.dx < 0) {
  185. packet.buttons |= MOUSE_SCROLL_UP;
  186. }
  187. mouse_device_packet_t bitbucket;
  188. while (pipe_size(mouse_pipe) > (int)(DISCARD_POINT * sizeof(packet))) {
  189. read_fs(mouse_pipe, 0, sizeof(packet), (uint8_t *)&bitbucket);
  190. }
  191. write_fs(mouse_pipe, 0, sizeof(packet), (uint8_t *)&packet);
  192. }
  193. static int detect_device(void) {
  194. vmware_cmd cmd;
  195. /* read version */
  196. cmd.bx = ~VMWARE_MAGIC;
  197. cmd.command = CMD_GETVERSION;
  198. vmware_send(&cmd);
  199. if (cmd.bx != VMWARE_MAGIC || cmd.ax == 0xFFFFFFFF) {
  200. /* Not a vmware device... */
  201. return 0;
  202. }
  203. /* Good to go! */
  204. return 1;
  205. }
  206. static int open_msg_channel(uint32_t proto) {
  207. vmware_cmd cmd;
  208. cmd.cx = CMD_MESSAGE | 0x00000000; /* CMD_MESSAGE */
  209. cmd.bx = proto;
  210. vmware_send(&cmd);
  211. if ((cmd.cx & 0x10000) == 0) {
  212. return -1;
  213. }
  214. return cmd.dx >> 16;
  215. }
  216. static void msg_close(int channel) {
  217. vmware_cmd cmd = {0};
  218. cmd.cx = CMD_MESSAGE | 0x00060000;
  219. cmd.bx = 0;
  220. cmd.dx = channel << 16;
  221. vmware_send(&cmd);
  222. }
  223. static int open_rpci_channel(void) {
  224. return open_msg_channel(MESSAGE_RPCI);
  225. }
  226. static int tclo_channel = -1;
  227. static int open_tclo_channel(void) {
  228. if (tclo_channel != -1) {
  229. msg_close(tclo_channel);
  230. }
  231. tclo_channel = open_msg_channel(MESSAGE_TCLO);
  232. return tclo_channel;
  233. }
  234. static int msg_send(int channel, char * msg, size_t size) {
  235. {
  236. vmware_cmd cmd = {0};
  237. cmd.cx = CMD_MESSAGE | 0x00010000; /* CMD_MESSAGE size */
  238. cmd.size = size;
  239. cmd.dx = channel << 16;
  240. vmware_send(&cmd);
  241. if (size == 0) return 0;
  242. if (((cmd.cx >> 16) & 0x0081) != 0x0081) {
  243. return -2;
  244. }
  245. }
  246. {
  247. vmware_cmd cmd = {0};
  248. cmd.bx = 0x0010000;
  249. cmd.cx = size;
  250. cmd.dx = channel << 16;
  251. cmd.si = (uint32_t)msg;
  252. vmware_send_hb(&cmd);
  253. if (!(cmd.bx & 0x0010000)) {
  254. return -3;
  255. }
  256. }
  257. return 0;
  258. }
  259. static int msg_recv(int channel, char * buf, size_t bufsize) {
  260. size_t size;
  261. {
  262. vmware_cmd cmd = {0};
  263. cmd.cx = CMD_MESSAGE | 0x00030000; /* CMD_MESSAGE receive ize */
  264. cmd.dx = channel << 16;
  265. vmware_send(&cmd);
  266. size = cmd.bx;
  267. if (size == 0) return 0;
  268. if (((cmd.cx >> 16) & 0x0083) != 0x0083) {
  269. return -2;
  270. }
  271. if (size > bufsize) return -1;
  272. }
  273. {
  274. vmware_cmd cmd = {0};
  275. cmd.bx = 0x00010000;
  276. cmd.cx = size;
  277. cmd.dx = channel << 16;
  278. cmd.di = (uint32_t)buf;
  279. vmware_get_hb(&cmd);
  280. if (!(cmd.bx & 0x00010000)) {
  281. return -3;
  282. }
  283. }
  284. {
  285. vmware_cmd cmd = {0};
  286. cmd.cx = CMD_MESSAGE | 0x00050000;
  287. cmd.bx = 0x0001;
  288. cmd.dx = channel << 16;
  289. vmware_send(&cmd);
  290. }
  291. return size;
  292. }
  293. static int rpci_string(char * request) {
  294. /* Open channel */
  295. int channel = open_rpci_channel();
  296. if (channel < 0) return channel;
  297. size_t size = strlen(request) + 1;
  298. msg_send(channel, request, size);
  299. char buf[16];
  300. int recv_size = msg_recv(channel, buf, 16);
  301. msg_close(channel);
  302. if (recv_size < 0) return recv_size;
  303. return 0;
  304. }
  305. static int attempt_scale(void) {
  306. int i;
  307. int c = open_tclo_channel();
  308. if (c < 0) {
  309. return 1;
  310. }
  311. char buf[256];
  312. if ((i = msg_send(c, buf, 0)) < 0) { return 1; }
  313. int resend = 0;
  314. while (1) {
  315. i = msg_recv(c, buf, 256);
  316. if (i < 0) {
  317. return 1;
  318. } else if (i == 0) {
  319. if (resend) {
  320. if ((i = rpci_string("tools.capability.resolution_set 1")) < 0) { return 1; }
  321. if ((i = rpci_string("tools.capability.resolution_server toolbox 1")) < 0) { return 1; }
  322. if ((i = rpci_string("tools.capability.display_topology_set 1")) < 0) { return 1; }
  323. if ((i = rpci_string("tools.capability.color_depth_set 1")) < 0) { return 1; }
  324. if ((i = rpci_string("tools.capability.resolution_min 0 0")) < 0) { return 1; }
  325. if ((i = rpci_string("tools.capability.unity 1")) < 0) { return 1; }
  326. resend = 0;
  327. } else {
  328. unsigned long s, ss;
  329. relative_time(0, 10, &s, &ss);
  330. sleep_until((process_t *)current_process, s, ss);
  331. switch_task(0);
  332. }
  333. if ((i = msg_send(c, buf, 0)) < 0) { return 1; }
  334. } else {
  335. buf[i] = '\0';
  336. if (startswith(buf, "reset")) {
  337. if ((i = msg_send(c, "OK ATR toolbox", strlen("OK ATR toolbox"))) < 0) {
  338. return 1;
  339. }
  340. } else if (startswith(buf, "ping")) {
  341. if ((i = msg_send(c, "OK ", strlen("OK "))) < 0) {
  342. return 1;
  343. }
  344. } else if (startswith(buf, "Capabilities_Register")) {
  345. if ((i = msg_send(c, "OK ", strlen("OK "))) < 0) {
  346. return 1;
  347. }
  348. resend = 1;
  349. } else if (startswith(buf, "Resolution_Set")) {
  350. char * x = &buf[15];
  351. char * y = strstr(x," ");
  352. if (!y) {
  353. return 1;
  354. }
  355. *y = '\0';
  356. y++;
  357. int _x = atoi(x);
  358. int _y = atoi(y);
  359. if (lfb_resolution_x && _x && (_x != lfb_resolution_x || _y != lfb_resolution_y)) {
  360. lfb_set_resolution(_x, _y);
  361. }
  362. if ((i = msg_send(c, "OK ", strlen("OK "))) < 0) {
  363. return 1;
  364. }
  365. msg_close(c);
  366. return 0;
  367. } else {
  368. if ((i = msg_send(c, "ERROR Unknown command", strlen("ERROR Unknown command"))) < 0) {
  369. return 1;
  370. }
  371. }
  372. }
  373. }
  374. }
  375. static void vmware_resize(void * data, char * name) {
  376. while (1) {
  377. attempt_scale();
  378. unsigned long s, ss;
  379. relative_time(1, 0, &s, &ss);
  380. sleep_until((process_t *)current_process, s, ss);
  381. switch_task(0);
  382. }
  383. }
  384. static int ioctl_mouse(fs_node_t * node, int request, void * argp) {
  385. switch (request) {
  386. case 1:
  387. /* Disable */
  388. mouse_off();
  389. ps2_mouse_alternate = NULL;
  390. return 0;
  391. case 2:
  392. /* Enable */
  393. ps2_mouse_alternate = vmware_mouse;
  394. mouse_absolute();
  395. return 0;
  396. default:
  397. return -EINVAL;
  398. }
  399. }
  400. static int init(void) {
  401. if (detect_device()) {
  402. mouse_pipe = make_pipe(sizeof(mouse_device_packet_t) * PACKETS_IN_PIPE);
  403. mouse_pipe->flags = FS_CHARDEVICE;
  404. vfs_mount("/dev/vmmouse", mouse_pipe);
  405. mouse_pipe->flags = FS_CHARDEVICE;
  406. mouse_pipe->ioctl = ioctl_mouse;
  407. /*
  408. * We have a hack in the PS/2 mouse driver that lets us
  409. * take over for the normal mouse driver and essential
  410. * intercept the interrputs when they are valid.
  411. */
  412. ps2_mouse_alternate = vmware_mouse;
  413. mouse_absolute();
  414. if (lfb_driver_name && !strcmp(lfb_driver_name, "vmware") && !args_present("novmwareresset")) {
  415. create_kernel_tasklet(vmware_resize, "[vmware]", NULL);
  416. }
  417. }
  418. return 0;
  419. }
  420. static int fini(void) {
  421. return 0;
  422. }
  423. MODULE_DEF(vmmware, init, fini);
  424. MODULE_DEPENDS(ps2mouse); /* For ps2_mouse_alternate */
  425. MODULE_DEPENDS(lfbvideo); /* For lfb resolution */