aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorJavier <dev.git@javispedro.com>2022-02-28 02:37:56 +0000
committerJavier <dev.git@javispedro.com>2022-02-28 02:37:56 +0000
commitb649293e1f0b8ad13aeb4c662a4eeae78634c129 (patch)
tree06ac8552550341b1b03c297111104aee7c70b033
parentdee9ca901fd3f2edec7af8f82300257b285067b0 (diff)
downloadvbmouse-b649293e1f0b8ad13aeb4c662a4eeae78634c129.tar.gz
vbmouse-b649293e1f0b8ad13aeb4c662a4eeae78634c129.zip
use virtual DMA service on windows with paging enabled
-rw-r--r--makefile2
-rw-r--r--mousew16.c12
-rw-r--r--vbox.c219
-rw-r--r--vbox.h6
-rw-r--r--vds.h125
5 files changed, 267 insertions, 97 deletions
diff --git a/makefile b/makefile
index 1366864..9cd59d6 100644
--- a/makefile
+++ b/makefile
@@ -6,7 +6,7 @@
set include=$(%watcom)/h/win;$(%watcom)/h
# The main driver file
-vbmouse.drv: mousew16.c mousew16.h vbox.c vbox.h vboxdev.h ps2.h pci.h
+vbmouse.drv: mousew16.c mousew16.h vbox.c vbox.h vboxdev.h ps2.h pci.h vds.h
# -bd to build DLL
# -mc to use compact memory model (far data pointers, since ss != ds)
# -zu for DLL calling convention (ss != ds)
diff --git a/mousew16.c b/mousew16.c
index 94bd309..c017133 100644
--- a/mousew16.c
+++ b/mousew16.c
@@ -123,7 +123,6 @@ BOOL FAR PASCAL LibMain(HINSTANCE hInstance, WORD wDataSegment,
// However we will check whether VirtualBox exists:
if (vbox_init() == 0) {
vbox_logs("VirtualBox found\n");
- vbox_report_guest_info(VBOXOSTYPE_Win31);
// VirtualBox connection was succesful, remember that
mouseflags |= MOUSEFLAGS_HAS_VBOX;
@@ -171,11 +170,16 @@ VOID FAR PASCAL Enable(LPFN_MOUSEEVENT lpEventProc)
#if ENABLE_VBOX
if (mouseflags & MOUSEFLAGS_HAS_VBOX) {
- vbox_init_callbacks();
+ if ((err = vbox_alloc_buffers())) {
+ vbox_logs("VBox alloc failure\n");
+ return;
+ }
+
+ vbox_report_guest_info(VBOXOSTYPE_Win31);
if ((err = vbox_set_mouse(true))) {
vbox_logs("VBox enable failure\n");
- vbox_deinit_callbacks();
+ vbox_free_buffers();
return;
}
@@ -199,7 +203,7 @@ VOID FAR PASCAL Disable(VOID)
#if ENABLE_VBOX
if (mouseflags & MOUSEFLAGS_VBOX_ENABLED) {
vbox_set_mouse(false);
- vbox_deinit_callbacks();
+ vbox_free_buffers();
vbox_logs("VBOX Disabled!\n");
mouseflags &= ~MOUSEFLAGS_VBOX_ENABLED;
}
diff --git a/vbox.c b/vbox.c
index 042a470..456877b 100644
--- a/vbox.c
+++ b/vbox.c
@@ -19,11 +19,14 @@
#include <windows.h>
#include <i86.h>
+#include <string.h>
#include "pci.h"
+#include "vds.h"
#include "vboxdev.h"
#include "vbox.h"
+// PCI device information
/** VBox's PCI bus, device number, etc. */
static pcisel vbpci;
/** IO base of VBox's PCI device. */
@@ -31,14 +34,16 @@ static uint16_t vbiobase;
/** IRQ number of VBox's PCI device. Unused. */
static uint8_t vbirq;
-/** Keep a pre-allocated VBox VMMDevRequest struct for the benefit of the callbacks,
- which may be called in a IRQ handler where I'd prefer to avoid making API calls. */
-static VMMDevReqMouseStatus __far * reqMouse;
-static HANDLE reqMouseH;
-static uint32_t reqMouseAddr;
+/** Handle to fixed buffer used for communication with VBox device.
+ * We also use Virtual DMA Service to obtain the physical address of this buffer,
+ * so it must remain fixed in memory. */
+static HANDLE hBuf;
+static LPVOID pBuf;
+/** The DMA descriptor used by VDS corresponding to the above buffer. */
+static VDS_DDS bufdds;
/** Actually send a request to the VirtualBox VMM device.
- * @param addr 32-bit linear address containing the VMMDevRequest struct.
+ * @param addr 32-bit physical address containing the VMMDevRequest struct.
*/
static void vbox_send_request(uint32_t addr);
#pragma aux vbox_send_request = \
@@ -61,7 +66,7 @@ static uint32_t vbox_irq_ack();
"in eax, dx" \
"mov edx, eax" \
"shr edx, 16" \
- __value [dx ax] \
+ __value [dx ax]
// Classic PCI defines
#define VBOX_PCI_VEND_ID 0x80ee
@@ -73,21 +78,6 @@ enum {
CFG_BAR1 = 0x14, /* DWord */
};
-/** Gets the 32-bit linear address of a 16:16 far pointer.
- Uses GetSelectorBase() since we are supposed to work under protected mode. */
-static inline uint32_t get_physical_addr(void far *obj)
-{
- return GetSelectorBase(FP_SEG(obj)) + FP_OFF(obj);
-}
-
-/** Send a request to the VirtualBox VMM device at vbiobase.
- * @param request must be a VMMDevRequest (e.g. VMMDevReqMouseStatus). */
-static inline void vbox_request(void far *request)
-{
- uint32_t addr = get_physical_addr(request);
- vbox_send_request(addr);
-}
-
/** Finds the VirtualBox PCI device and reads the current IO base.
* @returns 0 if the device was found. */
int vbox_init(void)
@@ -126,56 +116,118 @@ int vbox_init(void)
return 0;
}
+/** Allocates the buffers that will be used to communicate with the VirtualBox device. */
+int vbox_alloc_buffers(void)
+{
+ const unsigned int bufferSize = 48; // This should be the largest of all VMMDevRequest* structs that we can send
+ int err;
+
+ // Allocate the buffer
+ hBuf = GlobalAlloc(GMEM_FIXED|GMEM_SHARE, bufferSize);
+ if (!hBuf) return -1;
+
+ // Keep it in memory at a fixed location
+ GlobalFix(hBuf);
+ GlobalPageLock(hBuf);
+
+ // Get the usable pointer / logical address of the buffer
+ pBuf = (LPVOID) GlobalLock(hBuf);
+
+ if (vds_available()) {
+ // Use the Virtual DMA Service to get the physical address of this buffer
+ bufdds.regionSize = bufferSize;
+ bufdds.segOrSelector = FP_SEG(pBuf);
+ bufdds.offset = FP_OFF(pBuf);
+ bufdds.bufferId = 0;
+ bufdds.physicalAddress = 0;
+
+ err = vds_lock_dma_buffer_region(&bufdds, VDS_NO_AUTO_ALLOC | VDS_NO_AUTO_REMAP);
+ } else {
+ bufdds.regionSize = 0; // Indicates we don't have to unlock this later on
+
+ // If VDS is not available, assume we are not paging
+ // Just use the linear address as physical
+ bufdds.physicalAddress = GetSelectorBase(FP_SEG(pBuf)) + FP_OFF(pBuf);
+ err = 0;
+ }
+
+ return err;
+}
+
+/** Frees the buffers allocated by vbox_alloc_buffers(). */
+int vbox_free_buffers(void)
+{
+ if (bufdds.regionSize > 0) {
+ vds_unlock_dma_buffer_region(&bufdds, 0);
+ }
+ bufdds.regionSize = 0;
+ bufdds.segOrSelector = 0;
+ bufdds.offset = 0;
+ bufdds.bufferId = 0;
+ bufdds.physicalAddress = 0;
+ GlobalFree(hBuf);
+ hBuf = NULL;
+ pBuf = NULL;
+
+ return 0;
+}
+
/** Lets VirtualBox know that there are VirtualBox Guest Additions on this guest.
* @param osType os installed on this guest. */
int vbox_report_guest_info(uint32_t osType)
{
- VMMDevReportGuestInfo req = {0};
+ VMMDevReportGuestInfo *req = pBuf;
+
+ memset(req, 0, sizeof(VMMDevReportGuestInfo));
- req.header.size = sizeof(req);
- req.header.version = VMMDEV_REQUEST_HEADER_VERSION;
- req.header.requestType = VMMDevReq_ReportGuestInfo;
- req.header.rc = -1;
- req.guestInfo.interfaceVersion = VMMDEV_VERSION;
- req.guestInfo.osType = osType;
+ req->header.size = sizeof(VMMDevReportGuestInfo);
+ req->header.version = VMMDEV_REQUEST_HEADER_VERSION;
+ req->header.requestType = VMMDevReq_ReportGuestInfo;
+ req->header.rc = -1;
+ req->guestInfo.interfaceVersion = VMMDEV_VERSION;
+ req->guestInfo.osType = osType;
- vbox_request(&req);
+ vbox_send_request(bufdds.physicalAddress);
- return req.header.rc;
+ return req->header.rc;
}
/** Tells VirtualBox about the events we are interested in receiving.
These events are notified via the PCI IRQ which we are not using, so this is not used either. */
int vbox_set_filter_mask(uint32_t add, uint32_t remove)
{
- VMMDevCtlGuestFilterMask req = {0};
+ VMMDevCtlGuestFilterMask *req = pBuf;
- req.header.size = sizeof(req);
- req.header.version = VMMDEV_REQUEST_HEADER_VERSION;
- req.header.requestType = VMMDevReq_CtlGuestFilterMask;
- req.header.rc = -1;
- req.u32OrMask = add;
- req.u32NotMask = remove;
+ memset(req, 0, sizeof(VMMDevCtlGuestFilterMask));
- vbox_request(&req);
+ req->header.size = sizeof(VMMDevCtlGuestFilterMask);
+ req->header.version = VMMDEV_REQUEST_HEADER_VERSION;
+ req->header.requestType = VMMDevReq_CtlGuestFilterMask;
+ req->header.rc = -1;
+ req->u32OrMask = add;
+ req->u32NotMask = remove;
- return req.header.rc;
+ vbox_send_request(bufdds.physicalAddress);
+
+ return req->header.rc;
}
/** Tells VirtualBox whether we want absolute mouse information or not. */
int vbox_set_mouse(bool enable)
{
- VMMDevReqMouseStatus req = {0};
+ VMMDevReqMouseStatus *req = pBuf;
+
+ memset(req, 0, sizeof(VMMDevReqMouseStatus));
- req.header.size = sizeof(req);
- req.header.version = VMMDEV_REQUEST_HEADER_VERSION;
- req.header.requestType = VMMDevReq_SetMouseStatus;
- req.header.rc = -1;
- req.mouseFeatures = enable ? VMMDEV_MOUSE_GUEST_CAN_ABSOLUTE : 0;
+ req->header.size = sizeof(VMMDevReqMouseStatus);
+ req->header.version = VMMDEV_REQUEST_HEADER_VERSION;
+ req->header.requestType = VMMDevReq_SetMouseStatus;
+ req->header.rc = -1;
+ req->mouseFeatures = enable ? VMMDEV_MOUSE_GUEST_CAN_ABSOLUTE : 0;
- vbox_request(&req);
+ vbox_send_request(bufdds.physicalAddress);
- return req.header.rc;
+ return req->header.rc;
}
/** Gets the current absolute mouse position from VirtualBox.
@@ -183,57 +235,46 @@ int vbox_set_mouse(bool enable)
* in which case we should fallback to PS/2 relative events. */
int vbox_get_mouse(bool *abs, uint16_t *xpos, uint16_t *ypos)
{
- VMMDevReqMouseStatus req = {0};
+ VMMDevReqMouseStatus *req = pBuf;
- req.header.size = sizeof(req);
- req.header.version = VMMDEV_REQUEST_HEADER_VERSION;
- req.header.requestType = VMMDevReq_GetMouseStatus;
- req.header.rc = -1;
+ memset(req, 0, sizeof(VMMDevReqMouseStatus));
- vbox_request(&req);
+ req->header.size = sizeof(VMMDevReqMouseStatus);
+ req->header.version = VMMDEV_REQUEST_HEADER_VERSION;
+ req->header.requestType = VMMDevReq_GetMouseStatus;
+ req->header.rc = -1;
- *abs = req.mouseFeatures & VMMDEV_MOUSE_HOST_WANTS_ABSOLUTE;
- *xpos = req.pointerXPos;
- *ypos = req.pointerYPos;
+ vbox_send_request(bufdds.physicalAddress);
- return req.header.rc;
-}
+ *abs = req->mouseFeatures & VMMDEV_MOUSE_HOST_WANTS_ABSOLUTE;
+ *xpos = req->pointerXPos;
+ *ypos = req->pointerYPos;
-/** Just allocates the memory used by vbox_get_mouse_locked below. */
-void vbox_init_callbacks(void)
-{
- reqMouseH = GlobalAlloc(GMEM_FIXED|GMEM_SHARE, sizeof(VMMDevReqMouseStatus));
- reqMouse = (LPVOID) GlobalLock(reqMouseH);
- reqMouseAddr = get_physical_addr(reqMouse);
-}
-
-void vbox_deinit_callbacks(void)
-{
- GlobalFree(reqMouseH);
- reqMouse = NULL;
- reqMouseAddr = 0;
+ return req->header.rc;
}
#pragma code_seg ( "CALLBACKS" )
-/** This is a version of vbox_get_mouse() that does not call any other functions. */
+/** This is a version of vbox_get_mouse() that does not call any other functions,
+ * and may be called inside an interrupt handler. */
int vbox_get_mouse_locked(bool *abs, uint16_t *xpos, uint16_t *ypos)
{
- // Note that this may be called with interrupts disabled
- reqMouse->header.size = sizeof(VMMDevReqMouseStatus);
- reqMouse->header.version = VMMDEV_REQUEST_HEADER_VERSION;
- reqMouse->header.requestType = VMMDevReq_GetMouseStatus;
- reqMouse->header.rc = -1;
- reqMouse->mouseFeatures = 0;
- reqMouse->pointerXPos = 0;
- reqMouse->pointerYPos = 0;
-
- vbox_send_request(reqMouseAddr);
-
- *abs = reqMouse->mouseFeatures & VMMDEV_MOUSE_HOST_WANTS_ABSOLUTE;
- *xpos = reqMouse->pointerXPos;
- *ypos = reqMouse->pointerYPos;
-
- return reqMouse->header.rc;
+ VMMDevReqMouseStatus *req = pBuf;
+
+ req->header.size = sizeof(VMMDevReqMouseStatus);
+ req->header.version = VMMDEV_REQUEST_HEADER_VERSION;
+ req->header.requestType = VMMDevReq_GetMouseStatus;
+ req->header.rc = -1;
+ req->mouseFeatures = 0;
+ req->pointerXPos = 0;
+ req->pointerYPos = 0;
+
+ vbox_send_request(bufdds.physicalAddress);
+
+ *abs = req->mouseFeatures & VMMDEV_MOUSE_HOST_WANTS_ABSOLUTE;
+ *xpos = req->pointerXPos;
+ *ypos = req->pointerYPos;
+
+ return req->header.rc;
}
diff --git a/vbox.h b/vbox.h
index c711dd1..8fbd80e 100644
--- a/vbox.h
+++ b/vbox.h
@@ -55,6 +55,9 @@ static void vbox_logs(const char __far *str);
extern int vbox_init(void);
+extern int vbox_alloc_buffers(void);
+extern int vbox_free_buffers(void);
+
extern int vbox_report_guest_info(uint32_t osType);
extern int vbox_set_filter_mask(uint32_t add, uint32_t remove);
@@ -63,9 +66,6 @@ extern int vbox_set_mouse(bool enable);
extern int vbox_get_mouse(bool *abs, uint16_t *xpos, uint16_t *ypos);
-extern void vbox_init_callbacks();
-extern void vbox_deinit_callbacks();
-
// In CALLBACKS segment:
extern int vbox_get_mouse_locked(bool *abs, uint16_t *xpos, uint16_t *ypos);
diff --git a/vds.h b/vds.h
new file mode 100644
index 0000000..d09c375
--- /dev/null
+++ b/vds.h
@@ -0,0 +1,125 @@
+/*
+ * VBMouse - Interface to the Virtual DMA Service (VDS)
+ * 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 VDS_H
+#define VDS_H
+
+#include <stdbool.h>
+#include <stdint.h>
+
+typedef unsigned char vdserr;
+enum {
+ VDS_SUCCESSFUL = 0,
+ VDS_REGION_NOT_CONTIGUOUS = 1,
+ VDS_REGION_NOT_ALIGNED = 2,
+ VDS_UNABLE_TO_LOCK = 3,
+ VDS_NO_BUFFER_AVAIL = 4,
+ VDS_REGION_TOO_LARGE = 5,
+ VDS_BUFFER_IN_USE = 6,
+ VDS_INVALID_REGION = 7,
+ VDS_REGION_NOT_LOCKED = 8,
+ VDS_TOO_MANY_PAGES = 9,
+ VDS_INVALID_BUFFER_ID = 0xA,
+ VDS_BUFFER_BOUNDARY = 0xB,
+ VDS_INVALID_DMA_CHANNEL = 0xC,
+ VDS_DISABLE_COUNT_OVRFLOW = 0xD,
+ VDS_DISABLE_COUNT_UNDFLOW = 0xE,
+ VDS_NOT_SUPPORTED = 0xF,
+ VDS_FLAGS_NOT_SUPPORTED = 0x10,
+};
+
+enum {
+/*
+ Bit 1 = Automatically copy to/from buffer
+ Bit 2 = Disable automatic buffer allocation
+ Bit 3 = Disable automatic remap feature
+ Bit 4 = Region must not cross 64K physical alignment boundary
+ Bit 5 = Region must not cross 128K physical alignment boundary
+ Bit 6 = Copy page-table for scatter/gather remap
+ Bit 7 = Allow non-present pages for scatter/gather remap
+*/
+ VDS_AUTO_COPY_DATA = 1 << 1,
+ VDS_NO_AUTO_ALLOC = 1 << 2,
+ VDS_NO_AUTO_REMAP = 1 << 3,
+ VDS_ALIGN_64K = 1 << 4,
+ VDS_ALIGN_128K = 1 << 5
+};
+
+/** DMA Descriptor structure. Describes a potentially DMA-lockable buffer. */
+typedef _Packed struct VDS_DDS
+{
+ /** Size of this buffer. */
+ uint32_t regionSize;
+ /** Logical/segmented address of this buffer, offset part */
+ uint32_t offset;
+ /** Segment of this buffer. */
+ uint16_t segOrSelector;
+ /** Internal VDS buffer ID. */
+ uint16_t bufferId;
+ /** Physical address of this buffer. */
+ uint32_t physicalAddress;
+} VDS_DDS;
+
+static bool vds_available(void);
+#pragma aux vds_available = \
+ "mov ax, 0x40" \
+ "mov es, ax" \
+ "mov al, es:[0x7B]" \
+ "and al, 0x20" \
+ "setnz al" \
+ __value [al] \
+ __modify [ax es]
+
+/** Locks an already allocated buffer into a physical memory location.
+ * regionSize, offset and segment must be valid in the DDS,
+ * while the physical address is returned. */
+static vdserr vds_lock_dma_buffer_region(VDS_DDS __far * dds, unsigned char flags);
+#pragma aux vds_lock_dma_buffer_region = \
+ "stc" /* If nothing happens, assume failure. */ \
+ "mov ax, 0x8103" \
+ "int 0x4B" \
+ "jc fail" \
+ "mov ah, 0" \
+ "jmp end" \
+ "fail: test ah, ah" \
+ "jnz end" \
+ "mov ah, 0xFF" /* Force a error code if there was none. */ \
+ "end:" \
+ __parm [es di] [dx] \
+ __value [ah] \
+ __modify [ax]
+
+/** Unlocks a locked buffer. */
+static vdserr vds_unlock_dma_buffer_region(VDS_DDS __far * dds, unsigned char flags);
+#pragma aux vds_unlock_dma_buffer_region = \
+ "stc" \
+ "mov ax, 0x8104" \
+ "int 0x4B" \
+ "jc fail" \
+ "mov ah, 0" \
+ "jmp end" \
+ "fail: test ah, ah" \
+ "jnz end" \
+ "mov ah, 0xFF" \
+ "end:" \
+ __parm [es di] [dx] \
+ __value [ah] \
+ __modify [ax]
+
+#endif