123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515 |
- /* vim: tabstop=4 shiftwidth=4 noexpandtab
- * This file is part of ToaruOS and is released under the terms
- * of the NCSA / University of Illinois License - see LICENSE.md
- * Copyright (C) 2017-2018 K. Lange
- *
- * VMWare backdoor driver.
- *
- * Supports absolute mouse cursor and resolution setting.
- *
- * Mouse:
- * Toggle off / on with ioctl 1 and 2 respectively to /dev/vmmouse.
- * Supports mouse buttons, unlike the one in VirtualBox.
- * This device is also available by default in QEMU.
- *
- * Resolution setting:
- * Enabled when the "vmware" LFB driver is active. Automatically
- * resizes the display when the window size changes.
- */
- #include <kernel/system.h>
- #include <kernel/fs.h>
- #include <kernel/printf.h>
- #include <kernel/types.h>
- #include <kernel/logging.h>
- #include <kernel/module.h>
- #include <kernel/video.h>
- #include <kernel/pipe.h>
- #include <kernel/mouse.h>
- #include <kernel/args.h>
- #define VMWARE_MAGIC 0x564D5868 /* hXMV */
- #define VMWARE_PORT 0x5658
- #define VMWARE_PORTHB 0x5659
- #define PACKETS_IN_PIPE 1024
- #define DISCARD_POINT 32
- #define CMD_GETVERSION 10
- #define CMD_MESSAGE 30
- #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 */
- #define MESSAGE_RPCI 0x49435052 /* R P C I */
- #define MESSAGE_TCLO 0x4f4c4354 /* T C L O */
- /* -Wpedantic complains about unnamed unions */
- #pragma GCC diagnostic ignored "-Wpedantic"
- extern void (*ps2_mouse_alternate)(void); /* modules/mouse.c */
- static fs_node_t * mouse_pipe;
- 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;
- /** Low bandwidth backdoor */
- static 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));
- }
- /** Output to high bandwidth backdoor */
- 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));
- }
- /** Input from high bandwidth backdoor */
- 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));
- }
- static void mouse_off(void) {
- /* Disable the absolute mouse */
- vmware_cmd cmd;
- cmd.bx = ABSPOINTER_RELATIVE;
- cmd.command = CMD_ABSPOINTER_COMMAND;
- vmware_send(&cmd);
- }
- static void mouse_absolute(void) {
- /*
- * Set the mouse to absolute.
- *
- * You can also set a relative mode, but there's not
- * a lot of use in that as disabling the device just
- * falls back to the PS/2 (or USB, I guess) device anyway,
- * so instead of using that we just... turn it off.
- */
- 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);
- }
- volatile int8_t vmware_mouse_byte;
- static void vmware_mouse(void) {
- /* unused, but we need to read the fake mouse event bytes from the PS/2 device. */
- vmware_mouse_byte = inportb(0x60);
- /* Read status byte. */
- vmware_cmd cmd;
- cmd.bx = 0;
- cmd.command = CMD_ABSPOINTER_STATUS;
- vmware_send(&cmd);
- if (cmd.ax == 0xffff0000) {
- /* Device error; turn it off and back on again. */
- mouse_off();
- mouse_absolute();
- return;
- }
- int words = cmd.ax & 0xFFFF;
- if (!words || words % 4) {
- /* If we don't have data, or for some reason data isn't a multiple of 4... bail */
- return;
- }
- /* Read 4 bytes of data */
- cmd.bx = 4; /* how many */
- cmd.command = CMD_ABSPOINTER_DATA; /* read */
- vmware_send(&cmd);
- /*
- * I guess the flags tell you if this was relative or absolute, so if we
- * actually used the relative mode, we'd want to check that, but...
- */
- int flags = (cmd.ax & 0xFFFF0000) >> 16;
- int buttons = (cmd.ax & 0x0000FFFF);
- debug_print(INFO, "flags=%4x buttons=%4x", flags, buttons);
- debug_print(INFO, "x=%x y=%x z=%x", cmd.bx, cmd.cx, cmd.dx);
- unsigned int x = 0;
- unsigned int y = 0;
- if (lfb_vid_memory && lfb_resolution_x && lfb_resolution_y) {
- /*
- * Just like the virtualbox stuff, this is based on a mapping
- * to the display resolution, independently scaled in
- * each dimension...
- */
- x = ((unsigned int)cmd.bx * lfb_resolution_x) / 0xFFFF;
- y = ((unsigned int)cmd.cx * lfb_resolution_y) / 0xFFFF;
- } else {
- x = cmd.bx;
- y = cmd.cx;
- }
- mouse_device_packet_t packet;
- packet.magic = MOUSE_MAGIC;
- packet.x_difference = x;
- packet.y_difference = y;
- packet.buttons = 0;
- /* The particular bits for the buttons seem weird, but okay... */
- if (buttons & 0x20) {
- packet.buttons |= LEFT_CLICK;
- }
- if (buttons & 0x10) {
- packet.buttons |= RIGHT_CLICK;
- }
- if (buttons & 0x08) {
- packet.buttons |= MIDDLE_CLICK;
- }
- /* dx = z = scroll amount */
- if ((int8_t)cmd.dx > 0) {
- packet.buttons |= MOUSE_SCROLL_DOWN;
- } else if ((int8_t)cmd.dx < 0) {
- packet.buttons |= MOUSE_SCROLL_UP;
- }
- mouse_device_packet_t bitbucket;
- while (pipe_size(mouse_pipe) > (int)(DISCARD_POINT * sizeof(packet))) {
- read_fs(mouse_pipe, 0, sizeof(packet), (uint8_t *)&bitbucket);
- }
- write_fs(mouse_pipe, 0, sizeof(packet), (uint8_t *)&packet);
- }
- static int detect_device(void) {
- vmware_cmd cmd;
- /* read version */
- cmd.bx = ~VMWARE_MAGIC;
- cmd.command = CMD_GETVERSION;
- vmware_send(&cmd);
- if (cmd.bx != VMWARE_MAGIC || cmd.ax == 0xFFFFFFFF) {
- /* Not a vmware device... */
- return 0;
- }
- /* Good to go! */
- return 1;
- }
- static int open_msg_channel(uint32_t proto) {
- vmware_cmd cmd;
- cmd.cx = CMD_MESSAGE | 0x00000000; /* CMD_MESSAGE */
- cmd.bx = proto;
- vmware_send(&cmd);
- if ((cmd.cx & 0x10000) == 0) {
- return -1;
- }
- return cmd.dx >> 16;
- }
- static void msg_close(int channel) {
- vmware_cmd cmd = {0};
- cmd.cx = CMD_MESSAGE | 0x00060000;
- cmd.bx = 0;
- cmd.dx = channel << 16;
- vmware_send(&cmd);
- }
- static int open_rpci_channel(void) {
- return open_msg_channel(MESSAGE_RPCI);
- }
- static int tclo_channel = -1;
- static int open_tclo_channel(void) {
- if (tclo_channel != -1) {
- msg_close(tclo_channel);
- }
- tclo_channel = open_msg_channel(MESSAGE_TCLO);
- return tclo_channel;
- }
- static int msg_send(int channel, char * msg, size_t size) {
- {
- vmware_cmd cmd = {0};
- cmd.cx = CMD_MESSAGE | 0x00010000; /* CMD_MESSAGE size */
- cmd.size = size;
- cmd.dx = channel << 16;
- vmware_send(&cmd);
- if (size == 0) return 0;
- if (((cmd.cx >> 16) & 0x0081) != 0x0081) {
- return -2;
- }
- }
- {
- vmware_cmd cmd = {0};
- cmd.bx = 0x0010000;
- cmd.cx = size;
- cmd.dx = channel << 16;
- cmd.si = (uint32_t)msg;
- vmware_send_hb(&cmd);
- if (!(cmd.bx & 0x0010000)) {
- return -3;
- }
- }
- return 0;
- }
- static int msg_recv(int channel, char * buf, size_t bufsize) {
- size_t size;
- {
- vmware_cmd cmd = {0};
- cmd.cx = CMD_MESSAGE | 0x00030000; /* CMD_MESSAGE receive ize */
- cmd.dx = channel << 16;
- vmware_send(&cmd);
- size = cmd.bx;
- if (size == 0) return 0;
- if (((cmd.cx >> 16) & 0x0083) != 0x0083) {
- return -2;
- }
- if (size > bufsize) return -1;
- }
- {
- vmware_cmd cmd = {0};
- cmd.bx = 0x00010000;
- cmd.cx = size;
- cmd.dx = channel << 16;
- cmd.di = (uint32_t)buf;
- vmware_get_hb(&cmd);
- if (!(cmd.bx & 0x00010000)) {
- return -3;
- }
- }
- {
- vmware_cmd cmd = {0};
- cmd.cx = CMD_MESSAGE | 0x00050000;
- cmd.bx = 0x0001;
- cmd.dx = channel << 16;
- vmware_send(&cmd);
- }
- return size;
- }
- static int rpci_string(char * request) {
- /* Open channel */
- int channel = open_rpci_channel();
- if (channel < 0) return channel;
- size_t size = strlen(request) + 1;
- msg_send(channel, request, size);
- char buf[16];
- int recv_size = msg_recv(channel, buf, 16);
- msg_close(channel);
- if (recv_size < 0) return recv_size;
- return 0;
- }
- static int attempt_scale(void) {
- int i;
- int c = open_tclo_channel();
- if (c < 0) {
- return 1;
- }
- char buf[256];
- if ((i = msg_send(c, buf, 0)) < 0) { return 1; }
- int resend = 0;
- while (1) {
- i = msg_recv(c, buf, 256);
- if (i < 0) {
- return 1;
- } else if (i == 0) {
- if (resend) {
- if ((i = rpci_string("tools.capability.resolution_set 1")) < 0) { return 1; }
- if ((i = rpci_string("tools.capability.resolution_server toolbox 1")) < 0) { return 1; }
- if ((i = rpci_string("tools.capability.display_topology_set 1")) < 0) { return 1; }
- if ((i = rpci_string("tools.capability.color_depth_set 1")) < 0) { return 1; }
- if ((i = rpci_string("tools.capability.resolution_min 0 0")) < 0) { return 1; }
- if ((i = rpci_string("tools.capability.unity 1")) < 0) { return 1; }
- resend = 0;
- } else {
- unsigned long s, ss;
- relative_time(0, 10, &s, &ss);
- sleep_until((process_t *)current_process, s, ss);
- switch_task(0);
- }
- if ((i = msg_send(c, buf, 0)) < 0) { return 1; }
- } else {
- buf[i] = '\0';
- if (startswith(buf, "reset")) {
- if ((i = msg_send(c, "OK ATR toolbox", strlen("OK ATR toolbox"))) < 0) {
- return 1;
- }
- } else if (startswith(buf, "ping")) {
- if ((i = msg_send(c, "OK ", strlen("OK "))) < 0) {
- return 1;
- }
- } else if (startswith(buf, "Capabilities_Register")) {
- if ((i = msg_send(c, "OK ", strlen("OK "))) < 0) {
- return 1;
- }
- resend = 1;
- } else if (startswith(buf, "Resolution_Set")) {
- char * x = &buf[15];
- char * y = strstr(x," ");
- if (!y) {
- return 1;
- }
- *y = '\0';
- y++;
- int _x = atoi(x);
- int _y = atoi(y);
- if (lfb_resolution_x && _x && (_x != lfb_resolution_x || _y != lfb_resolution_y)) {
- lfb_set_resolution(_x, _y);
- }
- if ((i = msg_send(c, "OK ", strlen("OK "))) < 0) {
- return 1;
- }
- msg_close(c);
- return 0;
- } else {
- if ((i = msg_send(c, "ERROR Unknown command", strlen("ERROR Unknown command"))) < 0) {
- return 1;
- }
- }
- }
- }
- }
- static void vmware_resize(void * data, char * name) {
- while (1) {
- attempt_scale();
- unsigned long s, ss;
- relative_time(1, 0, &s, &ss);
- sleep_until((process_t *)current_process, s, ss);
- switch_task(0);
- }
- }
- static int ioctl_mouse(fs_node_t * node, int request, void * argp) {
- switch (request) {
- case 1:
- /* Disable */
- mouse_off();
- ps2_mouse_alternate = NULL;
- return 0;
- case 2:
- /* Enable */
- ps2_mouse_alternate = vmware_mouse;
- mouse_absolute();
- return 0;
- default:
- return -EINVAL;
- }
- }
- static int init(void) {
- if (detect_device()) {
- mouse_pipe = make_pipe(sizeof(mouse_device_packet_t) * PACKETS_IN_PIPE);
- mouse_pipe->flags = FS_CHARDEVICE;
- vfs_mount("/dev/vmmouse", mouse_pipe);
- mouse_pipe->flags = FS_CHARDEVICE;
- mouse_pipe->ioctl = ioctl_mouse;
- /*
- * We have a hack in the PS/2 mouse driver that lets us
- * take over for the normal mouse driver and essential
- * intercept the interrputs when they are valid.
- */
- ps2_mouse_alternate = vmware_mouse;
- mouse_absolute();
- if (lfb_driver_name && !strcmp(lfb_driver_name, "vmware") && !args_present("novmwareresset")) {
- create_kernel_tasklet(vmware_resize, "[vmware]", NULL);
- }
- }
- return 0;
- }
- static int fini(void) {
- return 0;
- }
- MODULE_DEF(vmmware, init, fini);
- MODULE_DEPENDS(ps2mouse); /* For ps2_mouse_alternate */
- MODULE_DEPENDS(lfbvideo); /* For lfb resolution */
|