From b649293e1f0b8ad13aeb4c662a4eeae78634c129 Mon Sep 17 00:00:00 2001 From: Javier Date: Mon, 28 Feb 2022 02:37:56 +0000 Subject: use virtual DMA service on windows with paging enabled --- makefile | 2 +- mousew16.c | 12 ++-- vbox.c | 219 ++++++++++++++++++++++++++++++++++++------------------------- vbox.h | 6 +- vds.h | 125 +++++++++++++++++++++++++++++++++++ 5 files changed, 267 insertions(+), 97 deletions(-) create mode 100644 vds.h 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 #include +#include #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 +#include + +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 -- cgit v1.2.3