Browse Source

Experimental vmware display resizing

K. Lange 1 year ago
parent
commit
37e30140ec
2 changed files with 284 additions and 52 deletions
  1. 19 11
      boot/cstuff.c
  2. 265 41
      modules/vmware.c

+ 19 - 11
boot/cstuff.c

@@ -135,9 +135,21 @@ int kmain() {
 			"Enable integration with VirtualBox, including",
 			"automatic mode setting and absolute mouse pointer.");
 
-	BOOT_OPTION(_vmware,      1, "VMWare mouse driver",
-			"Enable the VMware / QEMU absolute mouse pointer.",
-			NULL);
+	BOOT_OPTION(_vboxrects,   0, "VirtualBox Seamless support",
+			"(Requires Guest Additions) Enables support for the",
+			"Seamless Desktop mode in VirtualBox.");
+
+	BOOT_OPTION(_vboxpointer, 1, "VirtualBox Pointer",
+			"(Requires Guest Additions) Enables support for the",
+			"VirtualBox hardware pointer mapping.");
+
+	BOOT_OPTION(_vmware,      1, "VMWare driver",
+			"Enable the VMware / QEMU absolute mouse pointer,",
+			"and optional guest scaling.");
+
+	BOOT_OPTION(_vmwareres,   1, "VMware guest size",
+			"(Requires VMware driver) Enables support for",
+			"automatically setting display size in VMware");
 
 	BOOT_OPTION(_sound,       1, "Audio drivers",
 			"Enable the audio subsystem and AC'97 drivers.",
@@ -159,14 +171,6 @@ int kmain() {
 			"Downloads a userspace filesystem from a remote",
 			"server and extracts it at boot.");
 
-	BOOT_OPTION(_vboxrects,   0, "VirtualBox Seamless support",
-			"(Requires Guest Additions) Enables support for the",
-			"Seamless Desktop mode in VirtualBox.");
-
-	BOOT_OPTION(_vboxpointer, 1, "VirtualBox Pointer",
-			"(Requires Guest Additions) Enables support for the",
-			"VirtualBox hardware pointer mapping.");
-
 #ifdef EFI_PLATFORM
 	BOOT_OPTION(_efilargest,  0, "Prefer largest mode.",
 			"When using EFI mode setting, use the largest mode.",
@@ -237,6 +241,10 @@ int kmain() {
 		strcat(cmdline, "novboxpointer ");
 	}
 
+	if (_vmware && !_vmwareres) {
+		strcat(cmdline, "novmwareresset ");
+	}
+
 	/* Configure modules */
 	if (!_normal_ata) {
 		modules[6] = "NONE";

+ 265 - 41
modules/vmware.c

@@ -23,8 +23,9 @@
 #include <kernel/mouse.h>
 #include <kernel/args.h>
 
-#define VMWARE_MAGIC 0x564D5868
-#define VMWARE_PORT  0x5658
+#define VMWARE_MAGIC  0x564D5868
+#define VMWARE_PORT   0x5658
+#define VMWARE_PORTHB 0x5659
 
 #define PACKETS_IN_PIPE 1024
 #define DISCARD_POINT 32
@@ -58,32 +59,15 @@ typedef struct {
 } vmware_cmd;
 
 static void vmware_io(vmware_cmd * cmd) {
-	uint32_t dummy;
-
-	/* Now how's THAT for a VM backdoor... */
-
-	asm volatile(
-			"pushl %%ebx\n"
-			"pushl %%eax\n"
-			"movl 20(%%eax), %%edi\n" /* Load data into registers */
-			"movl 16(%%eax), %%esi\n"
-			"movl 12(%%eax), %%edx\n"
-			"movl  8(%%eax), %%ecx\n"
-			"movl  4(%%eax), %%ebx\n"
-			"movl   (%%eax), %%eax\n"
-			"inl %%dx, %%eax\n"       /* Then trip a magic i/o port */
-			"xchgl %%eax, (%%esp)\n"
-			"movl %%edi, 20(%%eax)\n" /* Data also comes back out by registers */
-			"movl %%esi, 16(%%eax)\n"
-			"movl %%edx, 12(%%eax)\n"
-			"movl %%ecx,  8(%%eax)\n"
-			"movl %%ebx,  4(%%eax)\n"
-			"popl (%%eax)\n"
-			"popl %%ebx\n"
-			: "=a"(dummy)
-			: "0"(cmd)
-			: "ecx", "edx", "esi", "edi", "memory" /* And vmware / qemu could trash anything they desire... */
-	);
+	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_io_hb_out(vmware_cmd * cmd) {
+	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_io_hb_in(vmware_cmd * cmd) {
+	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 vmware_send(vmware_cmd * cmd) {
@@ -93,6 +77,20 @@ static void vmware_send(vmware_cmd * cmd) {
 	vmware_io(cmd);
 }
 
+static void vmware_send_hb(vmware_cmd * cmd) {
+	cmd->magic = VMWARE_MAGIC;
+	cmd->port = VMWARE_PORTHB;
+
+	vmware_io_hb_out(cmd);
+}
+
+static void vmware_get_hb(vmware_cmd * cmd) {
+	cmd->magic = VMWARE_MAGIC;
+	cmd->port = VMWARE_PORTHB;
+
+	vmware_io_hb_in(cmd);
+}
+
 static void mouse_on(void) {
 	vmware_cmd cmd;
 
@@ -244,21 +242,243 @@ static int detect_device(void) {
 	return 1;
 }
 
-static int ioctl_mouse(fs_node_t * node, int request, void * argp) {
-	if (request == 1) {
-		/* Disable */
-		mouse_off();
-		ps2_mouse_alternate = NULL;
-		return 0;
+
+static int open_msg_channel(uint32_t proto) {
+	vmware_cmd cmd;
+	cmd.cx = 30 | 0x00000000; /* CMD_MESSAGE */
+	cmd.bx = proto;
+	vmware_send(&cmd);
+
+	if ((cmd.cx & 0x10000) == 0) {
+		return -1;
 	}
-	if (request == 2) {
-		/* Enable */
-		ps2_mouse_alternate = vmware_mouse;
-		mouse_on();
-		mouse_absolute();
-		return 0;
+
+	return cmd.dx >> 16;
+}
+
+static void msg_close(int channel) {
+	vmware_cmd cmd = {0};
+	cmd.cx = 30 | 0x00060000;
+	cmd.bx = 0;
+	cmd.dx = channel << 16;
+
+	vmware_send(&cmd);
+}
+
+static int open_rpci_channel(void) {
+	return open_msg_channel(0x49435052);
+}
+
+static int tclo_channel = 0;
+
+static int open_tclo_channel(void) {
+	if (tclo_channel) {
+		msg_close(tclo_channel);
+	}
+	tclo_channel = open_msg_channel(0x4f4c4354);
+	return tclo_channel;
+}
+
+static int msg_send(int channel, char * msg, size_t size) {
+	{
+		vmware_cmd cmd = {0};
+		cmd.cx = 30 | 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 = 30 | 0x00030000; /* CMD_MESSAGE receive ize */
+		cmd.dx   = channel << 16;
+		vmware_send(&cmd);
+
+#if 0
+		if ((cmd.dx >> 16) != 0x0001) {
+			return -4;
+		}
+#endif
+
+		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 = 30 | 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_on();
+			mouse_absolute();
+			return 0;
+		default:
+			return -EINVAL;
 	}
-	return -1;
 }
 
 static int init(void) {
@@ -282,6 +502,10 @@ static int init(void) {
 		mouse_on();
 		mouse_absolute();
 
+		if (lfb_driver_name && !strcmp(lfb_driver_name, "vmware") && !args_present("novmwareresset")) {
+			create_kernel_tasklet(vmware_resize, "[vmware]", NULL);
+		}
+
 	}
 
 	return 0;