diff options
| author | Javier <dev.git@javispedro.com> | 2022-02-28 02:37:56 +0000 | 
|---|---|---|
| committer | Javier <dev.git@javispedro.com> | 2022-02-28 02:37:56 +0000 | 
| commit | b649293e1f0b8ad13aeb4c662a4eeae78634c129 (patch) | |
| tree | 06ac8552550341b1b03c297111104aee7c70b033 | |
| parent | dee9ca901fd3f2edec7af8f82300257b285067b0 (diff) | |
| download | vbados-b649293e1f0b8ad13aeb4c662a4eeae78634c129.tar.gz vbados-b649293e1f0b8ad13aeb4c662a4eeae78634c129.zip | |
use virtual DMA service on windows with paging enabled
| -rw-r--r-- | makefile | 2 | ||||
| -rw-r--r-- | mousew16.c | 12 | ||||
| -rw-r--r-- | vbox.c | 219 | ||||
| -rw-r--r-- | vbox.h | 6 | ||||
| -rw-r--r-- | vds.h | 125 | 
5 files changed, 267 insertions, 97 deletions
| @@ -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) @@ -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;  		} @@ -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;  } @@ -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); @@ -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 | 
