diff options
| -rw-r--r-- | dlog.h | 66 | ||||
| -rw-r--r-- | dosmain.c | 138 | ||||
| -rw-r--r-- | dostsr.c | 220 | ||||
| -rw-r--r-- | dostsr.h | 31 | ||||
| -rw-r--r-- | makefile | 4 | ||||
| -rw-r--r-- | ps2.h | 8 | ||||
| -rw-r--r-- | vbox.h | 4 | ||||
| -rw-r--r-- | vboxlog.h | 12 | ||||
| -rw-r--r-- | vmware.h | 174 | 
9 files changed, 575 insertions, 82 deletions
| @@ -20,16 +20,67 @@  #ifndef DLOG_H  #define DLOG_H -#define ENABLE_DLOG 1 +#include <conio.h> + +// Customizable defines +/** If 0, these routines become nops */ +#define ENABLE_DLOG 0 +/** 1 means target serial port, 0 means target IO port. */ +#define DLOG_TARGET_SERIAL 1 +/** IO port to target. + *  VirtualBox uses 0x504, Bochs, DOSBox and descendants use 0xE9. + *  When using DLOG_TARGET_SERIAL, use desired UART IO base port. (e.g. COM1 = 0x3F8). */ +#define DLOG_TARGET_PORT 0x3f8 + +// End of customizable defines  #if ENABLE_DLOG -#if 1 -#include "vboxlog.h" -#define dlog_putc vbox_log_putc -#endif +/** Initializes the debug log port. */ +static void dlog_init(); + +/** Logs a single character to the debug message IO port. */ +static inline void dlog_putc(char c); + +#if DLOG_TARGET_SERIAL + +static void dlog_init() +{ +	// Comes straight from https://wiki.osdev.org/Serial_Ports#Initialization +	outp(DLOG_TARGET_PORT + 1, 0x00);    // Disable all interrupts +	outp(DLOG_TARGET_PORT + 3, 0x80);    // Enable DLAB (set baud rate divisor) +	outp(DLOG_TARGET_PORT + 0, 0x03);    // Set divisor to 3 (lo byte) 38400 baud +	outp(DLOG_TARGET_PORT + 1, 0x00);    //                  (hi byte) +	outp(DLOG_TARGET_PORT + 3, 0x03);    // 8 bits, no parity, one stop bit +	outp(DLOG_TARGET_PORT + 2, 0xC7);    // Enable FIFO, clear them, with 14-byte threshold +	outp(DLOG_TARGET_PORT + 4, 0x03);    // RTS/DSR set, IRQs disabled +} + +static inline void dlog_putc(char c) +{ +	while (!(inp(DLOG_TARGET_PORT + 5) & 0x20)); +	outp(DLOG_TARGET_PORT, c); +} + +#else /* DLOG_TARGET_SERIAL */ -#define dlog_endline() dlog_putc('\n') +static void dlog_init() +{ +} + +static inline void dlog_putc(char c) +{ +	outp(DLOG_TARGET_PORT, c); +} + + + +#endif /* DLOG_TARGET_SERIAL */ + +static void dlog_endline(void) +{ +	dlog_putc('\n'); +}  /** Print string to log */  static void dlog_print(const char *s) @@ -105,8 +156,9 @@ static void dlog_printd(int num)  	dlog_printdb(num, 10);  } -#else +#else /* ENABLE_DLOG */ +#define dlog_init()  #define dlog_putc(c)  #define dlog_endline()  #define dlog_print(s) @@ -27,6 +27,7 @@  #include "int33.h"  #include "ps2.h"  #include "vbox.h" +#include "vmware.h"  #include "dostsr.h"  static void detect_wheel(LPTSRDATA data) @@ -53,7 +54,7 @@ static int set_wheel(LPTSRDATA data, bool enable)  }  #if USE_VIRTUALBOX -static int set_integration(LPTSRDATA data, bool enable) +static int set_virtualbox_integration(LPTSRDATA data, bool enable)  {  	if (enable) {  		int err; @@ -100,38 +101,141 @@ static int set_integration(LPTSRDATA data, bool enable)  	return 0;  } -static int set_host_cursor(LPTSRDATA data, bool enable) +static int set_virtualbox_host_cursor(LPTSRDATA data, bool enable)  { -	if (data->vbavail) { -		printf("Setting host cursor to %s\n", enable ? "enabled" : "disabled"); -		data->vbwantcursor = enable; +	printf("Setting host cursor to %s\n", enable ? "enabled" : "disabled"); +	data->vbwantcursor = enable; + +	return 0; +} +#endif + +#if USE_VMWARE +static int set_vmware_integration(LPTSRDATA data, bool enable) +{ +	if (enable) { +		int32_t version; +		uint32_t status; +		uint16_t data_avail; + +		data->vmwavail = false; + +		version = vmware_get_version(); +		if (version < 0) { +			fprintf(stderr, "Could not detect VMware, err=%ld\n", version); +			return -1; +		} + +		printf("Found VMware protocol version %ld\n", version); + +		vmware_abspointer_cmd(VMWARE_ABSPOINTER_CMD_ENABLE); + +		status = vmware_abspointer_status(); +		if ((status & VMWARE_ABSPOINTER_STATUS_MASK_ERROR) +		        == VMWARE_ABSPOINTER_STATUS_MASK_ERROR) { +			fprintf(stderr, "VMware absolute pointer error, err=0x%lx\n", +			        status & VMWARE_ABSPOINTER_STATUS_MASK_ERROR); +			return -1; +		} + +		vmware_abspointer_data_clear(); + +		// TSR part will enable the absolute mouse when reset + +		printf("VMware integration enabled\n"); +		data->vmwavail = true;  	} else { -		printf("VirtualBox integration is not available\n"); +		if (data->vmwavail) { +			vmware_abspointer_cmd(VMWARE_ABSPOINTER_CMD_REQUEST_RELATIVE); +			vmware_abspointer_cmd(VMWARE_ABSPOINTER_CMD_DISABLE); + +			data->vmwavail = false; +			printf("Disabled VMware integration\n"); +		} else { +			printf("VMware integration already disabled or not available\n"); +		}  	}  	return 0;  }  #endif +static int set_integration(LPTSRDATA data, bool enable) +{ +	if (enable) { +		int err = -1; + +#if USE_VIRTUALBOX +		// First check if we can enable the VirtualBox integration, +		// since it's a PCI device it's easier to check if it's not present +		err = set_virtualbox_integration(data, true); +		if (!err) return 0; +#endif + +#if USE_VMWARE +		// Afterwards try VMWare integration +		err = set_vmware_integration(data, true); +		if (!err) return 0; +#endif + +		printf("Neither VirtualBox nor VMware integration available\n"); +		return err; +	} else { +#if USE_VIRTUALBOX +		if (data->vbavail) { +			set_virtualbox_integration(data, false); +		} +#endif +#if USE_VMWARE +		if (data->vmwavail) { +			set_vmware_integration(data, false); +		} +#endif +		return 0; +	} +} + +static int set_host_cursor(LPTSRDATA data, bool enable) +{ +#if USE_VIRTUALBOX +	if (data->vbavail) { +		return set_virtualbox_host_cursor(data, enable); +	} +#endif +	printf("VirtualBox integration not available\n"); +	return -1; +} +  static int configure_driver(LPTSRDATA data)  {  	int err; -	// First check for PS/2 mouse availability +	// Configure the debug logging port +	dlog_init(); + +	// Check for PS/2 mouse BIOS availability  	if ((err = ps2m_init(PS2M_PACKET_SIZE_PLAIN))) {  		fprintf(stderr, "Cannot init PS/2 mouse BIOS, err=%d\n", err);  		// Can't do anything without PS/2  		return err;  	} +#if USE_WHEEL  	// Let's utilize the wheel by default  	data->usewheel = true;  	detect_wheel(data); +#else +	data->usewheel = false; +#endif + +#if USE_INTEGRATION +	// Enable integration by default +	set_integration(data, true); +#endif  #if USE_VIRTUALBOX -	// Assume initially that we want integration and host cursor -	 set_integration(data, true); -	 data->vbwantcursor = data->vbavail; +	// Assume initially that we want host cursor +	data->vbwantcursor = data->vbavail;  #endif  	return 0; @@ -156,7 +260,7 @@ static int install_driver(LPTSRDATA data)  	data->prev_int33_handler = _dos_getvect(0x33);  	_dos_setvect(0x33, int33_isr); -#if USE_INT2F +#if USE_WIN386  	data->prev_int2f_handler = _dos_getvect(0x2f);  	_dos_setvect(0x2f, int2f_isr);  #endif @@ -177,7 +281,7 @@ static bool check_if_driver_uninstallable(LPTSRDATA data)  		return true;  	} -#if USE_INT2F +#if USE_WIN386  	{  		void (__interrupt __far *cur_int2f_handler)() = _dos_getvect(0x2f);  		void (__interrupt __far *our_int2f_handler)() = MK_FP(FP_SEG(data), FP_OFF(int2f_isr)); @@ -210,7 +314,7 @@ static int uninstall_driver(LPTSRDATA data)  {  	_dos_setvect(0x33, data->prev_int33_handler); -#if USE_INT2F +#if USE_WIN386  	_dos_setvect(0x2f, data->prev_int2f_handler);  #endif @@ -243,8 +347,10 @@ static void print_help(void)  	    "Supported actions:\n"  	    "\tinstall           install the driver (default)\n"  	    "\tuninstall         uninstall the driver from memory\n" +#if USE_WHEEL  	    "\twheel <ON|OFF>    enable/disable wheel API support\n" -#if USE_VIRTUALBOX +#endif +#if USE_INTEGRATION  	    "\tinteg <ON|OFF>    enable/disable virtualbox integration\n"  	    "\thostcur <ON|OFF>  enable/disable mouse cursor rendering in host\n"  #endif @@ -301,6 +407,7 @@ int main(int argc, const char *argv[])  			return EXIT_FAILURE;  		}  		return uninstall_driver(data); +#if USE_WHEEL  	} else if (stricmp(argv[argi], "wheel") == 0) {  		bool enable = true; @@ -312,7 +419,8 @@ int main(int argc, const char *argv[])  		}  		return set_wheel(data, enable); -#if USE_VIRTUALBOX +#endif +#if USE_INTEGRATION  	} else if (stricmp(argv[argi], "integ") == 0) {  		bool enable = true; @@ -26,6 +26,8 @@  #include "int10vga.h"  #include "int2fwin.h"  #include "int33.h" +#include "vbox.h" +#include "vmware.h"  #include "dostsr.h"  #define MSB_MASK 0x8000U @@ -303,7 +305,7 @@ static void refresh_cursor(void)  	bool should_show = data.visible_count >= 0;  	bool pos_changed, needs_refresh; -#if USE_INT2F +#if USE_WIN386  	// Windows 386 is already rendering the cursor for us.  	// Hide our own.  	if (data.w386cursor) should_show = false; @@ -646,58 +648,180 @@ static void handle_mouse_event(uint16_t buttons, bool absolute, int x, int y, in  }  /** PS/2 BIOS calls this routine to notify mouse events. */ -static void __far ps2_mouse_callback(uint8_t status, uint8_t x, uint8_t y, uint8_t z) +static void __far ps2_mouse_handler(uint16_t word1, uint16_t word2, uint16_t word3, uint16_t word4)  { -#pragma aux (PS2_CB) ps2_mouse_callback +#pragma aux ps2_mouse_handler "*" parm caller [ax] [bx] [cx] [dx] modify [ax bx cx dx si di es fs gs] -	int sx =   status & PS2M_STATUS_X_NEG ? 0xFF00 | x : x; -	int sy = -(status & PS2M_STATUS_Y_NEG ? 0xFF00 | y : y); -	int sz = z; +	unsigned status; +	int x, y, z;  	bool abs = false;  #if TRACE_EVENTS -	dlog_print("ps2 callback status="); -	dlog_printx(status); -	dlog_print(" sx="); -	dlog_printd(sx); -	dlog_print(" sy="); -	dlog_printd(sy); -	dlog_print(" sz="); -	dlog_printd(z); +	dlog_print("ps2 callback "); +	dlog_printx(word1); +	dlog_putc(' '); +	dlog_printx(word2); +	dlog_putc(' '); +	dlog_printx(word3); +	dlog_putc(' '); +	dlog_printx(word4);  	dlog_endline(); -#endif +#endif /* TRACE_EVENTS */ + +	// Decode the PS2 event args +	// In a normal IBM PS/2 BIOS (incl. VirtualBox/Bochs/qemu/SeaBIOS): +	//  word1 low byte = status (following PS2M_STATUS_*) +	//  word2 low byte = x +	//  word3 low byte = y +	//  word4 = always zero +	// In a PS/2 BIOS with wheel support (incl. VMware/DOSBox-X): +	// taken from CuteMouse/KoKo: +	//  word1 high byte = x +	//  word1 low byte = status +	//  word2 low byte = y +	//  word3 low byte = z +	//  word4 = always zero +	// VirtualBox/Bochs/qemu/SeaBIOS behave like a normal one, +	// but they also store the raw contents of all mouse packets in the EBDA (starting 0x28 = packet 0). +	// Other BIOSes don't do that so it is not a reliable option either. +	// So, how to detect which BIOS we have? +	// For now we are always assuming "normal" PS/2 BIOS. +	// But with VirtualBox integration on we'll get the wheel packet from the EBDA, +	// and with VMWare integration on we'll get it from the VMware protocol. +	status = (uint8_t) word1; +	x = (uint8_t) word2; +	y = (uint8_t) word3; +	z = 0; +	(void) word4; + +	// Sign-extend X, Y as per the status byte +	x =   status & PS2M_STATUS_X_NEG ? 0xFF00 | x : x; +	y = -(status & PS2M_STATUS_Y_NEG ? 0xFF00 | y : y);  #if USE_VIRTUALBOX  	if (data.vbavail) {  		uint16_t vbx, vby;  		if ((vbox_get_mouse(&data.vb, &abs, &vbx, &vby) == 0) && abs) { -			sx = scaleu(vbx, 0xFFFFU, MAX(data.max.x, data.screen_max.x)); -			sy = scaleu(vby, 0xFFFFU, MAX(data.max.y, data.screen_max.y)); +			// VirtualBox gives unsigned coordinates from 0...0xFFFFU, +			// scale to 0..screen_size (in pixels). +			// If the user is using a window larger than the screen, use it. +			x = scaleu(vbx, 0xFFFFU, MAX(data.max.x, data.screen_max.x)); +			y = scaleu(vby, 0xFFFFU, MAX(data.max.y, data.screen_max.y));  			data.vbhaveabs = true;  		} else {  			// VirtualBox does not support absolute coordinates,  			// or user has disabled them.  			data.vbhaveabs = false; +			// Rely on PS/2 relative coordinates. +		} + +		// VirtualBox/Bochs BIOS does not pass wheel data to the callback, +		// so we will fetch it directly from the BIOS data segment. +		if (data.haswheel) { +			int8_t __far * mouse_packet = MK_FP(bda_get_ebda_segment(), 0x28); +			z = mouse_packet[3];  		}  	} +#endif /* USE_VIRTUALBOX */ + +#if USE_VMWARE +	if (data.vmwavail) { +		uint32_t vmwstatus = vmware_abspointer_status(); +		uint16_t data_avail = vmwstatus & VMWARE_ABSPOINTER_STATUS_MASK_DATA; + +#if TRACE_EVENTS +		dlog_print("vmware status=0x"); +		dlog_printx(vmwstatus >> 16); +		dlog_print(" "); +		dlog_printx(vmwstatus & 0xFFFF); +		dlog_endline(); +#endif + +		if (data_avail >= VMWARE_ABSPOINTER_DATA_PACKET_SIZE) { +			struct vmware_abspointer_data vmw; +			vmware_abspointer_data(VMWARE_ABSPOINTER_DATA_PACKET_SIZE, &vmw); + +#if TRACE_EVENTS +			dlog_print("vmware pstatus=0x"); +			dlog_printx(status); +			dlog_print(" x=0x"); +			dlog_printx(vmw.x); +			dlog_print(" z="); +			dlog_printd((int8_t) (uint8_t) vmw.z); +			dlog_endline();  #endif -	// VirtualBox/Bochs BIOS does not pass wheel data to the callback, -	// so we will fetch it directly from the BIOS data segment. -	if (data.haswheel && !sz) { -		int8_t __far * mouse_packet = MK_FP(bda_get_ebda_segment(), 0x28); -		sz = mouse_packet[3]; +			if (vmw.status & VMWARE_ABSPOINTER_STATUS_RELATIVE) { +				x = (int16_t) vmw.x; +				y = (int16_t) vmw.y; +				z = (int8_t) (uint8_t) vmw.z; +			} else { +				abs = true; +				x = scaleu(vmw.x & 0xFFFFU, 0xFFFFU, +				            MAX(data.max.x, data.screen_max.x)); +				y = scaleu(vmw.y & 0xFFFFU, 0xFFFFU, +				            MAX(data.max.y, data.screen_max.y)); +				z = (int8_t) (uint8_t) vmw.z; +			} + +			if (vmw.status & VMWARE_ABSPOINTER_STATUS_BUTTON_LEFT) { +				status |= PS2M_STATUS_BUTTON_1; +			} +			if (vmw.status & VMWARE_ABSPOINTER_STATUS_BUTTON_RIGHT) { +				status |= PS2M_STATUS_BUTTON_2; +			} +			if (vmw.status & VMWARE_ABSPOINTER_STATUS_BUTTON_MIDDLE) { +				status |= PS2M_STATUS_BUTTON_3; +			} +		} else { +			return; // Ignore the PS/2 packet otherwise, it is likely garbage +		}  	} +#endif /* USE_VMWARE */  	handle_mouse_event(status & (PS2M_STATUS_BUTTON_1 | PS2M_STATUS_BUTTON_2 | PS2M_STATUS_BUTTON_3), -	                   abs, sx, sy, sz); +	                   abs, x, y, z);  } -#if USE_VIRTUALBOX -static void enable_vbox_absolute(bool enable) +void __declspec(naked) __far ps2_mouse_callback()  { -	data.vbhaveabs = false; +	__asm { +		pusha +		push ds +		push es +		push fs +		push gs +		; 8 + 4 saved registers, 24 bytes +		; plus 4 bytes for retf address +		; = 28 bytes of stack before callback args + +		mov bp, sp +		push cs +		pop ds + +		mov	ax,[bp+28+6]	; Status +		mov	bx,[bp+28+4]	; X +		mov	cx,[bp+28+2]	; Y +		mov	dx,[bp+28+0]	; Z + +		call ps2_mouse_handler + +		pop gs +		pop fs +		pop es +		pop ds +		popa + +		retf +	} +} + +#if USE_INTEGRATION +static void set_absolute(bool enable) +{ +#if USE_VIRTUALBOX +	data.vbhaveabs = false;  	if (data.vbavail) {  		int err = vbox_set_mouse(&data.vb, enable, false);  		if (enable && !err) { @@ -707,19 +831,34 @@ static void enable_vbox_absolute(bool enable)  			dlog_puts("VBox absolute mouse disabled");  		}  	} +#endif /* USE_VIRTUALBOX */ +#if USE_VMWARE +	if (data.vmwavail) { +		if (enable) { +			uint16_t data_avail; +			// It looks like a reset of the PS/2 mouse completely disables +			// the vmware interface, so we have to reenable it from scratch. +			vmware_abspointer_cmd(VMWARE_ABSPOINTER_CMD_ENABLE); +			vmware_abspointer_data_clear(); +			vmware_abspointer_cmd(VMWARE_ABSPOINTER_CMD_REQUEST_ABSOLUTE); + +			dlog_puts("VMware absolute mouse enabled"); +		} else { +			vmware_abspointer_cmd(VMWARE_ABSPOINTER_CMD_REQUEST_RELATIVE); +			vmware_abspointer_cmd(VMWARE_ABSPOINTER_CMD_DISABLE); + +			dlog_puts("VMware absolute mouse disabled"); +		} +	} +#endif /* USE_VMWARE */  } -#endif +#endif /* USE_INTEGRATION */  static void reset_mouse_hardware()  {  	ps2m_enable(false); -#if USE_VIRTUALBOX -	// By default, enable the integration -	enable_vbox_absolute(true); -	load_cursor(); -#endif - +#if USE_WHEEL  	if (data.usewheel && ps2m_detect_wheel()) {  		// Detect wheel also reinitializes the mouse to the proper packet size  		data.haswheel = true; @@ -728,6 +867,10 @@ static void reset_mouse_hardware()  		data.haswheel = false;  		ps2m_init(PS2M_PACKET_SIZE_PLAIN);  	} +#else +	data.haswheel = false; +	ps2m_init(PS2M_PACKET_SIZE_PLAIN); +#endif  	ps2m_set_resolution(3);     // 3 = 200 dpi, 8 counts per millimeter  	ps2m_set_sample_rate(4);    // 4 = 80 reports per second @@ -735,6 +878,13 @@ static void reset_mouse_hardware()  	ps2m_set_callback(ps2_mouse_callback); +#if USE_INTEGRATION +	// By default, enable absolute mouse +	set_absolute(true); +	// Reload hardware cursor +	load_cursor(); +#endif +  	ps2m_enable(true);  } @@ -813,7 +963,7 @@ static void return_clear_button_counter(union INTPACK __far *r, struct buttoncou  /** Entry point for our int33 API. */  static void int33_handler(union INTPACK r) -#pragma aux int33_handler "*" parm caller [] modify [ax bx cx dx es] +#pragma aux int33_handler "*" parm caller [] modify [ax bx cx dx si di es fs gs]  {  	switch (r.x.ax) {  	case INT33_RESET_MOUSE: @@ -1062,7 +1212,7 @@ void __declspec(naked) __far int33_isr(void)  	}  } -#if USE_INT2F +#if USE_WIN386  /** Windows will call this function to notify events when we are inside a DOS box. */  static void windows_mouse_handler(int action, int x, int y, int buttons, int events)  #pragma aux windows_mouse_handler "*" parm [ax] [bx] [cx] [dx] [si] modify [ax bx cx dx es] @@ -23,14 +23,26 @@  #include <stdbool.h>  #include <stdint.h> -#include "vbox.h"  #include "int2fwin.h"  #include "int10vga.h" +// User customizable defines + +/** Enable the VirtualBox integration */  #define USE_VIRTUALBOX 1 -#define USE_INT2F 1 +/** Enable the VMware integration */ +#define USE_VMWARE 1 +/** Enable the Windows 386/protected mode integration */ +#define USE_WIN386 1 +/** Enable the wheel. */ +#define USE_WHEEL 1 +/** Trace events verbosily */  #define TRACE_EVENTS 0 +// End of user customizable defines + +#define USE_INTEGRATION (USE_VIRTUALBOX || USE_VMWARE) +  #define NUM_BUTTONS 3  #define GRAPHIC_CURSOR_WIDTH 16 @@ -40,10 +52,14 @@  #define GRAPHIC_CURSOR_DATA_LEN (2 * GRAPHIC_CURSOR_MASK_LEN)  #define VERSION_MAJOR 0 -#define VERSION_MINOR 3 +#define VERSION_MINOR 4  #define REPORTED_VERSION_MAJOR 8  #define REPORTED_VERSION_MINOR 0x20 +#if USE_VIRTUALBOX +#include "vbox.h" +#endif +  struct point {  	int16_t x, y;  }; @@ -52,7 +68,7 @@ typedef struct tsrdata {  	// TSR installation data  	/** Previous int33 ISR, storing it for uninstall. */  	void (__interrupt __far *prev_int33_handler)(); -#if USE_INT2F +#if USE_WIN386  	void (__interrupt __far *prev_int2f_handler)();  #endif  	/** Whether to enable & use wheel mouse. */ @@ -139,7 +155,7 @@ typedef struct tsrdata {  	/** Events for which we should call the event handler. */  	uint16_t event_mask; -#if USE_INT2F +#if USE_WIN386  	/** Information that we pass to Windows 386 on startup. */  	win386_startup_info w386_startup;  	win386_instance_item w386_instance[2]; @@ -157,6 +173,11 @@ typedef struct tsrdata {  	bool vbhaveabs : 1;  	struct vboxcomm vb;  #endif + +#if USE_VMWARE +	/** VMware is available. */ +	bool vmwavail : 1; +#endif  } TSRDATA;  typedef TSRDATA * PTSRDATA; @@ -2,7 +2,7 @@  # Assuming you have sourced `owsetenv` beforehand.  #   dosobjs = dostsr.obj dosmain.obj vbox.obj -doscflags = -bt=dos -ms -6 -os -oi -w3 +doscflags = -bt=dos -ms -6 -os -oi -w3 -wcd=202  # -ms to use small memory model (though sometimes ss != ds...)  # -os to optimize for size  # -oi to put intrinsics online (don't use them much) @@ -11,7 +11,7 @@ dostsrcflags = -zu -s -g=RES_GROUP -nd=RES -nt=RES_TEXT -nc=RES_CODE  # -zu since ss != ds on the TSR  w16objs = w16mouse.obj -w16cflags = -bt=windows -bd -mc -zu -s -6 -oi -w3 +w16cflags = -bt=windows -bd -mc -zu -s -6 -oi -w3 -wcd=202  # -bd to build DLL  # -mc to use compact memory model (far data pointers, ss != ds always)  # -zu for DLL calling convention (ss != ds) @@ -80,12 +80,8 @@ enum ps2m_sample_rate {  	PS2M_SAMPLE_RATE_200 = 6  }; -#pragma aux PS2_CB far loadds parm reverse caller [] -// TODO: ax and es look not be preserved with this. VBox BIOS already preserves them though, as well as 32-bit registers - -/** Invoked by the BIOS when there is a mouse event. - *  @param status combination of PS2M_STATUS_* flags */ -typedef void (__far * LPFN_PS2CALLBACK)(uint8_t status, uint8_t x, uint8_t y, uint8_t z); +/** Invoked by the BIOS when there is a mouse event. */ +typedef void (__far * LPFN_PS2CALLBACK)();  static ps2m_err ps2m_init(uint8_t packet_size);  #pragma aux ps2m_init = \ @@ -20,6 +20,8 @@  #ifndef VBOX_H  #define VBOX_H +#pragma off (unreferenced) +  #include <stdbool.h>  #include <stdint.h>  #include <string.h> @@ -155,4 +157,6 @@ static inline unsigned vbox_req_mouse_pointer_size(unsigned width, unsigned heig  	return MAX(sizeof(VMMDevReqMousePointer), 24 + 20 + data_size);  } +#pragma pop (unreferenced) +  #endif diff --git a/vboxlog.h b/vboxlog.h deleted file mode 100644 index 559566b..0000000 --- a/vboxlog.h +++ /dev/null @@ -1,12 +0,0 @@ -#ifndef VBOXDLOG_H -#define VBOXDLOG_H - -#include <conio.h> - -/** Logs a single character to the VBox debug message port. */ -static inline void vbox_log_putc(char c) -{ -	outp(0x504, c); -} - -#endif // VBOXDLOG_H diff --git a/vmware.h b/vmware.h new file mode 100644 index 0000000..8cb8f0f --- /dev/null +++ b/vmware.h @@ -0,0 +1,174 @@ +/* + * VBMouse - VMware communication routines + * Copyright (C) 2022 Javier S. Pedro + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA. + */ + +#ifndef VMWARE_H +#define VMWARE_H + +#include <stdint.h> + +/* Ideas from https://wiki.osdev.org/VMware_tools */ + +#define VMWARE_MAGIC 0x564D5868UL +#define VMWARE_PORT  0x5658 + +enum vmware_cmds { +	VMWARE_CMD_GETVERSION          = 10, +	VMWARE_CMD_ABSPOINTER_DATA     = 39, +	VMWARE_CMD_ABSPOINTER_STATUS   = 40, +	VMWARE_CMD_ABSPOINTER_COMMAND  = 41 +}; + +enum vmware_abspointer_cmds { +	VMWARE_ABSPOINTER_CMD_ENABLE           = 0x45414552UL, +	VMWARE_ABSPOINTER_CMD_DISABLE          = 0x000000f5UL, +	VMWARE_ABSPOINTER_CMD_REQUEST_RELATIVE = 0x4c455252UL, +	VMWARE_ABSPOINTER_CMD_REQUEST_ABSOLUTE = 0x53424152UL, +}; + +enum vmware_abspointer_status { +	VMWARE_ABSPOINTER_STATUS_MASK_ERROR    = 0xFFFF0000UL, +	VMWARE_ABSPOINTER_STATUS_MASK_DATA     = 0x0000FFFFUL +}; + +enum vmware_abspointer_data_status { +	VMWARE_ABSPOINTER_STATUS_RELATIVE      = 0x00010000U, +	VMWARE_ABSPOINTER_STATUS_BUTTON_LEFT   = 0x20, +	VMWARE_ABSPOINTER_STATUS_BUTTON_RIGHT  = 0x10, +	VMWARE_ABSPOINTER_STATUS_BUTTON_MIDDLE = 0x08, +}; + +struct vmware_call_regs { +	union { +		uint32_t eax; +		uint32_t magic; +	}; +	union { +		uint32_t ebx; +		uint32_t size; +	}; +	union { +		uint32_t ecx; +		uint32_t command; +	}; +	union { +		uint32_t edx; +		uint32_t port; +	}; +}; + +struct vmware_abspointer_data { +	uint32_t status; +	int32_t x; +	int32_t y; +	int32_t z; +}; + +#define VMWARE_ABSPOINTER_DATA_PACKET_SIZE 4 + +static inline void vmware_call(struct vmware_call_regs __far * regs); +#pragma aux vmware_call = \ +	"pushad" /* First make a copy of all 32-bit registers, to avoid clobbering them. */ \ +	"mov eax, es:[di + 4*0]" \ +	"mov ebx, es:[di + 4*1]" \ +	"mov ecx, es:[di + 4*2]" \ +	"mov edx, es:[di + 4*3]" \ +	"in eax, dx"  /* This performs the actual backdoor call. */ \ +	"mov es:[di + 4*0], eax" \ +	"mov es:[di + 4*1], ebx" \ +	"mov es:[di + 4*2], ecx" \ +	"mov es:[di + 4*3], edx" \ +	"popad" \ +	__parm [es di] \ +	__modify [] + +/** Checks the presence of the VMware backdoor by comparing magic numbers. + *  @return detected version of the vmware protocol (0-6), -1 if not detected. */ +static int32_t vmware_get_version(void) +{ +	struct vmware_call_regs regs; +	regs.magic = VMWARE_MAGIC; +	regs.port = VMWARE_PORT; +	regs.command = VMWARE_CMD_GETVERSION; +	regs.ebx = ~VMWARE_MAGIC; +	vmware_call(®s); +	if (regs.ebx != VMWARE_MAGIC) { +		return -1; +	} +	return regs.eax; +} + +/** Sends a command to the VMware absolute pointing interface. + *  @param cmd see VMWARE_ABSPOINTER_CMD_*. */ +static void vmware_abspointer_cmd(uint32_t cmd) +{ +	struct vmware_call_regs regs; +	regs.magic = VMWARE_MAGIC; +	regs.port = VMWARE_PORT; +	regs.command = VMWARE_CMD_ABSPOINTER_COMMAND; +	regs.ebx = cmd; +	vmware_call(®s); +} + +/** Gets the status reg from the VMware absolute pointing interface. + *  @return status + *     VMWARE_ABSPOINTER_STATUS_MASK_ERROR bits: error flags. + *     VMWARE_ABSPOINTER_STATUS_MASK_DATA bits: amount of data available to read. */ +static uint32_t vmware_abspointer_status(void) +{ +	struct vmware_call_regs regs; +	regs.magic = VMWARE_MAGIC; +	regs.port = VMWARE_PORT; +	regs.command = VMWARE_CMD_ABSPOINTER_STATUS; +	regs.ebx = 0; +	vmware_call(®s); +	return regs.eax; +} + +/** Reads data from the VMware absolute pointing interface. + *  @param size amount of data to read (value between 1 and 4) + *    ensure it is less than the amount of data available, + *    qemu disconnects us if we read more than we can. + *  @return status + *     VMWARE_ABSPOINTER_STATUS_MASK_ERROR bits: error flags. + *     VMWARE_ABSPOINTER_STATUS_MASK_DATA bits: amount of data available to read. */ +static void vmware_abspointer_data(unsigned size, struct vmware_abspointer_data __far * data) +{ +	union u { +		struct vmware_call_regs regs; +		struct vmware_abspointer_data data; +	} __far *p = (union u __far *) data; +	p->regs.magic = VMWARE_MAGIC; +	p->regs.port = VMWARE_PORT; +	p->regs.command = VMWARE_CMD_ABSPOINTER_DATA; +	p->regs.size = size; +	vmware_call(&p->regs); +} + +/** Reads (and discards) all available data in the VMware absolute pointing interface. */ +static void vmware_abspointer_data_clear(void) +{ +	uint16_t data_avail; +	// Drop any data in the queue +	while ((data_avail = vmware_abspointer_status())) { +		struct vmware_abspointer_data data; +		vmware_abspointer_data(MIN(4, data_avail), &data); +	} +} + +#endif // VMWARE_H | 
