From 51cdb6b523be973e802cc940c6ac54cc8a50b802 Mon Sep 17 00:00:00 2001 From: Javier Date: Tue, 5 Apr 2022 22:24:47 +0200 Subject: add vmware support, debug logging to serial port, and fix ps2 routine clobbering registers --- dlog.h | 66 +++++++++++++++++-- dosmain.c | 138 ++++++++++++++++++++++++++++++++++----- dostsr.c | 220 ++++++++++++++++++++++++++++++++++++++++++++++++++++---------- dostsr.h | 31 +++++++-- makefile | 4 +- ps2.h | 8 +-- vbox.h | 4 ++ vboxlog.h | 12 ---- vmware.h | 174 +++++++++++++++++++++++++++++++++++++++++++++++++ 9 files changed, 575 insertions(+), 82 deletions(-) delete mode 100644 vboxlog.h create mode 100644 vmware.h diff --git a/dlog.h b/dlog.h index 6556089..3f853e7 100644 --- a/dlog.h +++ b/dlog.h @@ -20,16 +20,67 @@ #ifndef DLOG_H #define DLOG_H -#define ENABLE_DLOG 1 +#include + +// 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) diff --git a/dosmain.c b/dosmain.c index 1e0b072..0ac5f75 100644 --- a/dosmain.c +++ b/dosmain.c @@ -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 enable/disable wheel API support\n" -#if USE_VIRTUALBOX +#endif +#if USE_INTEGRATION "\tinteg enable/disable virtualbox integration\n" "\thostcur 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; diff --git a/dostsr.c b/dostsr.c index 0bed693..3d9066e 100644 --- a/dostsr.c +++ b/dostsr.c @@ -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] diff --git a/dostsr.h b/dostsr.h index 7f1200b..5aae98c 100644 --- a/dostsr.h +++ b/dostsr.h @@ -23,14 +23,26 @@ #include #include -#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; diff --git a/makefile b/makefile index f91b552..81c51cd 100644 --- a/makefile +++ b/makefile @@ -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) diff --git a/ps2.h b/ps2.h index 717ab84..ffcf7bb 100644 --- a/ps2.h +++ b/ps2.h @@ -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 = \ diff --git a/vbox.h b/vbox.h index 6bb8797..42b3c39 100644 --- a/vbox.h +++ b/vbox.h @@ -20,6 +20,8 @@ #ifndef VBOX_H #define VBOX_H +#pragma off (unreferenced) + #include #include #include @@ -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 - -/** 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 + +/* 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 -- cgit v1.2.3