diff options
author | Michael Thayer <michael.thayer@oracle.com> | 2017-10-14 10:23:47 +0200 |
---|---|---|
committer | Michael Thayer <michael.thayer@oracle.com> | 2017-10-14 10:23:47 +0200 |
commit | ab07f97612d045d8c0a376174b39a294d028fef2 (patch) | |
tree | 8f71def44da9cdfb6ecca6ec742bf48c173b3494 |
Initial commit.
37 files changed, 8748 insertions, 0 deletions
diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..a8dcac1 --- /dev/null +++ b/.gitignore @@ -0,0 +1,78 @@ +# +# X.Org module default exclusion patterns +# The next section if for module specific patterns +# +# Do not edit the following section +# GNU Build System (Autotools) +aclocal.m4 +autom4te.cache/ +autoscan.log +ChangeLog +compile +config.guess +config.h +config.h.in +config.log +config-ml.in +config.py +config.status +config.status.lineno +config.sub +configure +configure.scan +depcomp +.deps/ +INSTALL +install-sh +.libs/ +libtool +libtool.m4 +ltmain.sh +lt~obsolete.m4 +ltoptions.m4 +ltsugar.m4 +ltversion.m4 +Makefile +Makefile.in +mdate-sh +missing +mkinstalldirs +*.pc +py-compile +stamp-h? +symlink-tree +texinfo.tex +ylwrap + +# Do not edit the following section +# Edit Compile Debug Document Distribute +*~ +*.[0-9] +*.[0-9]x +*.bak +*.bin +core +*.dll +*.exe +*-ISO*.bdf +*-JIS*.bdf +*-KOI8*.bdf +*.kld +*.ko +*.ko.cmd +*.lai +*.l[oa] +*.[oa] +*.obj +*.patch +*.so +*.pcf.gz +*.pdb +*.tar.bz2 +*.tar.gz +# +# Add & Override patterns for xf86-video-vboxvideo +# +# Edit the following section as needed +# For example, !report.pc overrides *.pc. See 'man gitignore' +# @@ -0,0 +1,26 @@ +Copyright (c) 2000 by Conectiva S.A. (http://www.conectiva.com) +Copyright 2008 Red Hat, Inc. +Copyright (C) 2005-2017 Oracle Corporation + +Permission is hereby granted, free of charge, to any person obtaining a +copy of this software and associated documentation files (the "Software"), +to deal in the Software without restriction, including without limitation +the rights to use, copy, modify, merge, publish, distribute, sublicense, +and/or sell copies of the Software, and to permit persons to whom the +Software is furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in +all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL +CONECTIVA LINUX BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, +WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF +OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. + +Except as contained in this notice, the name of Conectiva Linux shall +not be used in advertising or otherwise to promote the sale, use or other +dealings in this Software without prior written authorization from +Conectiva Linux. diff --git a/Makefile.am b/Makefile.am new file mode 100644 index 0000000..a94ec19 --- /dev/null +++ b/Makefile.am @@ -0,0 +1,33 @@ +# Copyright 2005 Adam Jackson. +# Copyright 2017 Oracle Corporation +# +# Permission is hereby granted, free of charge, to any person obtaining a +# copy of this software and associated documentation files (the "Software"), +# to deal in the Software without restriction, including without limitation +# on the rights to use, copy, modify, merge, publish, distribute, sub +# license, and/or sell copies of the Software, and to permit persons to whom +# the Software is furnished to do so, subject to the following conditions: +# +# The above copyright notice and this permission notice (including the next +# paragraph) shall be included in all copies or substantial portions of the +# Software. +# +# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +# FITNESS FOR A PARTICULAR PURPOSE AND NON-INFRINGEMENT. IN NO EVENT SHALL +# THE COPYRIGHT HOLDERS, AUTHORS AND/OR ITS SUPPLIERS BE LIABLE FOR ANY CLAIM, +# DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR +# OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE +# USE OR OTHER DEALINGS IN THE SOFTWARE. + +SUBDIRS = src man +MAINTAINERCLEANFILES = ChangeLog INSTALL +.PHONY: ChangeLog INSTALL + +INSTALL: + $(INSTALL_CMD) + +ChangeLog: + $(CHANGELOG_CMD) + +dist-hook: ChangeLog INSTALL @@ -0,0 +1,20 @@ +xf86-video-vboxvideo - VirtualBox video driver for the Xorg X server + +Please submit bugs & patches to the Xorg bugzilla: + + https://bugs.freedesktop.org/enter_bug.cgi?product=xorg + +All questions regarding this software should be directed at the +Xorg mailing list: + + http://lists.freedesktop.org/mailman/listinfo/xorg + +The master development code repository can be found at: + + git://anongit.freedesktop.org/git/xorg/driver/xf86-video-vboxvideo + + http://cgit.freedesktop.org/xorg/driver/xf86-video-vboxvideo + +For more information on the git code manager, see: + + http://wiki.x.org/wiki/GitPage diff --git a/autogen.sh b/autogen.sh new file mode 100755 index 0000000..fc34bd5 --- /dev/null +++ b/autogen.sh @@ -0,0 +1,14 @@ +#! /bin/sh + +srcdir=`dirname $0` +test -z "$srcdir" && srcdir=. + +ORIGDIR=`pwd` +cd $srcdir + +autoreconf -v --install || exit 1 +cd $ORIGDIR || exit $? + +if test -z "$NOCONFIGURE"; then + $srcdir/configure "$@" +fi diff --git a/configure.ac b/configure.ac new file mode 100644 index 0000000..78abe2e --- /dev/null +++ b/configure.ac @@ -0,0 +1,97 @@ +# Copyright 2005 Adam Jackson. +# +# Permission is hereby granted, free of charge, to any person obtaining a +# copy of this software and associated documentation files (the "Software"), +# to deal in the Software without restriction, including without limitation +# on the rights to use, copy, modify, merge, publish, distribute, sub +# license, and/or sell copies of the Software, and to permit persons to whom +# the Software is furnished to do so, subject to the following conditions: +# +# The above copyright notice and this permission notice (including the next +# paragraph) shall be included in all copies or substantial portions of the +# Software. +# +# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +# FITNESS FOR A PARTICULAR PURPOSE AND NON-INFRINGEMENT. IN NO EVENT SHALL +# ADAM JACKSON BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER +# IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN +# CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. +# +# Process this file with autoconf to produce a configure script + +# Initialize Autoconf +AC_PREREQ([2.60]) +AC_INIT([xf86-video-vboxvideo], + [2.3.4], + [https://bugs.freedesktop.org/enter_bug.cgi?product=xorg], + [xf86-video-vboxvideo]) +AC_CONFIG_SRCDIR([Makefile.am]) +AC_CONFIG_HEADERS([config.h]) +AC_CONFIG_AUX_DIR(.) + +# Initialize Automake +AM_INIT_AUTOMAKE([foreign dist-bzip2]) + +# Require X.Org macros 1.8 or later for MAN_SUBSTS set by XORG_MANPAGE_SECTIONS +m4_ifndef([XORG_MACROS_VERSION], + [m4_fatal([must install xorg-macros 1.8 or later before running autoconf/autogen])]) +XORG_MACROS_VERSION(1.8) +XORG_DEFAULT_OPTIONS + +# Initialize libtool +AC_DISABLE_STATIC +AC_PROG_LIBTOOL + +AH_TOP([#include "xorg-server.h"]) + +# Define a configure option for an alternate module directory +AC_ARG_WITH(xorg-module-dir, [ --with-xorg-module-dir=DIR ], + [ moduledir="$withval" ], + [ moduledir="$libdir/xorg/modules" ]) +AC_SUBST(moduledir) + +# Store the list of server defined optional extensions in REQUIRED_MODULES +XORG_DRIVER_CHECK_EXT(RANDR, randrproto) +XORG_DRIVER_CHECK_EXT(RENDER, renderproto) +XORG_DRIVER_CHECK_EXT(DPMSExtension, xextproto) + +# Obtain compiler/linker options for the driver dependencies +PKG_CHECK_MODULES(XORG, xorg-server >= 1.6 xproto fontsproto $REQUIRED_MODULES) +PKG_CHECK_MODULES(XEXT, [xextproto >= 7.0.99.1], + HAVE_XEXTPROTO_71="yes", HAVE_XEXTPROTO_71="no") + +if test "x$HAVE_XEXTPROTO_71" = xyes; then + AC_DEFINE(HAVE_XEXTPROTO_71, 1, [xextproto 7.1 available]) +fi + +save_CFLAGS="$CFLAGS" +CFLAGS="$XORG_CFLAGS" +AC_CHECK_DECL(xf86ConfigIsaEntity, + [AC_DEFINE(HAVE_ISA, 1, [Have ISA support])], + [], + [#include "xf86.h"]) +CFLAGS="$save_CFLAGS" + +save_CFLAGS="$CFLAGS" +CFLAGS="$XORG_CFLAGS" +AC_CHECK_DECL(XSERVER_LIBPCIACCESS, + [XSERVER_LIBPCIACCESS=yes], [XSERVER_LIBPCIACCESS=no], + [#include "xorg-server.h"]) +CFLAGS="$save_CFLAGS" + +if test "x$XSERVER_LIBPCIACCESS" = xyes; then + PKG_CHECK_MODULES([PCIACCESS], [pciaccess >= 0.12.901]) +fi + +# Checks for libraries. + +DRIVER_NAME=vboxvideo +AC_SUBST([DRIVER_NAME]) + +AC_CONFIG_FILES([ + Makefile + src/Makefile + man/Makefile +]) +AC_OUTPUT diff --git a/man/Makefile.am b/man/Makefile.am new file mode 100644 index 0000000..b3688ce --- /dev/null +++ b/man/Makefile.am @@ -0,0 +1,41 @@ +# +# Copyright 2005 Sun Microsystems, Inc. All rights reserved. +# +# Permission is hereby granted, free of charge, to any person obtaining a +# copy of this software and associated documentation files (the "Software"), +# to deal in the Software without restriction, including without limitation +# the rights to use, copy, modify, merge, publish, distribute, sublicense, +# and/or sell copies of the Software, and to permit persons to whom the +# Software is furnished to do so, subject to the following conditions: +# +# The above copyright notice and this permission notice (including the next +# paragraph) shall be included in all copies or substantial portions of the +# Software. +# +# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL +# THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +# FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER +# DEALINGS IN THE SOFTWARE. +# + +drivermandir = $(DRIVER_MAN_DIR) + +driverman_PRE = @DRIVER_NAME@.man + +driverman_DATA = $(driverman_PRE:man=@DRIVER_MAN_SUFFIX@) + +EXTRA_DIST = @DRIVER_NAME@.man + +CLEANFILES = $(driverman_DATA) + + +# String replacements in MAN_SUBSTS now come from xorg-macros.m4 via configure + + +SUFFIXES = .$(DRIVER_MAN_SUFFIX) .man + +.man.$(DRIVER_MAN_SUFFIX): + $(AM_V_GEN)$(SED) $(MAN_SUBSTS) < $< > $@ diff --git a/man/vboxvideo.man b/man/vboxvideo.man new file mode 100644 index 0000000..d61a22a --- /dev/null +++ b/man/vboxvideo.man @@ -0,0 +1,27 @@ +.\" shorthand for double quote that works everywhere. +.ds q \N'34' +.TH VBOXVIDEO __drivermansuffix__ __vendorversion__ +.SH NAME +vboxvideo \- VirtualBox video driver +.SH SYNOPSIS +.nf +.B "Section \*qDevice\*q" +.BI " Identifier \*q" devname \*q +.B " Driver \*qvboxvideo\*q" +\ \ ... +.B EndSection +.fi +.SH DESCRIPTION +.B vboxvideo +is an __xservername__ driver for the VirtualBox virtual video card. +.SH SUPPORTED HARDWARE +The +.B vboxvideo +driver supports the VirtualBox virtual video card. +.SH CONFIGURATION DETAILS +Please refer to __xconfigfile__(__filemansuffix__) for general configuration +details. +.SH "SEE ALSO" +__xservername__(__appmansuffix__), __xconfigfile__(__filemansuffix__), Xserver(__appmansuffix__), X(__miscmansuffix__) +.SH AUTHORS +Authors include: Paulo Ce\'sar Pereira de Andrade, Michael Thayer. diff --git a/src/HGSMI.h b/src/HGSMI.h new file mode 100644 index 0000000..9b621b5 --- /dev/null +++ b/src/HGSMI.h @@ -0,0 +1,258 @@ +/* + * Copyright (C) 2006-2017 Oracle Corporation + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE COPYRIGHT HOLDER(S) OR AUTHOR(S) BE LIABLE FOR ANY CLAIM, DAMAGES OR + * OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, + * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR + * OTHER DEALINGS IN THE SOFTWARE. + */ + + +#ifndef ___VBox_Graphics_HGSMI_h +#define ___VBox_Graphics_HGSMI_h + +#include "VBoxVideoIPRT.h" + +#include "HGSMIDefs.h" +#include "HGSMIChannels.h" +#include "HGSMIMemAlloc.h" + +/* + * Basic mechanism for the HGSMI is to prepare and pass data buffer to the host and the guest. + * Data inside these buffers are opaque for the HGSMI and are interpreted by higher levels. + * + * Every shared memory buffer passed between the guest/host has the following structure: + * + * HGSMIBUFFERHEADER header; + * uint8_t data[header.u32BufferSize]; + * HGSMIBUFFERTAIL tail; + * + * Note: Offset of the 'header' in the memory is used for virtual hardware IO. + * + * Buffers are verifyed using the offset and the content of the header and the tail, + * which are constant during a call. + * + * Invalid buffers are ignored. + * + * Actual 'data' is not verifyed, as it is expected that the data can be changed by the + * called function. + * + * Since only the offset of the buffer is passed in a IO operation, the header and tail + * must contain: + * * size of data in this buffer; + * * checksum for buffer verification. + * + * For segmented transfers: + * * the sequence identifier; + * * offset of the current segment in the sequence; + * * total bytes in the transfer. + * + * Additionally contains: + * * the channel ID; + * * the channel information. + */ + +typedef struct HGSMIHEAP +{ + HGSMIAREA area; /* Description. */ + HGSMIMADATA ma; /* Memory allocator */ +} HGSMIHEAP; + +/* The size of the array of channels. Array indexes are uint8_t. Note: the value must not be changed. */ +#define HGSMI_NUMBER_OF_CHANNELS 0x100 + +/* Channel handler called when the guest submits a buffer. */ +typedef DECLCALLBACK(int) FNHGSMICHANNELHANDLER(void *pvHandler, uint16_t u16ChannelInfo, void *pvBuffer, HGSMISIZE cbBuffer); +typedef FNHGSMICHANNELHANDLER *PFNHGSMICHANNELHANDLER; + +/* Information about a handler: pfn + context. */ +typedef struct _HGSMICHANNELHANDLER +{ + PFNHGSMICHANNELHANDLER pfnHandler; + void *pvHandler; +} HGSMICHANNELHANDLER; + +/* Channel description. */ +typedef struct _HGSMICHANNEL +{ + HGSMICHANNELHANDLER handler; /* The channel handler. */ + const char *pszName; /* NULL for hardcoded channels or RTStrDup'ed name. */ + uint8_t u8Channel; /* The channel id, equal to the channel index in the array. */ + uint8_t u8Flags; /* HGSMI_CH_F_* */ +} HGSMICHANNEL; + +typedef struct _HGSMICHANNELINFO +{ + HGSMICHANNEL Channels[HGSMI_NUMBER_OF_CHANNELS]; /* Channel handlers indexed by the channel id. + * The array is accessed under the instance lock. + */ +} HGSMICHANNELINFO; + + +RT_C_DECLS_BEGIN + +DECLINLINE(HGSMIBUFFERHEADER *) HGSMIBufferHeaderFromPtr(void *pvBuffer) +{ + return (HGSMIBUFFERHEADER *)pvBuffer; +} + +DECLINLINE(uint8_t *) HGSMIBufferDataFromPtr(void *pvBuffer) +{ + return (uint8_t *)pvBuffer + sizeof(HGSMIBUFFERHEADER); +} + +DECLINLINE(HGSMIBUFFERTAIL *) HGSMIBufferTailFromPtr(void *pvBuffer, + uint32_t u32DataSize) +{ + return (HGSMIBUFFERTAIL *)(HGSMIBufferDataFromPtr(pvBuffer) + u32DataSize); +} + +DECLINLINE(HGSMISIZE) HGSMIBufferMinimumSize(void) +{ + return sizeof(HGSMIBUFFERHEADER) + sizeof(HGSMIBUFFERTAIL); +} + +DECLINLINE(HGSMIBUFFERHEADER *) HGSMIBufferHeaderFromData(const void *pvData) +{ + return (HGSMIBUFFERHEADER *)((uint8_t *)pvData - sizeof(HGSMIBUFFERHEADER)); +} + +DECLINLINE(HGSMISIZE) HGSMIBufferRequiredSize(uint32_t u32DataSize) +{ + return HGSMIBufferMinimumSize() + u32DataSize; +} + +DECLINLINE(HGSMIOFFSET) HGSMIPointerToOffset(const HGSMIAREA *pArea, + const void *pv) +{ + return pArea->offBase + (HGSMIOFFSET)((uint8_t *)pv - pArea->pu8Base); +} + +DECLINLINE(void *) HGSMIOffsetToPointer(const HGSMIAREA *pArea, + HGSMIOFFSET offBuffer) +{ + return pArea->pu8Base + (offBuffer - pArea->offBase); +} + +DECLINLINE(uint8_t *) HGSMIBufferDataFromOffset(const HGSMIAREA *pArea, + HGSMIOFFSET offBuffer) +{ + void *pvBuffer = HGSMIOffsetToPointer(pArea, offBuffer); + return HGSMIBufferDataFromPtr(pvBuffer); +} + +DECLINLINE(HGSMIOFFSET) HGSMIBufferOffsetFromData(const HGSMIAREA *pArea, + void *pvData) +{ + HGSMIBUFFERHEADER *pHeader = HGSMIBufferHeaderFromData(pvData); + return HGSMIPointerToOffset(pArea, pHeader); +} + +DECLINLINE(uint8_t *) HGSMIBufferDataAndChInfoFromOffset(const HGSMIAREA *pArea, + HGSMIOFFSET offBuffer, + uint16_t *pu16ChannelInfo) +{ + HGSMIBUFFERHEADER *pHeader = (HGSMIBUFFERHEADER *)HGSMIOffsetToPointer(pArea, offBuffer); + *pu16ChannelInfo = pHeader->u16ChannelInfo; + return HGSMIBufferDataFromPtr(pHeader); +} + +uint32_t HGSMIChecksum(HGSMIOFFSET offBuffer, + const HGSMIBUFFERHEADER *pHeader, + const HGSMIBUFFERTAIL *pTail); + +int HGSMIAreaInitialize(HGSMIAREA *pArea, + void *pvBase, + HGSMISIZE cbArea, + HGSMIOFFSET offBase); + +void HGSMIAreaClear(HGSMIAREA *pArea); + +DECLINLINE(bool) HGSMIAreaContainsOffset(const HGSMIAREA *pArea, HGSMIOFFSET off) +{ + return off >= pArea->offBase && off - pArea->offBase < pArea->cbArea; +} + +DECLINLINE(bool) HGSMIAreaContainsPointer(const HGSMIAREA *pArea, const void *pv) +{ + return (uintptr_t)pv >= (uintptr_t)pArea->pu8Base && (uintptr_t)pv - (uintptr_t)pArea->pu8Base < pArea->cbArea; +} + +HGSMIOFFSET HGSMIBufferInitializeSingle(const HGSMIAREA *pArea, + HGSMIBUFFERHEADER *pHeader, + HGSMISIZE cbBuffer, + uint8_t u8Channel, + uint16_t u16ChannelInfo); + +int HGSMIHeapSetup(HGSMIHEAP *pHeap, + void *pvBase, + HGSMISIZE cbArea, + HGSMIOFFSET offBase, + const HGSMIENV *pEnv); + +void HGSMIHeapDestroy(HGSMIHEAP *pHeap); + +void *HGSMIHeapBufferAlloc(HGSMIHEAP *pHeap, + HGSMISIZE cbBuffer); + +void HGSMIHeapBufferFree(HGSMIHEAP *pHeap, + void *pvBuf); + +void *HGSMIHeapAlloc(HGSMIHEAP *pHeap, + HGSMISIZE cbData, + uint8_t u8Channel, + uint16_t u16ChannelInfo); + +void HGSMIHeapFree(HGSMIHEAP *pHeap, + void *pvData); + +DECLINLINE(const HGSMIAREA *) HGSMIHeapArea(HGSMIHEAP *pHeap) +{ + return &pHeap->area; +} + +DECLINLINE(HGSMIOFFSET) HGSMIHeapOffset(HGSMIHEAP *pHeap) +{ + return HGSMIHeapArea(pHeap)->offBase; +} + +DECLINLINE(HGSMISIZE) HGSMIHeapSize(HGSMIHEAP *pHeap) +{ + return HGSMIHeapArea(pHeap)->cbArea; +} + +DECLINLINE(HGSMIOFFSET) HGSMIHeapBufferOffset(HGSMIHEAP *pHeap, + void *pvData) +{ + return HGSMIBufferOffsetFromData(HGSMIHeapArea(pHeap), pvData); +} + +HGSMICHANNEL *HGSMIChannelFindById(HGSMICHANNELINFO *pChannelInfo, + uint8_t u8Channel); + +int HGSMIChannelRegister(HGSMICHANNELINFO *pChannelInfo, + uint8_t u8Channel, + const char *pszName, + PFNHGSMICHANNELHANDLER pfnChannelHandler, + void *pvChannelHandler); + +int HGSMIBufferProcess(const HGSMIAREA *pArea, + HGSMICHANNELINFO *pChannelInfo, + HGSMIOFFSET offBuffer); +RT_C_DECLS_END + +#endif /* !___VBox_Graphics_HGSMI_h */ + diff --git a/src/HGSMIBase.c b/src/HGSMIBase.c new file mode 100644 index 0000000..f6cfbb3 --- /dev/null +++ b/src/HGSMIBase.c @@ -0,0 +1,293 @@ +/* + * Copyright (C) 2006-2017 Oracle Corporation + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE COPYRIGHT HOLDER(S) OR AUTHOR(S) BE LIABLE FOR ANY CLAIM, DAMAGES OR + * OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, + * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR + * OTHER DEALINGS IN THE SOFTWARE. + */ + +#include <HGSMIBase.h> +#include <VBoxVideoIPRT.h> +#include <VBoxVideoGuest.h> +#include <VBoxVideoVBE.h> +#include <HGSMIChannels.h> +#include <HGSMIChSetup.h> + +/** Detect whether HGSMI is supported by the host. */ +DECLHIDDEN(bool) VBoxHGSMIIsSupported(void) +{ + uint16_t DispiId; + + VBVO_PORT_WRITE_U16(VBE_DISPI_IOPORT_INDEX, VBE_DISPI_INDEX_ID); + VBVO_PORT_WRITE_U16(VBE_DISPI_IOPORT_DATA, VBE_DISPI_ID_HGSMI); + + DispiId = VBVO_PORT_READ_U16(VBE_DISPI_IOPORT_DATA); + + return (DispiId == VBE_DISPI_ID_HGSMI); +} + + +/** + * Inform the host of the location of the host flags in VRAM via an HGSMI command. + * @returns IPRT status value. + * @returns VERR_NOT_IMPLEMENTED if the host does not support the command. + * @returns VERR_NO_MEMORY if a heap allocation fails. + * @param pCtx the context of the guest heap to use. + * @param offLocation the offset chosen for the flags withing guest VRAM. + */ +DECLHIDDEN(int) VBoxHGSMIReportFlagsLocation(PHGSMIGUESTCOMMANDCONTEXT pCtx, HGSMIOFFSET offLocation) +{ + HGSMIBUFFERLOCATION *p; + + /* Allocate the IO buffer. */ + p = (HGSMIBUFFERLOCATION *)VBoxHGSMIBufferAlloc(pCtx, sizeof(*p), HGSMI_CH_HGSMI, + HGSMI_CC_HOST_FLAGS_LOCATION); + if (!p) + return VERR_NO_MEMORY; + + /* Prepare data to be sent to the host. */ + p->offLocation = offLocation; + p->cbLocation = sizeof(HGSMIHOSTFLAGS); + /* No need to check that the buffer is valid as we have just allocated it. */ + VBoxHGSMIBufferSubmit(pCtx, p); + /* Free the IO buffer. */ + VBoxHGSMIBufferFree(pCtx, p); + + return VINF_SUCCESS; +} + + +/** + * Notify the host of HGSMI-related guest capabilities via an HGSMI command. + * @returns IPRT status value. + * @returns VERR_NOT_IMPLEMENTED if the host does not support the command. + * @returns VERR_NO_MEMORY if a heap allocation fails. + * @param pCtx the context of the guest heap to use. + * @param fCaps the capabilities to report, see VBVACAPS. + */ +DECLHIDDEN(int) VBoxHGSMISendCapsInfo(PHGSMIGUESTCOMMANDCONTEXT pCtx, uint32_t fCaps) +{ + VBVACAPS *p; + + /* Allocate the IO buffer. */ + p = (VBVACAPS *)VBoxHGSMIBufferAlloc(pCtx, sizeof(*p), HGSMI_CH_VBVA, VBVA_INFO_CAPS); + + if (!p) + return VERR_NO_MEMORY; + + /* Prepare data to be sent to the host. */ + p->rc = VERR_NOT_IMPLEMENTED; + p->fCaps = fCaps; + /* No need to check that the buffer is valid as we have just allocated it. */ + VBoxHGSMIBufferSubmit(pCtx, p); + + AssertRC(p->rc); + /* Free the IO buffer. */ + VBoxHGSMIBufferFree(pCtx, p); + return p->rc; +} + + +/** + * Get the information needed to map the basic communication structures in + * device memory into our address space. All pointer parameters are optional. + * + * @param cbVRAM how much video RAM is allocated to the device + * @param poffVRAMBaseMapping where to save the offset from the start of the + * device VRAM of the whole area to map + * @param pcbMapping where to save the mapping size + * @param poffGuestHeapMemory where to save the offset into the mapped area + * of the guest heap backing memory + * @param pcbGuestHeapMemory where to save the size of the guest heap + * backing memory + * @param poffHostFlags where to save the offset into the mapped area + * of the host flags + */ +DECLHIDDEN(void) VBoxHGSMIGetBaseMappingInfo(uint32_t cbVRAM, + uint32_t *poffVRAMBaseMapping, + uint32_t *pcbMapping, + uint32_t *poffGuestHeapMemory, + uint32_t *pcbGuestHeapMemory, + uint32_t *poffHostFlags) +{ + AssertPtrNullReturnVoid(poffVRAMBaseMapping); + AssertPtrNullReturnVoid(pcbMapping); + AssertPtrNullReturnVoid(poffGuestHeapMemory); + AssertPtrNullReturnVoid(pcbGuestHeapMemory); + AssertPtrNullReturnVoid(poffHostFlags); + if (poffVRAMBaseMapping) + *poffVRAMBaseMapping = cbVRAM - VBVA_ADAPTER_INFORMATION_SIZE; + if (pcbMapping) + *pcbMapping = VBVA_ADAPTER_INFORMATION_SIZE; + if (poffGuestHeapMemory) + *poffGuestHeapMemory = 0; + if (pcbGuestHeapMemory) + *pcbGuestHeapMemory = VBVA_ADAPTER_INFORMATION_SIZE + - sizeof(HGSMIHOSTFLAGS); + if (poffHostFlags) + *poffHostFlags = VBVA_ADAPTER_INFORMATION_SIZE + - sizeof(HGSMIHOSTFLAGS); +} + +/** + * Query the host for an HGSMI configuration parameter via an HGSMI command. + * @returns iprt status value + * @param pCtx the context containing the heap used + * @param u32Index the index of the parameter to query, + * @see VBVACONF32::u32Index + * @param pulValue where to store the value of the parameter on success + */ +DECLHIDDEN(int) VBoxQueryConfHGSMI(PHGSMIGUESTCOMMANDCONTEXT pCtx, uint32_t u32Index, uint32_t *pulValue) +{ + VBVACONF32 *p; + + /* Allocate the IO buffer. */ + p = (VBVACONF32 *)VBoxHGSMIBufferAlloc(pCtx, sizeof(*p), HGSMI_CH_VBVA, + VBVA_QUERY_CONF32); + if (!p) + return VERR_NO_MEMORY; + + /* Prepare data to be sent to the host. */ + p->u32Index = u32Index; + p->u32Value = UINT32_MAX; + /* No need to check that the buffer is valid as we have just allocated it. */ + VBoxHGSMIBufferSubmit(pCtx, p); + *pulValue = p->u32Value; + /* Free the IO buffer. */ + VBoxHGSMIBufferFree(pCtx, p); + return VINF_SUCCESS; +} + +/** + * Pass the host a new mouse pointer shape via an HGSMI command. + * + * @returns success or failure + * @param pCtx the context containing the heap to be used + * @param fFlags cursor flags, @see VMMDevReqMousePointer::fFlags + * @param cHotX horizontal position of the hot spot + * @param cHotY vertical position of the hot spot + * @param cWidth width in pixels of the cursor + * @param cHeight height in pixels of the cursor + * @param pPixels pixel data, @see VMMDevReqMousePointer for the format + * @param cbLength size in bytes of the pixel data + */ +DECLHIDDEN(int) VBoxHGSMIUpdatePointerShape(PHGSMIGUESTCOMMANDCONTEXT pCtx, uint32_t fFlags, + uint32_t cHotX, uint32_t cHotY, uint32_t cWidth, uint32_t cHeight, + uint8_t *pPixels, uint32_t cbLength) +{ + VBVAMOUSEPOINTERSHAPE *p; + uint32_t cbPixels = 0; + int rc; + + if (fFlags & VBOX_MOUSE_POINTER_SHAPE) + { + /* + * Size of the pointer data: + * sizeof (AND mask) + sizeof (XOR_MASK) + */ + cbPixels = ((((cWidth + 7) / 8) * cHeight + 3) & ~3) + + cWidth * 4 * cHeight; + if (cbPixels > cbLength) + return VERR_INVALID_PARAMETER; + /* + * If shape is supplied, then always create the pointer visible. + * See comments in 'vboxUpdatePointerShape' + */ + fFlags |= VBOX_MOUSE_POINTER_VISIBLE; + } + /* Allocate the IO buffer. */ + p = (VBVAMOUSEPOINTERSHAPE *)VBoxHGSMIBufferAlloc(pCtx, sizeof(*p) + cbPixels, HGSMI_CH_VBVA, + VBVA_MOUSE_POINTER_SHAPE); + if (!p) + return VERR_NO_MEMORY; + /* Prepare data to be sent to the host. */ + /* Will be updated by the host. */ + p->i32Result = VINF_SUCCESS; + /* We have our custom flags in the field */ + p->fu32Flags = fFlags; + p->u32HotX = cHotX; + p->u32HotY = cHotY; + p->u32Width = cWidth; + p->u32Height = cHeight; + if (cbPixels) + /* Copy the actual pointer data. */ + memcpy (p->au8Data, pPixels, cbPixels); + /* No need to check that the buffer is valid as we have just allocated it. */ + VBoxHGSMIBufferSubmit(pCtx, p); + rc = p->i32Result; + /* Free the IO buffer. */ + VBoxHGSMIBufferFree(pCtx, p); + return rc; +} + + +/** + * Report the guest cursor position. The host may wish to use this information + * to re-position its own cursor (though this is currently unlikely). The + * current host cursor position is returned. + * @param pCtx The context containing the heap used. + * @param fReportPosition Are we reporting a position? + * @param x Guest cursor X position. + * @param y Guest cursor Y position. + * @param pxHost Host cursor X position is stored here. Optional. + * @param pyHost Host cursor Y position is stored here. Optional. + * @returns iprt status code. + * @returns VERR_NO_MEMORY HGSMI heap allocation failed. + */ +DECLHIDDEN(int) VBoxHGSMICursorPosition(PHGSMIGUESTCOMMANDCONTEXT pCtx, bool fReportPosition, + uint32_t x, uint32_t y, uint32_t *pxHost, uint32_t *pyHost) +{ + VBVACURSORPOSITION *p; + + /* Allocate the IO buffer. */ + p = (VBVACURSORPOSITION *)VBoxHGSMIBufferAlloc(pCtx, sizeof(*p), HGSMI_CH_VBVA, + VBVA_CURSOR_POSITION); + if (!p) + return VERR_NO_MEMORY; + /* Prepare data to be sent to the host. */ + p->fReportPosition = fReportPosition; + p->x = x; + p->y = y; + /* No need to check that the buffer is valid as we have just allocated it. */ + VBoxHGSMIBufferSubmit(pCtx, p); + if (pxHost) + *pxHost = p->x; + if (pyHost) + *pyHost = p->y; + /* Free the IO buffer. */ + VBoxHGSMIBufferFree(pCtx, p); + return VINF_SUCCESS; +} + + +/** + * @todo Mouse pointer position to be read from VMMDev memory, address of the + * memory region can be queried from VMMDev via an IOCTL. This VMMDev memory + * region will contain host information which is needed by the guest. + * + * Reading will not cause a switch to the host. + * + * Have to take into account: + * * synchronization: host must write to the memory only from EMT, + * large structures must be read under flag, which tells the host + * that the guest is currently reading the memory (OWNER flag?). + * * guest writes: may be allocate a page for the host info and make + * the page readonly for the guest. + * * the information should be available only for additions drivers. + * * VMMDev additions driver will inform the host which version of the info + * it expects, host must support all versions. + */ diff --git a/src/HGSMIBase.h b/src/HGSMIBase.h new file mode 100644 index 0000000..c8d898c --- /dev/null +++ b/src/HGSMIBase.h @@ -0,0 +1,54 @@ +/* + * Copyright (C) 2006-2017 Oracle Corporation + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NON-INFRINGEMENT. IN NO EVENT SHALL + * THE COPYRIGHT HOLDERS, AUTHORS AND/OR ITS SUPPLIERS BE LIABLE FOR ANY CLAIM, + * DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR + * OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE + * USE OR OTHER DEALINGS IN THE SOFTWARE. + */ + + +#ifndef ___VBox_Graphics_HGSMIBase_h___ +#define ___VBox_Graphics_HGSMIBase_h___ + +#include "HGSMI.h" +#include "HGSMIContext.h" +#include "VBoxVideoIPRT.h" + +RT_C_DECLS_BEGIN + +/** @name Base HGSMI Buffer APIs + * @{ */ + +/** Acknowlege an IRQ. */ +DECLINLINE(void) VBoxHGSMIClearIrq(PHGSMIHOSTCOMMANDCONTEXT pCtx) +{ + VBVO_PORT_WRITE_U32(pCtx->port, HGSMIOFFSET_VOID); +} + +DECLHIDDEN(void *) VBoxHGSMIBufferAlloc(PHGSMIGUESTCOMMANDCONTEXT pCtx, + HGSMISIZE cbData, + uint8_t u8Ch, + uint16_t u16Op); +DECLHIDDEN(void) VBoxHGSMIBufferFree(PHGSMIGUESTCOMMANDCONTEXT pCtx, + void *pvBuffer); +DECLHIDDEN(int) VBoxHGSMIBufferSubmit(PHGSMIGUESTCOMMANDCONTEXT pCtx, + void *pvBuffer); +/** @} */ + +RT_C_DECLS_END + +#endif diff --git a/src/HGSMIBuffers.c b/src/HGSMIBuffers.c new file mode 100644 index 0000000..1f8ed07 --- /dev/null +++ b/src/HGSMIBuffers.c @@ -0,0 +1,118 @@ +/* + * Copyright (C) 2006-2017 Oracle Corporation + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE COPYRIGHT HOLDER(S) OR AUTHOR(S) BE LIABLE FOR ANY CLAIM, DAMAGES OR + * OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, + * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR + * OTHER DEALINGS IN THE SOFTWARE. + */ + +#include <VBoxVideoGuest.h> +#include <VBoxVideoVBE.h> +#include <VBoxVideoIPRT.h> + +/** + * Set up the HGSMI guest-to-host command context. + * @returns iprt status value + * @param pCtx the context to set up + * @param pvGuestHeapMemory a pointer to the mapped backing memory for + * the guest heap + * @param cbGuestHeapMemory the size of the backing memory area + * @param offVRAMGuestHeapMemory the offset of the memory pointed to by + * @a pvGuestHeapMemory within the video RAM + * @param pEnv HGSMI environment. + */ +DECLHIDDEN(int) VBoxHGSMISetupGuestContext(PHGSMIGUESTCOMMANDCONTEXT pCtx, + void *pvGuestHeapMemory, + uint32_t cbGuestHeapMemory, + uint32_t offVRAMGuestHeapMemory, + const HGSMIENV *pEnv) +{ + /** @todo should we be using a fixed ISA port value here? */ + pCtx->port = (RTIOPORT)VGA_PORT_HGSMI_GUEST; +#ifdef VBOX_WDDM_MINIPORT + return VBoxSHGSMIInit(&pCtx->heapCtx, pvGuestHeapMemory, + cbGuestHeapMemory, offVRAMGuestHeapMemory, pEnv); +#else + return HGSMIHeapSetup(&pCtx->heapCtx, pvGuestHeapMemory, + cbGuestHeapMemory, offVRAMGuestHeapMemory, pEnv); +#endif +} + + +/** + * Allocate and initialise a command descriptor in the guest heap for a + * guest-to-host command. + * + * @returns pointer to the descriptor's command data buffer + * @param pCtx the context containing the heap to be used + * @param cbData the size of the command data to go into the descriptor + * @param u8Ch the HGSMI channel to be used, set to the descriptor + * @param u16Op the HGSMI command to be sent, set to the descriptor + */ +DECLHIDDEN(void *) VBoxHGSMIBufferAlloc(PHGSMIGUESTCOMMANDCONTEXT pCtx, + HGSMISIZE cbData, + uint8_t u8Ch, + uint16_t u16Op) +{ +#ifdef VBOX_WDDM_MINIPORT + return VBoxSHGSMIHeapAlloc (&pCtx->heapCtx, cbData, u8Ch, u16Op); +#else + return HGSMIHeapAlloc (&pCtx->heapCtx, cbData, u8Ch, u16Op); +#endif +} + + +/** + * Free a descriptor allocated by @a VBoxHGSMIBufferAlloc. + * + * @param pCtx the context containing the heap used + * @param pvBuffer the pointer returned by @a VBoxHGSMIBufferAlloc + */ +DECLHIDDEN(void) VBoxHGSMIBufferFree(PHGSMIGUESTCOMMANDCONTEXT pCtx, + void *pvBuffer) +{ +#ifdef VBOX_WDDM_MINIPORT + VBoxSHGSMIHeapFree (&pCtx->heapCtx, pvBuffer); +#else + HGSMIHeapFree (&pCtx->heapCtx, pvBuffer); +#endif +} + +/** + * Submit a command descriptor allocated by @a VBoxHGSMIBufferAlloc. + * + * @param pCtx the context containing the heap used + * @param pvBuffer the pointer returned by @a VBoxHGSMIBufferAlloc + */ +DECLHIDDEN(int) VBoxHGSMIBufferSubmit(PHGSMIGUESTCOMMANDCONTEXT pCtx, + void *pvBuffer) +{ + /* Initialize the buffer and get the offset for port IO. */ + HGSMIOFFSET offBuffer = HGSMIHeapBufferOffset (HGSMIGUESTCMDHEAP_GET(&pCtx->heapCtx), pvBuffer); + + Assert(offBuffer != HGSMIOFFSET_VOID); + if (offBuffer != HGSMIOFFSET_VOID) + { + /* Submit the buffer to the host. */ + VBVO_PORT_WRITE_U32(pCtx->port, offBuffer); + /* Make the compiler aware that the host has changed memory. */ + ASMCompilerBarrier(); + return VINF_SUCCESS; + } + + return VERR_INVALID_PARAMETER; +} diff --git a/src/HGSMIChSetup.h b/src/HGSMIChSetup.h new file mode 100644 index 0000000..fcf62f3 --- /dev/null +++ b/src/HGSMIChSetup.h @@ -0,0 +1,79 @@ +/* + * Copyright (C) 2006-2017 Oracle Corporation + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE COPYRIGHT HOLDER(S) OR AUTHOR(S) BE LIABLE FOR ANY CLAIM, DAMAGES OR + * OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, + * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR + * OTHER DEALINGS IN THE SOFTWARE. + */ + +#ifndef ___VBox_Graphics_HGSMIChSetup_h +#define ___VBox_Graphics_HGSMIChSetup_h + +#include "HGSMIDefs.h" + +/* HGSMI setup and configuration channel commands and data structures. */ +/* + * Tell the host the location of hgsmi_host_flags structure, where the host + * can write information about pending buffers, etc, and which can be quickly + * polled by the guest without a need to port IO. + */ +#define HGSMI_CC_HOST_FLAGS_LOCATION 0 + +typedef struct HGSMIBUFFERLOCATION +{ + HGSMIOFFSET offLocation; + HGSMISIZE cbLocation; +} HGSMIBUFFERLOCATION; +AssertCompileSize(HGSMIBUFFERLOCATION, 8); + +/* HGSMI setup and configuration data structures. */ +/* host->guest commands pending, should be accessed under FIFO lock only */ +#define HGSMIHOSTFLAGS_COMMANDS_PENDING UINT32_C(0x01) +/* IRQ is fired, should be accessed under VGAState::lock only */ +#define HGSMIHOSTFLAGS_IRQ UINT32_C(0x02) +#ifdef VBOX_WITH_WDDM +/* one or more guest commands is completed, should be accessed under FIFO lock only */ +# define HGSMIHOSTFLAGS_GCOMMAND_COMPLETED UINT32_C(0x04) +/* watchdog timer interrupt flag (used for debugging), should be accessed under VGAState::lock only */ +# define HGSMIHOSTFLAGS_WATCHDOG UINT32_C(0x08) +#endif +/* vsync interrupt flag, should be accessed under VGAState::lock only */ +#define HGSMIHOSTFLAGS_VSYNC UINT32_C(0x10) +/** monitor hotplug flag, should be accessed under VGAState::lock only */ +#define HGSMIHOSTFLAGS_HOTPLUG UINT32_C(0x20) +/** + * Cursor capability state change flag, should be accessed under + * VGAState::lock only. @see VBVACONF32. + */ +#define HGSMIHOSTFLAGS_CURSOR_CAPABILITIES UINT32_C(0x40) + +typedef struct HGSMIHOSTFLAGS +{ + /* + * Host flags can be accessed and modified in multiple threads + * concurrently, e.g. CrOpenGL HGCM and GUI threads when completing + * HGSMI 3D and Video Accel respectively, EMT thread when dealing with + * HGSMI command processing, etc. + * Besides settings/cleaning flags atomically, some flags have their + * own special sync restrictions, see comments for flags above. + */ + volatile uint32_t u32HostFlags; + uint32_t au32Reserved[3]; +} HGSMIHOSTFLAGS; +AssertCompileSize(HGSMIHOSTFLAGS, 16); + +#endif diff --git a/src/HGSMIChannels.h b/src/HGSMIChannels.h new file mode 100644 index 0000000..12af291 --- /dev/null +++ b/src/HGSMIChannels.h @@ -0,0 +1,64 @@ +/* + * Copyright (C) 2006-2017 Oracle Corporation + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE COPYRIGHT HOLDER(S) OR AUTHOR(S) BE LIABLE FOR ANY CLAIM, DAMAGES OR + * OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, + * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR + * OTHER DEALINGS IN THE SOFTWARE. + */ + + +#ifndef __HGSMIChannels_h__ +#define __HGSMIChannels_h__ + + +/* + * Each channel has an 8 bit identifier. There are a number of predefined + * (hardcoded) channels. + * + * HGSMI_CH_HGSMI channel can be used to map a string channel identifier + * to a free 16 bit numerical value. values are allocated in range + * [HGSMI_CH_STRING_FIRST;HGSMI_CH_STRING_LAST]. + */ + + +/* Predefined channel identifiers. Used internally by VBOX to simplify the channel setup. */ +/* A reserved channel value */ +#define HGSMI_CH_RESERVED 0x00 +/* HGCMI: setup and configuration */ +#define HGSMI_CH_HGSMI 0x01 +/* Graphics: VBVA */ +#define HGSMI_CH_VBVA 0x02 +/* Graphics: Seamless with a single guest region */ +#define HGSMI_CH_SEAMLESS 0x03 +/* Graphics: Seamless with separate host windows */ +#define HGSMI_CH_SEAMLESS2 0x04 +/* Graphics: OpenGL HW acceleration */ +#define HGSMI_CH_OPENGL 0x05 + + +/* Dynamically allocated channel identifiers. */ +/* The first channel index to be used for string mappings (inclusive) */ +#define HGSMI_CH_STRING_FIRST 0x20 +/* The last channel index for string mappings (inclusive) */ +#define HGSMI_CH_STRING_LAST 0xff + + +/* Check whether the channel identifier is allocated for a dynamic channel */ +#define HGSMI_IS_DYNAMIC_CHANNEL(_channel) (((uint8_t)(_channel) & 0xE0) != 0) + + +#endif /* !__HGSMIChannels_h__*/ diff --git a/src/HGSMICommon.c b/src/HGSMICommon.c new file mode 100644 index 0000000..59d3325 --- /dev/null +++ b/src/HGSMICommon.c @@ -0,0 +1,449 @@ +/* + * Copyright (C) 2006-2017 Oracle Corporation + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE COPYRIGHT HOLDER(S) OR AUTHOR(S) BE LIABLE FOR ANY CLAIM, DAMAGES OR + * OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, + * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR + * OTHER DEALINGS IN THE SOFTWARE. + */ + +#define LOG_DISABLED /* Maybe we can enabled it all the time now? */ +/** @note commented out all logging statements to avoid pulling the logging + * sub-system into places like the Linux kernel driver. Perhaps the best + * thing would be to use return enough information for callers to log what + * is needed. */ +#define LOG_GROUP LOG_GROUP_HGSMI + +#include <VBoxVideoIPRT.h> + +#include <HGSMI.h> + +/* Channel flags. */ +#define HGSMI_CH_F_REGISTERED 0x01 + +/* Assertions for situations which could happen and normally must be processed properly + * but must be investigated during development: guest misbehaving, etc. + */ +#ifdef HGSMI_STRICT +#define HGSMI_STRICT_ASSERT_FAILED() AssertFailed() +#define HGSMI_STRICT_ASSERT(expr) Assert(expr) +#else +#define HGSMI_STRICT_ASSERT_FAILED() do {} while (0) +#define HGSMI_STRICT_ASSERT(expr) do {} while (0) +#endif /* !HGSMI_STRICT */ + +/* + * We do not want assertions in Linux kernel code to reduce symbol dependencies. + */ +#if defined(IN_RING0) && defined(RT_OS_LINUX) +# define HGSMI_ASSERT_PTR_RETURN(a, b) if (!(a)) return (b) +#else +# define HGSMI_ASSERT_PTR_RETURN(a, b) if (!(a)) return (b) +#endif /* !IN_RING0 && RT_OS_LINUX */ + +/* One-at-a-Time Hash from + * http://www.burtleburtle.net/bob/hash/doobs.html + * + * ub4 one_at_a_time(char *key, ub4 len) + * { + * ub4 hash, i; + * for (hash=0, i=0; i<len; ++i) + * { + * hash += key[i]; + * hash += (hash << 10); + * hash ^= (hash >> 6); + * } + * hash += (hash << 3); + * hash ^= (hash >> 11); + * hash += (hash << 15); + * return hash; + * } + */ + +static uint32_t hgsmiHashBegin(void) +{ + return 0; +} + +static uint32_t hgsmiHashProcess(uint32_t hash, + const void *pvData, + size_t cbData) +{ + const uint8_t *pu8Data = (const uint8_t *)pvData; + + while (cbData--) + { + hash += *pu8Data++; + hash += (hash << 10); + hash ^= (hash >> 6); + } + + return hash; +} + +static uint32_t hgsmiHashEnd(uint32_t hash) +{ + hash += (hash << 3); + hash ^= (hash >> 11); + hash += (hash << 15); + + return hash; +} + +uint32_t HGSMIChecksum(HGSMIOFFSET offBuffer, + const HGSMIBUFFERHEADER *pHeader, + const HGSMIBUFFERTAIL *pTail) +{ + uint32_t u32Checksum = hgsmiHashBegin(); + + u32Checksum = hgsmiHashProcess(u32Checksum, &offBuffer, sizeof(offBuffer)); + u32Checksum = hgsmiHashProcess(u32Checksum, pHeader, sizeof(HGSMIBUFFERHEADER)); + u32Checksum = hgsmiHashProcess(u32Checksum, pTail, offsetof(HGSMIBUFFERTAIL, u32Checksum)); + + return hgsmiHashEnd(u32Checksum); +} + +int HGSMIAreaInitialize(HGSMIAREA *pArea, + void *pvBase, + HGSMISIZE cbArea, + HGSMIOFFSET offBase) +{ + uint8_t *pu8Base = (uint8_t *)pvBase; + + if ( !pArea /* Check that the area: */ + || cbArea < HGSMIBufferMinimumSize() /* large enough; */ + || pu8Base + cbArea < pu8Base /* no address space wrap; */ + || offBase > UINT32_C(0xFFFFFFFF) - cbArea /* area within the 32 bit space: offBase + cbMem <= 0xFFFFFFFF. */ + ) + { + return VERR_INVALID_PARAMETER; + } + + pArea->pu8Base = pu8Base; + pArea->offBase = offBase; + pArea->offLast = cbArea - HGSMIBufferMinimumSize() + offBase; + pArea->cbArea = cbArea; + + return VINF_SUCCESS; +} + +void HGSMIAreaClear(HGSMIAREA *pArea) +{ + if (pArea) + { + memset(pArea, 0, sizeof(*pArea)); + } +} + +/* Initialize the memory buffer including its checksum. + * No changes alloed to the header and the tail after that. + */ +HGSMIOFFSET HGSMIBufferInitializeSingle(const HGSMIAREA *pArea, + HGSMIBUFFERHEADER *pHeader, + HGSMISIZE cbBuffer, + uint8_t u8Channel, + uint16_t u16ChannelInfo) +{ + if ( !pArea + || !pHeader + || cbBuffer < HGSMIBufferMinimumSize()) + { + return HGSMIOFFSET_VOID; + } + + /* Buffer must be within the area: + * * header data size do not exceed the maximum data size; + * * buffer address is greater than the area base address; + * * buffer address is lower than the maximum allowed for the given data size. + */ + HGSMISIZE cbMaximumDataSize = pArea->offLast - pArea->offBase; + uint32_t u32DataSize = cbBuffer - HGSMIBufferMinimumSize(); + + if ( u32DataSize > cbMaximumDataSize + || (uint8_t *)pHeader < pArea->pu8Base + || (uint8_t *)pHeader > pArea->pu8Base + cbMaximumDataSize - u32DataSize) + { + return HGSMIOFFSET_VOID; + } + + HGSMIOFFSET offBuffer = HGSMIPointerToOffset(pArea, pHeader); + + pHeader->u8Flags = HGSMI_BUFFER_HEADER_F_SEQ_SINGLE; + pHeader->u32DataSize = u32DataSize; + pHeader->u8Channel = u8Channel; + pHeader->u16ChannelInfo = u16ChannelInfo; + memset(&pHeader->u.au8Union, 0, sizeof(pHeader->u.au8Union)); + + HGSMIBUFFERTAIL *pTail = HGSMIBufferTailFromPtr(pHeader, u32DataSize); + pTail->u32Reserved = 0; + pTail->u32Checksum = HGSMIChecksum(offBuffer, pHeader, pTail); + + return offBuffer; +} + +int HGSMIHeapSetup(HGSMIHEAP *pHeap, + void *pvBase, + HGSMISIZE cbArea, + HGSMIOFFSET offBase, + const HGSMIENV *pEnv) +{ + HGSMI_ASSERT_PTR_RETURN(pHeap, VERR_INVALID_PARAMETER); + HGSMI_ASSERT_PTR_RETURN(pvBase, VERR_INVALID_PARAMETER); + + int rc = HGSMIAreaInitialize(&pHeap->area, pvBase, cbArea, offBase); + if (RT_SUCCESS(rc)) + { + rc = HGSMIMAInit(&pHeap->ma, &pHeap->area, NULL, 0, 0, pEnv); + if (RT_FAILURE(rc)) + { + HGSMIAreaClear(&pHeap->area); + } + } + + return rc; +} + +void HGSMIHeapDestroy(HGSMIHEAP *pHeap) +{ + if (pHeap) + { + HGSMIMAUninit(&pHeap->ma); + memset(pHeap, 0, sizeof(*pHeap)); + } +} + +void *HGSMIHeapAlloc(HGSMIHEAP *pHeap, + HGSMISIZE cbData, + uint8_t u8Channel, + uint16_t u16ChannelInfo) +{ + HGSMISIZE cbAlloc = HGSMIBufferRequiredSize(cbData); + HGSMIBUFFERHEADER *pHeader = (HGSMIBUFFERHEADER *)HGSMIHeapBufferAlloc(pHeap, cbAlloc); + if (pHeader) + { + HGSMIOFFSET offBuffer = HGSMIBufferInitializeSingle(HGSMIHeapArea(pHeap), pHeader, + cbAlloc, u8Channel, u16ChannelInfo); + if (offBuffer == HGSMIOFFSET_VOID) + { + HGSMIHeapBufferFree(pHeap, pHeader); + pHeader = NULL; + } + } + + return pHeader? HGSMIBufferDataFromPtr(pHeader): NULL; +} + +void HGSMIHeapFree(HGSMIHEAP *pHeap, + void *pvData) +{ + if (pvData) + { + HGSMIBUFFERHEADER *pHeader = HGSMIBufferHeaderFromData(pvData); + HGSMIHeapBufferFree(pHeap, pHeader); + } +} + +void *HGSMIHeapBufferAlloc(HGSMIHEAP *pHeap, + HGSMISIZE cbBuffer) +{ + void *pvBuf = HGSMIMAAlloc(&pHeap->ma, cbBuffer); + return pvBuf; +} + +void HGSMIHeapBufferFree(HGSMIHEAP *pHeap, + void *pvBuf) +{ + HGSMIMAFree(&pHeap->ma, pvBuf); +} + +typedef struct HGSMIBUFFERCONTEXT +{ + const HGSMIBUFFERHEADER *pHeader; /* The original buffer header. */ + void *pvData; /* Payload data in the buffer./ */ + uint32_t cbData; /* Size of data */ +} HGSMIBUFFERCONTEXT; + +/** Verify that the given offBuffer points to a valid buffer, which is within the area. + * + * @returns VBox status and the buffer information in pBufferContext. + * @param pArea Area which supposed to contain the buffer. + * @param offBuffer The buffer location in the area. + * @param pBufferContext Where to write information about the buffer. + */ +static int hgsmiVerifyBuffer(const HGSMIAREA *pArea, + HGSMIOFFSET offBuffer, + HGSMIBUFFERCONTEXT *pBufferContext) +{ + // LogFlowFunc(("buffer 0x%x, area %p %x [0x%x;0x%x]\n", + // offBuffer, pArea->pu8Base, pArea->cbArea, pArea->offBase, pArea->offLast)); + + int rc = VINF_SUCCESS; + + if ( offBuffer < pArea->offBase + || offBuffer > pArea->offLast) + { + // LogFunc(("offset 0x%x is outside the area [0x%x;0x%x]!!!\n", + // offBuffer, pArea->offBase, pArea->offLast)); + rc = VERR_INVALID_PARAMETER; + HGSMI_STRICT_ASSERT_FAILED(); + } + else + { + void *pvBuffer = HGSMIOffsetToPointer(pArea, offBuffer); + HGSMIBUFFERHEADER header = *HGSMIBufferHeaderFromPtr(pvBuffer); + + /* Quick check of the data size, it should be less than the maximum + * data size for the buffer at this offset. + */ + // LogFlowFunc(("datasize check: header.u32DataSize = 0x%x pArea->offLast - offBuffer = 0x%x\n", + // header.u32DataSize, pArea->offLast - offBuffer)); + + if (header.u32DataSize <= pArea->offLast - offBuffer) + { + HGSMIBUFFERTAIL tail = *HGSMIBufferTailFromPtr(pvBuffer, header.u32DataSize); + + /* At least both header and tail structures are in the area. Check the checksum. */ + uint32_t u32Checksum = HGSMIChecksum(offBuffer, &header, &tail); + // LogFlowFunc(("checksum check: u32Checksum = 0x%x pTail->u32Checksum = 0x%x\n", + // u32Checksum, tail.u32Checksum)); + if (u32Checksum == tail.u32Checksum) + { + /* Success. */ + pBufferContext->pHeader = HGSMIBufferHeaderFromPtr(pvBuffer); + pBufferContext->pvData = HGSMIBufferDataFromPtr(pvBuffer); + pBufferContext->cbData = header.u32DataSize; + } + else + { + // LogFunc(("invalid checksum 0x%x, expected 0x%x!!!\n", + // u32Checksum, tail.u32Checksum)); + rc = VERR_INVALID_STATE; + HGSMI_STRICT_ASSERT_FAILED(); + } + } + else + { + // LogFunc(("invalid data size 0x%x, maximum is 0x%x!!!\n", + // header.u32DataSize, pArea->offLast - offBuffer)); + rc = VERR_TOO_MUCH_DATA; + HGSMI_STRICT_ASSERT_FAILED(); + } + } + + return rc; +} + +/** Helper to convert HGSMI channel index to the channel structure pointer. + * + * @returns Pointer to the channel data. + * @param pChannelInfo The channel pool. + * @param u8Channel The channel index. + */ +HGSMICHANNEL *HGSMIChannelFindById(HGSMICHANNELINFO *pChannelInfo, + uint8_t u8Channel) +{ + AssertCompile(RT_ELEMENTS(pChannelInfo->Channels) >= 0x100); + HGSMICHANNEL *pChannel = &pChannelInfo->Channels[u8Channel]; + + if (pChannel->u8Flags & HGSMI_CH_F_REGISTERED) + { + return pChannel; + } + + return NULL; +} + +/** Process a guest buffer. + * + * @returns VBox status code. + * @param pArea Area which supposed to contain the buffer. + * @param pChannelInfo The channel pool. + * @param offBuffer The buffer location in the area. + */ +int HGSMIBufferProcess(const HGSMIAREA *pArea, + HGSMICHANNELINFO *pChannelInfo, + HGSMIOFFSET offBuffer) +{ + // LogFlowFunc(("pArea %p, offBuffer 0x%x\n", pArea, offBuffer)); + + HGSMI_ASSERT_PTR_RETURN(pArea, VERR_INVALID_PARAMETER); + HGSMI_ASSERT_PTR_RETURN(pChannelInfo, VERR_INVALID_PARAMETER); + + /* Guest has prepared a command description at 'offBuffer'. */ + HGSMIBUFFERCONTEXT bufferContext = { NULL, NULL, 0 }; /* Makes old GCC happier. */ + int rc = hgsmiVerifyBuffer(pArea, offBuffer, &bufferContext); + if (RT_SUCCESS(rc)) + { + /* Pass the command to the appropriate handler registered with this instance. + * Start with the handler list head, which is the preallocated HGSMI setup channel. + */ + const HGSMICHANNEL *pChannel = HGSMIChannelFindById(pChannelInfo, bufferContext.pHeader->u8Channel); + if (pChannel) + { + const HGSMICHANNELHANDLER *pHandler = &pChannel->handler; + if (pHandler->pfnHandler) + { + pHandler->pfnHandler(pHandler->pvHandler, bufferContext.pHeader->u16ChannelInfo, + bufferContext.pvData, bufferContext.cbData); + } + HGSMI_STRICT_ASSERT(RT_SUCCESS(hgsmiVerifyBuffer(pArea, offBuffer, &bufferContext))); + } + else + { + rc = VERR_INVALID_FUNCTION; + HGSMI_STRICT_ASSERT_FAILED(); + } + } + + return rc; +} + +/** Register a new HGSMI channel by index. + * + * @returns VBox status code. + * @param pChannelInfo The channel pool managed by the caller. + * @param u8Channel Index of the channel. + * @param pszName Name of the channel (optional, allocated by the caller). + * @param pfnChannelHandler The channel callback. + * @param pvChannelHandler The callback pointer. + */ +int HGSMIChannelRegister(HGSMICHANNELINFO *pChannelInfo, + uint8_t u8Channel, + const char *pszName, + PFNHGSMICHANNELHANDLER pfnChannelHandler, + void *pvChannelHandler) +{ + /* Check whether the channel is already registered. */ + HGSMICHANNEL *pChannel = HGSMIChannelFindById(pChannelInfo, u8Channel); + if (pChannel) + { + HGSMI_STRICT_ASSERT_FAILED(); + return VERR_ALREADY_EXISTS; + } + + /* Channel is not yet registered. */ + pChannel = &pChannelInfo->Channels[u8Channel]; + + pChannel->u8Flags = HGSMI_CH_F_REGISTERED; + pChannel->u8Channel = u8Channel; + + pChannel->handler.pfnHandler = pfnChannelHandler; + pChannel->handler.pvHandler = pvChannelHandler; + + pChannel->pszName = pszName; + + return VINF_SUCCESS; +} diff --git a/src/HGSMIContext.h b/src/HGSMIContext.h new file mode 100644 index 0000000..716dc42 --- /dev/null +++ b/src/HGSMIContext.h @@ -0,0 +1,104 @@ +/* + * Copyright (C) 2006-2017 Oracle Corporation + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NON-INFRINGEMENT. IN NO EVENT SHALL + * THE COPYRIGHT HOLDERS, AUTHORS AND/OR ITS SUPPLIERS BE LIABLE FOR ANY CLAIM, + * DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR + * OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE + * USE OR OTHER DEALINGS IN THE SOFTWARE. + */ + + +#ifndef ___VBox_Graphics_HGSMIContext_h___ +#define ___VBox_Graphics_HGSMIContext_h___ + +#include "HGSMI.h" +#include "HGSMIChSetup.h" +#include "VBoxVideoIPRT.h" + +#ifdef VBOX_WDDM_MINIPORT +# include "wddm/VBoxMPShgsmi.h" + typedef VBOXSHGSMI HGSMIGUESTCMDHEAP; +# define HGSMIGUESTCMDHEAP_GET(_p) (&(_p)->Heap) +#else + typedef HGSMIHEAP HGSMIGUESTCMDHEAP; +# define HGSMIGUESTCMDHEAP_GET(_p) (_p) +#endif + +RT_C_DECLS_BEGIN + +/** + * Structure grouping the context needed for submitting commands to the host + * via HGSMI + */ +typedef struct HGSMIGUESTCOMMANDCONTEXT +{ + /** Information about the memory heap located in VRAM from which data + * structures to be sent to the host are allocated. */ + HGSMIGUESTCMDHEAP heapCtx; + /** The I/O port used for submitting commands to the host by writing their + * offsets into the heap. */ + RTIOPORT port; +} HGSMIGUESTCOMMANDCONTEXT, *PHGSMIGUESTCOMMANDCONTEXT; + + +/** + * Structure grouping the context needed for receiving commands from the host + * via HGSMI + */ +typedef struct HGSMIHOSTCOMMANDCONTEXT +{ + /** Information about the memory area located in VRAM in which the host + * places data structures to be read by the guest. */ + HGSMIAREA areaCtx; + /** Convenience structure used for matching host commands to handlers. */ + /** @todo handlers are registered individually in code rather than just + * passing a static structure in order to gain extra flexibility. There is + * currently no expected usage case for this though. Is the additional + * complexity really justified? */ + HGSMICHANNELINFO channels; + /** Flag to indicate that one thread is currently processing the command + * queue. */ + volatile bool fHostCmdProcessing; + /* Pointer to the VRAM location where the HGSMI host flags are kept. */ + volatile HGSMIHOSTFLAGS *pfHostFlags; + /** The I/O port used for receiving commands from the host as offsets into + * the memory area and sending back confirmations (command completion, + * IRQ acknowlegement). */ + RTIOPORT port; +} HGSMIHOSTCOMMANDCONTEXT, *PHGSMIHOSTCOMMANDCONTEXT; + +/** @name HGSMI context initialisation APIs. + * @{ */ + +/** @todo we should provide a cleanup function too as part of the API */ +DECLHIDDEN(int) VBoxHGSMISetupGuestContext(PHGSMIGUESTCOMMANDCONTEXT pCtx, + void *pvGuestHeapMemory, + uint32_t cbGuestHeapMemory, + uint32_t offVRAMGuestHeapMemory, + const HGSMIENV *pEnv); +DECLHIDDEN(void) VBoxHGSMISetupHostContext(PHGSMIHOSTCOMMANDCONTEXT pCtx, + void *pvBaseMapping, + uint32_t offHostFlags, + void *pvHostAreaMapping, + uint32_t offVRAMHostArea, + uint32_t cbHostArea); + +/** @} */ + +RT_C_DECLS_END + +#endif + diff --git a/src/HGSMIDefs.h b/src/HGSMIDefs.h new file mode 100644 index 0000000..14e51b4 --- /dev/null +++ b/src/HGSMIDefs.h @@ -0,0 +1,118 @@ +/* + * Copyright (C) 2006-2017 Oracle Corporation + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE COPYRIGHT HOLDER(S) OR AUTHOR(S) BE LIABLE FOR ANY CLAIM, DAMAGES OR + * OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, + * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR + * OTHER DEALINGS IN THE SOFTWARE. + */ + + +#ifndef ___VBox_Graphics_HGSMIDefs_h +#define ___VBox_Graphics_HGSMIDefs_h + +#include "VBoxVideoIPRT.h" + +/* HGSMI uses 32 bit offsets and sizes. */ +typedef uint32_t HGSMISIZE; +typedef uint32_t HGSMIOFFSET; + +#define HGSMIOFFSET_VOID ((HGSMIOFFSET)~0) + +/* Describes a shared memory area buffer. + * Used for calculations with offsets and for buffers verification. + */ +typedef struct HGSMIAREA +{ + uint8_t *pu8Base; /* The starting address of the area. Corresponds to offset 'offBase'. */ + HGSMIOFFSET offBase; /* The starting offset of the area. */ + HGSMIOFFSET offLast; /* The last valid offset: + * offBase + cbArea - 1 - (sizeof(header) + sizeof(tail)). + */ + HGSMISIZE cbArea; /* Size of the area. */ +} HGSMIAREA; + + +/* The buffer description flags. */ +#define HGSMI_BUFFER_HEADER_F_SEQ_MASK 0x03 /* Buffer sequence type mask. */ +#define HGSMI_BUFFER_HEADER_F_SEQ_SINGLE 0x00 /* Single buffer, not a part of a sequence. */ +#define HGSMI_BUFFER_HEADER_F_SEQ_START 0x01 /* The first buffer in a sequence. */ +#define HGSMI_BUFFER_HEADER_F_SEQ_CONTINUE 0x02 /* A middle buffer in a sequence. */ +#define HGSMI_BUFFER_HEADER_F_SEQ_END 0x03 /* The last buffer in a sequence. */ + + +#pragma pack(1) /** @todo not necessary. use AssertCompileSize instead. */ +/* 16 bytes buffer header. */ +typedef struct HGSMIBUFFERHEADER +{ + uint32_t u32DataSize; /* Size of data that follows the header. */ + + uint8_t u8Flags; /* The buffer description: HGSMI_BUFFER_HEADER_F_* */ + + uint8_t u8Channel; /* The channel the data must be routed to. */ + uint16_t u16ChannelInfo; /* Opaque to the HGSMI, used by the channel. */ + + union { + uint8_t au8Union[8]; /* Opaque placeholder to make the union 8 bytes. */ + + struct + { /* HGSMI_BUFFER_HEADER_F_SEQ_SINGLE */ + uint32_t u32Reserved1; /* A reserved field, initialize to 0. */ + uint32_t u32Reserved2; /* A reserved field, initialize to 0. */ + } Buffer; + + struct + { /* HGSMI_BUFFER_HEADER_F_SEQ_START */ + uint32_t u32SequenceNumber; /* The sequence number, the same for all buffers in the sequence. */ + uint32_t u32SequenceSize; /* The total size of the sequence. */ + } SequenceStart; + + struct + { /* HGSMI_BUFFER_HEADER_F_SEQ_CONTINUE and HGSMI_BUFFER_HEADER_F_SEQ_END */ + uint32_t u32SequenceNumber; /* The sequence number, the same for all buffers in the sequence. */ + uint32_t u32SequenceOffset; /* Data offset in the entire sequence. */ + } SequenceContinue; + } u; +} HGSMIBUFFERHEADER; + +/* 8 bytes buffer tail. */ +typedef struct HGSMIBUFFERTAIL +{ + uint32_t u32Reserved; /* Reserved, must be initialized to 0. */ + uint32_t u32Checksum; /* Verifyer for the buffer header and offset and for first 4 bytes of the tail. */ +} HGSMIBUFFERTAIL; +#pragma pack() + +AssertCompileSize(HGSMIBUFFERHEADER, 16); +AssertCompileSize(HGSMIBUFFERTAIL, 8); + +/* The size of the array of channels. Array indexes are uint8_t. Note: the value must not be changed. */ +#define HGSMI_NUMBER_OF_CHANNELS 0x100 + +typedef struct HGSMIENV +{ + /* Environment context pointer. */ + void *pvEnv; + + /* Allocate system memory. */ + DECLCALLBACKMEMBER(void *, pfnAlloc)(void *pvEnv, HGSMISIZE cb); + + /* Free system memory. */ + DECLCALLBACKMEMBER(void, pfnFree)(void *pvEnv, void *pv); +} HGSMIENV; + +#endif /* !___VBox_Graphics_HGSMIDefs_h */ + diff --git a/src/HGSMIMemAlloc.h b/src/HGSMIMemAlloc.h new file mode 100644 index 0000000..fc324ce --- /dev/null +++ b/src/HGSMIMemAlloc.h @@ -0,0 +1,56 @@ +/* $Id: HGSMIMemAlloc.h 118341 2017-10-12 14:07:17Z michael $ */ +/* + * Copyright (C) 2017 Oracle Corporation + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NON-INFRINGEMENT. IN NO EVENT SHALL + * THE COPYRIGHT HOLDERS, AUTHORS AND/OR ITS SUPPLIERS BE LIABLE FOR ANY CLAIM, + * DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR + * OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE + * USE OR OTHER DEALINGS IN THE SOFTWARE. + */ + + +/* In builds inside of the VirtualBox source tree we override the default + * HGSMIMemAlloc.h using -include, therefore this define must match the one + * there. */ +#ifndef ___VBox_Graphics_HGSMIMemAlloc_h +#define ___VBox_Graphics_HGSMIMemAlloc_h + +#include "HGSMIDefs.h" +#include "VBoxVideoIPRT.h" + +#define HGSMI_MA_DESC_ORDER_BASE UINT32_C(5) + +#define HGSMI_MA_BLOCK_SIZE_MIN (UINT32_C(1) << (HGSMI_MA_DESC_ORDER_BASE + 0)) + +typedef struct HGSMIMADATA +{ + HGSMIAREA area; + bool fAllocated; +} HGSMIMADATA; + +RT_C_DECLS_BEGIN + +int HGSMIMAInit(HGSMIMADATA *pMA, const HGSMIAREA *pArea, + HGSMIOFFSET *paDescriptors, uint32_t cDescriptors, HGSMISIZE cbMaxBlock, + const HGSMIENV *pEnv); +void HGSMIMAUninit(HGSMIMADATA *pMA); + +void *HGSMIMAAlloc(HGSMIMADATA *pMA, HGSMISIZE cb); +void HGSMIMAFree(HGSMIMADATA *pMA, void *pv); + +RT_C_DECLS_END + +#endif /* !___VBox_Graphics_HGSMIMemAlloc_h */ diff --git a/src/Makefile.am b/src/Makefile.am new file mode 100644 index 0000000..fe691a6 --- /dev/null +++ b/src/Makefile.am @@ -0,0 +1,68 @@ +# Copyright 2005 Adam Jackson. +# Copyright 2017 Oracle Corporation +# +# Permission is hereby granted, free of charge, to any person obtaining a +# copy of this software and associated documentation files (the "Software"), +# to deal in the Software without restriction, including without limitation +# on the rights to use, copy, modify, merge, publish, distribute, sub +# license, and/or sell copies of the Software, and to permit persons to whom +# the Software is furnished to do so, subject to the following conditions: +# +# The above copyright notice and this permission notice (including the next +# paragraph) shall be included in all copies or substantial portions of the +# Software. +# +# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +# FITNESS FOR A PARTICULAR PURPOSE AND NON-INFRINGEMENT. IN NO EVENT SHALL +# THE COPYRIGHT HOLDERS, AUTHORS AND/OR ITS SUPPLIERS BE LIABLE FOR ANY CLAIM, +# DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR +# OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE +# USE OR OTHER DEALINGS IN THE SOFTWARE. + + +# this is obnoxious: +# -module lets us name the module exactly how we want +# -avoid-version prevents gratuitous .0.0.0 version numbers on the end +# _ladir passes a dummy rpath to libtool so the thing will actually link +# TODO: -nostdlib/-Bstatic/-lgcc platform magic, not installing the .a, etc. + +AM_CFLAGS = $(XORG_CFLAGS) $(PCIACCESS_CFLAGS) \ + -DXORG_7X \ + -DIN_RING3 \ + -DPCIACCESS \ + -DVBOXVIDEO_13 \ + -include xorg-server.h + +vboxvideo_drv_la_LTLIBRARIES = vboxvideo_drv.la +vboxvideo_drv_la_LDFLAGS = -module -avoid-version +vboxvideo_drv_ladir = @moduledir@/drivers + +vboxvideo_drv_la_SOURCES = \ + edid.c \ + getmode.c \ + helpers.c \ + HGSMIBase.c \ + HGSMIBase.h \ + HGSMIBuffers.c \ + HGSMIChannels.h \ + HGSMIChSetup.h \ + HGSMICommon.c \ + HGSMIContext.h \ + HGSMIDefs.h \ + HGSMI.h \ + hgsmimemalloc.c \ + HGSMIMemAlloc.h \ + Modesetting.c \ + pointer.c \ + setmode.c \ + vboxvideo.c \ + VBoxVideoErr.h \ + VBoxVideoGuest.h \ + vboxvideo.h \ + VBoxVideo.h \ + VBoxVideoIPRT.h \ + VBoxVideoVBE.h \ + VBVABase.c \ + vbva.c \ + version-generated.h diff --git a/src/Modesetting.c b/src/Modesetting.c new file mode 100644 index 0000000..6da53e2 --- /dev/null +++ b/src/Modesetting.c @@ -0,0 +1,386 @@ +/* + * Copyright (C) 2006-2017 Oracle Corporation + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE COPYRIGHT HOLDER(S) OR AUTHOR(S) BE LIABLE FOR ANY CLAIM, DAMAGES OR + * OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, + * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR + * OTHER DEALINGS IN THE SOFTWARE. + */ + +#include <VBoxVideoGuest.h> +#include <VBoxVideoVBE.h> +#include <HGSMIChannels.h> + +#ifndef VBOX_GUESTR3XF86MOD +# include <VBoxVideoIPRT.h> +#endif + +/** + * Gets the count of virtual monitors attached to the guest via an HGSMI + * command + * + * @returns the right count on success or 1 on failure. + * @param pCtx the context containing the heap to use + */ +DECLHIDDEN(uint32_t) VBoxHGSMIGetMonitorCount(PHGSMIGUESTCOMMANDCONTEXT pCtx) +{ + /* Query the configured number of displays. */ + uint32_t cDisplays = 0; + VBoxQueryConfHGSMI(pCtx, VBOX_VBVA_CONF32_MONITOR_COUNT, &cDisplays); + // LogFunc(("cDisplays = %d\n", cDisplays)); + if (cDisplays == 0 || cDisplays > VBOX_VIDEO_MAX_SCREENS) + /* Host reported some bad value. Continue in the 1 screen mode. */ + cDisplays = 1; + return cDisplays; +} + + +/** + * Returns the size of the video RAM in bytes. + * + * @returns the size + */ +DECLHIDDEN(uint32_t) VBoxVideoGetVRAMSize(void) +{ + /** @note A 32bit read on this port returns the VRAM size. */ + return VBVO_PORT_READ_U32(VBE_DISPI_IOPORT_DATA); +} + + +/** + * Check whether this hardware allows the display width to have non-multiple- + * of-eight values. + * + * @returns true if any width is allowed, false otherwise. + */ +DECLHIDDEN(bool) VBoxVideoAnyWidthAllowed(void) +{ + unsigned DispiId; + VBVO_PORT_WRITE_U16(VBE_DISPI_IOPORT_INDEX, VBE_DISPI_INDEX_ID); + VBVO_PORT_WRITE_U16(VBE_DISPI_IOPORT_DATA, VBE_DISPI_ID_ANYX); + DispiId = VBVO_PORT_READ_U16(VBE_DISPI_IOPORT_DATA); + return (DispiId == VBE_DISPI_ID_ANYX); +} + + +/** + * Tell the host about how VRAM is divided up between each screen via an HGSMI + * command. It is acceptable to specifiy identical data for each screen if + * they share a single framebuffer. + * + * @returns iprt status code, either VERR_NO_MEMORY or the status returned by + * @a pfnFill + * @todo What was I thinking of with that callback function? It + * would be much simpler to just pass in a structure in normal + * memory and copy it. + * @param pCtx the context containing the heap to use + * @param u32Count the number of screens we are activating + * @param pfnFill a callback which initialises the VBVAINFOVIEW structures + * for all screens + * @param pvData context data for @a pfnFill + */ +DECLHIDDEN(int) VBoxHGSMISendViewInfo(PHGSMIGUESTCOMMANDCONTEXT pCtx, + uint32_t u32Count, + PFNHGSMIFILLVIEWINFO pfnFill, + void *pvData) +{ + int rc; + /* Issue the screen info command. */ + void *p = VBoxHGSMIBufferAlloc(pCtx, sizeof(VBVAINFOVIEW) * u32Count, + HGSMI_CH_VBVA, VBVA_INFO_VIEW); + if (p) + { + VBVAINFOVIEW *pInfo = (VBVAINFOVIEW *)p; + rc = pfnFill(pvData, pInfo, u32Count); + if (RT_SUCCESS(rc)) + VBoxHGSMIBufferSubmit (pCtx, p); + VBoxHGSMIBufferFree(pCtx, p); + } + else + rc = VERR_NO_MEMORY; + return rc; +} + + +/** + * Set a video mode using port registers. This must be done for the first + * screen before every HGSMI modeset and also works when HGSM is not enabled. + * @param cWidth the mode width + * @param cHeight the mode height + * @param cVirtWidth the mode pitch + * @param cBPP the colour depth of the mode + * @param fFlags flags for the mode. These will be or-ed with the + * default _ENABLED flag, so unless you are restoring + * a saved mode or have special requirements you can pass + * zero here. + * @param cx the horizontal panning offset + * @param cy the vertical panning offset + */ +DECLHIDDEN(void) VBoxVideoSetModeRegisters(uint16_t cWidth, uint16_t cHeight, + uint16_t cVirtWidth, uint16_t cBPP, + uint16_t fFlags, uint16_t cx, + uint16_t cy) +{ + /* set the mode characteristics */ + VBVO_PORT_WRITE_U16(VBE_DISPI_IOPORT_INDEX, VBE_DISPI_INDEX_XRES); + VBVO_PORT_WRITE_U16(VBE_DISPI_IOPORT_DATA, cWidth); + VBVO_PORT_WRITE_U16(VBE_DISPI_IOPORT_INDEX, VBE_DISPI_INDEX_YRES); + VBVO_PORT_WRITE_U16(VBE_DISPI_IOPORT_DATA, cHeight); + VBVO_PORT_WRITE_U16(VBE_DISPI_IOPORT_INDEX, + VBE_DISPI_INDEX_VIRT_WIDTH); + VBVO_PORT_WRITE_U16(VBE_DISPI_IOPORT_DATA, cVirtWidth); + VBVO_PORT_WRITE_U16(VBE_DISPI_IOPORT_INDEX, VBE_DISPI_INDEX_BPP); + VBVO_PORT_WRITE_U16(VBE_DISPI_IOPORT_DATA, cBPP); + /* enable the mode */ + VBVO_PORT_WRITE_U16(VBE_DISPI_IOPORT_INDEX, + VBE_DISPI_INDEX_ENABLE); + VBVO_PORT_WRITE_U16(VBE_DISPI_IOPORT_DATA, + fFlags | VBE_DISPI_ENABLED); + /* Panning registers */ + VBVO_PORT_WRITE_U16(VBE_DISPI_IOPORT_INDEX, + VBE_DISPI_INDEX_X_OFFSET); + VBVO_PORT_WRITE_U16(VBE_DISPI_IOPORT_DATA, cx); + VBVO_PORT_WRITE_U16(VBE_DISPI_IOPORT_INDEX, + VBE_DISPI_INDEX_Y_OFFSET); + VBVO_PORT_WRITE_U16(VBE_DISPI_IOPORT_DATA, cy); + /** @todo read from the port to see if the mode switch was successful */ +} + + +/** + * Get the video mode for the first screen using the port registers. All + * parameters are optional + * @returns true if the VBE mode returned is active, false if we are in VGA + * mode + * @note If anyone else needs additional register values just extend the + * function with additional parameters and fix any existing callers. + * @param pcWidth where to store the mode width + * @param pcHeight where to store the mode height + * @param pcVirtWidth where to store the mode pitch + * @param pcBPP where to store the colour depth of the mode + * @param pfFlags where to store the flags for the mode + */ +DECLHIDDEN(bool) VBoxVideoGetModeRegisters(uint16_t *pcWidth, uint16_t *pcHeight, + uint16_t *pcVirtWidth, uint16_t *pcBPP, + uint16_t *pfFlags) +{ + uint16_t fFlags; + + VBVO_PORT_WRITE_U16(VBE_DISPI_IOPORT_INDEX, + VBE_DISPI_INDEX_ENABLE); + fFlags = VBVO_PORT_READ_U16(VBE_DISPI_IOPORT_DATA); + if (pcWidth) + { + VBVO_PORT_WRITE_U16(VBE_DISPI_IOPORT_INDEX, + VBE_DISPI_INDEX_XRES); + *pcWidth = VBVO_PORT_READ_U16(VBE_DISPI_IOPORT_DATA); + } + if (pcHeight) + { + VBVO_PORT_WRITE_U16(VBE_DISPI_IOPORT_INDEX, + VBE_DISPI_INDEX_YRES); + *pcHeight = VBVO_PORT_READ_U16(VBE_DISPI_IOPORT_DATA); + } + if (pcVirtWidth) + { + VBVO_PORT_WRITE_U16(VBE_DISPI_IOPORT_INDEX, + VBE_DISPI_INDEX_VIRT_WIDTH); + *pcVirtWidth = VBVO_PORT_READ_U16(VBE_DISPI_IOPORT_DATA); + } + if (pcBPP) + { + VBVO_PORT_WRITE_U16(VBE_DISPI_IOPORT_INDEX, + VBE_DISPI_INDEX_BPP); + *pcBPP = VBVO_PORT_READ_U16(VBE_DISPI_IOPORT_DATA); + } + if (pfFlags) + *pfFlags = fFlags; + return !!(fFlags & VBE_DISPI_ENABLED); +} + + +/** + * Disable our extended graphics mode and go back to VGA mode. + */ +DECLHIDDEN(void) VBoxVideoDisableVBE(void) +{ + VBVO_PORT_WRITE_U16(VBE_DISPI_IOPORT_INDEX, + VBE_DISPI_INDEX_ENABLE); + VBVO_PORT_WRITE_U16(VBE_DISPI_IOPORT_DATA, 0); +} + + +/** + * Set a video mode via an HGSMI request. The views must have been + * initialised first using @a VBoxHGSMISendViewInfo and if the mode is being + * set on the first display then it must be set first using registers. + * @param pCtx The context containing the heap to use. + * @param cDisplay the screen number + * @param cOriginX the horizontal displacement relative to the first screen + * @param cOriginY the vertical displacement relative to the first screen + * @param offStart the offset of the visible area of the framebuffer + * relative to the framebuffer start + * @param cbPitch the offset in bytes between the starts of two adjecent + * scan lines in video RAM + * @param cWidth the mode width + * @param cHeight the mode height + * @param cBPP the colour depth of the mode + * @param fFlags flags + */ +DECLHIDDEN(void) VBoxHGSMIProcessDisplayInfo(PHGSMIGUESTCOMMANDCONTEXT pCtx, + uint32_t cDisplay, + int32_t cOriginX, + int32_t cOriginY, + uint32_t offStart, + uint32_t cbPitch, + uint32_t cWidth, + uint32_t cHeight, + uint16_t cBPP, + uint16_t fFlags) +{ + /* Issue the screen info command. */ + void *p = VBoxHGSMIBufferAlloc(pCtx, + sizeof (VBVAINFOSCREEN), + HGSMI_CH_VBVA, + VBVA_INFO_SCREEN); + if (!p) + { + // LogFunc(("HGSMIHeapAlloc failed\n")); + } + else + { + VBVAINFOSCREEN *pScreen = (VBVAINFOSCREEN *)p; + + pScreen->u32ViewIndex = cDisplay; + pScreen->i32OriginX = cOriginX; + pScreen->i32OriginY = cOriginY; + pScreen->u32StartOffset = offStart; + pScreen->u32LineSize = cbPitch; + pScreen->u32Width = cWidth; + pScreen->u32Height = cHeight; + pScreen->u16BitsPerPixel = cBPP; + pScreen->u16Flags = fFlags; + + VBoxHGSMIBufferSubmit(pCtx, p); + + VBoxHGSMIBufferFree(pCtx, p); + } +} + + +/** Report the rectangle relative to which absolute pointer events should be + * expressed. This information remains valid until the next VBVA resize event + * for any screen, at which time it is reset to the bounding rectangle of all + * virtual screens. + * @param pCtx The context containing the heap to use. + * @param cOriginX Upper left X co-ordinate relative to the first screen. + * @param cOriginY Upper left Y co-ordinate relative to the first screen. + * @param cWidth Rectangle width. + * @param cHeight Rectangle height. + * @returns iprt status code. + * @returns VERR_NO_MEMORY HGSMI heap allocation failed. + */ +DECLHIDDEN(int) VBoxHGSMIUpdateInputMapping(PHGSMIGUESTCOMMANDCONTEXT pCtx, int32_t cOriginX, int32_t cOriginY, + uint32_t cWidth, uint32_t cHeight) +{ + int rc = VINF_SUCCESS; + VBVAREPORTINPUTMAPPING *p; + // Log(("%s: cOriginX=%d, cOriginY=%d, cWidth=%u, cHeight=%u\n", __PRETTY_FUNCTION__, (int)cOriginX, (int)cOriginX, + // (unsigned)cWidth, (unsigned)cHeight)); + + /* Allocate the IO buffer. */ + p = (VBVAREPORTINPUTMAPPING *)VBoxHGSMIBufferAlloc(pCtx, sizeof(VBVAREPORTINPUTMAPPING), HGSMI_CH_VBVA, + VBVA_REPORT_INPUT_MAPPING); + if (p) + { + /* Prepare data to be sent to the host. */ + p->x = cOriginX; + p->y = cOriginY; + p->cx = cWidth; + p->cy = cHeight; + rc = VBoxHGSMIBufferSubmit(pCtx, p); + /* Free the IO buffer. */ + VBoxHGSMIBufferFree(pCtx, p); + } + else + rc = VERR_NO_MEMORY; + // LogFunc(("rc = %d\n", rc)); + return rc; +} + + +/** + * Get most recent video mode hints. + * @param pCtx the context containing the heap to use + * @param cScreens the number of screens to query hints for, starting at 0. + * @param paHints array of VBVAMODEHINT structures for receiving the hints. + * @returns iprt status code + * @returns VERR_NO_MEMORY HGSMI heap allocation failed. + * @returns VERR_NOT_SUPPORTED Host does not support this command. + */ +DECLHIDDEN(int) VBoxHGSMIGetModeHints(PHGSMIGUESTCOMMANDCONTEXT pCtx, + unsigned cScreens, VBVAMODEHINT *paHints) +{ + int rc; + void *p; + + AssertPtr(paHints); + if (!paHints) + return VERR_INVALID_POINTER; + + p = VBoxHGSMIBufferAlloc(pCtx, sizeof(VBVAQUERYMODEHINTS) + + cScreens * sizeof(VBVAMODEHINT), + HGSMI_CH_VBVA, VBVA_QUERY_MODE_HINTS); + if (!p) + { + // LogFunc(("HGSMIHeapAlloc failed\n")); + return VERR_NO_MEMORY; + } + else + { + VBVAQUERYMODEHINTS *pQuery = (VBVAQUERYMODEHINTS *)p; + + pQuery->cHintsQueried = cScreens; + pQuery->cbHintStructureGuest = sizeof(VBVAMODEHINT); + pQuery->rc = VERR_NOT_SUPPORTED; + + VBoxHGSMIBufferSubmit(pCtx, p); + rc = pQuery->rc; + if (RT_SUCCESS(rc)) + memcpy(paHints, ((uint8_t *)p) + sizeof(VBVAQUERYMODEHINTS), + cScreens * sizeof(VBVAMODEHINT)); + + VBoxHGSMIBufferFree(pCtx, p); + } + return rc; +} + + +/** + * Query the supported flags in VBVAINFOSCREEN::u16Flags. + * + * @returns The mask of VBVA_SCREEN_F_* flags or 0 if host does not support the request. + * @param pCtx the context containing the heap to use + */ +DECLHIDDEN(uint16_t) VBoxHGSMIGetScreenFlags(PHGSMIGUESTCOMMANDCONTEXT pCtx) +{ + uint32_t u32Flags = 0; + int rc = VBoxQueryConfHGSMI(pCtx, VBOX_VBVA_CONF32_SCREEN_FLAGS, &u32Flags); + // LogFunc(("u32Flags = 0x%x rc %Rrc\n", u32Flags, rc)); + if (RT_FAILURE(rc) || u32Flags > UINT16_MAX) + u32Flags = 0; + return (uint16_t)u32Flags; +} diff --git a/src/README.testing b/src/README.testing new file mode 100644 index 0000000..dc441c1 --- /dev/null +++ b/src/README.testing @@ -0,0 +1,34 @@ +This file contains some notes about things to try out to give the X.Org video +driver a reasonably thorough test. We will add cases of things which have been +known to fail in the past to this file as we discover them. + + * Test XFree86 guests (CentOS 3), early X.Org (CentOS 5) and recent + (CentOS 6 and 7, current Ubuntu/Fedora). Test Solaris guests (10 and 11?). + * Dynamic resizing should work, on CentOS 6 and later Linux guests it should + work without VBoxClient running. + * Disabling and enabling virtual screens (VBoxManage in 4.3). + * Dynamic resizing with one of more virtual screens disabled. + * Test switching to virtual terminals and back from windowed, full screen and + seamless modes (seamless currently only works properly with VBoxClient + running). + * Test switching directly between normal, full-screen, seamless and scaled + modes. + * execute "xprop -root | grep VBOX" after resizing a screen: VBOX_SIZE_HINTS + should be set, and VBOX_SIZE_HINTS_MISMATCH should equal 0 on CentOS 6 and + later (4.4 and later). + * Test re-ordering the virtual screen using the native guest operating system + tools and make sure that mouse integration still works as expected. + * Test disabling and re-enabling guest screens with the native system tools. + * Try disabling and re-enabling mouse integration and check that capturing + works with multiple guest screens. + * Shutting down and re-starting a virtual machine should restore the last size + for all monitors (note: currently only after log-in). Full shut-down, not + a reboot. + * Test power management by disabling guest screens ("xrandr --output VGA-n + --off") and re-enabling them ("xrandr --output VGA-n --preferred --pos XxY") + where X and Y are the position of the screen before disabling it. + * Test sending video mode hints with screen position information via + VBoxManage. The screen position is a hint only. The approximate position + should be preserved after a shut down and re-start of the guest. + * Test re-starting the X server after resizing all guest windows. The server + should not crash. diff --git a/src/VBVABase.c b/src/VBVABase.c new file mode 100644 index 0000000..8f1d545 --- /dev/null +++ b/src/VBVABase.c @@ -0,0 +1,390 @@ +/* + * Copyright (C) 2006-2017 Oracle Corporation + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE COPYRIGHT HOLDER(S) OR AUTHOR(S) BE LIABLE FOR ANY CLAIM, DAMAGES OR + * OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, + * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR + * OTHER DEALINGS IN THE SOFTWARE. + */ + +#include <VBoxVideoGuest.h> +#include <VBoxVideoIPRT.h> +#include <HGSMIChannels.h> + +/* + * There is a hardware ring buffer in the graphics device video RAM, formerly + * in the VBox VMMDev PCI memory space. + * All graphics commands go there serialized by VBoxVBVABufferBeginUpdate. + * and vboxHwBufferEndUpdate. + * + * off32Free is writing position. off32Data is reading position. + * off32Free == off32Data means buffer is empty. + * There must be always gap between off32Data and off32Free when data + * are in the buffer. + * Guest only changes off32Free, host changes off32Data. + */ + +/* Forward declarations of internal functions. */ +static void vboxHwBufferFlush(PHGSMIGUESTCOMMANDCONTEXT pCtx); +static void vboxHwBufferPlaceDataAt(PVBVABUFFERCONTEXT pCtx, const void *p, + uint32_t cb, uint32_t offset); +static bool vboxHwBufferWrite(PVBVABUFFERCONTEXT pCtx, + PHGSMIGUESTCOMMANDCONTEXT pHGSMICtx, + const void *p, uint32_t cb); + + +static bool vboxVBVAInformHost(PVBVABUFFERCONTEXT pCtx, + PHGSMIGUESTCOMMANDCONTEXT pHGSMICtx, + int32_t cScreen, bool bEnable) +{ + bool bRc = false; + +#if 0 /* All callers check this */ + if (ppdev->bHGSMISupported) +#endif + { + void *p = VBoxHGSMIBufferAlloc(pHGSMICtx, + sizeof (VBVAENABLE_EX), + HGSMI_CH_VBVA, + VBVA_ENABLE); + if (!p) + { + // LogFunc(("HGSMIHeapAlloc failed\n")); + } + else + { + VBVAENABLE_EX *pEnable = (VBVAENABLE_EX *)p; + + pEnable->Base.u32Flags = bEnable? VBVA_F_ENABLE: VBVA_F_DISABLE; + pEnable->Base.u32Offset = pCtx->offVRAMBuffer; + pEnable->Base.i32Result = VERR_NOT_SUPPORTED; + if (cScreen >= 0) + { + pEnable->Base.u32Flags |= VBVA_F_EXTENDED | VBVA_F_ABSOFFSET; + pEnable->u32ScreenId = cScreen; + } + + VBoxHGSMIBufferSubmit(pHGSMICtx, p); + + if (bEnable) + { + bRc = RT_SUCCESS(pEnable->Base.i32Result); + } + else + { + bRc = true; + } + + VBoxHGSMIBufferFree(pHGSMICtx, p); + } + } + + return bRc; +} + +/* + * Public hardware buffer methods. + */ +DECLHIDDEN(bool) VBoxVBVAEnable(PVBVABUFFERCONTEXT pCtx, + PHGSMIGUESTCOMMANDCONTEXT pHGSMICtx, + VBVABUFFER *pVBVA, int32_t cScreen) +{ + bool bRc = false; + + // LogFlowFunc(("pVBVA %p\n", pVBVA)); + +#if 0 /* All callers check this */ + if (ppdev->bHGSMISupported) +#endif + { + // LogFunc(("pVBVA %p vbva off 0x%x\n", pVBVA, pCtx->offVRAMBuffer)); + + pVBVA->hostFlags.u32HostEvents = 0; + pVBVA->hostFlags.u32SupportedOrders = 0; + pVBVA->off32Data = 0; + pVBVA->off32Free = 0; + memset(pVBVA->aRecords, 0, sizeof (pVBVA->aRecords)); + pVBVA->indexRecordFirst = 0; + pVBVA->indexRecordFree = 0; + pVBVA->cbPartialWriteThreshold = 256; + pVBVA->cbData = pCtx->cbBuffer - sizeof (VBVABUFFER) + sizeof (pVBVA->au8Data); + + pCtx->fHwBufferOverflow = false; + pCtx->pRecord = NULL; + pCtx->pVBVA = pVBVA; + + bRc = vboxVBVAInformHost(pCtx, pHGSMICtx, cScreen, true); + } + + if (!bRc) + { + VBoxVBVADisable(pCtx, pHGSMICtx, cScreen); + } + + return bRc; +} + +DECLHIDDEN(void) VBoxVBVADisable(PVBVABUFFERCONTEXT pCtx, + PHGSMIGUESTCOMMANDCONTEXT pHGSMICtx, + int32_t cScreen) +{ + // LogFlowFunc(("\n")); + + pCtx->fHwBufferOverflow = false; + pCtx->pRecord = NULL; + pCtx->pVBVA = NULL; + + vboxVBVAInformHost(pCtx, pHGSMICtx, cScreen, false); + + return; +} + +DECLHIDDEN(bool) VBoxVBVABufferBeginUpdate(PVBVABUFFERCONTEXT pCtx, + PHGSMIGUESTCOMMANDCONTEXT pHGSMICtx) +{ + bool bRc = false; + + // LogFunc(("flags = 0x%08X\n", pCtx->pVBVA? pCtx->pVBVA->u32HostEvents: -1)); + + if ( pCtx->pVBVA + && (pCtx->pVBVA->hostFlags.u32HostEvents & VBVA_F_MODE_ENABLED)) + { + uint32_t indexRecordNext; + + Assert(!pCtx->fHwBufferOverflow); + Assert(pCtx->pRecord == NULL); + + indexRecordNext = (pCtx->pVBVA->indexRecordFree + 1) % VBVA_MAX_RECORDS; + + if (indexRecordNext == pCtx->pVBVA->indexRecordFirst) + { + /* All slots in the records queue are used. */ + vboxHwBufferFlush (pHGSMICtx); + } + + if (indexRecordNext == pCtx->pVBVA->indexRecordFirst) + { + /* Even after flush there is no place. Fail the request. */ + // LogFunc(("no space in the queue of records!!! first %d, last %d\n", + // pCtx->pVBVA->indexRecordFirst, pCtx->pVBVA->indexRecordFree)); + } + else + { + /* Initialize the record. */ + VBVARECORD *pRecord = &pCtx->pVBVA->aRecords[pCtx->pVBVA->indexRecordFree]; + + pRecord->cbRecord = VBVA_F_RECORD_PARTIAL; + + pCtx->pVBVA->indexRecordFree = indexRecordNext; + + // LogFunc(("indexRecordNext = %d\n", indexRecordNext)); + + /* Remember which record we are using. */ + pCtx->pRecord = pRecord; + + bRc = true; + } + } + + return bRc; +} + +DECLHIDDEN(void) VBoxVBVABufferEndUpdate(PVBVABUFFERCONTEXT pCtx) +{ + VBVARECORD *pRecord; + + // LogFunc(("\n")); + + Assert(pCtx->pVBVA); + + pRecord = pCtx->pRecord; + Assert(pRecord && (pRecord->cbRecord & VBVA_F_RECORD_PARTIAL)); + + /* Mark the record completed. */ + pRecord->cbRecord &= ~VBVA_F_RECORD_PARTIAL; + + pCtx->fHwBufferOverflow = false; + pCtx->pRecord = NULL; + + return; +} + +/* + * Private operations. + */ +static uint32_t vboxHwBufferAvail (const VBVABUFFER *pVBVA) +{ + int32_t i32Diff = pVBVA->off32Data - pVBVA->off32Free; + + return i32Diff > 0? i32Diff: pVBVA->cbData + i32Diff; +} + +static void vboxHwBufferFlush(PHGSMIGUESTCOMMANDCONTEXT pCtx) +{ + /* Issue the flush command. */ + void *p = VBoxHGSMIBufferAlloc(pCtx, + sizeof (VBVAFLUSH), + HGSMI_CH_VBVA, + VBVA_FLUSH); + if (!p) + { + // LogFunc(("HGSMIHeapAlloc failed\n")); + } + else + { + VBVAFLUSH *pFlush = (VBVAFLUSH *)p; + + pFlush->u32Reserved = 0; + + VBoxHGSMIBufferSubmit(pCtx, p); + + VBoxHGSMIBufferFree(pCtx, p); + } + + return; +} + +static void vboxHwBufferPlaceDataAt(PVBVABUFFERCONTEXT pCtx, const void *p, + uint32_t cb, uint32_t offset) +{ + VBVABUFFER *pVBVA = pCtx->pVBVA; + uint32_t u32BytesTillBoundary = pVBVA->cbData - offset; + uint8_t *dst = &pVBVA->au8Data[offset]; + int32_t i32Diff = cb - u32BytesTillBoundary; + + if (i32Diff <= 0) + { + /* Chunk will not cross buffer boundary. */ + memcpy (dst, p, cb); + } + else + { + /* Chunk crosses buffer boundary. */ + memcpy (dst, p, u32BytesTillBoundary); + memcpy (&pVBVA->au8Data[0], (uint8_t *)p + u32BytesTillBoundary, i32Diff); + } + + return; +} + +static bool vboxHwBufferWrite(PVBVABUFFERCONTEXT pCtx, + PHGSMIGUESTCOMMANDCONTEXT pHGSMICtx, + const void *p, uint32_t cb) +{ + VBVARECORD *pRecord; + uint32_t cbHwBufferAvail; + + uint32_t cbWritten = 0; + + VBVABUFFER *pVBVA = pCtx->pVBVA; + Assert(pVBVA); + + if (!pVBVA || pCtx->fHwBufferOverflow) + { + return false; + } + + Assert(pVBVA->indexRecordFirst != pVBVA->indexRecordFree); + + pRecord = pCtx->pRecord; + Assert(pRecord && (pRecord->cbRecord & VBVA_F_RECORD_PARTIAL)); + + // LogFunc(("%d\n", cb)); + + cbHwBufferAvail = vboxHwBufferAvail (pVBVA); + + while (cb > 0) + { + uint32_t cbChunk = cb; + + // LogFunc(("pVBVA->off32Free %d, pRecord->cbRecord 0x%08X, cbHwBufferAvail %d, cb %d, cbWritten %d\n", + // pVBVA->off32Free, pRecord->cbRecord, cbHwBufferAvail, cb, cbWritten)); + + if (cbChunk >= cbHwBufferAvail) + { + // LogFunc(("1) avail %d, chunk %d\n", cbHwBufferAvail, cbChunk)); + + vboxHwBufferFlush (pHGSMICtx); + + cbHwBufferAvail = vboxHwBufferAvail (pVBVA); + + if (cbChunk >= cbHwBufferAvail) + { + // LogFunc(("no place for %d bytes. Only %d bytes available after flush. Going to partial writes.\n", + // cb, cbHwBufferAvail)); + + if (cbHwBufferAvail <= pVBVA->cbPartialWriteThreshold) + { + // LogFunc(("Buffer overflow!!!\n")); + pCtx->fHwBufferOverflow = true; + Assert(false); + return false; + } + + cbChunk = cbHwBufferAvail - pVBVA->cbPartialWriteThreshold; + } + } + + Assert(cbChunk <= cb); + Assert(cbChunk <= vboxHwBufferAvail (pVBVA)); + + vboxHwBufferPlaceDataAt (pCtx, (uint8_t *)p + cbWritten, cbChunk, pVBVA->off32Free); + + pVBVA->off32Free = (pVBVA->off32Free + cbChunk) % pVBVA->cbData; + pRecord->cbRecord += cbChunk; + cbHwBufferAvail -= cbChunk; + + cb -= cbChunk; + cbWritten += cbChunk; + } + + return true; +} + +/* + * Public writer to the hardware buffer. + */ +DECLHIDDEN(bool) VBoxVBVAWrite(PVBVABUFFERCONTEXT pCtx, + PHGSMIGUESTCOMMANDCONTEXT pHGSMICtx, + const void *pv, uint32_t cb) +{ + return vboxHwBufferWrite (pCtx, pHGSMICtx, pv, cb); +} + +DECLHIDDEN(bool) VBoxVBVAOrderSupported(PVBVABUFFERCONTEXT pCtx, unsigned code) +{ + VBVABUFFER *pVBVA = pCtx->pVBVA; + + if (!pVBVA) + { + return false; + } + + if (pVBVA->hostFlags.u32SupportedOrders & (1 << code)) + { + return true; + } + + return false; +} + +DECLHIDDEN(void) VBoxVBVASetupBufferContext(PVBVABUFFERCONTEXT pCtx, + uint32_t offVRAMBuffer, + uint32_t cbBuffer) +{ + pCtx->offVRAMBuffer = offVRAMBuffer; + pCtx->cbBuffer = cbBuffer; +} diff --git a/src/VBoxVideo.h b/src/VBoxVideo.h new file mode 100644 index 0000000..2001246 --- /dev/null +++ b/src/VBoxVideo.h @@ -0,0 +1,2106 @@ +/** @file + * VirtualBox Video interface. + */ + +/* + * Copyright (C) 2006-2016 Oracle Corporation + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the + * "Software"), to deal in the Software without restriction, including + * without limitation the rights to use, copy, modify, merge, publish, + * distribute, sub license, and/or sell copies of the Software, and to + * permit persons to whom the Software is furnished to do so, subject to + * the following conditions: + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NON-INFRINGEMENT. IN NO EVENT SHALL + * THE COPYRIGHT HOLDERS, AUTHORS AND/OR ITS SUPPLIERS BE LIABLE FOR ANY CLAIM, + * DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR + * OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE + * USE OR OTHER DEALINGS IN THE SOFTWARE. + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + */ + +#ifndef ___VBox_Graphics_VBoxVideo_h +#define ___VBox_Graphics_VBoxVideo_h + +#include "VBoxVideoIPRT.h" + +/* this should be in sync with monitorCount <xsd:maxInclusive value="64"/> in src/VBox/Main/xml/VirtualBox-settings-common.xsd */ +#define VBOX_VIDEO_MAX_SCREENS 64 + +/* + * The last 4096 bytes of the guest VRAM contains the generic info for all + * DualView chunks: sizes and offsets of chunks. This is filled by miniport. + * + * Last 4096 bytes of each chunk contain chunk specific data: framebuffer info, + * etc. This is used exclusively by the corresponding instance of a display driver. + * + * The VRAM layout: + * Last 4096 bytes - Adapter information area. + * 4096 bytes aligned miniport heap (value specified in the config rouded up). + * Slack - what left after dividing the VRAM. + * 4096 bytes aligned framebuffers: + * last 4096 bytes of each framebuffer is the display information area. + * + * The Virtual Graphics Adapter information in the guest VRAM is stored by the + * guest video driver using structures prepended by VBOXVIDEOINFOHDR. + * + * When the guest driver writes dword 0 to the VBE_DISPI_INDEX_VBOX_VIDEO + * the host starts to process the info. The first element at the start of + * the 4096 bytes region should be normally be a LINK that points to + * actual information chain. That way the guest driver can have some + * fixed layout of the information memory block and just rewrite + * the link to point to relevant memory chain. + * + * The processing stops at the END element. + * + * The host can access the memory only when the port IO is processed. + * All data that will be needed later must be copied from these 4096 bytes. + * But other VRAM can be used by host until the mode is disabled. + * + * The guest driver writes dword 0xffffffff to the VBE_DISPI_INDEX_VBOX_VIDEO + * to disable the mode. + * + * VBE_DISPI_INDEX_VBOX_VIDEO is used to read the configuration information + * from the host and issue commands to the host. + * + * The guest writes the VBE_DISPI_INDEX_VBOX_VIDEO index register, the the + * following operations with the VBE data register can be performed: + * + * Operation Result + * write 16 bit value NOP + * read 16 bit value count of monitors + * write 32 bit value sets the vbox command value and the command processed by the host + * read 32 bit value result of the last vbox command is returned + */ + +#define VBOX_VIDEO_PRIMARY_SCREEN 0 +#define VBOX_VIDEO_NO_SCREEN ~0 + +/** + * VBVA command header. + * + * @todo Where does this fit in? + */ +typedef struct VBVACMDHDR +{ + /** Coordinates of affected rectangle. */ + int16_t x; + int16_t y; + uint16_t w; + uint16_t h; +} VBVACMDHDR; +AssertCompileSize(VBVACMDHDR, 8); + +/** @name VBVA ring defines. + * + * The VBVA ring buffer is suitable for transferring large (< 2GB) amount of + * data. For example big bitmaps which do not fit to the buffer. + * + * Guest starts writing to the buffer by initializing a record entry in the + * aRecords queue. VBVA_F_RECORD_PARTIAL indicates that the record is being + * written. As data is written to the ring buffer, the guest increases off32End + * for the record. + * + * The host reads the aRecords on flushes and processes all completed records. + * When host encounters situation when only a partial record presents and + * cbRecord & ~VBVA_F_RECORD_PARTIAL >= VBVA_RING_BUFFER_SIZE - + * VBVA_RING_BUFFER_THRESHOLD, the host fetched all record data and updates + * off32Head. After that on each flush the host continues fetching the data + * until the record is completed. + * + */ +#define VBVA_RING_BUFFER_SIZE (_4M - _1K) +#define VBVA_RING_BUFFER_THRESHOLD (4 * _1K) + +#define VBVA_MAX_RECORDS (64) + +#define VBVA_F_MODE_ENABLED UINT32_C(0x00000001) +#define VBVA_F_MODE_VRDP UINT32_C(0x00000002) +#define VBVA_F_MODE_VRDP_RESET UINT32_C(0x00000004) +#define VBVA_F_MODE_VRDP_ORDER_MASK UINT32_C(0x00000008) + +#define VBVA_F_STATE_PROCESSING UINT32_C(0x00010000) + +#define VBVA_F_RECORD_PARTIAL UINT32_C(0x80000000) + +/** + * VBVA record. + */ +typedef struct VBVARECORD +{ + /** The length of the record. Changed by guest. */ + uint32_t cbRecord; +} VBVARECORD; +AssertCompileSize(VBVARECORD, 4); + +/* The size of the information. */ +/* + * The minimum HGSMI heap size is PAGE_SIZE (4096 bytes) and is a restriction of the + * runtime heapsimple API. Use minimum 2 pages here, because the info area also may + * contain other data (for example HGSMIHOSTFLAGS structure). + */ +#ifndef VBOX_XPDM_MINIPORT +# define VBVA_ADAPTER_INFORMATION_SIZE (64*_1K) +#else +#define VBVA_ADAPTER_INFORMATION_SIZE (16*_1K) +#define VBVA_DISPLAY_INFORMATION_SIZE (64*_1K) +#endif +#define VBVA_MIN_BUFFER_SIZE (64*_1K) + + +/* The value for port IO to let the adapter to interpret the adapter memory. */ +#define VBOX_VIDEO_DISABLE_ADAPTER_MEMORY 0xFFFFFFFF + +/* The value for port IO to let the adapter to interpret the adapter memory. */ +#define VBOX_VIDEO_INTERPRET_ADAPTER_MEMORY 0x00000000 + +/* The value for port IO to let the adapter to interpret the display memory. + * The display number is encoded in low 16 bits. + */ +#define VBOX_VIDEO_INTERPRET_DISPLAY_MEMORY_BASE 0x00010000 + + +/* The end of the information. */ +#define VBOX_VIDEO_INFO_TYPE_END 0 +/* Instructs the host to fetch the next VBOXVIDEOINFOHDR at the given offset of VRAM. */ +#define VBOX_VIDEO_INFO_TYPE_LINK 1 +/* Information about a display memory position. */ +#define VBOX_VIDEO_INFO_TYPE_DISPLAY 2 +/* Information about a screen. */ +#define VBOX_VIDEO_INFO_TYPE_SCREEN 3 +/* Information about host notifications for the driver. */ +#define VBOX_VIDEO_INFO_TYPE_HOST_EVENTS 4 +/* Information about non-volatile guest VRAM heap. */ +#define VBOX_VIDEO_INFO_TYPE_NV_HEAP 5 +/* VBVA enable/disable. */ +#define VBOX_VIDEO_INFO_TYPE_VBVA_STATUS 6 +/* VBVA flush. */ +#define VBOX_VIDEO_INFO_TYPE_VBVA_FLUSH 7 +/* Query configuration value. */ +#define VBOX_VIDEO_INFO_TYPE_QUERY_CONF32 8 + + +#pragma pack(1) +typedef struct VBOXVIDEOINFOHDR +{ + uint8_t u8Type; + uint8_t u8Reserved; + uint16_t u16Length; +} VBOXVIDEOINFOHDR; + + +typedef struct VBOXVIDEOINFOLINK +{ + /* Relative offset in VRAM */ + int32_t i32Offset; +} VBOXVIDEOINFOLINK; + + +/* Resides in adapter info memory. Describes a display VRAM chunk. */ +typedef struct VBOXVIDEOINFODISPLAY +{ + /* Index of the framebuffer assigned by guest. */ + uint32_t u32Index; + + /* Absolute offset in VRAM of the framebuffer to be displayed on the monitor. */ + uint32_t u32Offset; + + /* The size of the memory that can be used for the screen. */ + uint32_t u32FramebufferSize; + + /* The size of the memory that is used for the Display information. + * The information is at u32Offset + u32FramebufferSize + */ + uint32_t u32InformationSize; + +} VBOXVIDEOINFODISPLAY; + + +/* Resides in display info area, describes the current video mode. */ +#define VBOX_VIDEO_INFO_SCREEN_F_NONE 0x00 +#define VBOX_VIDEO_INFO_SCREEN_F_ACTIVE 0x01 + +typedef struct VBOXVIDEOINFOSCREEN +{ + /* Physical X origin relative to the primary screen. */ + int32_t xOrigin; + + /* Physical Y origin relative to the primary screen. */ + int32_t yOrigin; + + /* The scan line size in bytes. */ + uint32_t u32LineSize; + + /* Width of the screen. */ + uint16_t u16Width; + + /* Height of the screen. */ + uint16_t u16Height; + + /* Color depth. */ + uint8_t bitsPerPixel; + + /* VBOX_VIDEO_INFO_SCREEN_F_* */ + uint8_t u8Flags; +} VBOXVIDEOINFOSCREEN; + +/* The guest initializes the structure to 0. The positions of the structure in the + * display info area must not be changed, host will update the structure. Guest checks + * the events and modifies the structure as a response to host. + */ +#define VBOX_VIDEO_INFO_HOST_EVENTS_F_NONE 0x00000000 +#define VBOX_VIDEO_INFO_HOST_EVENTS_F_VRDP_RESET 0x00000080 + +typedef struct VBOXVIDEOINFOHOSTEVENTS +{ + /* Host events. */ + uint32_t fu32Events; +} VBOXVIDEOINFOHOSTEVENTS; + +/* Resides in adapter info memory. Describes the non-volatile VRAM heap. */ +typedef struct VBOXVIDEOINFONVHEAP +{ + /* Absolute offset in VRAM of the start of the heap. */ + uint32_t u32HeapOffset; + + /* The size of the heap. */ + uint32_t u32HeapSize; + +} VBOXVIDEOINFONVHEAP; + +/* Display information area. */ +typedef struct VBOXVIDEOINFOVBVASTATUS +{ + /* Absolute offset in VRAM of the start of the VBVA QUEUE. 0 to disable VBVA. */ + uint32_t u32QueueOffset; + + /* The size of the VBVA QUEUE. 0 to disable VBVA. */ + uint32_t u32QueueSize; + +} VBOXVIDEOINFOVBVASTATUS; + +typedef struct VBOXVIDEOINFOVBVAFLUSH +{ + uint32_t u32DataStart; + + uint32_t u32DataEnd; + +} VBOXVIDEOINFOVBVAFLUSH; + +#define VBOX_VIDEO_QCI32_MONITOR_COUNT 0 +#define VBOX_VIDEO_QCI32_OFFSCREEN_HEAP_SIZE 1 + +typedef struct VBOXVIDEOINFOQUERYCONF32 +{ + uint32_t u32Index; + + uint32_t u32Value; + +} VBOXVIDEOINFOQUERYCONF32; +#pragma pack() + +#ifdef VBOX_WITH_VIDEOHWACCEL +#pragma pack(1) + +#define VBOXVHWA_VERSION_MAJ 0 +#define VBOXVHWA_VERSION_MIN 0 +#define VBOXVHWA_VERSION_BLD 6 +#define VBOXVHWA_VERSION_RSV 0 + +typedef enum +{ + VBOXVHWACMD_TYPE_SURF_CANCREATE = 1, + VBOXVHWACMD_TYPE_SURF_CREATE, + VBOXVHWACMD_TYPE_SURF_DESTROY, + VBOXVHWACMD_TYPE_SURF_LOCK, + VBOXVHWACMD_TYPE_SURF_UNLOCK, + VBOXVHWACMD_TYPE_SURF_BLT, + VBOXVHWACMD_TYPE_SURF_FLIP, + VBOXVHWACMD_TYPE_SURF_OVERLAY_UPDATE, + VBOXVHWACMD_TYPE_SURF_OVERLAY_SETPOSITION, + VBOXVHWACMD_TYPE_SURF_COLORKEY_SET, + VBOXVHWACMD_TYPE_QUERY_INFO1, + VBOXVHWACMD_TYPE_QUERY_INFO2, + VBOXVHWACMD_TYPE_ENABLE, + VBOXVHWACMD_TYPE_DISABLE, + VBOXVHWACMD_TYPE_HH_CONSTRUCT, + VBOXVHWACMD_TYPE_HH_RESET +#ifdef VBOX_WITH_WDDM + , VBOXVHWACMD_TYPE_SURF_GETINFO + , VBOXVHWACMD_TYPE_SURF_COLORFILL +#endif + , VBOXVHWACMD_TYPE_HH_DISABLE + , VBOXVHWACMD_TYPE_HH_ENABLE + , VBOXVHWACMD_TYPE_HH_SAVESTATE_SAVEBEGIN + , VBOXVHWACMD_TYPE_HH_SAVESTATE_SAVEEND + , VBOXVHWACMD_TYPE_HH_SAVESTATE_SAVEPERFORM + , VBOXVHWACMD_TYPE_HH_SAVESTATE_LOADPERFORM +} VBOXVHWACMD_TYPE; + +/* the command processing was asynch, set by the host to indicate asynch command completion + * must not be cleared once set, the command completion is performed by issuing a host->guest completion command + * while keeping this flag unchanged */ +#define VBOXVHWACMD_FLAG_HG_ASYNCH 0x00010000 +/* asynch completion is performed by issuing the event */ +#define VBOXVHWACMD_FLAG_GH_ASYNCH_EVENT 0x00000001 +/* issue interrupt on asynch completion */ +#define VBOXVHWACMD_FLAG_GH_ASYNCH_IRQ 0x00000002 +/* guest does not do any op on completion of this command, the host may copy the command and indicate that it does not need the command anymore + * by setting the VBOXVHWACMD_FLAG_HG_ASYNCH_RETURNED flag */ +#define VBOXVHWACMD_FLAG_GH_ASYNCH_NOCOMPLETION 0x00000004 +/* the host has copied the VBOXVHWACMD_FLAG_GH_ASYNCH_NOCOMPLETION command and returned it to the guest */ +#define VBOXVHWACMD_FLAG_HG_ASYNCH_RETURNED 0x00020000 +/* this is the host->host cmd, i.e. a configuration command posted by the host to the framebuffer */ +#define VBOXVHWACMD_FLAG_HH_CMD 0x10000000 + +typedef struct VBOXVHWACMD +{ + VBOXVHWACMD_TYPE enmCmd; /* command type */ + volatile int32_t rc; /* command result */ + int32_t iDisplay; /* display index */ + volatile int32_t Flags; /* ored VBOXVHWACMD_FLAG_xxx values */ + uint64_t GuestVBVAReserved1; /* field internally used by the guest VBVA cmd handling, must NOT be modified by clients */ + uint64_t GuestVBVAReserved2; /* field internally used by the guest VBVA cmd handling, must NOT be modified by clients */ + volatile uint32_t cRefs; + int32_t Reserved; + union + { + struct VBOXVHWACMD *pNext; + uint32_t offNext; + uint64_t Data; /* the body is 64-bit aligned */ + } u; + char body[1]; +} VBOXVHWACMD; + +#define VBOXVHWACMD_HEADSIZE() (RT_OFFSETOF(VBOXVHWACMD, body)) +#define VBOXVHWACMD_SIZE_FROMBODYSIZE(_s) (VBOXVHWACMD_HEADSIZE() + (_s)) +#define VBOXVHWACMD_SIZE(_tCmd) (VBOXVHWACMD_SIZE_FROMBODYSIZE(sizeof(_tCmd))) +typedef unsigned int VBOXVHWACMD_LENGTH; +typedef uint64_t VBOXVHWA_SURFHANDLE; +#define VBOXVHWA_SURFHANDLE_INVALID 0ULL +#define VBOXVHWACMD_BODY(_p, _t) ((_t*)(_p)->body) +#define VBOXVHWACMD_HEAD(_pb) ((VBOXVHWACMD*)((uint8_t *)(_pb) - RT_OFFSETOF(VBOXVHWACMD, body))) + +typedef struct VBOXVHWA_RECTL +{ + int32_t left; + int32_t top; + int32_t right; + int32_t bottom; +} VBOXVHWA_RECTL; + +typedef struct VBOXVHWA_COLORKEY +{ + uint32_t low; + uint32_t high; +} VBOXVHWA_COLORKEY; + +typedef struct VBOXVHWA_PIXELFORMAT +{ + uint32_t flags; + uint32_t fourCC; + union + { + uint32_t rgbBitCount; + uint32_t yuvBitCount; + } c; + + union + { + uint32_t rgbRBitMask; + uint32_t yuvYBitMask; + } m1; + + union + { + uint32_t rgbGBitMask; + uint32_t yuvUBitMask; + } m2; + + union + { + uint32_t rgbBBitMask; + uint32_t yuvVBitMask; + } m3; + + union + { + uint32_t rgbABitMask; + } m4; + + uint32_t Reserved; +} VBOXVHWA_PIXELFORMAT; + +typedef struct VBOXVHWA_SURFACEDESC +{ + uint32_t flags; + uint32_t height; + uint32_t width; + uint32_t pitch; + uint32_t sizeX; + uint32_t sizeY; + uint32_t cBackBuffers; + uint32_t Reserved; + VBOXVHWA_COLORKEY DstOverlayCK; + VBOXVHWA_COLORKEY DstBltCK; + VBOXVHWA_COLORKEY SrcOverlayCK; + VBOXVHWA_COLORKEY SrcBltCK; + VBOXVHWA_PIXELFORMAT PixelFormat; + uint32_t surfCaps; + uint32_t Reserved2; + VBOXVHWA_SURFHANDLE hSurf; + uint64_t offSurface; +} VBOXVHWA_SURFACEDESC; + +typedef struct VBOXVHWA_BLTFX +{ + uint32_t flags; + uint32_t rop; + uint32_t rotationOp; + uint32_t rotation; + uint32_t fillColor; + uint32_t Reserved; + VBOXVHWA_COLORKEY DstCK; + VBOXVHWA_COLORKEY SrcCK; +} VBOXVHWA_BLTFX; + +typedef struct VBOXVHWA_OVERLAYFX +{ + uint32_t flags; + uint32_t Reserved1; + uint32_t fxFlags; + uint32_t Reserved2; + VBOXVHWA_COLORKEY DstCK; + VBOXVHWA_COLORKEY SrcCK; +} VBOXVHWA_OVERLAYFX; + +#define VBOXVHWA_CAPS_BLT 0x00000040 +#define VBOXVHWA_CAPS_BLTCOLORFILL 0x04000000 +#define VBOXVHWA_CAPS_BLTFOURCC 0x00000100 +#define VBOXVHWA_CAPS_BLTSTRETCH 0x00000200 +#define VBOXVHWA_CAPS_BLTQUEUE 0x00000080 + +#define VBOXVHWA_CAPS_OVERLAY 0x00000800 +#define VBOXVHWA_CAPS_OVERLAYFOURCC 0x00002000 +#define VBOXVHWA_CAPS_OVERLAYSTRETCH 0x00004000 +#define VBOXVHWA_CAPS_OVERLAYCANTCLIP 0x00001000 + +#define VBOXVHWA_CAPS_COLORKEY 0x00400000 +#define VBOXVHWA_CAPS_COLORKEYHWASSIST 0x01000000 + +#define VBOXVHWA_SCAPS_BACKBUFFER 0x00000004 +#define VBOXVHWA_SCAPS_COMPLEX 0x00000008 +#define VBOXVHWA_SCAPS_FLIP 0x00000010 +#define VBOXVHWA_SCAPS_FRONTBUFFER 0x00000020 +#define VBOXVHWA_SCAPS_OFFSCREENPLAIN 0x00000040 +#define VBOXVHWA_SCAPS_OVERLAY 0x00000080 +#define VBOXVHWA_SCAPS_PRIMARYSURFACE 0x00000200 +#define VBOXVHWA_SCAPS_SYSTEMMEMORY 0x00000800 +#define VBOXVHWA_SCAPS_VIDEOMEMORY 0x00004000 +#define VBOXVHWA_SCAPS_VISIBLE 0x00008000 +#define VBOXVHWA_SCAPS_LOCALVIDMEM 0x10000000 + +#define VBOXVHWA_PF_PALETTEINDEXED8 0x00000020 +#define VBOXVHWA_PF_RGB 0x00000040 +#define VBOXVHWA_PF_RGBTOYUV 0x00000100 +#define VBOXVHWA_PF_YUV 0x00000200 +#define VBOXVHWA_PF_FOURCC 0x00000004 + +#define VBOXVHWA_LOCK_DISCARDCONTENTS 0x00002000 + +#define VBOXVHWA_CFG_ENABLED 0x00000001 + +#define VBOXVHWA_SD_BACKBUFFERCOUNT 0x00000020 +#define VBOXVHWA_SD_CAPS 0x00000001 +#define VBOXVHWA_SD_CKDESTBLT 0x00004000 +#define VBOXVHWA_SD_CKDESTOVERLAY 0x00002000 +#define VBOXVHWA_SD_CKSRCBLT 0x00010000 +#define VBOXVHWA_SD_CKSRCOVERLAY 0x00008000 +#define VBOXVHWA_SD_HEIGHT 0x00000002 +#define VBOXVHWA_SD_PITCH 0x00000008 +#define VBOXVHWA_SD_PIXELFORMAT 0x00001000 +/*#define VBOXVHWA_SD_REFRESHRATE 0x00040000*/ +#define VBOXVHWA_SD_WIDTH 0x00000004 + +#define VBOXVHWA_CKEYCAPS_DESTBLT 0x00000001 +#define VBOXVHWA_CKEYCAPS_DESTBLTCLRSPACE 0x00000002 +#define VBOXVHWA_CKEYCAPS_DESTBLTCLRSPACEYUV 0x00000004 +#define VBOXVHWA_CKEYCAPS_DESTBLTYUV 0x00000008 +#define VBOXVHWA_CKEYCAPS_DESTOVERLAY 0x00000010 +#define VBOXVHWA_CKEYCAPS_DESTOVERLAYCLRSPACE 0x00000020 +#define VBOXVHWA_CKEYCAPS_DESTOVERLAYCLRSPACEYUV 0x00000040 +#define VBOXVHWA_CKEYCAPS_DESTOVERLAYONEACTIVE 0x00000080 +#define VBOXVHWA_CKEYCAPS_DESTOVERLAYYUV 0x00000100 +#define VBOXVHWA_CKEYCAPS_SRCBLT 0x00000200 +#define VBOXVHWA_CKEYCAPS_SRCBLTCLRSPACE 0x00000400 +#define VBOXVHWA_CKEYCAPS_SRCBLTCLRSPACEYUV 0x00000800 +#define VBOXVHWA_CKEYCAPS_SRCBLTYUV 0x00001000 +#define VBOXVHWA_CKEYCAPS_SRCOVERLAY 0x00002000 +#define VBOXVHWA_CKEYCAPS_SRCOVERLAYCLRSPACE 0x00004000 +#define VBOXVHWA_CKEYCAPS_SRCOVERLAYCLRSPACEYUV 0x00008000 +#define VBOXVHWA_CKEYCAPS_SRCOVERLAYONEACTIVE 0x00010000 +#define VBOXVHWA_CKEYCAPS_SRCOVERLAYYUV 0x00020000 +#define VBOXVHWA_CKEYCAPS_NOCOSTOVERLAY 0x00040000 + +#define VBOXVHWA_BLT_COLORFILL 0x00000400 +#define VBOXVHWA_BLT_DDFX 0x00000800 +#define VBOXVHWA_BLT_EXTENDED_FLAGS 0x40000000 +#define VBOXVHWA_BLT_EXTENDED_LINEAR_CONTENT 0x00000004 +#define VBOXVHWA_BLT_EXTENDED_PRESENTATION_STRETCHFACTOR 0x00000010 +#define VBOXVHWA_BLT_KEYDESTOVERRIDE 0x00004000 +#define VBOXVHWA_BLT_KEYSRCOVERRIDE 0x00010000 +#define VBOXVHWA_BLT_LAST_PRESENTATION 0x20000000 +#define VBOXVHWA_BLT_PRESENTATION 0x10000000 +#define VBOXVHWA_BLT_ROP 0x00020000 + + +#define VBOXVHWA_OVER_DDFX 0x00080000 +#define VBOXVHWA_OVER_HIDE 0x00000200 +#define VBOXVHWA_OVER_KEYDEST 0x00000400 +#define VBOXVHWA_OVER_KEYDESTOVERRIDE 0x00000800 +#define VBOXVHWA_OVER_KEYSRC 0x00001000 +#define VBOXVHWA_OVER_KEYSRCOVERRIDE 0x00002000 +#define VBOXVHWA_OVER_SHOW 0x00004000 + +#define VBOXVHWA_CKEY_COLORSPACE 0x00000001 +#define VBOXVHWA_CKEY_DESTBLT 0x00000002 +#define VBOXVHWA_CKEY_DESTOVERLAY 0x00000004 +#define VBOXVHWA_CKEY_SRCBLT 0x00000008 +#define VBOXVHWA_CKEY_SRCOVERLAY 0x00000010 + +#define VBOXVHWA_BLT_ARITHSTRETCHY 0x00000001 +#define VBOXVHWA_BLT_MIRRORLEFTRIGHT 0x00000002 +#define VBOXVHWA_BLT_MIRRORUPDOWN 0x00000004 + +#define VBOXVHWA_OVERFX_ARITHSTRETCHY 0x00000001 +#define VBOXVHWA_OVERFX_MIRRORLEFTRIGHT 0x00000002 +#define VBOXVHWA_OVERFX_MIRRORUPDOWN 0x00000004 + +#define VBOXVHWA_CAPS2_CANRENDERWINDOWED 0x00080000 +#define VBOXVHWA_CAPS2_WIDESURFACES 0x00001000 +#define VBOXVHWA_CAPS2_COPYFOURCC 0x00008000 +/*#define VBOXVHWA_CAPS2_FLIPINTERVAL 0x00200000*/ +/*#define VBOXVHWA_CAPS2_FLIPNOVSYNC 0x00400000*/ + + +#define VBOXVHWA_OFFSET64_VOID (UINT64_MAX) + +typedef struct VBOXVHWA_VERSION +{ + uint32_t maj; + uint32_t min; + uint32_t bld; + uint32_t reserved; +} VBOXVHWA_VERSION; + +#define VBOXVHWA_VERSION_INIT(_pv) do { \ + (_pv)->maj = VBOXVHWA_VERSION_MAJ; \ + (_pv)->min = VBOXVHWA_VERSION_MIN; \ + (_pv)->bld = VBOXVHWA_VERSION_BLD; \ + (_pv)->reserved = VBOXVHWA_VERSION_RSV; \ + } while(0) + +typedef struct VBOXVHWACMD_QUERYINFO1 +{ + union + { + struct + { + VBOXVHWA_VERSION guestVersion; + } in; + + struct + { + uint32_t cfgFlags; + uint32_t caps; + + uint32_t caps2; + uint32_t colorKeyCaps; + + uint32_t stretchCaps; + uint32_t surfaceCaps; + + uint32_t numOverlays; + uint32_t curOverlays; + + uint32_t numFourCC; + uint32_t reserved; + } out; + } u; +} VBOXVHWACMD_QUERYINFO1; + +typedef struct VBOXVHWACMD_QUERYINFO2 +{ + uint32_t numFourCC; + uint32_t FourCC[1]; +} VBOXVHWACMD_QUERYINFO2; + +#define VBOXVHWAINFO2_SIZE(_cFourCC) RT_OFFSETOF(VBOXVHWACMD_QUERYINFO2, FourCC[_cFourCC]) + +typedef struct VBOXVHWACMD_SURF_CANCREATE +{ + VBOXVHWA_SURFACEDESC SurfInfo; + union + { + struct + { + uint32_t bIsDifferentPixelFormat; + uint32_t Reserved; + } in; + + struct + { + int32_t ErrInfo; + } out; + } u; +} VBOXVHWACMD_SURF_CANCREATE; + +typedef struct VBOXVHWACMD_SURF_CREATE +{ + VBOXVHWA_SURFACEDESC SurfInfo; +} VBOXVHWACMD_SURF_CREATE; + +#ifdef VBOX_WITH_WDDM +typedef struct VBOXVHWACMD_SURF_GETINFO +{ + VBOXVHWA_SURFACEDESC SurfInfo; +} VBOXVHWACMD_SURF_GETINFO; +#endif + +typedef struct VBOXVHWACMD_SURF_DESTROY +{ + union + { + struct + { + VBOXVHWA_SURFHANDLE hSurf; + } in; + } u; +} VBOXVHWACMD_SURF_DESTROY; + +typedef struct VBOXVHWACMD_SURF_LOCK +{ + union + { + struct + { + VBOXVHWA_SURFHANDLE hSurf; + uint64_t offSurface; + uint32_t flags; + uint32_t rectValid; + VBOXVHWA_RECTL rect; + } in; + } u; +} VBOXVHWACMD_SURF_LOCK; + +typedef struct VBOXVHWACMD_SURF_UNLOCK +{ + union + { + struct + { + VBOXVHWA_SURFHANDLE hSurf; + uint32_t xUpdatedMemValid; + uint32_t reserved; + VBOXVHWA_RECTL xUpdatedMemRect; + } in; + } u; +} VBOXVHWACMD_SURF_UNLOCK; + +typedef struct VBOXVHWACMD_SURF_BLT +{ + uint64_t DstGuestSurfInfo; + uint64_t SrcGuestSurfInfo; + union + { + struct + { + VBOXVHWA_SURFHANDLE hDstSurf; + uint64_t offDstSurface; + VBOXVHWA_RECTL dstRect; + VBOXVHWA_SURFHANDLE hSrcSurf; + uint64_t offSrcSurface; + VBOXVHWA_RECTL srcRect; + uint32_t flags; + uint32_t xUpdatedSrcMemValid; + VBOXVHWA_BLTFX desc; + VBOXVHWA_RECTL xUpdatedSrcMemRect; + } in; + } u; +} VBOXVHWACMD_SURF_BLT; + +#ifdef VBOX_WITH_WDDM +typedef struct VBOXVHWACMD_SURF_COLORFILL +{ + union + { + struct + { + VBOXVHWA_SURFHANDLE hSurf; + uint64_t offSurface; + uint32_t u32Reserved; + uint32_t cRects; + VBOXVHWA_RECTL aRects[1]; + } in; + } u; +} VBOXVHWACMD_SURF_COLORFILL; +#endif + +typedef struct VBOXVHWACMD_SURF_FLIP +{ + uint64_t TargGuestSurfInfo; + uint64_t CurrGuestSurfInfo; + union + { + struct + { + VBOXVHWA_SURFHANDLE hTargSurf; + uint64_t offTargSurface; + VBOXVHWA_SURFHANDLE hCurrSurf; + uint64_t offCurrSurface; + uint32_t flags; + uint32_t xUpdatedTargMemValid; + VBOXVHWA_RECTL xUpdatedTargMemRect; + } in; + } u; +} VBOXVHWACMD_SURF_FLIP; + +typedef struct VBOXVHWACMD_SURF_COLORKEY_SET +{ + union + { + struct + { + VBOXVHWA_SURFHANDLE hSurf; + uint64_t offSurface; + VBOXVHWA_COLORKEY CKey; + uint32_t flags; + uint32_t reserved; + } in; + } u; +} VBOXVHWACMD_SURF_COLORKEY_SET; + +#define VBOXVHWACMD_SURF_OVERLAY_UPDATE_F_SRCMEMRECT 0x00000001 +#define VBOXVHWACMD_SURF_OVERLAY_UPDATE_F_DSTMEMRECT 0x00000002 + +typedef struct VBOXVHWACMD_SURF_OVERLAY_UPDATE +{ + union + { + struct + { + VBOXVHWA_SURFHANDLE hDstSurf; + uint64_t offDstSurface; + VBOXVHWA_RECTL dstRect; + VBOXVHWA_SURFHANDLE hSrcSurf; + uint64_t offSrcSurface; + VBOXVHWA_RECTL srcRect; + uint32_t flags; + uint32_t xFlags; + VBOXVHWA_OVERLAYFX desc; + VBOXVHWA_RECTL xUpdatedSrcMemRect; + VBOXVHWA_RECTL xUpdatedDstMemRect; + } in; + } u; +}VBOXVHWACMD_SURF_OVERLAY_UPDATE; + +typedef struct VBOXVHWACMD_SURF_OVERLAY_SETPOSITION +{ + union + { + struct + { + VBOXVHWA_SURFHANDLE hDstSurf; + uint64_t offDstSurface; + VBOXVHWA_SURFHANDLE hSrcSurf; + uint64_t offSrcSurface; + uint32_t xPos; + uint32_t yPos; + uint32_t flags; + uint32_t reserved; + } in; + } u; +} VBOXVHWACMD_SURF_OVERLAY_SETPOSITION; + +typedef struct VBOXVHWACMD_HH_CONSTRUCT +{ + void *pVM; + /* VRAM info for the backend to be able to properly translate VRAM offsets */ + void *pvVRAM; + uint32_t cbVRAM; +} VBOXVHWACMD_HH_CONSTRUCT; + +typedef struct VBOXVHWACMD_HH_SAVESTATE_SAVEPERFORM +{ + struct SSMHANDLE * pSSM; +} VBOXVHWACMD_HH_SAVESTATE_SAVEPERFORM; + +typedef struct VBOXVHWACMD_HH_SAVESTATE_LOADPERFORM +{ + struct SSMHANDLE * pSSM; +} VBOXVHWACMD_HH_SAVESTATE_LOADPERFORM; + +typedef DECLCALLBACK(void) FNVBOXVHWA_HH_CALLBACK(void*); +typedef FNVBOXVHWA_HH_CALLBACK *PFNVBOXVHWA_HH_CALLBACK; + +#define VBOXVHWA_HH_CALLBACK_SET(_pCmd, _pfn, _parg) \ + do { \ + (_pCmd)->GuestVBVAReserved1 = (uint64_t)(uintptr_t)(_pfn); \ + (_pCmd)->GuestVBVAReserved2 = (uint64_t)(uintptr_t)(_parg); \ + }while(0) + +#define VBOXVHWA_HH_CALLBACK_GET(_pCmd) ((PFNVBOXVHWA_HH_CALLBACK)(_pCmd)->GuestVBVAReserved1) +#define VBOXVHWA_HH_CALLBACK_GET_ARG(_pCmd) ((void*)(_pCmd)->GuestVBVAReserved2) + +#pragma pack() +#endif /* #ifdef VBOX_WITH_VIDEOHWACCEL */ + +/* All structures are without alignment. */ +#pragma pack(1) + +typedef struct VBVAHOSTFLAGS +{ + uint32_t u32HostEvents; + uint32_t u32SupportedOrders; +} VBVAHOSTFLAGS; + +typedef struct VBVABUFFER +{ + VBVAHOSTFLAGS hostFlags; + + /* The offset where the data start in the buffer. */ + uint32_t off32Data; + /* The offset where next data must be placed in the buffer. */ + uint32_t off32Free; + + /* The queue of record descriptions. */ + VBVARECORD aRecords[VBVA_MAX_RECORDS]; + uint32_t indexRecordFirst; + uint32_t indexRecordFree; + + /* Space to leave free in the buffer when large partial records are transferred. */ + uint32_t cbPartialWriteThreshold; + + uint32_t cbData; + uint8_t au8Data[1]; /* variable size for the rest of the VBVABUFFER area in VRAM. */ +} VBVABUFFER; + +#define VBVA_MAX_RECORD_SIZE (128*_1M) + +/* guest->host commands */ +#define VBVA_QUERY_CONF32 1 +#define VBVA_SET_CONF32 2 +#define VBVA_INFO_VIEW 3 +#define VBVA_INFO_HEAP 4 +#define VBVA_FLUSH 5 +#define VBVA_INFO_SCREEN 6 +#define VBVA_ENABLE 7 +#define VBVA_MOUSE_POINTER_SHAPE 8 +#ifdef VBOX_WITH_VIDEOHWACCEL +# define VBVA_VHWA_CMD 9 +#endif /* # ifdef VBOX_WITH_VIDEOHWACCEL */ +#ifdef VBOX_WITH_VDMA +# define VBVA_VDMA_CTL 10 /* setup G<->H DMA channel info */ +# define VBVA_VDMA_CMD 11 /* G->H DMA command */ +#endif +#define VBVA_INFO_CAPS 12 /* informs host about HGSMI caps. see VBVACAPS below */ +#define VBVA_SCANLINE_CFG 13 /* configures scanline, see VBVASCANLINECFG below */ +#define VBVA_SCANLINE_INFO 14 /* requests scanline info, see VBVASCANLINEINFO below */ +#define VBVA_CMDVBVA_SUBMIT 16 /* inform host about VBVA Command submission */ +#define VBVA_CMDVBVA_FLUSH 17 /* inform host about VBVA Command submission */ +#define VBVA_CMDVBVA_CTL 18 /* G->H DMA command */ +#define VBVA_QUERY_MODE_HINTS 19 /* Query most recent mode hints sent. */ +/** Report the guest virtual desktop position and size for mapping host and + * guest pointer positions. */ +#define VBVA_REPORT_INPUT_MAPPING 20 +/** Report the guest cursor position and query the host position. */ +#define VBVA_CURSOR_POSITION 21 + +/* host->guest commands */ +#define VBVAHG_EVENT 1 +#define VBVAHG_DISPLAY_CUSTOM 2 +#ifdef VBOX_WITH_VDMA +#define VBVAHG_SHGSMI_COMPLETION 3 +#endif + +#ifdef VBOX_WITH_VIDEOHWACCEL +#define VBVAHG_DCUSTOM_VHWA_CMDCOMPLETE 1 +#pragma pack(1) +typedef struct VBVAHOSTCMDVHWACMDCOMPLETE +{ + uint32_t offCmd; +}VBVAHOSTCMDVHWACMDCOMPLETE; +#pragma pack() +#endif /* # ifdef VBOX_WITH_VIDEOHWACCEL */ + +#pragma pack(1) +typedef enum +{ + VBVAHOSTCMD_OP_EVENT = 1, + VBVAHOSTCMD_OP_CUSTOM +}VBVAHOSTCMD_OP_TYPE; + +typedef struct VBVAHOSTCMDEVENT +{ + uint64_t pEvent; +}VBVAHOSTCMDEVENT; + + +typedef struct VBVAHOSTCMD +{ + /* destination ID if >=0 specifies display index, otherwize the command is directed to the miniport */ + int32_t iDstID; + int32_t customOpCode; + union + { + struct VBVAHOSTCMD *pNext; + uint32_t offNext; + uint64_t Data; /* the body is 64-bit aligned */ + } u; + char body[1]; +}VBVAHOSTCMD; + +#define VBVAHOSTCMD_SIZE(_size) (sizeof(VBVAHOSTCMD) + (_size)) +#define VBVAHOSTCMD_BODY(_pCmd, _tBody) ((_tBody*)(_pCmd)->body) +#define VBVAHOSTCMD_HDR(_pBody) ((VBVAHOSTCMD*)(((uint8_t*)_pBody) - RT_OFFSETOF(VBVAHOSTCMD, body))) +#define VBVAHOSTCMD_HDRSIZE (RT_OFFSETOF(VBVAHOSTCMD, body)) + +#pragma pack() + +/* VBVACONF32::u32Index */ +#define VBOX_VBVA_CONF32_MONITOR_COUNT 0 +#define VBOX_VBVA_CONF32_HOST_HEAP_SIZE 1 +/** Returns VINF_SUCCESS if the host can report mode hints via VBVA. + * Set value to VERR_NOT_SUPPORTED before calling. */ +#define VBOX_VBVA_CONF32_MODE_HINT_REPORTING 2 +/** Returns VINF_SUCCESS if the host can report guest cursor enabled status via + * VBVA. Set value to VERR_NOT_SUPPORTED before calling. */ +#define VBOX_VBVA_CONF32_GUEST_CURSOR_REPORTING 3 +/** Returns the currently available host cursor capabilities. Available if + * VBVACONF32::VBOX_VBVA_CONF32_GUEST_CURSOR_REPORTING returns success. + * @see VMMDevReqMouseStatus::mouseFeatures. */ +#define VBOX_VBVA_CONF32_CURSOR_CAPABILITIES 4 +/** Returns the supported flags in VBVAINFOSCREEN::u8Flags. */ +#define VBOX_VBVA_CONF32_SCREEN_FLAGS 5 +/** Returns the max size of VBVA record. */ +#define VBOX_VBVA_CONF32_MAX_RECORD_SIZE 6 + +typedef struct VBVACONF32 +{ + uint32_t u32Index; + uint32_t u32Value; +} VBVACONF32; + +/** Reserved for historical reasons. */ +#define VBOX_VBVA_CURSOR_CAPABILITY_RESERVED0 RT_BIT(0) +/** Guest cursor capability: can the host show a hardware cursor at the host + * pointer location? */ +#define VBOX_VBVA_CURSOR_CAPABILITY_HARDWARE RT_BIT(1) +/** Reserved for historical reasons. */ +#define VBOX_VBVA_CURSOR_CAPABILITY_RESERVED2 RT_BIT(2) +/** Reserved for historical reasons. Must always be unset. */ +#define VBOX_VBVA_CURSOR_CAPABILITY_RESERVED3 RT_BIT(3) +/** Reserved for historical reasons. */ +#define VBOX_VBVA_CURSOR_CAPABILITY_RESERVED4 RT_BIT(4) +/** Reserved for historical reasons. */ +#define VBOX_VBVA_CURSOR_CAPABILITY_RESERVED5 RT_BIT(5) + +typedef struct VBVAINFOVIEW +{ + /* Index of the screen, assigned by the guest. */ + uint32_t u32ViewIndex; + + /* The screen offset in VRAM, the framebuffer starts here. */ + uint32_t u32ViewOffset; + + /* The size of the VRAM memory that can be used for the view. */ + uint32_t u32ViewSize; + + /* The recommended maximum size of the VRAM memory for the screen. */ + uint32_t u32MaxScreenSize; +} VBVAINFOVIEW; + +typedef struct VBVAINFOHEAP +{ + /* Absolute offset in VRAM of the start of the heap. */ + uint32_t u32HeapOffset; + + /* The size of the heap. */ + uint32_t u32HeapSize; + +} VBVAINFOHEAP; + +typedef struct VBVAFLUSH +{ + uint32_t u32Reserved; + +} VBVAFLUSH; + +typedef struct VBVACMDVBVASUBMIT +{ + uint32_t u32Reserved; +} VBVACMDVBVASUBMIT; + +/* flush is requested because due to guest command buffer overflow */ +#define VBVACMDVBVAFLUSH_F_GUEST_BUFFER_OVERFLOW 1 + +typedef struct VBVACMDVBVAFLUSH +{ + uint32_t u32Flags; +} VBVACMDVBVAFLUSH; + + +/* VBVAINFOSCREEN::u8Flags */ +#define VBVA_SCREEN_F_NONE 0x0000 +#define VBVA_SCREEN_F_ACTIVE 0x0001 +/** The virtual monitor has been disabled by the guest and should be removed + * by the host and ignored for purposes of pointer position calculation. */ +#define VBVA_SCREEN_F_DISABLED 0x0002 +/** The virtual monitor has been blanked by the guest and should be blacked + * out by the host using width, height, etc values from the VBVAINFOSCREEN request. */ +#define VBVA_SCREEN_F_BLANK 0x0004 +/** The virtual monitor has been blanked by the guest and should be blacked + * out by the host using the previous mode values for width. height, etc. */ +#define VBVA_SCREEN_F_BLANK2 0x0008 + +typedef struct VBVAINFOSCREEN +{ + /* Which view contains the screen. */ + uint32_t u32ViewIndex; + + /* Physical X origin relative to the primary screen. */ + int32_t i32OriginX; + + /* Physical Y origin relative to the primary screen. */ + int32_t i32OriginY; + + /* Offset of visible framebuffer relative to the framebuffer start. */ + uint32_t u32StartOffset; + + /* The scan line size in bytes. */ + uint32_t u32LineSize; + + /* Width of the screen. */ + uint32_t u32Width; + + /* Height of the screen. */ + uint32_t u32Height; + + /* Color depth. */ + uint16_t u16BitsPerPixel; + + /* VBVA_SCREEN_F_* */ + uint16_t u16Flags; +} VBVAINFOSCREEN; + + +/* VBVAENABLE::u32Flags */ +#define VBVA_F_NONE 0x00000000 +#define VBVA_F_ENABLE 0x00000001 +#define VBVA_F_DISABLE 0x00000002 +/* extended VBVA to be used with WDDM */ +#define VBVA_F_EXTENDED 0x00000004 +/* vbva offset is absolute VRAM offset */ +#define VBVA_F_ABSOFFSET 0x00000008 + +typedef struct VBVAENABLE +{ + uint32_t u32Flags; + uint32_t u32Offset; + int32_t i32Result; +} VBVAENABLE; + +typedef struct VBVAENABLE_EX +{ + VBVAENABLE Base; + uint32_t u32ScreenId; +} VBVAENABLE_EX; + + +typedef struct VBVAMOUSEPOINTERSHAPE +{ + /* The host result. */ + int32_t i32Result; + + /* VBOX_MOUSE_POINTER_* bit flags. */ + uint32_t fu32Flags; + + /* X coordinate of the hot spot. */ + uint32_t u32HotX; + + /* Y coordinate of the hot spot. */ + uint32_t u32HotY; + + /* Width of the pointer in pixels. */ + uint32_t u32Width; + + /* Height of the pointer in scanlines. */ + uint32_t u32Height; + + /* Pointer data. + * + **** + * The data consists of 1 bpp AND mask followed by 32 bpp XOR (color) mask. + * + * For pointers without alpha channel the XOR mask pixels are 32 bit values: (lsb)BGR0(msb). + * For pointers with alpha channel the XOR mask consists of (lsb)BGRA(msb) 32 bit values. + * + * Guest driver must create the AND mask for pointers with alpha channel, so if host does not + * support alpha, the pointer could be displayed as a normal color pointer. The AND mask can + * be constructed from alpha values. For example alpha value >= 0xf0 means bit 0 in the AND mask. + * + * The AND mask is 1 bpp bitmap with byte aligned scanlines. Size of AND mask, + * therefore, is cbAnd = (width + 7) / 8 * height. The padding bits at the + * end of any scanline are undefined. + * + * The XOR mask follows the AND mask on the next 4 bytes aligned offset: + * uint8_t *pXor = pAnd + (cbAnd + 3) & ~3 + * Bytes in the gap between the AND and the XOR mask are undefined. + * XOR mask scanlines have no gap between them and size of XOR mask is: + * cXor = width * 4 * height. + **** + * + * Preallocate 4 bytes for accessing actual data as p->au8Data. + */ + uint8_t au8Data[4]; + +} VBVAMOUSEPOINTERSHAPE; + +/** @name VBVAMOUSEPOINTERSHAPE::fu32Flags + * @note The VBOX_MOUSE_POINTER_* flags are used in the guest video driver, + * values must be <= 0x8000 and must not be changed. (try make more sense + * of this, please). + * @{ + */ +/** pointer is visible */ +#define VBOX_MOUSE_POINTER_VISIBLE (0x0001) +/** pointer has alpha channel */ +#define VBOX_MOUSE_POINTER_ALPHA (0x0002) +/** pointerData contains new pointer shape */ +#define VBOX_MOUSE_POINTER_SHAPE (0x0004) +/** @} */ + +/* the guest driver can handle asynch guest cmd completion by reading the command offset from io port */ +#define VBVACAPS_COMPLETEGCMD_BY_IOREAD 0x00000001 +/* the guest driver can handle video adapter IRQs */ +#define VBVACAPS_IRQ 0x00000002 +/** The guest can read video mode hints sent via VBVA. */ +#define VBVACAPS_VIDEO_MODE_HINTS 0x00000004 +/** The guest can switch to a software cursor on demand. */ +#define VBVACAPS_DISABLE_CURSOR_INTEGRATION 0x00000008 +/** The guest does not depend on host handling the VBE registers. */ +#define VBVACAPS_USE_VBVA_ONLY 0x00000010 +typedef struct VBVACAPS +{ + int32_t rc; + uint32_t fCaps; +} VBVACAPS; + +/* makes graphics device generate IRQ on VSYNC */ +#define VBVASCANLINECFG_ENABLE_VSYNC_IRQ 0x00000001 +/* guest driver may request the current scanline */ +#define VBVASCANLINECFG_ENABLE_SCANLINE_INFO 0x00000002 +/* request the current refresh period, returned in u32RefreshPeriodMs */ +#define VBVASCANLINECFG_QUERY_REFRESH_PERIOD 0x00000004 +/* set new refresh period specified in u32RefreshPeriodMs. + * if used with VBVASCANLINECFG_QUERY_REFRESH_PERIOD, + * u32RefreshPeriodMs is set to the previous refresh period on return */ +#define VBVASCANLINECFG_SET_REFRESH_PERIOD 0x00000008 + +typedef struct VBVASCANLINECFG +{ + int32_t rc; + uint32_t fFlags; + uint32_t u32RefreshPeriodMs; + uint32_t u32Reserved; +} VBVASCANLINECFG; + +typedef struct VBVASCANLINEINFO +{ + int32_t rc; + uint32_t u32ScreenId; + uint32_t u32InVBlank; + uint32_t u32ScanLine; +} VBVASCANLINEINFO; + +/** Query the most recent mode hints received from the host. */ +typedef struct VBVAQUERYMODEHINTS +{ + /** The maximum number of screens to return hints for. */ + uint16_t cHintsQueried; + /** The size of the mode hint structures directly following this one. */ + uint16_t cbHintStructureGuest; + /** The return code for the operation. Initialise to VERR_NOT_SUPPORTED. */ + int32_t rc; +} VBVAQUERYMODEHINTS; + +/** Structure in which a mode hint is returned. The guest allocates an array + * of these immediately after the VBVAQUERYMODEHINTS structure. To accomodate + * future extensions, the VBVAQUERYMODEHINTS structure specifies the size of + * the VBVAMODEHINT structures allocated by the guest, and the host only fills + * out structure elements which fit into that size. The host should fill any + * unused members (e.g. dx, dy) or structure space on the end with ~0. The + * whole structure can legally be set to ~0 to skip a screen. */ +typedef struct VBVAMODEHINT +{ + uint32_t magic; + uint32_t cx; + uint32_t cy; + uint32_t cBPP; /* Which has never been used... */ + uint32_t cDisplay; + uint32_t dx; /**< X offset into the virtual frame-buffer. */ + uint32_t dy; /**< Y offset into the virtual frame-buffer. */ + uint32_t fEnabled; /* Not fFlags. Add new members for new flags. */ +} VBVAMODEHINT; + +#define VBVAMODEHINT_MAGIC UINT32_C(0x0801add9) + +/** Report the rectangle relative to which absolute pointer events should be + * expressed. This information remains valid until the next VBVA resize event + * for any screen, at which time it is reset to the bounding rectangle of all + * virtual screens and must be re-set. + * @see VBVA_REPORT_INPUT_MAPPING. */ +typedef struct VBVAREPORTINPUTMAPPING +{ + int32_t x; /**< Upper left X co-ordinate relative to the first screen. */ + int32_t y; /**< Upper left Y co-ordinate relative to the first screen. */ + uint32_t cx; /**< Rectangle width. */ + uint32_t cy; /**< Rectangle height. */ +} VBVAREPORTINPUTMAPPING; + +/** Report the guest cursor position and query the host one. The host may wish + * to use the guest information to re-position its own cursor (though this is + * currently unlikely). + * @see VBVA_CURSOR_POSITION */ +typedef struct VBVACURSORPOSITION +{ + uint32_t fReportPosition; /**< Are we reporting a position? */ + uint32_t x; /**< Guest cursor X position */ + uint32_t y; /**< Guest cursor Y position */ +} VBVACURSORPOSITION; + +#pragma pack() + +typedef uint64_t VBOXVIDEOOFFSET; + +#define VBOXVIDEOOFFSET_VOID ((VBOXVIDEOOFFSET)~0) + +#pragma pack(1) + +/* + * VBOXSHGSMI made on top HGSMI and allows receiving notifications + * about G->H command completion + */ +/* SHGSMI command header */ +typedef struct VBOXSHGSMIHEADER +{ + uint64_t pvNext; /*<- completion processing queue */ + uint32_t fFlags; /*<- see VBOXSHGSMI_FLAG_XXX Flags */ + uint32_t cRefs; /*<- command referece count */ + uint64_t u64Info1; /*<- contents depends on the fFlags value */ + uint64_t u64Info2; /*<- contents depends on the fFlags value */ +} VBOXSHGSMIHEADER, *PVBOXSHGSMIHEADER; + +typedef enum +{ + VBOXVDMACMD_TYPE_UNDEFINED = 0, + VBOXVDMACMD_TYPE_DMA_PRESENT_BLT = 1, + VBOXVDMACMD_TYPE_DMA_BPB_TRANSFER, + VBOXVDMACMD_TYPE_DMA_BPB_FILL, + VBOXVDMACMD_TYPE_DMA_PRESENT_SHADOW2PRIMARY, + VBOXVDMACMD_TYPE_DMA_PRESENT_CLRFILL, + VBOXVDMACMD_TYPE_DMA_PRESENT_FLIP, + VBOXVDMACMD_TYPE_DMA_NOP, + VBOXVDMACMD_TYPE_CHROMIUM_CMD, /* chromium cmd */ + VBOXVDMACMD_TYPE_DMA_BPB_TRANSFER_VRAMSYS, + VBOXVDMACMD_TYPE_CHILD_STATUS_IRQ /* make the device notify child (monitor) state change IRQ */ +} VBOXVDMACMD_TYPE; + +#pragma pack() + +/* the command processing was asynch, set by the host to indicate asynch command completion + * must not be cleared once set, the command completion is performed by issuing a host->guest completion command + * while keeping this flag unchanged */ +#define VBOXSHGSMI_FLAG_HG_ASYNCH 0x00010000 +#if 0 +/* if set - asynch completion is performed by issuing the event, + * if cleared - asynch completion is performed by calling a callback */ +#define VBOXSHGSMI_FLAG_GH_ASYNCH_EVENT 0x00000001 +#endif +/* issue interrupt on asynch completion, used for critical G->H commands, + * i.e. for completion of which guest is waiting. */ +#define VBOXSHGSMI_FLAG_GH_ASYNCH_IRQ 0x00000002 +/* guest does not do any op on completion of this command, + * the host may copy the command and indicate that it does not need the command anymore + * by not setting VBOXSHGSMI_FLAG_HG_ASYNCH */ +#define VBOXSHGSMI_FLAG_GH_ASYNCH_NOCOMPLETION 0x00000004 +/* guest requires the command to be processed asynchronously, + * not setting VBOXSHGSMI_FLAG_HG_ASYNCH by the host in this case is treated as command failure */ +#define VBOXSHGSMI_FLAG_GH_ASYNCH_FORCE 0x00000008 +/* force IRQ on cmd completion */ +#define VBOXSHGSMI_FLAG_GH_ASYNCH_IRQ_FORCE 0x00000010 +/* an IRQ-level callback is associated with the command */ +#define VBOXSHGSMI_FLAG_GH_ASYNCH_CALLBACK_IRQ 0x00000020 +/* guest expects this command to be completed synchronously */ +#define VBOXSHGSMI_FLAG_GH_SYNCH 0x00000040 + + +DECLINLINE(uint8_t *) VBoxSHGSMIBufferData (const VBOXSHGSMIHEADER* pHeader) +{ + return (uint8_t *)pHeader + sizeof (VBOXSHGSMIHEADER); +} + +#define VBoxSHGSMIBufferHeaderSize() (sizeof (VBOXSHGSMIHEADER)) + +DECLINLINE(PVBOXSHGSMIHEADER) VBoxSHGSMIBufferHeader (const void *pvData) +{ + return (PVBOXSHGSMIHEADER)((uint8_t *)pvData - sizeof (VBOXSHGSMIHEADER)); +} + +#ifdef VBOX_WITH_VDMA +# pragma pack(1) + +/* VDMA - Video DMA */ + +/* VDMA Control API */ +/* VBOXVDMA_CTL::u32Flags */ +typedef enum +{ + VBOXVDMA_CTL_TYPE_NONE = 0, + VBOXVDMA_CTL_TYPE_ENABLE, + VBOXVDMA_CTL_TYPE_DISABLE, + VBOXVDMA_CTL_TYPE_FLUSH, + VBOXVDMA_CTL_TYPE_WATCHDOG +} VBOXVDMA_CTL_TYPE; + +typedef struct VBOXVDMA_CTL +{ + VBOXVDMA_CTL_TYPE enmCtl; + uint32_t u32Offset; + int32_t i32Result; +} VBOXVDMA_CTL, *PVBOXVDMA_CTL; + +typedef struct VBOXVDMA_RECTL +{ + int16_t left; + int16_t top; + uint16_t width; + uint16_t height; +} VBOXVDMA_RECTL, *PVBOXVDMA_RECTL; + +typedef enum +{ + VBOXVDMA_PIXEL_FORMAT_UNKNOWN = 0, + VBOXVDMA_PIXEL_FORMAT_R8G8B8 = 20, + VBOXVDMA_PIXEL_FORMAT_A8R8G8B8 = 21, + VBOXVDMA_PIXEL_FORMAT_X8R8G8B8 = 22, + VBOXVDMA_PIXEL_FORMAT_R5G6B5 = 23, + VBOXVDMA_PIXEL_FORMAT_X1R5G5B5 = 24, + VBOXVDMA_PIXEL_FORMAT_A1R5G5B5 = 25, + VBOXVDMA_PIXEL_FORMAT_A4R4G4B4 = 26, + VBOXVDMA_PIXEL_FORMAT_R3G3B2 = 27, + VBOXVDMA_PIXEL_FORMAT_A8 = 28, + VBOXVDMA_PIXEL_FORMAT_A8R3G3B2 = 29, + VBOXVDMA_PIXEL_FORMAT_X4R4G4B4 = 30, + VBOXVDMA_PIXEL_FORMAT_A2B10G10R10 = 31, + VBOXVDMA_PIXEL_FORMAT_A8B8G8R8 = 32, + VBOXVDMA_PIXEL_FORMAT_X8B8G8R8 = 33, + VBOXVDMA_PIXEL_FORMAT_G16R16 = 34, + VBOXVDMA_PIXEL_FORMAT_A2R10G10B10 = 35, + VBOXVDMA_PIXEL_FORMAT_A16B16G16R16 = 36, + VBOXVDMA_PIXEL_FORMAT_A8P8 = 40, + VBOXVDMA_PIXEL_FORMAT_P8 = 41, + VBOXVDMA_PIXEL_FORMAT_L8 = 50, + VBOXVDMA_PIXEL_FORMAT_A8L8 = 51, + VBOXVDMA_PIXEL_FORMAT_A4L4 = 52, + VBOXVDMA_PIXEL_FORMAT_V8U8 = 60, + VBOXVDMA_PIXEL_FORMAT_L6V5U5 = 61, + VBOXVDMA_PIXEL_FORMAT_X8L8V8U8 = 62, + VBOXVDMA_PIXEL_FORMAT_Q8W8V8U8 = 63, + VBOXVDMA_PIXEL_FORMAT_V16U16 = 64, + VBOXVDMA_PIXEL_FORMAT_W11V11U10 = 65, + VBOXVDMA_PIXEL_FORMAT_A2W10V10U10 = 67 +} VBOXVDMA_PIXEL_FORMAT; + +typedef struct VBOXVDMA_SURF_DESC +{ + uint32_t width; + uint32_t height; + VBOXVDMA_PIXEL_FORMAT format; + uint32_t bpp; + uint32_t pitch; + uint32_t fFlags; +} VBOXVDMA_SURF_DESC, *PVBOXVDMA_SURF_DESC; + +/*typedef uint64_t VBOXVDMAPHADDRESS;*/ +typedef uint64_t VBOXVDMASURFHANDLE; + +/* region specified as a rectangle, otherwize it is a size of memory pointed to by phys address */ +#define VBOXVDMAOPERAND_FLAGS_RECTL 0x1 +/* Surface handle is valid */ +#define VBOXVDMAOPERAND_FLAGS_PRIMARY 0x2 +/* address is offset in VRAM */ +#define VBOXVDMAOPERAND_FLAGS_VRAMOFFSET 0x4 + + +/* VBOXVDMACBUF_DR::phBuf specifies offset in VRAM */ +#define VBOXVDMACBUF_FLAG_BUF_VRAM_OFFSET 0x00000001 +/* command buffer follows the VBOXVDMACBUF_DR in VRAM, VBOXVDMACBUF_DR::phBuf is ignored */ +#define VBOXVDMACBUF_FLAG_BUF_FOLLOWS_DR 0x00000002 + +/* + * We can not submit the DMA command via VRAM since we do not have control over + * DMA command buffer [de]allocation, i.e. we only control the buffer contents. + * In other words the system may call one of our callbacks to fill a command buffer + * with the necessary commands and then discard the buffer w/o any notification. + * + * We have only DMA command buffer physical address at submission time. + * + * so the only way is to */ +typedef struct VBOXVDMACBUF_DR +{ + uint16_t fFlags; + uint16_t cbBuf; + /* RT_SUCCESS() - on success + * VERR_INTERRUPTED - on preemption + * VERR_xxx - on error */ + int32_t rc; + union + { + uint64_t phBuf; + VBOXVIDEOOFFSET offVramBuf; + } Location; + uint64_t aGuestData[7]; +} VBOXVDMACBUF_DR, *PVBOXVDMACBUF_DR; + +#define VBOXVDMACBUF_DR_TAIL(_pCmd, _t) ( (_t*)(((uint8_t*)(_pCmd)) + sizeof (VBOXVDMACBUF_DR)) ) +#define VBOXVDMACBUF_DR_FROM_TAIL(_pCmd) ( (VBOXVDMACBUF_DR*)(((uint8_t*)(_pCmd)) - sizeof (VBOXVDMACBUF_DR)) ) + +typedef struct VBOXVDMACMD +{ + VBOXVDMACMD_TYPE enmType; + uint32_t u32CmdSpecific; +} VBOXVDMACMD, *PVBOXVDMACMD; + +#define VBOXVDMACMD_HEADER_SIZE() sizeof (VBOXVDMACMD) +#define VBOXVDMACMD_SIZE_FROMBODYSIZE(_s) (VBOXVDMACMD_HEADER_SIZE() + (_s)) +#define VBOXVDMACMD_SIZE(_t) (VBOXVDMACMD_SIZE_FROMBODYSIZE(sizeof (_t))) +#define VBOXVDMACMD_BODY(_pCmd, _t) ( (_t*)(((uint8_t*)(_pCmd)) + VBOXVDMACMD_HEADER_SIZE()) ) +#define VBOXVDMACMD_BODY_SIZE(_s) ( (_s) - VBOXVDMACMD_HEADER_SIZE() ) +#define VBOXVDMACMD_FROM_BODY(_pCmd) ( (VBOXVDMACMD*)(((uint8_t*)(_pCmd)) - VBOXVDMACMD_HEADER_SIZE()) ) +#define VBOXVDMACMD_BODY_FIELD_OFFSET(_ot, _t, _f) ( (_ot)(uintptr_t)( VBOXVDMACMD_BODY(0, uint8_t) + RT_OFFSETOF(_t, _f) ) ) + +typedef struct VBOXVDMACMD_DMA_PRESENT_BLT +{ + VBOXVIDEOOFFSET offSrc; + VBOXVIDEOOFFSET offDst; + VBOXVDMA_SURF_DESC srcDesc; + VBOXVDMA_SURF_DESC dstDesc; + VBOXVDMA_RECTL srcRectl; + VBOXVDMA_RECTL dstRectl; + uint32_t u32Reserved; + uint32_t cDstSubRects; + VBOXVDMA_RECTL aDstSubRects[1]; +} VBOXVDMACMD_DMA_PRESENT_BLT, *PVBOXVDMACMD_DMA_PRESENT_BLT; + +typedef struct VBOXVDMACMD_DMA_PRESENT_SHADOW2PRIMARY +{ + VBOXVDMA_RECTL Rect; +} VBOXVDMACMD_DMA_PRESENT_SHADOW2PRIMARY, *PVBOXVDMACMD_DMA_PRESENT_SHADOW2PRIMARY; + + +#define VBOXVDMACMD_DMA_BPB_TRANSFER_F_SRC_VRAMOFFSET 0x00000001 +#define VBOXVDMACMD_DMA_BPB_TRANSFER_F_DST_VRAMOFFSET 0x00000002 + +typedef struct VBOXVDMACMD_DMA_BPB_TRANSFER +{ + uint32_t cbTransferSize; + uint32_t fFlags; + union + { + uint64_t phBuf; + VBOXVIDEOOFFSET offVramBuf; + } Src; + union + { + uint64_t phBuf; + VBOXVIDEOOFFSET offVramBuf; + } Dst; +} VBOXVDMACMD_DMA_BPB_TRANSFER, *PVBOXVDMACMD_DMA_BPB_TRANSFER; + +#define VBOXVDMACMD_SYSMEMEL_F_PAGELIST 0x00000001 + +typedef struct VBOXVDMACMD_SYSMEMEL +{ + uint32_t cPages; + uint32_t fFlags; + uint64_t phBuf[1]; +} VBOXVDMACMD_SYSMEMEL, *PVBOXVDMACMD_SYSMEMEL; + +#define VBOXVDMACMD_SYSMEMEL_NEXT(_pEl) (((_pEl)->fFlags & VBOXVDMACMD_SYSMEMEL_F_PAGELIST) ? \ + ((PVBOXVDMACMD_SYSMEMEL)(((uint8_t*)(_pEl))+RT_OFFSETOF(VBOXVDMACMD_SYSMEMEL, phBuf[(_pEl)->cPages]))) \ + : \ + ((_pEl)+1) + +#define VBOXVDMACMD_DMA_BPB_TRANSFER_VRAMSYS_SYS2VRAM 0x00000001 + +typedef struct VBOXVDMACMD_DMA_BPB_TRANSFER_VRAMSYS +{ + uint32_t cTransferPages; + uint32_t fFlags; + VBOXVIDEOOFFSET offVramBuf; + VBOXVDMACMD_SYSMEMEL FirstEl; +} VBOXVDMACMD_DMA_BPB_TRANSFER_VRAMSYS, *PVBOXVDMACMD_DMA_BPB_TRANSFER_VRAMSYS; + +typedef struct VBOXVDMACMD_DMA_BPB_FILL +{ + VBOXVIDEOOFFSET offSurf; + uint32_t cbFillSize; + uint32_t u32FillPattern; +} VBOXVDMACMD_DMA_BPB_FILL, *PVBOXVDMACMD_DMA_BPB_FILL; + +#define VBOXVDMA_CHILD_STATUS_F_CONNECTED 0x01 +#define VBOXVDMA_CHILD_STATUS_F_DISCONNECTED 0x02 +#define VBOXVDMA_CHILD_STATUS_F_ROTATED 0x04 + +typedef struct VBOXVDMA_CHILD_STATUS +{ + uint32_t iChild; + uint8_t fFlags; + uint8_t u8RotationAngle; + uint16_t u16Reserved; +} VBOXVDMA_CHILD_STATUS, *PVBOXVDMA_CHILD_STATUS; + +/* apply the aInfos are applied to all targets, the iTarget is ignored */ +#define VBOXVDMACMD_CHILD_STATUS_IRQ_F_APPLY_TO_ALL 0x00000001 + +typedef struct VBOXVDMACMD_CHILD_STATUS_IRQ +{ + uint32_t cInfos; + uint32_t fFlags; + VBOXVDMA_CHILD_STATUS aInfos[1]; +} VBOXVDMACMD_CHILD_STATUS_IRQ, *PVBOXVDMACMD_CHILD_STATUS_IRQ; + +# pragma pack() +#endif /* #ifdef VBOX_WITH_VDMA */ + +#pragma pack(1) +typedef struct VBOXVDMACMD_CHROMIUM_BUFFER +{ + VBOXVIDEOOFFSET offBuffer; + uint32_t cbBuffer; + uint32_t u32GuestData; + uint64_t u64GuestData; +} VBOXVDMACMD_CHROMIUM_BUFFER, *PVBOXVDMACMD_CHROMIUM_BUFFER; + +typedef struct VBOXVDMACMD_CHROMIUM_CMD +{ + uint32_t cBuffers; + uint32_t u32Reserved; + VBOXVDMACMD_CHROMIUM_BUFFER aBuffers[1]; +} VBOXVDMACMD_CHROMIUM_CMD, *PVBOXVDMACMD_CHROMIUM_CMD; + +typedef enum +{ + VBOXVDMACMD_CHROMIUM_CTL_TYPE_UNKNOWN = 0, + VBOXVDMACMD_CHROMIUM_CTL_TYPE_CRHGSMI_SETUP, + VBOXVDMACMD_CHROMIUM_CTL_TYPE_SAVESTATE_BEGIN, + VBOXVDMACMD_CHROMIUM_CTL_TYPE_SAVESTATE_END, + VBOXVDMACMD_CHROMIUM_CTL_TYPE_CRHGSMI_SETUP_MAINCB, + VBOXVDMACMD_CHROMIUM_CTL_TYPE_CRCONNECT, + VBOXVDMACMD_CHROMIUM_CTL_TYPE_SIZEHACK = 0x7fffffff +} VBOXVDMACMD_CHROMIUM_CTL_TYPE; + +typedef struct VBOXVDMACMD_CHROMIUM_CTL +{ + VBOXVDMACMD_CHROMIUM_CTL_TYPE enmType; + uint32_t cbCmd; +} VBOXVDMACMD_CHROMIUM_CTL, *PVBOXVDMACMD_CHROMIUM_CTL; + + +typedef struct PDMIDISPLAYVBVACALLBACKS *HCRHGSMICMDCOMPLETION; +typedef DECLCALLBACK(int) FNCRHGSMICMDCOMPLETION(HCRHGSMICMDCOMPLETION hCompletion, PVBOXVDMACMD_CHROMIUM_CMD pCmd, int rc); +typedef FNCRHGSMICMDCOMPLETION *PFNCRHGSMICMDCOMPLETION; + +/* tells whether 3D backend has some 3D overlay data displayed */ +typedef DECLCALLBACK(bool) FNCROGLHASDATA(void); +typedef FNCROGLHASDATA *PFNCROGLHASDATA; + +/* same as PFNCROGLHASDATA, but for specific screen */ +typedef DECLCALLBACK(bool) FNCROGLHASDATAFORSCREEN(uint32_t i32ScreenID); +typedef FNCROGLHASDATAFORSCREEN *PFNCROGLHASDATAFORSCREEN; + +/* callbacks chrogl gives to main */ +typedef struct CR_MAIN_INTERFACE +{ + PFNCROGLHASDATA pfnHasData; + PFNCROGLHASDATAFORSCREEN pfnHasDataForScreen; +} CR_MAIN_INTERFACE; + +typedef struct VBOXVDMACMD_CHROMIUM_CTL_CRHGSMI_SETUP_MAINCB +{ + VBOXVDMACMD_CHROMIUM_CTL Hdr; + /*in*/ + HCRHGSMICMDCOMPLETION hCompletion; + PFNCRHGSMICMDCOMPLETION pfnCompletion; + /*out*/ + CR_MAIN_INTERFACE MainInterface; +} VBOXVDMACMD_CHROMIUM_CTL_CRHGSMI_SETUP_MAINCB, *PVBOXVDMACMD_CHROMIUM_CTL_CRHGSMI_SETUP_MAINCB; + +typedef struct VBOXCRCON_SERVER *HVBOXCRCON_SERVER; +typedef struct PDMIDISPLAYVBVACALLBACKS* HVBOXCRCON_CLIENT; + +typedef struct VBOXCRCON_3DRGN_CLIENT* HVBOXCRCON_3DRGN_CLIENT; +typedef struct VBOXCRCON_3DRGN_ASYNCCLIENT* HVBOXCRCON_3DRGN_ASYNCCLIENT; + +/* server callbacks */ +/* submit chromium cmd */ +typedef DECLCALLBACK(int) FNVBOXCRCON_SVR_CRCMD(HVBOXCRCON_SERVER hServer, PVBOXVDMACMD_CHROMIUM_CMD pCmd, uint32_t cbCmd); +typedef FNVBOXCRCON_SVR_CRCMD *PFNVBOXCRCON_SVR_CRCMD; + +/* submit chromium control cmd */ +typedef DECLCALLBACK(int) FNVBOXCRCON_SVR_CRCTL(HVBOXCRCON_SERVER hServer, PVBOXVDMACMD_CHROMIUM_CTL pCtl, uint32_t cbCmd); +typedef FNVBOXCRCON_SVR_CRCTL *PFNVBOXCRCON_SVR_CRCTL; + +/* request 3D data. + * The protocol is the following: + * 1. if there is no 3D data displayed on screen, returns VINF_EOF immediately w/o calling any PFNVBOXCRCON_3DRGN_XXX callbacks + * 2. otherwise calls PFNVBOXCRCON_3DRGN_ONSUBMIT, submits the "regions get" request to the CrOpenGL server to process it asynchronously and returns VINF_SUCCESS + * 2.a on "regions get" request processing calls PFNVBOXCRCON_3DRGN_BEGIN, + * 2.b then PFNVBOXCRCON_3DRGN_REPORT zero or more times for each 3D region, + * 2.c and then PFNVBOXCRCON_3DRGN_END + * 3. returns VERR_XXX code on failure + * */ +typedef DECLCALLBACK(int) FNVBOXCRCON_SVR_3DRGN_GET(HVBOXCRCON_SERVER hServer, HVBOXCRCON_3DRGN_CLIENT hRgnClient, uint32_t idScreen); +typedef FNVBOXCRCON_SVR_3DRGN_GET *PFNVBOXCRCON_SVR_3DRGN_GET; + +/* 3D Regions Client callbacks */ +/* called from the PFNVBOXCRCON_SVR_3DRGN_GET callback in case server has 3D data and is going to process the request asynchronously, + * see comments for PFNVBOXCRCON_SVR_3DRGN_GET above */ +typedef DECLCALLBACK(int) FNVBOXCRCON_3DRGN_ONSUBMIT(HVBOXCRCON_3DRGN_CLIENT hRgnClient, uint32_t idScreen, HVBOXCRCON_3DRGN_ASYNCCLIENT *phRgnAsyncClient); +typedef FNVBOXCRCON_3DRGN_ONSUBMIT *PFNVBOXCRCON_3DRGN_ONSUBMIT; + +/* called from the "regions get" command processing thread, to indicate that the "regions get" is started. + * see comments for PFNVBOXCRCON_SVR_3DRGN_GET above */ +typedef DECLCALLBACK(int) FNVBOXCRCON_3DRGN_BEGIN(HVBOXCRCON_3DRGN_ASYNCCLIENT hRgnAsyncClient, uint32_t idScreen); +typedef FNVBOXCRCON_3DRGN_BEGIN *PFNVBOXCRCON_3DRGN_BEGIN; + +/* called from the "regions get" command processing thread, to report a 3D region. + * see comments for PFNVBOXCRCON_SVR_3DRGN_GET above */ +typedef DECLCALLBACK(int) FNVBOXCRCON_3DRGN_REPORT(HVBOXCRCON_3DRGN_ASYNCCLIENT hRgnAsyncClient, uint32_t idScreen, void *pvData, uint32_t cbStride, const RTRECT *pRect); +typedef FNVBOXCRCON_3DRGN_REPORT *PFNVBOXCRCON_3DRGN_REPORT; + +/* called from the "regions get" command processing thread, to indicate that the "regions get" is completed. + * see comments for PFNVBOXCRCON_SVR_3DRGN_GET above */ +typedef DECLCALLBACK(int) FNVBOXCRCON_3DRGN_END(HVBOXCRCON_3DRGN_ASYNCCLIENT hRgnAsyncClient, uint32_t idScreen); +typedef FNVBOXCRCON_3DRGN_END *PFNVBOXCRCON_3DRGN_END; + + +/* client callbacks */ +/* complete chromium cmd */ +typedef DECLCALLBACK(int) FNVBOXCRCON_CLT_CRCTL_COMPLETE(HVBOXCRCON_CLIENT hClient, PVBOXVDMACMD_CHROMIUM_CTL pCtl, int rc); +typedef FNVBOXCRCON_CLT_CRCTL_COMPLETE *PFNVBOXCRCON_CLT_CRCTL_COMPLETE; + +/* complete chromium control cmd */ +typedef DECLCALLBACK(int) FNVBOXCRCON_CLT_CRCMD_COMPLETE(HVBOXCRCON_CLIENT hClient, PVBOXVDMACMD_CHROMIUM_CMD pCmd, int rc); +typedef FNVBOXCRCON_CLT_CRCMD_COMPLETE *PFNVBOXCRCON_CLT_CRCMD_COMPLETE; + +typedef struct VBOXCRCON_SERVER_CALLBACKS +{ + HVBOXCRCON_SERVER hServer; + PFNVBOXCRCON_SVR_CRCMD pfnCrCmd; + PFNVBOXCRCON_SVR_CRCTL pfnCrCtl; + PFNVBOXCRCON_SVR_3DRGN_GET pfn3DRgnGet; +} VBOXCRCON_SERVER_CALLBACKS, *PVBOXCRCON_SERVER_CALLBACKS; + +typedef struct VBOXCRCON_CLIENT_CALLBACKS +{ + HVBOXCRCON_CLIENT hClient; + PFNVBOXCRCON_CLT_CRCMD_COMPLETE pfnCrCmdComplete; + PFNVBOXCRCON_CLT_CRCTL_COMPLETE pfnCrCtlComplete; + PFNVBOXCRCON_3DRGN_ONSUBMIT pfn3DRgnOnSubmit; + PFNVBOXCRCON_3DRGN_BEGIN pfn3DRgnBegin; + PFNVBOXCRCON_3DRGN_REPORT pfn3DRgnReport; + PFNVBOXCRCON_3DRGN_END pfn3DRgnEnd; +} VBOXCRCON_CLIENT_CALLBACKS, *PVBOXCRCON_CLIENT_CALLBACKS; + +/* issued by Main to establish connection between Main and CrOpenGL service */ +typedef struct VBOXVDMACMD_CHROMIUM_CTL_CRCONNECT +{ + VBOXVDMACMD_CHROMIUM_CTL Hdr; + /*input (filled by Client) :*/ + /*class VMMDev*/void *pVMMDev; + VBOXCRCON_CLIENT_CALLBACKS ClientCallbacks; + /*output (filled by Server) :*/ + VBOXCRCON_SERVER_CALLBACKS ServerCallbacks; +} VBOXVDMACMD_CHROMIUM_CTL_CRCONNECT, *PVBOXVDMACMD_CHROMIUM_CTL_CRCONNECT; + +/* ring command buffer dr */ +#define VBOXCMDVBVA_STATE_SUBMITTED 1 +#define VBOXCMDVBVA_STATE_CANCELLED 2 +#define VBOXCMDVBVA_STATE_IN_PROGRESS 3 +/* the "completed" state is signalled via the ring buffer values */ + +/* CrHgsmi command */ +#define VBOXCMDVBVA_OPTYPE_CRCMD 1 +/* blit command that does blitting of allocations identified by VRAM offset or host id + * for VRAM-offset ones the size and format are same as primary */ +#define VBOXCMDVBVA_OPTYPE_BLT 2 +/* flip */ +#define VBOXCMDVBVA_OPTYPE_FLIP 3 +/* ColorFill */ +#define VBOXCMDVBVA_OPTYPE_CLRFILL 4 +/* allocation paging transfer request */ +#define VBOXCMDVBVA_OPTYPE_PAGING_TRANSFER 5 +/* allocation paging fill request */ +#define VBOXCMDVBVA_OPTYPE_PAGING_FILL 6 +/* same as VBOXCMDVBVA_OPTYPE_NOP, but contains VBOXCMDVBVA_HDR data */ +#define VBOXCMDVBVA_OPTYPE_NOPCMD 7 +/* actual command is stored in guest system memory */ +#define VBOXCMDVBVA_OPTYPE_SYSMEMCMD 8 +/* complex command - i.e. can contain multiple commands + * i.e. the VBOXCMDVBVA_OPTYPE_COMPLEXCMD VBOXCMDVBVA_HDR is followed + * by one or more VBOXCMDVBVA_HDR commands. + * Each command's size is specified in it's VBOXCMDVBVA_HDR's u32FenceID field */ +#define VBOXCMDVBVA_OPTYPE_COMPLEXCMD 9 + +/* nop - is a one-bit command. The buffer size to skip is determined by VBVA buffer size */ +#define VBOXCMDVBVA_OPTYPE_NOP 0x80 + +/* u8Flags flags */ +/* transfer from RAM to Allocation */ +#define VBOXCMDVBVA_OPF_PAGING_TRANSFER_IN 0x80 + +#define VBOXCMDVBVA_OPF_BLT_TYPE_SAMEDIM_A8R8G8B8 0 +#define VBOXCMDVBVA_OPF_BLT_TYPE_GENERIC_A8R8G8B8 1 +#define VBOXCMDVBVA_OPF_BLT_TYPE_OFFPRIMSZFMT_OR_ID 2 + +#define VBOXCMDVBVA_OPF_BLT_TYPE_MASK 3 + + +#define VBOXCMDVBVA_OPF_CLRFILL_TYPE_GENERIC_A8R8G8B8 0 + +#define VBOXCMDVBVA_OPF_CLRFILL_TYPE_MASK 1 + + +/* blit direction is from first operand to second */ +#define VBOXCMDVBVA_OPF_BLT_DIR_IN_2 0x10 +/* operand 1 contains host id */ +#define VBOXCMDVBVA_OPF_OPERAND1_ISID 0x20 +/* operand 2 contains host id */ +#define VBOXCMDVBVA_OPF_OPERAND2_ISID 0x40 +/* primary hint id is src */ +#define VBOXCMDVBVA_OPF_PRIMARY_HINT_SRC 0x80 + +/* trying to make the header as small as possible, + * we'd have pretty few op codes actually, so 8bit is quite enough, + * we will be able to extend it in any way. */ +typedef struct VBOXCMDVBVA_HDR +{ + /* one VBOXCMDVBVA_OPTYPE_XXX, except NOP, see comments above */ + uint8_t u8OpCode; + /* command-specific + * VBOXCMDVBVA_OPTYPE_CRCMD - must be null + * VBOXCMDVBVA_OPTYPE_BLT - OR-ed VBOXCMDVBVA_OPF_ALLOC_XXX flags + * VBOXCMDVBVA_OPTYPE_PAGING_TRANSFER - must be null + * VBOXCMDVBVA_OPTYPE_PAGING_FILL - must be null + * VBOXCMDVBVA_OPTYPE_NOPCMD - must be null + * VBOXCMDVBVA_OPTYPE_NOP - not applicable (as the entire VBOXCMDVBVA_HDR is not valid) */ + uint8_t u8Flags; + /* one of VBOXCMDVBVA_STATE_XXX*/ + volatile uint8_t u8State; + union + { + /* result, 0 on success, otherwise contains the failure code TBD */ + int8_t i8Result; + uint8_t u8PrimaryID; + } u; + union + { + /* complex command (VBOXCMDVBVA_OPTYPE_COMPLEXCMD) element data */ + struct + { + /* command length */ + uint16_t u16CbCmdHost; + /* guest-specific data, host expects it to be NULL */ + uint16_t u16CbCmdGuest; + } complexCmdEl; + /* DXGK DDI fence ID */ + uint32_t u32FenceID; + } u2; +} VBOXCMDVBVA_HDR; + +typedef uint32_t VBOXCMDVBVAOFFSET; +typedef uint64_t VBOXCMDVBVAPHADDR; +typedef uint32_t VBOXCMDVBVAPAGEIDX; + +typedef struct VBOXCMDVBVA_CRCMD_BUFFER +{ + uint32_t cbBuffer; + VBOXCMDVBVAOFFSET offBuffer; +} VBOXCMDVBVA_CRCMD_BUFFER; + +typedef struct VBOXCMDVBVA_CRCMD_CMD +{ + uint32_t cBuffers; + VBOXCMDVBVA_CRCMD_BUFFER aBuffers[1]; +} VBOXCMDVBVA_CRCMD_CMD; + +typedef struct VBOXCMDVBVA_CRCMD +{ + VBOXCMDVBVA_HDR Hdr; + VBOXCMDVBVA_CRCMD_CMD Cmd; +} VBOXCMDVBVA_CRCMD; + +typedef struct VBOXCMDVBVA_ALLOCINFO +{ + union + { + VBOXCMDVBVAOFFSET offVRAM; + uint32_t id; + } u; +} VBOXCMDVBVA_ALLOCINFO; + +typedef struct VBOXCMDVBVA_ALLOCDESC +{ + VBOXCMDVBVA_ALLOCINFO Info; + uint16_t u16Width; + uint16_t u16Height; +} VBOXCMDVBVA_ALLOCDESC; + +typedef struct VBOXCMDVBVA_RECT +{ + /** Coordinates of affected rectangle. */ + int16_t xLeft; + int16_t yTop; + int16_t xRight; + int16_t yBottom; +} VBOXCMDVBVA_RECT; + +typedef struct VBOXCMDVBVA_POINT +{ + int16_t x; + int16_t y; +} VBOXCMDVBVA_POINT; + +typedef struct VBOXCMDVBVA_BLT_HDR +{ + VBOXCMDVBVA_HDR Hdr; + VBOXCMDVBVA_POINT Pos; +} VBOXCMDVBVA_BLT_HDR; + +typedef struct VBOXCMDVBVA_BLT_PRIMARY +{ + VBOXCMDVBVA_BLT_HDR Hdr; + VBOXCMDVBVA_ALLOCINFO alloc; + /* the rects count is determined from the command size */ + VBOXCMDVBVA_RECT aRects[1]; +} VBOXCMDVBVA_BLT_PRIMARY; + +typedef struct VBOXCMDVBVA_BLT_PRIMARY_GENERIC_A8R8G8B8 +{ + VBOXCMDVBVA_BLT_HDR Hdr; + VBOXCMDVBVA_ALLOCDESC alloc; + /* the rects count is determined from the command size */ + VBOXCMDVBVA_RECT aRects[1]; +} VBOXCMDVBVA_BLT_PRIMARY_GENERIC_A8R8G8B8; + +typedef struct VBOXCMDVBVA_BLT_OFFPRIMSZFMT_OR_ID +{ + VBOXCMDVBVA_BLT_HDR Hdr; + VBOXCMDVBVA_ALLOCINFO alloc; + uint32_t id; + /* the rects count is determined from the command size */ + VBOXCMDVBVA_RECT aRects[1]; +} VBOXCMDVBVA_BLT_OFFPRIMSZFMT_OR_ID; + +typedef struct VBOXCMDVBVA_BLT_SAMEDIM_A8R8G8B8 +{ + VBOXCMDVBVA_BLT_HDR Hdr; + VBOXCMDVBVA_ALLOCDESC alloc1; + VBOXCMDVBVA_ALLOCINFO info2; + /* the rects count is determined from the command size */ + VBOXCMDVBVA_RECT aRects[1]; +} VBOXCMDVBVA_BLT_SAMEDIM_A8R8G8B8; + +typedef struct VBOXCMDVBVA_BLT_GENERIC_A8R8G8B8 +{ + VBOXCMDVBVA_BLT_HDR Hdr; + VBOXCMDVBVA_ALLOCDESC alloc1; + VBOXCMDVBVA_ALLOCDESC alloc2; + /* the rects count is determined from the command size */ + VBOXCMDVBVA_RECT aRects[1]; +} VBOXCMDVBVA_BLT_GENERIC_A8R8G8B8; + +#define VBOXCMDVBVA_SIZEOF_BLTSTRUCT_MAX (sizeof (VBOXCMDVBVA_BLT_GENERIC_A8R8G8B8)) + +typedef struct VBOXCMDVBVA_FLIP +{ + VBOXCMDVBVA_HDR Hdr; + VBOXCMDVBVA_ALLOCINFO src; + VBOXCMDVBVA_RECT aRects[1]; +} VBOXCMDVBVA_FLIP; + +#define VBOXCMDVBVA_SIZEOF_FLIPSTRUCT_MIN (RT_OFFSETOF(VBOXCMDVBVA_FLIP, aRects)) + +typedef struct VBOXCMDVBVA_CLRFILL_HDR +{ + VBOXCMDVBVA_HDR Hdr; + uint32_t u32Color; +} VBOXCMDVBVA_CLRFILL_HDR; + +typedef struct VBOXCMDVBVA_CLRFILL_PRIMARY +{ + VBOXCMDVBVA_CLRFILL_HDR Hdr; + VBOXCMDVBVA_RECT aRects[1]; +} VBOXCMDVBVA_CLRFILL_PRIMARY; + +typedef struct VBOXCMDVBVA_CLRFILL_GENERIC_A8R8G8B8 +{ + VBOXCMDVBVA_CLRFILL_HDR Hdr; + VBOXCMDVBVA_ALLOCDESC dst; + VBOXCMDVBVA_RECT aRects[1]; +} VBOXCMDVBVA_CLRFILL_GENERIC_A8R8G8B8; + +#define VBOXCMDVBVA_SIZEOF_CLRFILLSTRUCT_MAX (sizeof (VBOXCMDVBVA_CLRFILL_GENERIC_A8R8G8B8)) + +#if 0 +#define VBOXCMDVBVA_SYSMEMEL_CPAGES_MAX 0x1000 + +typedef struct VBOXCMDVBVA_SYSMEMEL +{ + uint32_t cPagesAfterFirst : 12; + uint32_t iPage1 : 20; + uint32_t iPage2; +} VBOXCMDVBVA_SYSMEMEL; +#endif + +typedef struct VBOXCMDVBVA_PAGING_TRANSFER_DATA +{ + /* for now can only contain offVRAM. + * paging transfer can NOT be initiated for allocations having host 3D object (hostID) associated */ + VBOXCMDVBVA_ALLOCINFO Alloc; + VBOXCMDVBVAPAGEIDX aPageNumbers[1]; +} VBOXCMDVBVA_PAGING_TRANSFER_DATA; + +typedef struct VBOXCMDVBVA_PAGING_TRANSFER +{ + VBOXCMDVBVA_HDR Hdr; + VBOXCMDVBVA_PAGING_TRANSFER_DATA Data; +} VBOXCMDVBVA_PAGING_TRANSFER; + +typedef struct VBOXCMDVBVA_PAGING_FILL +{ + VBOXCMDVBVA_HDR Hdr; + uint32_t u32CbFill; + uint32_t u32Pattern; + /* paging transfer can NOT be initiated for allocations having host 3D object (hostID) associated */ + VBOXCMDVBVAOFFSET offVRAM; +} VBOXCMDVBVA_PAGING_FILL; + +typedef struct VBOXCMDVBVA_SYSMEMCMD +{ + VBOXCMDVBVA_HDR Hdr; + VBOXCMDVBVAPHADDR phCmd; +} VBOXCMDVBVA_SYSMEMCMD; + +#define VBOXCMDVBVACTL_TYPE_ENABLE 1 +#define VBOXCMDVBVACTL_TYPE_3DCTL 2 +#define VBOXCMDVBVACTL_TYPE_RESIZE 3 + +typedef struct VBOXCMDVBVA_CTL +{ + uint32_t u32Type; + int32_t i32Result; +} VBOXCMDVBVA_CTL; + +typedef struct VBOXCMDVBVA_CTL_ENABLE +{ + VBOXCMDVBVA_CTL Hdr; + VBVAENABLE Enable; +} VBOXCMDVBVA_CTL_ENABLE; + +#define VBOXCMDVBVA_SCREENMAP_SIZE(_elType) ((VBOX_VIDEO_MAX_SCREENS + sizeof (_elType) - 1) / sizeof (_elType)) +#define VBOXCMDVBVA_SCREENMAP_DECL(_elType, _name) _elType _name[VBOXCMDVBVA_SCREENMAP_SIZE(_elType)] + +typedef struct VBOXCMDVBVA_RESIZE_ENTRY +{ + VBVAINFOSCREEN Screen; + VBOXCMDVBVA_SCREENMAP_DECL(uint32_t, aTargetMap); +} VBOXCMDVBVA_RESIZE_ENTRY; + +typedef struct VBOXCMDVBVA_RESIZE +{ + VBOXCMDVBVA_RESIZE_ENTRY aEntries[1]; +} VBOXCMDVBVA_RESIZE; + +typedef struct VBOXCMDVBVA_CTL_RESIZE +{ + VBOXCMDVBVA_CTL Hdr; + VBOXCMDVBVA_RESIZE Resize; +} VBOXCMDVBVA_CTL_RESIZE; + +#define VBOXCMDVBVA3DCTL_TYPE_CONNECT 1 +#define VBOXCMDVBVA3DCTL_TYPE_DISCONNECT 2 +#define VBOXCMDVBVA3DCTL_TYPE_CMD 3 + +typedef struct VBOXCMDVBVA_3DCTL +{ + uint32_t u32Type; + uint32_t u32CmdClientId; +} VBOXCMDVBVA_3DCTL; + +typedef struct VBOXCMDVBVA_3DCTL_CONNECT +{ + VBOXCMDVBVA_3DCTL Hdr; + uint32_t u32MajorVersion; + uint32_t u32MinorVersion; + uint64_t u64Pid; +} VBOXCMDVBVA_3DCTL_CONNECT; + +typedef struct VBOXCMDVBVA_3DCTL_CMD +{ + VBOXCMDVBVA_3DCTL Hdr; + VBOXCMDVBVA_HDR Cmd; +} VBOXCMDVBVA_3DCTL_CMD; + +typedef struct VBOXCMDVBVA_CTL_3DCTL_CMD +{ + VBOXCMDVBVA_CTL Hdr; + VBOXCMDVBVA_3DCTL_CMD Cmd; +} VBOXCMDVBVA_CTL_3DCTL_CMD; + +typedef struct VBOXCMDVBVA_CTL_3DCTL_CONNECT +{ + VBOXCMDVBVA_CTL Hdr; + VBOXCMDVBVA_3DCTL_CONNECT Connect; +} VBOXCMDVBVA_CTL_3DCTL_CONNECT; + +typedef struct VBOXCMDVBVA_CTL_3DCTL +{ + VBOXCMDVBVA_CTL Hdr; + VBOXCMDVBVA_3DCTL Ctl; +} VBOXCMDVBVA_CTL_3DCTL; + +#pragma pack() + + +#ifdef VBOXVDMA_WITH_VBVA +# pragma pack(1) + +typedef struct VBOXVDMAVBVACMD +{ + HGSMIOFFSET offCmd; +} VBOXVDMAVBVACMD; + +#pragma pack() +#endif + +#endif + diff --git a/src/VBoxVideoErr.h b/src/VBoxVideoErr.h new file mode 100644 index 0000000..81c6c46 --- /dev/null +++ b/src/VBoxVideoErr.h @@ -0,0 +1,64 @@ +/* + * Copyright (C) 2017 Oracle Corporation + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE COPYRIGHT HOLDER(S) OR AUTHOR(S) BE LIABLE FOR ANY CLAIM, DAMAGES OR + * OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, + * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR + * OTHER DEALINGS IN THE SOFTWARE. + * + * Authors: Michael Thayer <michael.thayer@oracle.com> + */ + +#ifndef __VBOX_ERR_H__ +#define __VBOX_ERR_H__ + +/** @name VirtualBox error macros + * @{ */ + +#define VINF_SUCCESS 0 +#define VERR_INVALID_PARAMETER (-2) +#define VERR_INVALID_POINTER (-6) +#define VERR_NO_MEMORY (-8) +#define VERR_NOT_IMPLEMENTED (-12) +#define VERR_INVALID_FUNCTION (-36) +#define VERR_NOT_SUPPORTED (-37) +#define VERR_TOO_MUCH_DATA (-42) +#define VERR_NOT_FOUND (-78) +#define VERR_INVALID_STATE (-79) +#define VERR_OUT_OF_RESOURCES (-80) +#define VERR_ALREADY_EXISTS (-105) +#define VERR_INTERNAL_ERROR (-225) + +#define RT_SUCCESS_NP(rc) ( (int)(rc) >= VINF_SUCCESS ) +#define RT_SUCCESS(rc) ( likely(RT_SUCCESS_NP(rc)) ) +#define RT_FAILURE(rc) ( unlikely(!RT_SUCCESS_NP(rc)) ) + +/** @} */ + +/** @name VirtualBox assertions + * @{ */ + +/* Unlike BUILD_BUG_ON(), these can be used outside of functions. */ +extern int vbox_assert_var[1]; +#define assert_compile(expr) \ + extern int vbox_assert_var[1] __attribute__((__unused__)), \ + vbox_assert_var[(expr) ? 1 : 0] __attribute__((__unused__)) +#define assert_compile_size(type, size) \ + assert_compile(sizeof(type) == (size)) + +/** @} */ + +#endif diff --git a/src/VBoxVideoGuest.h b/src/VBoxVideoGuest.h new file mode 100644 index 0000000..ce6e563 --- /dev/null +++ b/src/VBoxVideoGuest.h @@ -0,0 +1,175 @@ +/* + * Copyright (C) 2006-2017 Oracle Corporation + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE COPYRIGHT HOLDER(S) OR AUTHOR(S) BE LIABLE FOR ANY CLAIM, DAMAGES OR + * OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, + * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR + * OTHER DEALINGS IN THE SOFTWARE. + */ + + +#ifndef ___VBox_Graphics_VBoxVideoGuest_h___ +#define ___VBox_Graphics_VBoxVideoGuest_h___ + +#include "VBoxVideoIPRT.h" +#include "HGSMIBase.h" +#include "VBoxVideo.h" + +RT_C_DECLS_BEGIN + +/** + * Structure grouping the context needed for sending graphics acceleration + * information to the host via VBVA. Each screen has its own VBVA buffer. + */ +typedef struct VBVABUFFERCONTEXT +{ + /** Offset of the buffer in the VRAM section for the screen */ + uint32_t offVRAMBuffer; + /** Length of the buffer in bytes */ + uint32_t cbBuffer; + /** This flag is set if we wrote to the buffer faster than the host could + * read it. */ + bool fHwBufferOverflow; + /** The VBVA record that we are currently preparing for the host, NULL if + * none. */ + struct VBVARECORD *pRecord; + /** Pointer to the VBVA buffer mapped into the current address space. Will + * be NULL if VBVA is not enabled. */ + struct VBVABUFFER *pVBVA; +} VBVABUFFERCONTEXT, *PVBVABUFFERCONTEXT; + +/** @name Base HGSMI APIs + * @{ */ + +DECLHIDDEN(bool) VBoxHGSMIIsSupported(void); +DECLHIDDEN(void) VBoxHGSMIGetBaseMappingInfo(uint32_t cbVRAM, + uint32_t *poffVRAMBaseMapping, + uint32_t *pcbMapping, + uint32_t *poffGuestHeapMemory, + uint32_t *pcbGuestHeapMemory, + uint32_t *poffHostFlags); +DECLHIDDEN(int) VBoxHGSMIReportFlagsLocation(PHGSMIGUESTCOMMANDCONTEXT pCtx, + HGSMIOFFSET offLocation); +DECLHIDDEN(int) VBoxHGSMISendCapsInfo(PHGSMIGUESTCOMMANDCONTEXT pCtx, + uint32_t fCaps); +DECLHIDDEN(void) VBoxHGSMIGetHostAreaMapping(PHGSMIGUESTCOMMANDCONTEXT pCtx, + uint32_t cbVRAM, + uint32_t offVRAMBaseMapping, + uint32_t *poffVRAMHostArea, + uint32_t *pcbHostArea); +DECLHIDDEN(int) VBoxHGSMISendHostCtxInfo(PHGSMIGUESTCOMMANDCONTEXT pCtx, + HGSMIOFFSET offVRAMFlagsLocation, + uint32_t fCaps, + uint32_t offVRAMHostArea, + uint32_t cbHostArea); +DECLHIDDEN(int) VBoxQueryConfHGSMI(PHGSMIGUESTCOMMANDCONTEXT pCtx, + uint32_t u32Index, uint32_t *pulValue); +DECLHIDDEN(int) VBoxQueryConfHGSMIDef(PHGSMIGUESTCOMMANDCONTEXT pCtx, + uint32_t u32Index, uint32_t u32DefValue, uint32_t *pulValue); +DECLHIDDEN(int) VBoxHGSMIUpdatePointerShape(PHGSMIGUESTCOMMANDCONTEXT pCtx, + uint32_t fFlags, + uint32_t cHotX, + uint32_t cHotY, + uint32_t cWidth, + uint32_t cHeight, + uint8_t *pPixels, + uint32_t cbLength); +DECLHIDDEN(int) VBoxHGSMICursorPosition(PHGSMIGUESTCOMMANDCONTEXT pCtx, bool fReportPosition, uint32_t x, uint32_t y, + uint32_t *pxHost, uint32_t *pyHost); + +/** @} */ + +/** @name VBVA APIs + * @{ */ +DECLHIDDEN(bool) VBoxVBVAEnable(PVBVABUFFERCONTEXT pCtx, + PHGSMIGUESTCOMMANDCONTEXT pHGSMICtx, + struct VBVABUFFER *pVBVA, int32_t cScreen); +DECLHIDDEN(void) VBoxVBVADisable(PVBVABUFFERCONTEXT pCtx, + PHGSMIGUESTCOMMANDCONTEXT pHGSMICtx, + int32_t cScreen); +DECLHIDDEN(bool) VBoxVBVABufferBeginUpdate(PVBVABUFFERCONTEXT pCtx, + PHGSMIGUESTCOMMANDCONTEXT pHGSMICtx); +DECLHIDDEN(void) VBoxVBVABufferEndUpdate(PVBVABUFFERCONTEXT pCtx); +DECLHIDDEN(bool) VBoxVBVAWrite(PVBVABUFFERCONTEXT pCtx, + PHGSMIGUESTCOMMANDCONTEXT pHGSMICtx, + const void *pv, uint32_t cb); +DECLHIDDEN(bool) VBoxVBVAOrderSupported(PVBVABUFFERCONTEXT pCtx, unsigned code); +DECLHIDDEN(void) VBoxVBVASetupBufferContext(PVBVABUFFERCONTEXT pCtx, + uint32_t offVRAMBuffer, + uint32_t cbBuffer); + +/** @} */ + +/** @name Modesetting APIs + * @{ */ + +DECLHIDDEN(uint32_t) VBoxHGSMIGetMonitorCount(PHGSMIGUESTCOMMANDCONTEXT pCtx); +DECLHIDDEN(uint32_t) VBoxVideoGetVRAMSize(void); +DECLHIDDEN(bool) VBoxVideoAnyWidthAllowed(void); +DECLHIDDEN(uint16_t) VBoxHGSMIGetScreenFlags(PHGSMIGUESTCOMMANDCONTEXT pCtx); + +struct VBVAINFOVIEW; +/** + * Callback funtion called from @a VBoxHGSMISendViewInfo to initialise + * the @a VBVAINFOVIEW structure for each screen. + * + * @returns iprt status code + * @param pvData context data for the callback, passed to @a + * VBoxHGSMISendViewInfo along with the callback + * @param pInfo array of @a VBVAINFOVIEW structures to be filled in + * @todo explicitly pass the array size + */ +typedef DECLCALLBACK(int) FNHGSMIFILLVIEWINFO(void *pvData, + struct VBVAINFOVIEW *pInfo, + uint32_t cViews); +/** Pointer to a FNHGSMIFILLVIEWINFO callback */ +typedef FNHGSMIFILLVIEWINFO *PFNHGSMIFILLVIEWINFO; + +DECLHIDDEN(int) VBoxHGSMISendViewInfo(PHGSMIGUESTCOMMANDCONTEXT pCtx, + uint32_t u32Count, + PFNHGSMIFILLVIEWINFO pfnFill, + void *pvData); +DECLHIDDEN(void) VBoxVideoSetModeRegisters(uint16_t cWidth, uint16_t cHeight, + uint16_t cVirtWidth, uint16_t cBPP, + uint16_t fFlags, + uint16_t cx, uint16_t cy); +DECLHIDDEN(bool) VBoxVideoGetModeRegisters(uint16_t *pcWidth, + uint16_t *pcHeight, + uint16_t *pcVirtWidth, + uint16_t *pcBPP, + uint16_t *pfFlags); +DECLHIDDEN(void) VBoxVideoDisableVBE(void); +DECLHIDDEN(void) VBoxHGSMIProcessDisplayInfo(PHGSMIGUESTCOMMANDCONTEXT pCtx, + uint32_t cDisplay, + int32_t cOriginX, + int32_t cOriginY, + uint32_t offStart, + uint32_t cbPitch, + uint32_t cWidth, + uint32_t cHeight, + uint16_t cBPP, + uint16_t fFlags); +DECLHIDDEN(int) VBoxHGSMIUpdateInputMapping(PHGSMIGUESTCOMMANDCONTEXT pCtx, int32_t cOriginX, int32_t cOriginY, + uint32_t cWidth, uint32_t cHeight); +DECLHIDDEN(int) VBoxHGSMIGetModeHints(PHGSMIGUESTCOMMANDCONTEXT pCtx, + unsigned cScreens, VBVAMODEHINT *paHints); + +/** @} */ + +RT_C_DECLS_END + +#endif + diff --git a/src/VBoxVideoIPRT.h b/src/VBoxVideoIPRT.h new file mode 100644 index 0000000..773044c --- /dev/null +++ b/src/VBoxVideoIPRT.h @@ -0,0 +1,221 @@ +/* $Id: VBoxVideoIPRT.h 118373 2017-10-13 17:01:46Z michael $ */ +/* + * Copyright (C) 2017 Oracle Corporation + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NON-INFRINGEMENT. IN NO EVENT SHALL + * THE COPYRIGHT HOLDERS, AUTHORS AND/OR ITS SUPPLIERS BE LIABLE FOR ANY CLAIM, + * DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR + * OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE + * USE OR OTHER DEALINGS IN THE SOFTWARE. + */ + +/* In builds inside of the VirtualBox source tree we override the default + * VBoxVideoIPRT.h using -include, therefore this define must match the one + * there. */ +#ifndef ___VBox_Graphics_VBoxVideoIPRT_h +#define ___VBox_Graphics_VBoxVideoIPRT_h + +# include "VBoxVideoErr.h" + +#ifndef __cplusplus +typedef enum +{ + false = 0, + true +} bool; +# define RT_C_DECLS_BEGIN +# define RT_C_DECLS_END +#else +# define RT_C_DECLS_BEGIN extern "C" { +# define RT_C_DECLS_END } +#endif + +#if defined(IN_XF86_MODULE) && !defined(NO_ANSIC) +# ifdef __cplusplus +/* xf86Module.h redefines this. */ +# define NULL 0 +# endif +RT_C_DECLS_BEGIN +# include "xf86_ansic.h" +RT_C_DECLS_END +#endif /* defined(IN_XF86_MODULE) && !defined(NO_ANSIC) */ +#define __STDC_LIMIT_MACROS /* define *INT*_MAX on C++ too. */ +#include "compiler.h" /* Can pull in <sdtint.h>. Must come after xf86_ansic.h on XFree86. */ +#include <X11/Xfuncproto.h> +#include <stdint.h> +#if defined(IN_XF86_MODULE) && !defined(NO_ANSIC) +/* XFree86 did not have these. Not that I care much for micro-optimisations + * in most cases anyway. */ +# define _X_LIKELY(x) (x) +# define _X_UNLIKELY(x) (x) +# ifndef offsetof +# define offsetof(type, member) ( (int)(uintptr_t)&( ((type *)(void *)0)->member) ) +# endif +#else /* !(defined(IN_XF86_MODULE) && !defined(NO_ANSIC)) */ +# include <stdarg.h> +# include <stddef.h> +# include <string.h> +#endif /* !(defined(IN_XF86_MODULE) && !defined(NO_ANSIC)) */ + +RT_C_DECLS_BEGIN +extern int RTASSERTVAR[1]; +RT_C_DECLS_END + +#define AssertCompile(expr) \ + extern int RTASSERTVAR[1] __attribute__((__unused__)), \ + RTASSERTVAR[(expr) ? 1 : 0] __attribute__((__unused__)) +#define AssertCompileSize(type, size) \ + AssertCompile(sizeof(type) == (size)) +#define AssertPtrNullReturnVoid(a) do { } while(0) + +#if !defined(IN_XF86_MODULE) && defined(DEBUG) +# include <assert.h> +# define Assert assert +# define AssertFailed() assert(0) +# define AssertMsg(expr, msg) \ + do { \ + if (!(expr)) xf86ErrorF msg; \ + assert((expr)); \ + } while (0) +# define AssertPtr assert +# define AssertRC(expr) assert (!expr) +#else +# define Assert(expr) do { } while(0) +# define AssertFailed() do { } while(0) +# define AssertMsg(expr, msg) do { } while(0) +# define AssertPtr(expr) do { } while(0) +# define AssertRC(expr) do { } while(0) +#endif + +#define DECLCALLBACK(type) type +#define DECLCALLBACKMEMBER(type, name) type (* name) +#if __GNUC__ >= 4 +# define DECLHIDDEN(type) __attribute__((visibility("hidden"))) type +#else +# define DECLHIDDEN(type) type +#endif +#define DECLINLINE(type) static __inline__ type + +#define _1K 1024 +#define ASMCompilerBarrier mem_barrier +#define RT_BIT(bit) ( 1U << (bit) ) +#define RT_BOOL(Value) ( !!(Value) ) +#define RT_BZERO(pv, cb) do { memset((pv), 0, cb); } while (0) +#define RT_CLAMP(Value, Min, Max) ( (Value) > (Max) ? (Max) : (Value) < (Min) ? (Min) : (Value) ) +#define RT_ELEMENTS(aArray) ( sizeof(aArray) / sizeof((aArray)[0]) ) +#define RTIOPORT unsigned short +#define RT_NOREF(...) (void)(__VA_ARGS__) +#define RT_OFFSETOF(type, member) offsetof(type, member) +#define RT_ZERO(Obj) RT_BZERO(&(Obj), sizeof(Obj)) +#define VALID_PTR(ptr) ( (uintptr_t)(ptr) + 0x1000U >= 0x2000U ) +#ifndef INT16_C +# define INT16_C(Value) (Value) +#endif +#ifndef UINT16_C +# define UINT16_C(Value) (Value) +#endif +#ifndef INT32_C +# define INT32_C(Value) (Value ## U) +#endif +#ifndef UINT32_C +# define UINT32_C(Value) (Value ## U) +#endif + +#define likely _X_LIKELY +#define unlikely _X_UNLIKELY + +/** + * A point in a two dimentional coordinate system. + */ +typedef struct RTPOINT +{ + /** X coordinate. */ + int32_t x; + /** Y coordinate. */ + int32_t y; +} RTPOINT; + +/** + * Rectangle data type, double point. + */ +typedef struct RTRECT +{ + /** left X coordinate. */ + int32_t xLeft; + /** top Y coordinate. */ + int32_t yTop; + /** right X coordinate. (exclusive) */ + int32_t xRight; + /** bottom Y coordinate. (exclusive) */ + int32_t yBottom; +} RTRECT; + +/** + * Rectangle data type, point + size. + */ +typedef struct RTRECT2 +{ + /** X coordinate. + * Unless stated otherwise, this is the top left corner. */ + int32_t x; + /** Y coordinate. + * Unless stated otherwise, this is the top left corner. */ + int32_t y; + /** The width. + * Unless stated otherwise, this is to the right of (x,y) and will not + * be a negative number. */ + int32_t cx; + /** The height. + * Unless stated otherwise, this is down from (x,y) and will not be a + * negative number. */ + int32_t cy; +} RTRECT2; + +/** + * The size of a rectangle. + */ +typedef struct RTRECTSIZE +{ + /** The width (along the x-axis). */ + uint32_t cx; + /** The height (along the y-axis). */ + uint32_t cy; +} RTRECTSIZE; + +/** @name Port I/O helpers + * @{ */ + +/** Write an 8-bit value to an I/O port. */ +#define VBVO_PORT_WRITE_U8(Port, Value) \ + outb(Port, Value) +/** Write a 16-bit value to an I/O port. */ +#define VBVO_PORT_WRITE_U16(Port, Value) \ + outw(Port, Value) +/** Write a 32-bit value to an I/O port. */ +#define VBVO_PORT_WRITE_U32(Port, Value) \ + outl(Port, Value) +/** Read an 8-bit value from an I/O port. */ +#define VBVO_PORT_READ_U8(Port) \ + inb(Port) +/** Read a 16-bit value from an I/O port. */ +#define VBVO_PORT_READ_U16(Port) \ + inw(Port) +/** Read a 32-bit value from an I/O port. */ +#define VBVO_PORT_READ_U32(Port) \ + inl(Port) + +/** @} */ + +#endif diff --git a/src/VBoxVideoVBE.h b/src/VBoxVideoVBE.h new file mode 100644 index 0000000..78f8a89 --- /dev/null +++ b/src/VBoxVideoVBE.h @@ -0,0 +1,81 @@ +/* + * Copyright (C) 2006-2017 Oracle Corporation + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE COPYRIGHT HOLDER(S) OR AUTHOR(S) BE LIABLE FOR ANY CLAIM, DAMAGES OR + * OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, + * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR + * OTHER DEALINGS IN THE SOFTWARE. + */ + +#ifndef ___VBox_Graphics_VBoxVideoVBE_h +#define ___VBox_Graphics_VBoxVideoVBE_h + +/* GUEST <-> HOST Communication API */ + +/** @todo FIXME: Either dynamicly ask host for this or put somewhere high in + * physical memory like 0xE0000000. */ + +#define VBE_DISPI_BANK_ADDRESS 0xA0000 +#define VBE_DISPI_BANK_SIZE_KB 64 + +#define VBE_DISPI_MAX_XRES 16384 +#define VBE_DISPI_MAX_YRES 16384 +#define VBE_DISPI_MAX_BPP 32 + +#define VBE_DISPI_IOPORT_INDEX 0x01CE +#define VBE_DISPI_IOPORT_DATA 0x01CF + +#define VBE_DISPI_IOPORT_DAC_WRITE_INDEX 0x03C8 +#define VBE_DISPI_IOPORT_DAC_DATA 0x03C9 + +#define VBE_DISPI_INDEX_ID 0x0 +#define VBE_DISPI_INDEX_XRES 0x1 +#define VBE_DISPI_INDEX_YRES 0x2 +#define VBE_DISPI_INDEX_BPP 0x3 +#define VBE_DISPI_INDEX_ENABLE 0x4 +#define VBE_DISPI_INDEX_BANK 0x5 +#define VBE_DISPI_INDEX_VIRT_WIDTH 0x6 +#define VBE_DISPI_INDEX_VIRT_HEIGHT 0x7 +#define VBE_DISPI_INDEX_X_OFFSET 0x8 +#define VBE_DISPI_INDEX_Y_OFFSET 0x9 +#define VBE_DISPI_INDEX_VBOX_VIDEO 0xa +#define VBE_DISPI_INDEX_FB_BASE_HI 0xb + +#define VBE_DISPI_ID0 0xB0C0 +#define VBE_DISPI_ID1 0xB0C1 +#define VBE_DISPI_ID2 0xB0C2 +#define VBE_DISPI_ID3 0xB0C3 +#define VBE_DISPI_ID4 0xB0C4 + +#define VBE_DISPI_ID_VBOX_VIDEO 0xBE00 +/* The VBOX interface id. Indicates support for VBVA shared memory interface. */ +#define VBE_DISPI_ID_HGSMI 0xBE01 +#define VBE_DISPI_ID_ANYX 0xBE02 + +#define VBE_DISPI_DISABLED 0x00 +#define VBE_DISPI_ENABLED 0x01 +#define VBE_DISPI_GETCAPS 0x02 +#define VBE_DISPI_8BIT_DAC 0x20 +/** @note this definition is a BOCHS legacy, used only in the video BIOS + * code and ignored by the emulated hardware. */ +#define VBE_DISPI_LFB_ENABLED 0x40 +#define VBE_DISPI_NOCLEARMEM 0x80 + +#define VGA_PORT_HGSMI_HOST 0x3b0 +#define VGA_PORT_HGSMI_GUEST 0x3d0 + +#endif /* !___VBox_Graphics_VBoxVideoVBE_h */ + diff --git a/src/edid.c b/src/edid.c new file mode 100644 index 0000000..fa88639 --- /dev/null +++ b/src/edid.c @@ -0,0 +1,166 @@ +/* $Id: edid.c 118361 2017-10-13 14:17:38Z michael $ */ +/** @file + * + * Linux Additions X11 graphics driver, EDID construction + */ + +/* + * Copyright (C) 2006-2017 Oracle Corporation + * + * This code is based on drmmode_display.c from the X.Org xf86-video-intel + * driver with the following copyright notice: + * + * Copyright © 2007 Red Hat, Inc. + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice (including the next + * paragraph) shall be included in all copies or substantial portions of the + * Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + * + * Authors: + * Dave Airlie <airlied@redhat.com> + * Michael Thayer <michael.thayer@oracle.com> + */ + +#include "misc.h" +#include "xf86DDC.h" +#include "xf86Crtc.h" +#include "vboxvideo.h" + +enum { EDID_SIZE = 128 }; + +const unsigned char g_acszEDIDBase[EDID_SIZE] = +{ + 0x00, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0x00, /* header */ + 0x58, 0x58, /* manufacturer (VBX) */ + 0x00, 0x00, /* product code */ + 0x00, 0x00,0x00, 0x00, /* serial number goes here */ + 0x01, /* week of manufacture */ + 0x00, /* year of manufacture */ + 0x01, 0x03, /* EDID version */ + 0x80, /* capabilities - digital */ + 0x00, /* horiz. res in cm, zero for projectors */ + 0x00, /* vert. res in cm */ + 0x78, /* display gamma (120 == 2.2). Should we ask the host for this? */ + 0xEE, /* features (standby, suspend, off, RGB, standard colour space, + * preferred timing mode) */ + 0xEE, 0x91, 0xA3, 0x54, 0x4C, 0x99, 0x26, 0x0F, 0x50, 0x54, + /* chromaticity for standard colour space - should we ask the host? */ + 0x00, 0x00, 0x00, /* no default timings */ + 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, + 0x01, 0x01, 0x01, 0x01, /* no standard timings */ + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* descriptor block 1 goes here */ + 0x00, 0x00, 0x00, 0xFD, 0x00, /* descriptor block 2, monitor ranges */ + 0x00, 0xC8, 0x00, 0xC8, 0x64, 0x00, 0x0A, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x20, /* 0-200Hz vertical, 0-200KHz horizontal, 1000MHz pixel clock */ + 0x00, 0x00, 0x00, 0xFC, 0x00, /* descriptor block 3, monitor name */ + 'V', 'B', 'O', 'X', ' ', 'm', 'o', 'n', 'i', 't', 'o', 'r', '\n', + 0x00, 0x00, 0x00, 0x10, 0x00, /* descriptor block 4: dummy data */ + 0x0A, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x20, + 0x00, /* number of extensions */ + 0x00 /* checksum goes here */ +}; + +static void fillDescBlockTimings(unsigned char *pchDescBlock, + DisplayModePtr mode) +{ + struct detailed_timings timing; + + timing.clock = mode->Clock * 1000; + timing.h_active = mode->HDisplay; + timing.h_blanking = mode->HTotal - mode->HDisplay; + timing.v_active = mode->VDisplay; + timing.v_blanking = mode->VTotal - mode->VDisplay; + timing.h_sync_off = mode->HSyncStart - mode->HDisplay; + timing.h_sync_width = mode->HSyncEnd - mode->HSyncStart; + timing.v_sync_off = mode->VSyncStart - mode->VDisplay; + timing.v_sync_width = mode->VSyncEnd - mode->VSyncStart; + pchDescBlock[0] = (timing.clock / 10000) & 0xff; + pchDescBlock[1] = (timing.clock / 10000) >> 8; + pchDescBlock[2] = timing.h_active & 0xff; + pchDescBlock[3] = timing.h_blanking & 0xff; + pchDescBlock[4] = (timing.h_active >> 4) & 0xf0; + pchDescBlock[4] |= (timing.h_blanking >> 8) & 0xf; + pchDescBlock[5] = timing.v_active & 0xff; + pchDescBlock[6] = timing.v_blanking & 0xff; + pchDescBlock[7] = (timing.v_active >> 4) & 0xf0; + pchDescBlock[7] |= (timing.v_blanking >> 8) & 0xf; + pchDescBlock[8] = timing.h_sync_off & 0xff; + pchDescBlock[9] = timing.h_sync_width & 0xff; + pchDescBlock[10] = (timing.v_sync_off << 4) & 0xf0; + pchDescBlock[10] |= timing.v_sync_width & 0xf; + pchDescBlock[11] = (timing.h_sync_off >> 2) & 0xC0; + pchDescBlock[11] |= (timing.h_sync_width >> 4) & 0x30; + pchDescBlock[11] |= (timing.v_sync_off >> 2) & 0xC; + pchDescBlock[11] |= (timing.v_sync_width >> 4) & 0x3; + pchDescBlock[12] = pchDescBlock[13] = pchDescBlock[14] + = pchDescBlock[15] = pchDescBlock[16] + = pchDescBlock[17] = 0; +} + + +static void setEDIDChecksum(unsigned char *pch) +{ + unsigned i, sum = 0; + for (i = 0; i < EDID_SIZE - 1; ++i) + sum += pch[i]; + pch[EDID_SIZE - 1] = (0x100 - (sum & 0xFF)) & 0xFF; +} + + +/** + * Construct an EDID for an output given a preferred mode. The main reason for + * doing this is to confound gnome-settings-deamon which tries to reset the + * last mode configuration if the same monitors are plugged in again, which is + * a reasonable thing to do but not what we want in a VM. We evily store + * the (empty) raw EDID data at the end of the structure so that it gets + * freed automatically along with the structure. + */ +Bool VBOXEDIDSet(xf86OutputPtr output, DisplayModePtr pmode) +{ + unsigned char *pch, *pchEDID; + xf86MonPtr pEDIDMon; + + pch = calloc(1, sizeof(xf86Monitor) + EDID_SIZE); + if (!pch) + { + xf86DrvMsg(output->scrn->scrnIndex, X_ERROR, + "Can't allocate memory for EDID structure.\n"); + return FALSE; + } + pchEDID = pch + sizeof(xf86Monitor); + memcpy(pchEDID, g_acszEDIDBase, EDID_SIZE); + pchEDID[12] = pmode->HDisplay & 0xff; + pchEDID[13] = pmode->HDisplay >> 8; + pchEDID[14] = pmode->VDisplay & 0xff; + pchEDID[15] = pmode->VDisplay >> 8; + fillDescBlockTimings(pchEDID + 54, pmode); + setEDIDChecksum(pchEDID); + pEDIDMon = xf86InterpretEDID(output->scrn->scrnIndex, pchEDID); + if (!pEDIDMon) + { + free(pch); + return FALSE; + } + memcpy(pch, pEDIDMon, sizeof(xf86Monitor)); + free(pEDIDMon); + pEDIDMon = (xf86MonPtr)pch; + xf86OutputSetEDID(output, pEDIDMon); + return TRUE; +} diff --git a/src/getmode.c b/src/getmode.c new file mode 100644 index 0000000..72947fe --- /dev/null +++ b/src/getmode.c @@ -0,0 +1,318 @@ +/* $Id: getmode.c 118345 2017-10-12 18:18:01Z michael $ */ +/** @file + * VirtualBox X11 Additions graphics driver dynamic video mode functions. + */ + +/* + * Copyright (C) 2006-2017 Oracle Corporation + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NON-INFRINGEMENT. IN NO EVENT SHALL + * THE COPYRIGHT HOLDERS, AUTHORS AND/OR ITS SUPPLIERS BE LIABLE FOR ANY CLAIM, + * DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR + * OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE + * USE OR OTHER DEALINGS IN THE SOFTWARE. + */ + +#include "vboxvideo.h" + +#define NEED_XF86_TYPES +#include "xf86.h" + +#ifdef XORG_7X +# include <stdio.h> +# include <stdlib.h> +# include <string.h> +#endif + +#ifdef VBOXVIDEO_13 +# ifdef RT_OS_LINUX +# include <linux/input.h> +# ifndef EVIOCGRAB +# define EVIOCGRAB _IOW('E', 0x90, int) +# endif +# ifndef KEY_SWITCHVIDEOMODE +# define KEY_SWITCHVIDEOMODE 227 +# endif +# include <dirent.h> +# include <errno.h> +# include <fcntl.h> +# include <unistd.h> +# endif /* RT_OS_LINUX */ +#endif /* VBOXVIDEO_13 */ + +/************************************************************************** +* Main functions * +**************************************************************************/ + +/** + * Fills a display mode M with a built-in mode of name pszName and dimensions + * cx and cy. + */ +static void vboxFillDisplayMode(ScrnInfoPtr pScrn, DisplayModePtr m, + const char *pszName, unsigned cx, unsigned cy) +{ + VBOXPtr pVBox = pScrn->driverPrivate; + char szName[256]; + DisplayModePtr pPrev = m->prev; + DisplayModePtr pNext = m->next; + + if (!pszName) + { + sprintf(szName, "%ux%u", cx, cy); + pszName = szName; + } + TRACE_LOG("pszName=%s, cx=%u, cy=%u\n", pszName, cx, cy); + if (m->name) + free((void*)m->name); + memset(m, '\0', sizeof(*m)); + m->prev = pPrev; + m->next = pNext; + m->status = MODE_OK; + m->type = M_T_BUILTIN; + /* Older versions of VBox only support screen widths which are a multiple + * of 8 */ + if (pVBox->fAnyX) + m->HDisplay = cx; + else + m->HDisplay = cx & ~7; + m->HSyncStart = m->HDisplay + 2; + m->HSyncEnd = m->HDisplay + 4; + m->HTotal = m->HDisplay + 6; + m->VDisplay = cy; + m->VSyncStart = m->VDisplay + 2; + m->VSyncEnd = m->VDisplay + 4; + m->VTotal = m->VDisplay + 6; + m->Clock = m->HTotal * m->VTotal * 60 / 1000; /* kHz */ + m->name = xnfstrdup(pszName); +} + +/** + * Allocates an empty display mode and links it into the doubly linked list of + * modes pointed to by pScrn->modes. Returns a pointer to the newly allocated + * memory. + */ +static DisplayModePtr vboxAddEmptyScreenMode(ScrnInfoPtr pScrn) +{ + DisplayModePtr pMode = xnfcalloc(sizeof(DisplayModeRec), 1); + + TRACE_ENTRY(); + if (!pScrn->modes) + { + pScrn->modes = pMode; + pMode->next = pMode; + pMode->prev = pMode; + } + else + { + pMode->next = pScrn->modes; + pMode->prev = pScrn->modes->prev; + pMode->next->prev = pMode; + pMode->prev->next = pMode; + } + return pMode; +} + +/** + * Create display mode entries in the screen information structure for each + * of the graphics modes that we wish to support, that is: + * - A dynamic mode in first place which will be updated by the RandR code. + * - Any modes that the user requested in xorg.conf/XFree86Config. + */ +void vboxAddModes(ScrnInfoPtr pScrn) +{ + unsigned cx = 0; + unsigned cy = 0; + unsigned i; + DisplayModePtr pMode; + + /* Add two dynamic mode entries. When we receive a new size hint we will + * update whichever of these is not current. */ + pMode = vboxAddEmptyScreenMode(pScrn); + vboxFillDisplayMode(pScrn, pMode, NULL, 800, 600); + pMode = vboxAddEmptyScreenMode(pScrn); + vboxFillDisplayMode(pScrn, pMode, NULL, 800, 600); + /* Add any modes specified by the user. We assume here that the mode names + * reflect the mode sizes. */ + for (i = 0; pScrn->display->modes && pScrn->display->modes[i]; i++) + { + if (sscanf(pScrn->display->modes[i], "%ux%u", &cx, &cy) == 2) + { + pMode = vboxAddEmptyScreenMode(pScrn); + vboxFillDisplayMode(pScrn, pMode, pScrn->display->modes[i], cx, cy); + } + } +} + +/** Set the initial values for the guest screen size hints to standard values + * in case nothing else is available. */ +void VBoxInitialiseSizeHints(ScrnInfoPtr pScrn) +{ + VBOXPtr pVBox = VBOXGetRec(pScrn); + unsigned i; + + for (i = 0; i < pVBox->cScreens; ++i) + { + pVBox->pScreens[i].aPreferredSize.cx = 800; + pVBox->pScreens[i].aPreferredSize.cy = 600; + pVBox->pScreens[i].afConnected = true; + } + /* Set up the first mode correctly to match the requested initial mode. */ + pScrn->modes->HDisplay = pVBox->pScreens[0].aPreferredSize.cx; + pScrn->modes->VDisplay = pVBox->pScreens[0].aPreferredSize.cy; +} + +static Bool useHardwareCursor(uint32_t fCursorCapabilities) +{ + if (fCursorCapabilities & VBOX_VBVA_CURSOR_CAPABILITY_HARDWARE) + return true; + return false; +} + +static void compareAndMaybeSetUseHardwareCursor(VBOXPtr pVBox, uint32_t fCursorCapabilities, Bool *pfChanged, Bool fSet) +{ + if (pVBox->fUseHardwareCursor != useHardwareCursor(fCursorCapabilities)) + *pfChanged = true; + if (fSet) + pVBox->fUseHardwareCursor = useHardwareCursor(fCursorCapabilities); +} + +#define COMPARE_AND_MAYBE_SET(pDest, src, pfChanged, fSet) \ +do { \ + if (*(pDest) != (src)) \ + { \ + if (fSet) \ + *(pDest) = (src); \ + *(pfChanged) = true; \ + } \ +} while(0) + +/** Read in information about the most recent size hints and cursor + * capabilities requested for the guest screens from HGSMI. */ +void vbvxReadSizesAndCursorIntegrationFromHGSMI(ScrnInfoPtr pScrn, Bool *pfNeedUpdate) +{ + VBOXPtr pVBox = VBOXGetRec(pScrn); + int rc; + unsigned i; + Bool fChanged = false; + uint32_t fCursorCapabilities; + + if (!pVBox->fHaveHGSMIModeHints) + return; + rc = VBoxHGSMIGetModeHints(&pVBox->guestCtx, pVBox->cScreens, pVBox->paVBVAModeHints); + AssertMsg(rc == VINF_SUCCESS, ("VBoxHGSMIGetModeHints failed, rc=%d.\n", rc)); + for (i = 0; i < pVBox->cScreens; ++i) + if (pVBox->paVBVAModeHints[i].magic == VBVAMODEHINT_MAGIC) + { + COMPARE_AND_MAYBE_SET(&pVBox->pScreens[i].aPreferredSize.cx, pVBox->paVBVAModeHints[i].cx & 0x8fff, &fChanged, true); + COMPARE_AND_MAYBE_SET(&pVBox->pScreens[i].aPreferredSize.cy, pVBox->paVBVAModeHints[i].cy & 0x8fff, &fChanged, true); + COMPARE_AND_MAYBE_SET(&pVBox->pScreens[i].afConnected, RT_BOOL(pVBox->paVBVAModeHints[i].fEnabled), &fChanged, true); + COMPARE_AND_MAYBE_SET(&pVBox->pScreens[i].aPreferredLocation.x, (int32_t)pVBox->paVBVAModeHints[i].dx & 0x8fff, &fChanged, + true); + COMPARE_AND_MAYBE_SET(&pVBox->pScreens[i].aPreferredLocation.y, (int32_t)pVBox->paVBVAModeHints[i].dy & 0x8fff, &fChanged, + true); + if (pVBox->paVBVAModeHints[i].dx != ~(uint32_t)0 && pVBox->paVBVAModeHints[i].dy != ~(uint32_t)0) + COMPARE_AND_MAYBE_SET(&pVBox->pScreens[i].afHaveLocation, true, &fChanged, true); + else + COMPARE_AND_MAYBE_SET(&pVBox->pScreens[i].afHaveLocation, false, &fChanged, true); + } + rc = VBoxQueryConfHGSMI(&pVBox->guestCtx, VBOX_VBVA_CONF32_CURSOR_CAPABILITIES, &fCursorCapabilities); + AssertMsg(rc == VINF_SUCCESS, ("Getting VBOX_VBVA_CONF32_CURSOR_CAPABILITIES failed, rc=%d.\n", rc)); + compareAndMaybeSetUseHardwareCursor(pVBox, fCursorCapabilities, &fChanged, true); + if (pfNeedUpdate != NULL && fChanged) + *pfNeedUpdate = true; +} + +#undef COMPARE_AND_MAYBE_SET + +#ifdef VBOXVIDEO_13 +# ifdef RT_OS_LINUX +/** We have this for two purposes: one is to ensure that the X server is woken + * up when we get a video ACPI event. Two is to grab ACPI video events to + * prevent gnome-settings-daemon from seeing them, as older versions ignored + * the time stamp and handled them at the wrong time. */ +static void acpiEventHandler(int fd, void *pvData) +{ + struct input_event event; + ssize_t rc; + RT_NOREF(pvData); + + do + rc = read(fd, &event, sizeof(event)); + while (rc > 0 || (rc == -1 && errno == EINTR)); + /* Why do they return EAGAIN instead of zero bytes read like everyone else does? */ + AssertMsg(rc != -1 || errno == EAGAIN, ("Reading ACPI input event failed.\n")); +} + +void vbvxSetUpLinuxACPI(ScreenPtr pScreen) +{ + VBOXPtr pVBox = VBOXGetRec(xf86Screens[pScreen->myNum]); + struct dirent *pDirent; + DIR *pDir; + int fd = -1; + + if (pVBox->fdACPIDevices != -1 || pVBox->hACPIEventHandler != NULL) + FatalError("ACPI input file descriptor not initialised correctly.\n"); + pDir = opendir("/dev/input"); + if (pDir == NULL) + return; + for (pDirent = readdir(pDir); pDirent != NULL; pDirent = readdir(pDir)) + { + if (strncmp(pDirent->d_name, "event", sizeof("event") - 1) == 0) + { +#define BITS_PER_BLOCK (sizeof(unsigned long) * 8) + char szFile[64] = "/dev/input/"; + char szDevice[64] = ""; + unsigned long afKeys[KEY_MAX / BITS_PER_BLOCK]; + + strncat(szFile, pDirent->d_name, sizeof(szFile) - sizeof("/dev/input/")); + if (fd != -1) + close(fd); + fd = open(szFile, O_RDONLY | O_NONBLOCK); + if ( fd == -1 + || ioctl(fd, EVIOCGNAME(sizeof(szDevice)), szDevice) == -1 + || strcmp(szDevice, "Video Bus") != 0) + continue; + if ( ioctl(fd, EVIOCGBIT(EV_KEY, sizeof(afKeys)), afKeys) == -1 + || (( afKeys[KEY_SWITCHVIDEOMODE / BITS_PER_BLOCK] + >> KEY_SWITCHVIDEOMODE % BITS_PER_BLOCK) & 1) == 0) + break; + if (ioctl(fd, EVIOCGRAB, (void *)1) != 0) + break; + pVBox->hACPIEventHandler + = xf86AddGeneralHandler(fd, acpiEventHandler, pScreen); + if (pVBox->hACPIEventHandler == NULL) + break; + pVBox->fdACPIDevices = fd; + fd = -1; + break; +#undef BITS_PER_BLOCK + } + } + if (fd != -1) + close(fd); + closedir(pDir); +} + +void vbvxCleanUpLinuxACPI(ScreenPtr pScreen) +{ + VBOXPtr pVBox = VBOXGetRec(xf86Screens[pScreen->myNum]); + if (pVBox->fdACPIDevices != -1) + close(pVBox->fdACPIDevices); + pVBox->fdACPIDevices = -1; + xf86RemoveGeneralHandler(pVBox->hACPIEventHandler); + pVBox->hACPIEventHandler = NULL; +} +# endif /* RT_OS_LINUX */ +#endif /* VBOXVIDEO_13 */ diff --git a/src/helpers.c b/src/helpers.c new file mode 100644 index 0000000..ed35bbf --- /dev/null +++ b/src/helpers.c @@ -0,0 +1,81 @@ +/* + * Copyright (C) 2014-2017 Oracle Corporation + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NON-INFRINGEMENT. IN NO EVENT SHALL + * THE COPYRIGHT HOLDERS, AUTHORS AND/OR ITS SUPPLIERS BE LIABLE FOR ANY CLAIM, + * DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR + * OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE + * USE OR OTHER DEALINGS IN THE SOFTWARE. + */ + +#include "vboxvideo.h" +#include "os.h" +#include "propertyst.h" +#include "windowstr.h" +#include "xf86.h" +#include <X11/Xatom.h> +#ifdef XORG_7X +# include <string.h> +#endif +#include "VBoxVideoErr.h" + +VBOXPtr vbvxGetRec(ScrnInfoPtr pScrn) +{ + return ((VBOXPtr)pScrn->driverPrivate); +} + +int vbvxGetIntegerPropery(ScrnInfoPtr pScrn, char *pszName, size_t *pcData, int32_t **ppaData) +{ + Atom atom; + PropertyPtr prop; + + /* We can get called early, before the root window is created. */ + if (!ROOT_WINDOW(pScrn)) + return VERR_NOT_FOUND; + atom = MakeAtom(pszName, strlen(pszName), TRUE); + if (atom == BAD_RESOURCE) + return VERR_NOT_FOUND; + for (prop = wUserProps(ROOT_WINDOW(pScrn)); + prop != NULL && prop->propertyName != atom; prop = prop->next); + if (prop == NULL) + return VERR_NOT_FOUND; + if (prop->type != XA_INTEGER || prop->format != 32) + return VERR_NOT_FOUND; + *pcData = prop->size; + *ppaData = (int32_t *)prop->data; + return VINF_SUCCESS; +} + +void vbvxSetIntegerPropery(ScrnInfoPtr pScrn, char *pszName, size_t cData, int32_t *paData, Bool fSendEvent) +{ + Atom property_name; + + property_name = MakeAtom(pszName, strlen(pszName), TRUE); + AssertMsg(property_name != BAD_RESOURCE, ("Failed to set atom \"%s\"\n", pszName)); + ChangeWindowProperty(ROOT_WINDOW(pScrn), property_name, XA_INTEGER, 32, PropModeReplace, cData, paData, fSendEvent); +} + +void vbvxReprobeCursor(ScrnInfoPtr pScrn) +{ + if (ROOT_WINDOW(pScrn) == NULL) + return; +#ifdef XF86_SCRN_INTERFACE + pScrn->EnableDisableFBAccess(pScrn, FALSE); + pScrn->EnableDisableFBAccess(pScrn, TRUE); +#else + pScrn->EnableDisableFBAccess(pScrn->scrnIndex, FALSE); + pScrn->EnableDisableFBAccess(pScrn->scrnIndex, TRUE); +#endif +} diff --git a/src/hgsmimemalloc.c b/src/hgsmimemalloc.c new file mode 100644 index 0000000..e689e20 --- /dev/null +++ b/src/hgsmimemalloc.c @@ -0,0 +1,106 @@ +/* $Id: hgsmimemalloc.c 118341 2017-10-12 14:07:17Z michael $ */ +/* + * Copyright (C) 2017 Oracle Corporation + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NON-INFRINGEMENT. IN NO EVENT SHALL + * THE COPYRIGHT HOLDERS, AUTHORS AND/OR ITS SUPPLIERS BE LIABLE FOR ANY CLAIM, + * DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR + * OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE + * USE OR OTHER DEALINGS IN THE SOFTWARE. + */ + +/* + * Memory allocator + * ---------------- + * + * Implementation + * -------------- + * + * Since the X.Org driver is single threaded and works using an allocate, + * submit and free pattern, we replace the generic allocator with a simple + * Boolean. Need more be said? + */ + +#include <VBoxVideoIPRT.h> +#include <HGSMIMemAlloc.h> +#include <HGSMI.h> + +int HGSMIMAInit(HGSMIMADATA *pMA, const HGSMIAREA *pArea, + HGSMIOFFSET *paDescriptors, uint32_t cDescriptors, HGSMISIZE cbMaxBlock, + const HGSMIENV *pEnv) +{ + (void)paDescriptors; + (void)cDescriptors; + (void)cbMaxBlock; + (void)pEnv; + if (!(pArea->cbArea < UINT32_C(0x80000000))) + return VERR_INVALID_PARAMETER; + if (!(pArea->cbArea >= HGSMI_MA_BLOCK_SIZE_MIN)) + return VERR_INVALID_PARAMETER; + + pMA->area = *pArea; + pMA->fAllocated = false; + return VINF_SUCCESS; +} + +void HGSMIMAUninit(HGSMIMADATA *pMA) +{ + (void)pMA; +} + +static HGSMIOFFSET HGSMIMAPointerToOffset(const HGSMIMADATA *pMA, const void *pv) +{ + if (HGSMIAreaContainsPointer(&pMA->area, pv)) + { + return HGSMIPointerToOffset(&pMA->area, pv); + } + + AssertFailed(); + return HGSMIOFFSET_VOID; +} + +static void *HGSMIMAOffsetToPointer(const HGSMIMADATA *pMA, HGSMIOFFSET off) +{ + if (HGSMIAreaContainsOffset(&pMA->area, off)) + { + return HGSMIOffsetToPointer(&pMA->area, off); + } + + AssertFailed(); + return NULL; +} + +void *HGSMIMAAlloc(HGSMIMADATA *pMA, HGSMISIZE cb) +{ + (void)cb; + if (pMA->fAllocated) + return NULL; + HGSMIOFFSET off = pMA->area.offBase; + return HGSMIMAOffsetToPointer(pMA, off); + pMA->fAllocated = true; +} + +void HGSMIMAFree(HGSMIMADATA *pMA, void *pv) +{ + HGSMIOFFSET off = HGSMIMAPointerToOffset(pMA, pv); + if (off != HGSMIOFFSET_VOID) + { + pMA->fAllocated = false; + } + else + { + AssertFailed(); + } +} diff --git a/src/pointer.c b/src/pointer.c new file mode 100644 index 0000000..0b01ab4 --- /dev/null +++ b/src/pointer.c @@ -0,0 +1,493 @@ +/* $Id: pointer.c 118361 2017-10-13 14:17:38Z michael $ */ +/** @file + * VirtualBox X11 Additions graphics driver utility functions + */ + +/* + * Copyright (C) 2006-2017 Oracle Corporation + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NON-INFRINGEMENT. IN NO EVENT SHALL + * THE COPYRIGHT HOLDERS, AUTHORS AND/OR ITS SUPPLIERS BE LIABLE FOR ANY CLAIM, + * DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR + * OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE + * USE OR OTHER DEALINGS IN THE SOFTWARE. + */ + +#ifndef PCIACCESS +# include "xf86Pci.h" +# include <Pci.h> +#endif + +#include "xf86.h" +#define NEED_XF86_TYPES +#include "compiler.h" +#include "cursorstr.h" +#include "servermd.h" + +#include "vboxvideo.h" + +#ifdef XORG_7X +# include <stdlib.h> +# include <string.h> +#endif + +#define VBOX_MAX_CURSOR_WIDTH 64 +#define VBOX_MAX_CURSOR_HEIGHT 64 + +/************************************************************************** +* Debugging functions and macros * +**************************************************************************/ + +/* #define DEBUG_POINTER */ + +#ifdef DEBUG +# define PUT_PIXEL(c) ErrorF ("%c", c) +#else /* DEBUG_VIDEO not defined */ +# define PUT_PIXEL(c) do { } while(0) +#endif /* DEBUG_VIDEO not defined */ + +/** Macro to printf an error message and return from a function */ +#define RETERROR(scrnIndex, RetVal, ...) \ + do \ + { \ + xf86DrvMsg(scrnIndex, X_ERROR, __VA_ARGS__); \ + return RetVal; \ + } \ + while (0) + +/** Structure to pass cursor image data between realise_cursor() and + * load_cursor_image(). The members match the parameters to + * @a VBoxHGSMIUpdatePointerShape(). */ +struct vboxCursorImage +{ + uint32_t fFlags; + uint32_t cHotX; + uint32_t cHotY; + uint32_t cWidth; + uint32_t cHeight; + uint8_t *pPixels; + uint32_t cbLength; +}; + +#ifdef DEBUG_POINTER +static void +vbox_show_shape(unsigned short w, unsigned short h, CARD32 bg, unsigned char *image) +{ + size_t x, y; + unsigned short pitch; + CARD32 *color; + unsigned char *mask; + size_t sizeMask; + + image += sizeof(struct vboxCursorImage); + mask = image; + pitch = (w + 7) / 8; + sizeMask = (pitch * h + 3) & ~3; + color = (CARD32 *)(image + sizeMask); + + TRACE_ENTRY(); + for (y = 0; y < h; ++y, mask += pitch, color += w) + { + for (x = 0; x < w; ++x) + { + if (mask[x / 8] & (1 << (7 - (x % 8)))) + ErrorF (" "); + else + { + CARD32 c = color[x]; + if (c == bg) + ErrorF("Y"); + else + ErrorF("X"); + } + } + ErrorF("\n"); + } +} +#endif + +/************************************************************************** +* Main functions * +**************************************************************************/ + +void vbvxCursorTerm(VBOXPtr pVBox) +{ + TRACE_ENTRY(); + + xf86DestroyCursorInfoRec(pVBox->pCurs); + pVBox->pCurs = NULL; + TRACE_EXIT(); +} + +static void +vbox_vmm_hide_cursor(ScrnInfoPtr pScrn, VBOXPtr pVBox) +{ + int rc; + RT_NOREF(pScrn); + + rc = VBoxHGSMIUpdatePointerShape(&pVBox->guestCtx, 0, 0, 0, 0, 0, NULL, 0); + AssertMsg(rc == VINF_SUCCESS, ("Could not hide the virtual mouse pointer, VBox error %d.\n", rc)); +} + +static void +vbox_vmm_show_cursor(ScrnInfoPtr pScrn, VBOXPtr pVBox) +{ + int rc; + RT_NOREF(pScrn); + + if (!pVBox->fUseHardwareCursor) + return; + rc = VBoxHGSMIUpdatePointerShape(&pVBox->guestCtx, VBOX_MOUSE_POINTER_VISIBLE, + 0, 0, 0, 0, NULL, 0); + AssertMsg(rc == VINF_SUCCESS, ("Could not unhide the virtual mouse pointer.\n")); +} + +static void +vbox_vmm_load_cursor_image(ScrnInfoPtr pScrn, VBOXPtr pVBox, + unsigned char *pvImage) +{ + int rc; + struct vboxCursorImage *pImage; + pImage = (struct vboxCursorImage *)pvImage; + RT_NOREF(pScrn); + +#ifdef DEBUG_POINTER + vbox_show_shape(pImage->cWidth, pImage->cHeight, 0, pvImage); +#endif + + rc = VBoxHGSMIUpdatePointerShape(&pVBox->guestCtx, pImage->fFlags, + pImage->cHotX, pImage->cHotY, pImage->cWidth, pImage->cHeight, + pImage->pPixels, pImage->cbLength); + AssertMsg(rc == VINF_SUCCESS, ("Unable to set the virtual mouse pointer image.\n")); +} + +static void +vbox_set_cursor_colors(ScrnInfoPtr pScrn, int bg, int fg) +{ + RT_NOREF(pScrn); + RT_NOREF(bg); + RT_NOREF(fg); + /* ErrorF("vbox_set_cursor_colors NOT IMPLEMENTED\n"); */ +} + + +static void +vbox_set_cursor_position(ScrnInfoPtr pScrn, int x, int y) +{ + VBOXPtr pVBox = pScrn->driverPrivate; + + /* This currently does nothing. */ + VBoxHGSMICursorPosition(&pVBox->guestCtx, true, x, y, NULL, NULL); +} + +static void +vbox_hide_cursor(ScrnInfoPtr pScrn) +{ + VBOXPtr pVBox = pScrn->driverPrivate; + + vbox_vmm_hide_cursor(pScrn, pVBox); +} + +static void +vbox_show_cursor(ScrnInfoPtr pScrn) +{ + VBOXPtr pVBox = pScrn->driverPrivate; + + vbox_vmm_show_cursor(pScrn, pVBox); +} + +static void +vbox_load_cursor_image(ScrnInfoPtr pScrn, unsigned char *image) +{ + VBOXPtr pVBox = pScrn->driverPrivate; + + vbox_vmm_load_cursor_image(pScrn, pVBox, image); +} + +static Bool +vbox_use_hw_cursor(ScreenPtr pScreen, CursorPtr pCurs) +{ + ScrnInfoPtr pScrn = xf86Screens[pScreen->myNum]; + VBOXPtr pVBox = pScrn->driverPrivate; + RT_NOREF(pCurs); + return pVBox->fUseHardwareCursor; +} + +static unsigned char +color_to_byte(unsigned c) +{ + return (c >> 8) & 0xff; +} + +static unsigned char * +vbox_realize_cursor(xf86CursorInfoPtr infoPtr, CursorPtr pCurs) +{ + VBOXPtr pVBox; + CursorBitsPtr bitsp; + unsigned short w, h, x, y; + unsigned char *c, *p, *pm, *ps, *m; + size_t sizeRequest, sizeRgba, sizeMask, srcPitch, dstPitch; + CARD32 fc, bc, *cp; + int scrnIndex = infoPtr->pScrn->scrnIndex; + struct vboxCursorImage *pImage; + + pVBox = infoPtr->pScrn->driverPrivate; + bitsp = pCurs->bits; + w = bitsp->width; + h = bitsp->height; + + if (!w || !h || w > VBOX_MAX_CURSOR_WIDTH || h > VBOX_MAX_CURSOR_HEIGHT) + RETERROR(scrnIndex, NULL, + "Error invalid cursor dimensions %dx%d\n", w, h); + + if ((bitsp->xhot > w) || (bitsp->yhot > h)) + RETERROR(scrnIndex, NULL, + "Error invalid cursor hotspot location %dx%d (max %dx%d)\n", + bitsp->xhot, bitsp->yhot, w, h); + + srcPitch = PixmapBytePad (bitsp->width, 1); + dstPitch = (w + 7) / 8; + sizeMask = ((dstPitch * h) + 3) & (size_t) ~3; + sizeRgba = w * h * 4; + sizeRequest = sizeMask + sizeRgba + sizeof(*pImage); + + p = c = calloc (1, sizeRequest); + if (!c) + RETERROR(scrnIndex, NULL, + "Error failed to alloc %lu bytes for cursor\n", + (unsigned long) sizeRequest); + + pImage = (struct vboxCursorImage *)p; + pImage->pPixels = m = p + sizeof(*pImage); + cp = (CARD32 *)(m + sizeMask); + + TRACE_LOG ("w=%d h=%d sm=%d sr=%d p=%d\n", + w, h, (int) sizeMask, (int) sizeRgba, (int) dstPitch); + TRACE_LOG ("m=%p c=%p cp=%p\n", m, c, (void *)cp); + + fc = color_to_byte (pCurs->foreBlue) + | (color_to_byte (pCurs->foreGreen) << 8) + | (color_to_byte (pCurs->foreRed) << 16); + + bc = color_to_byte (pCurs->backBlue) + | (color_to_byte (pCurs->backGreen) << 8) + | (color_to_byte (pCurs->backRed) << 16); + + /* + * Convert the Xorg source/mask bits to the and/xor bits VBox needs. + * Xorg: + * The mask is a bitmap indicating which parts of the cursor are + * transparent and which parts are drawn. The source is a bitmap + * indicating which parts of the non-transparent portion of the + * the cursor should be painted in the foreground color and which + * should be painted in the background color. By default, set bits + * indicate the opaque part of the mask bitmap and clear bits + * indicate the transparent part. + * VBox: + * The color data is the XOR mask. The AND mask bits determine + * which pixels of the color data (XOR mask) will replace (overwrite) + * the screen pixels (AND mask bit = 0) and which ones will be XORed + * with existing screen pixels (AND mask bit = 1). + * For example when you have the AND mask all 0, then you see the + * correct mouse pointer image surrounded by black square. + */ + for (pm = bitsp->mask, ps = bitsp->source, y = 0; + y < h; + ++y, pm += srcPitch, ps += srcPitch, m += dstPitch) + { + for (x = 0; x < w; ++x) + { + if (pm[x / 8] & (1 << (x % 8))) + { + /* opaque, leave AND mask bit at 0 */ + if (ps[x / 8] & (1 << (x % 8))) + { + *cp++ = fc; + PUT_PIXEL('X'); + } + else + { + *cp++ = bc; + PUT_PIXEL('*'); + } + } + else + { + /* transparent, set AND mask bit */ + m[x / 8] |= 1 << (7 - (x % 8)); + /* don't change the screen pixel */ + *cp++ = 0; + PUT_PIXEL(' '); + } + } + PUT_PIXEL('\n'); + } + + pImage->cWidth = w; + pImage->cHeight = h; + pImage->cHotX = bitsp->xhot; + pImage->cHotY = bitsp->yhot; + pImage->fFlags = VBOX_MOUSE_POINTER_VISIBLE | VBOX_MOUSE_POINTER_SHAPE; + pImage->cbLength = sizeRequest - sizeof(*pImage); + +#ifdef DEBUG_POINTER + ErrorF("shape = %p\n", p); + vbox_show_shape(w, h, bc, c); +#endif + + return p; +} + +#ifdef ARGB_CURSOR +static Bool +vbox_use_hw_cursor_argb(ScreenPtr pScreen, CursorPtr pCurs) +{ + ScrnInfoPtr pScrn = xf86Screens[pScreen->myNum]; + VBOXPtr pVBox = pScrn->driverPrivate; + + if (!pVBox->fUseHardwareCursor) + return FALSE; + if ( (pCurs->bits->height > VBOX_MAX_CURSOR_HEIGHT) + || (pCurs->bits->width > VBOX_MAX_CURSOR_WIDTH) + || (pScrn->bitsPerPixel <= 8)) + return FALSE; + return TRUE; +} + + +static void +vbox_load_cursor_argb(ScrnInfoPtr pScrn, CursorPtr pCurs) +{ + VBOXPtr pVBox; + CursorBitsPtr bitsp; + unsigned short w, h; + unsigned short cx, cy; + unsigned char *pm; + CARD32 *pc; + size_t sizeData, sizeMask; + CARD8 *p; + int scrnIndex; + uint32_t fFlags = VBOX_MOUSE_POINTER_VISIBLE | VBOX_MOUSE_POINTER_SHAPE + | VBOX_MOUSE_POINTER_ALPHA; + + pVBox = pScrn->driverPrivate; + bitsp = pCurs->bits; + w = bitsp->width; + h = bitsp->height; + scrnIndex = pScrn->scrnIndex; + + /* Mask must be generated for alpha cursors, that is required by VBox. */ + /* note: (michael) the next struct must be 32bit aligned. */ + sizeMask = ((w + 7) / 8 * h + 3) & ~3; + + if (!w || !h || w > VBOX_MAX_CURSOR_WIDTH || h > VBOX_MAX_CURSOR_HEIGHT) + RETERROR(scrnIndex, , + "Error invalid cursor dimensions %dx%d\n", w, h); + + if ((bitsp->xhot > w) || (bitsp->yhot > h)) + RETERROR(scrnIndex, , + "Error invalid cursor hotspot location %dx%d (max %dx%d)\n", + bitsp->xhot, bitsp->yhot, w, h); + + sizeData = w * h * 4 + sizeMask; + p = calloc(1, sizeData); + if (!p) + RETERROR(scrnIndex, , + "Error failed to alloc %lu bytes for cursor\n", + (unsigned long)sizeData); + + memcpy(p + sizeMask, bitsp->argb, w * h * 4); + + /* Emulate the AND mask. */ + pm = p; + pc = bitsp->argb; + + /* Init AND mask to 1 */ + memset(pm, 0xFF, sizeMask); + + /* + * The additions driver must provide the AND mask for alpha cursors. The host frontend + * which can handle alpha channel, will ignore the AND mask and draw an alpha cursor. + * But if the host does not support ARGB, then it simply uses the AND mask and the color + * data to draw a normal color cursor. + */ + for (cy = 0; cy < h; cy++) + { + unsigned char bitmask = 0x80; + + for (cx = 0; cx < w; cx++, bitmask >>= 1) + { + if (bitmask == 0) + bitmask = 0x80; + + if (pc[cx] >= 0xF0000000) + pm[cx / 8] &= ~bitmask; + } + + /* Point to next source and dest scans */ + pc += w; + pm += (w + 7) / 8; + } + + VBoxHGSMIUpdatePointerShape(&pVBox->guestCtx, fFlags, bitsp->xhot, + bitsp->yhot, w, h, p, sizeData); + free(p); +} +#endif + +Bool vbvxCursorInit(ScreenPtr pScreen) +{ + ScrnInfoPtr pScrn = xf86Screens[pScreen->myNum]; + VBOXPtr pVBox = pScrn->driverPrivate; + xf86CursorInfoPtr pCurs = NULL; + Bool rc = TRUE; + + TRACE_ENTRY(); + pVBox->pCurs = pCurs = xf86CreateCursorInfoRec(); + if (!pCurs) { + xf86DrvMsg(pScrn->scrnIndex, X_ERROR, + "Failed to create X Window cursor information structures for virtual mouse.\n"); + rc = FALSE; + } + if (rc) { + pCurs->MaxWidth = VBOX_MAX_CURSOR_WIDTH; + pCurs->MaxHeight = VBOX_MAX_CURSOR_HEIGHT; + pCurs->Flags = HARDWARE_CURSOR_TRUECOLOR_AT_8BPP + | HARDWARE_CURSOR_SOURCE_MASK_INTERLEAVE_1 + | HARDWARE_CURSOR_BIT_ORDER_MSBFIRST + | HARDWARE_CURSOR_UPDATE_UNHIDDEN; + + pCurs->SetCursorColors = vbox_set_cursor_colors; + pCurs->SetCursorPosition = vbox_set_cursor_position; + pCurs->LoadCursorImage = vbox_load_cursor_image; + pCurs->HideCursor = vbox_hide_cursor; + pCurs->ShowCursor = vbox_show_cursor; + pCurs->UseHWCursor = vbox_use_hw_cursor; + pCurs->RealizeCursor = vbox_realize_cursor; + +#ifdef ARGB_CURSOR + pCurs->UseHWCursorARGB = vbox_use_hw_cursor_argb; + pCurs->LoadCursorARGB = vbox_load_cursor_argb; +#endif + + rc = xf86InitCursor(pScreen, pCurs); + } + if (!rc) + xf86DrvMsg(pScrn->scrnIndex, X_ERROR, + "Failed to enable mouse pointer integration.\n"); + if (!rc && (pCurs != NULL)) + xf86DestroyCursorInfoRec(pCurs); + return rc; +} diff --git a/src/setmode.c b/src/setmode.c new file mode 100644 index 0000000..e3b95a1 --- /dev/null +++ b/src/setmode.c @@ -0,0 +1,132 @@ +/* $Id: setmode.c 118346 2017-10-12 18:48:38Z michael $ */ +/** @file + * Linux Additions X11 graphics driver, mode setting + */ + +/* + * Copyright (C) 2006-2017 Oracle Corporation + * + * This code is based on: + * + * X11 VESA driver + * + * Copyright (c) 2000 by Conectiva S.A. (http://www.conectiva.com) + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NON-INFRINGEMENT. IN NO EVENT SHALL + * THE COPYRIGHT HOLDERS, AUTHORS AND/OR ITS SUPPLIERS BE LIABLE FOR ANY CLAIM, + * DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR + * OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE + * USE OR OTHER DEALINGS IN THE SOFTWARE. + * + * Except as contained in this notice, the name of Conectiva Linux shall + * not be used in advertising or otherwise to promote the sale, use or other + * dealings in this Software without prior written authorization from + * Conectiva Linux. + * + * Authors: Paulo César Pereira de Andrade <pcpa@conectiva.com.br> + * Michael Thayer <michael.thayer@oracle.com> + */ + +#ifdef XORG_7X +/* We include <unistd.h> for Solaris below, and the ANSI C emulation layer + * interferes with that. */ +# define _XF86_ANSIC_H +# define XF86_LIBC_H +# include <string.h> +#endif +#include "vboxvideo.h" +#include "xf86.h" + +/* VGA hardware functions for setting and restoring text mode */ +#include "vgaHW.h" + +#ifdef RT_OS_SOLARIS +# include <sys/vuid_event.h> +# include <sys/msio.h> +# include <errno.h> +# include <fcntl.h> +# include <unistd.h> +#endif + +/** Clear the virtual framebuffer in VRAM. Optionally also clear up to the + * size of a new framebuffer. Framebuffer sizes larger than available VRAM + * be treated as zero and passed over. */ +void vbvxClearVRAM(ScrnInfoPtr pScrn, size_t cbOldSize, size_t cbNewSize) +{ + VBOXPtr pVBox = VBOXGetRec(pScrn); + + /* Assume 32BPP - this is just a sanity test. */ + AssertMsg( cbOldSize / 4 <= VBOX_VIDEO_MAX_VIRTUAL * VBOX_VIDEO_MAX_VIRTUAL + && cbNewSize / 4 <= VBOX_VIDEO_MAX_VIRTUAL * VBOX_VIDEO_MAX_VIRTUAL, + ("cbOldSize=%llu cbNewSize=%llu, max=%u.\n", (unsigned long long)cbOldSize, (unsigned long long)cbNewSize, + VBOX_VIDEO_MAX_VIRTUAL * VBOX_VIDEO_MAX_VIRTUAL)); + if (cbOldSize > (size_t)pVBox->cbFBMax) + cbOldSize = pVBox->cbFBMax; + if (cbNewSize > (size_t)pVBox->cbFBMax) + cbNewSize = pVBox->cbFBMax; + memset(pVBox->base, 0, max(cbOldSize, cbNewSize)); +} + +/** Set a graphics mode. Poke any required values into registers, do an HGSMI + * mode set and tell the host we support advanced graphics functions. + */ +void vbvxSetMode(ScrnInfoPtr pScrn, unsigned cDisplay, unsigned cWidth, unsigned cHeight, int x, int y, Bool fEnabled, + Bool fConnected, struct vbvxFrameBuffer *pFrameBuffer) +{ + VBOXPtr pVBox = VBOXGetRec(pScrn); + uint32_t offStart; + uint16_t fFlags; + int rc; + Bool fEnabledAndVisible = fEnabled && x + cWidth <= pFrameBuffer->cWidth && y + cHeight <= pFrameBuffer->cHeight; + /* Recent host code has a flag to blank the screen; older code needs BPP set to zero. */ + uint32_t cBPP = fEnabledAndVisible || pVBox->fHostHasScreenBlankingFlag ? pFrameBuffer->cBPP : 0; + + TRACE_LOG("cDisplay=%u, cWidth=%u, cHeight=%u, x=%d, y=%d, fEnabled=%d, fConnected=%d, pFrameBuffer: { x0=%d, y0=%d, cWidth=%u, cHeight=%u, cBPP=%u }\n", + cDisplay, cWidth, cHeight, x, y, fEnabled, fConnected, pFrameBuffer->x0, pFrameBuffer->y0, pFrameBuffer->cWidth, + pFrameBuffer->cHeight, pFrameBuffer->cBPP); + AssertMsg(cWidth != 0 && cHeight != 0, ("cWidth = 0 or cHeight = 0\n")); + offStart = (y * pFrameBuffer->cWidth + x) * pFrameBuffer->cBPP / 8; + if (cDisplay == 0 && fEnabled) + VBoxVideoSetModeRegisters(cWidth, cHeight, pFrameBuffer->cWidth, pFrameBuffer->cBPP, 0, x, y); + fFlags = VBVA_SCREEN_F_ACTIVE; + fFlags |= (fConnected ? 0 : VBVA_SCREEN_F_DISABLED); + fFlags |= (!fEnabledAndVisible && pVBox->fHostHasScreenBlankingFlag ? VBVA_SCREEN_F_BLANK : 0); + VBoxHGSMIProcessDisplayInfo(&pVBox->guestCtx, cDisplay, x - pFrameBuffer->x0, y - pFrameBuffer->y0, offStart, + pFrameBuffer->cWidth * pFrameBuffer->cBPP / 8, cWidth, cHeight, cBPP, fFlags); + rc = VBoxHGSMIUpdateInputMapping(&pVBox->guestCtx, 0 - pFrameBuffer->x0, 0 - pFrameBuffer->y0, pFrameBuffer->cWidth, + pFrameBuffer->cHeight); + if (RT_FAILURE(rc)) + FatalError("Failed to update the input mapping.\n"); +} + +/** Tell the virtual mouse device about the new virtual desktop size. */ +void vbvxSetSolarisMouseRange(int width, int height) +{ +#ifdef RT_OS_SOLARIS + int rc; + int hMouse = open("/dev/mouse", O_RDWR); + + if (hMouse >= 0) + { + do { + Ms_screen_resolution Res = { height, width }; + rc = ioctl(hMouse, MSIOSRESOLUTION, &Res); + } while ((rc != 0) && (errno == EINTR)); + close(hMouse); + } +#else + (void)width; (void)height; +#endif +} diff --git a/src/vboxvideo.c b/src/vboxvideo.c new file mode 100644 index 0000000..facb004 --- /dev/null +++ b/src/vboxvideo.c @@ -0,0 +1,1492 @@ +/* $Id: vboxvideo.c 118369 2017-10-13 15:50:47Z michael $ */ +/** @file + * Linux Additions X11 graphics driver + */ + +/* + * Copyright (C) 2006-2017 Oracle Corporation + * + * This code is based on the X.Org VESA driver with the following copyrights: + * + * Copyright (c) 2000 by Conectiva S.A. (http://www.conectiva.com) + * Copyright 2008 Red Hat, Inc. + * Copyright 2012 Red Hat, Inc. + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NON-INFRINGEMENT. IN NO EVENT SHALL + * THE COPYRIGHT HOLDERS, AUTHORS AND/OR ITS SUPPLIERS BE LIABLE FOR ANY CLAIM, + * DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR + * OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE + * USE OR OTHER DEALINGS IN THE SOFTWARE. + * + * Except as contained in this notice, the name of Conectiva Linux shall + * not be used in advertising or otherwise to promote the sale, use or other + * dealings in this Software without prior written authorization from + * Conectiva Linux. + * + * Authors: Paulo César Pereira de Andrade <pcpa@conectiva.com.br> + * David Dawes <dawes@xfree86.org> + * Adam Jackson <ajax@redhat.com> + * Dave Airlie <airlied@redhat.com> + * Michael Thayer <michael.thayer@oracle.com> + */ + +#include "vboxvideo.h" +#include <VBoxVideoVBE.h> + +/* Basic definitions and functions needed by all drivers. */ +#include "xf86.h" +/* For video memory mapping. */ +#include "xf86_OSproc.h" +#if GET_ABI_MAJOR(ABI_VIDEODRV_VERSION) < 6 +/* PCI resources. */ +# include "xf86Resources.h" +#endif +/* Generic server linear frame-buffer APIs. */ +#include "fb.h" +/* Colormap and visual handling. */ +#include "micmap.h" +#include "xf86cmap.h" +/* ShadowFB support */ +#include "shadowfb.h" +/* VGA hardware functions for setting and restoring text mode */ +#include "vgaHW.h" +#ifdef VBOXVIDEO_13 +/* X.org 1.3+ mode setting */ +# define _HAVE_STRING_ARCH_strsep /* bits/string2.h, __strsep_1c. */ +# include "xf86Crtc.h" +# include "xf86Modes.h" +/* For xf86RandR12GetOriginalVirtualSize(). */ +# include "xf86RandR12.h" +#endif +/* For setting the root window property. */ +#include "property.h" +#include <X11/Xatom.h> + +#ifdef XORG_7X +# include <stdlib.h> +# include <string.h> +# include <fcntl.h> +# include <unistd.h> +#endif + +/* Mandatory functions */ + +static const OptionInfoRec * VBOXAvailableOptions(int chipid, int busid); +static void VBOXIdentify(int flags); +#ifndef PCIACCESS +static Bool VBOXProbe(DriverPtr drv, int flags); +#else +static Bool VBOXPciProbe(DriverPtr drv, int entity_num, + struct pci_device *dev, intptr_t match_data); +#endif +static Bool VBOXPreInit(ScrnInfoPtr pScrn, int flags); +static Bool VBOXScreenInit(ScreenPtr pScreen, int argc, char **argv); +static Bool VBOXEnterVT(ScrnInfoPtr pScrn); +static void VBOXLeaveVT(ScrnInfoPtr pScrn); +static Bool VBOXCloseScreen(ScreenPtr pScreen); +#ifndef VBOXVIDEO_13 +static Bool VBOXSaveScreen(ScreenPtr pScreen, int mode); +#endif +static Bool VBOXSwitchMode(ScrnInfoPtr pScrn, DisplayModePtr pMode); +static void VBOXAdjustFrame(ScrnInfoPtr pScrn, int x, int y); +static void VBOXFreeScreen(ScrnInfoPtr pScrn); +#ifndef VBOXVIDEO_13 +static void VBOXDisplayPowerManagementSet(ScrnInfoPtr pScrn, int mode, int flags); +#endif + +/* locally used functions */ +static Bool VBOXMapVidMem(ScrnInfoPtr pScrn); +static void VBOXUnmapVidMem(ScrnInfoPtr pScrn); +static void VBOXSaveMode(ScrnInfoPtr pScrn); +static void VBOXRestoreMode(ScrnInfoPtr pScrn); +static void setSizesAndCursorIntegration(ScrnInfoPtr pScrn, Bool fScreenInitTime); + +#ifndef XF86_SCRN_INTERFACE +# define xf86ScreenToScrn(pScreen) xf86Screens[(pScreen)->myNum] +# define xf86ScrnToScreen(pScrn) screenInfo.screens[(pScrn)->scrnIndex] +#endif + +static inline void VBOXSetRec(ScrnInfoPtr pScrn) +{ + if (!pScrn->driverPrivate) + { + VBOXPtr pVBox = (VBOXPtr)xnfcalloc(sizeof(VBOXRec), 1); + pScrn->driverPrivate = pVBox; +#if defined(VBOXVIDEO_13) && defined(RT_OS_LINUX) + pVBox->fdACPIDevices = -1; +#endif + } +} + +enum GenericTypes +{ + CHIP_VBOX_GENERIC +}; + +#ifdef PCIACCESS +static const struct pci_id_match vbox_device_match[] = { + { + VBOX_VENDORID, VBOX_DEVICEID, PCI_MATCH_ANY, PCI_MATCH_ANY, + 0, 0, 0 + }, + + { 0, 0, 0 }, +}; +#endif + +/* Supported chipsets */ +static SymTabRec VBOXChipsets[] = +{ + {VBOX_DEVICEID, "vbox"}, + {-1, NULL} +}; + +static PciChipsets VBOXPCIchipsets[] = { + { VBOX_DEVICEID, VBOX_DEVICEID, RES_SHARED_VGA }, + { -1, -1, RES_UNDEFINED }, +}; + +/* + * This contains the functions needed by the server after loading the + * driver module. It must be supplied, and gets added the driver list by + * the Module Setup function in the dynamic case. In the static case a + * reference to this is compiled in, and this requires that the name of + * this DriverRec be an upper-case version of the driver name. + */ + +#ifdef XORG_7X +_X_EXPORT +#endif +DriverRec VBOXVIDEO = { + VBOX_VERSION, + VBOX_DRIVER_NAME, + VBOXIdentify, +#ifdef PCIACCESS + NULL, +#else + VBOXProbe, +#endif + VBOXAvailableOptions, + NULL, + 0, +#ifdef XORG_7X + NULL, +#endif +#ifdef PCIACCESS + vbox_device_match, + VBOXPciProbe +#endif +}; + +/* No options for now */ +static const OptionInfoRec VBOXOptions[] = { + { -1, NULL, OPTV_NONE, {0}, FALSE } +}; + +#ifndef XORG_7X +/* + * List of symbols from other modules that this module references. This + * list is used to tell the loader that it is OK for symbols here to be + * unresolved providing that it hasn't been told that they haven't been + * told that they are essential via a call to xf86LoaderReqSymbols() or + * xf86LoaderReqSymLists(). The purpose is this is to avoid warnings about + * unresolved symbols that are not required. + */ +static const char *fbSymbols[] = { + "fbPictureInit", + "fbScreenInit", + NULL +}; + +static const char *shadowfbSymbols[] = { + "ShadowFBInit2", + NULL +}; + +static const char *ramdacSymbols[] = { + "xf86DestroyCursorInfoRec", + "xf86InitCursor", + "xf86CreateCursorInfoRec", + NULL +}; + +static const char *vgahwSymbols[] = { + "vgaHWFreeHWRec", + "vgaHWGetHWRec", + "vgaHWGetIOBase", + "vgaHWGetIndex", + "vgaHWRestore", + "vgaHWSave", + "vgaHWSetStdFuncs", + NULL +}; +#endif /* !XORG_7X */ + +/** Resize the virtual framebuffer. */ +static Bool adjustScreenPixmap(ScrnInfoPtr pScrn, int width, int height) +{ + ScreenPtr pScreen = xf86ScrnToScreen(pScrn); + VBOXPtr pVBox = VBOXGetRec(pScrn); + int adjustedWidth = pScrn->bitsPerPixel == 16 ? (width + 1) & ~1 : width; + int cbLine = adjustedWidth * pScrn->bitsPerPixel / 8; + PixmapPtr pPixmap; + + TRACE_LOG("width=%d, height=%d\n", width, height); + AssertMsg(width >= 0 && height >= 0, ("Invalid negative width (%d) or height (%d)\n", width, height)); + if (pScreen == NULL) /* Not yet initialised. */ + return TRUE; + pPixmap = pScreen->GetScreenPixmap(pScreen); + AssertMsg(pPixmap != NULL, ("Failed to get the screen pixmap.\n")); + TRACE_LOG("pPixmap=%p adjustedWidth=%d height=%d pScrn->depth=%d pScrn->bitsPerPixel=%d cbLine=%d pVBox->base=%p pPixmap->drawable.width=%d pPixmap->drawable.height=%d\n", + (void *)pPixmap, adjustedWidth, height, pScrn->depth, + pScrn->bitsPerPixel, cbLine, pVBox->base, + pPixmap->drawable.width, pPixmap->drawable.height); + if ( adjustedWidth != pPixmap->drawable.width + || height != pPixmap->drawable.height) + { + if ( adjustedWidth > VBOX_VIDEO_MAX_VIRTUAL || height > VBOX_VIDEO_MAX_VIRTUAL + || (unsigned)cbLine * (unsigned)height >= pVBox->cbFBMax) + { + xf86DrvMsg(pScrn->scrnIndex, X_ERROR, + "Virtual framebuffer %dx%d too large. For information, video memory: %u Kb.\n", + adjustedWidth, height, (unsigned) pVBox->cbFBMax / 1024); + return FALSE; + } + if (pScrn->vtSema) + vbvxClearVRAM(pScrn, ((size_t)pScrn->virtualX) * pScrn->virtualY * (pScrn->bitsPerPixel / 8), + ((size_t)adjustedWidth) * height * (pScrn->bitsPerPixel / 8)); + pScreen->ModifyPixmapHeader(pPixmap, adjustedWidth, height, pScrn->depth, pScrn->bitsPerPixel, cbLine, pVBox->base); + } + pScrn->displayWidth = pScrn->virtualX = adjustedWidth; + pScrn->virtualY = height; + return TRUE; +} + +#ifndef VBOXVIDEO_13 +/** Set a video mode to the hardware, RandR 1.1 version. + * + * Since we no longer do virtual frame buffers, adjust the screen pixmap + * dimensions to match. The "override" parameters are for when we received a + * mode hint while switched to a virtual terminal. In this case VBoxClient will + * have told us about the mode, but not yet been able to do a mode switch using + * RandR. We solve this by setting the requested mode to the host but keeping + * the virtual frame- + * buffer matching what the X server expects. */ +static void setModeRandR11(ScrnInfoPtr pScrn, DisplayModePtr pMode, Bool fScreenInitTime, Bool fEnterVTTime, + int cXOverRide, int cYOverRide) +{ + VBOXPtr pVBox = VBOXGetRec(pScrn); + struct vbvxFrameBuffer frameBuffer = { 0, 0, pMode->HDisplay, pMode->VDisplay, pScrn->bitsPerPixel}; + int cXPhysical = cXOverRide > 0 ? min(cXOverRide, pMode->HDisplay) : pMode->HDisplay; + int cYPhysical = cYOverRide > 0 ? min(cYOverRide, pMode->VDisplay) : pMode->VDisplay; + + pVBox->pScreens[0].aScreenLocation.cx = pMode->HDisplay; + pVBox->pScreens[0].aScreenLocation.cy = pMode->VDisplay; + if (fScreenInitTime) + { + /* The screen structure is not fully set up yet, so do not touch it. */ + pScrn->displayWidth = pScrn->virtualX = pMode->HDisplay; + pScrn->virtualY = pMode->VDisplay; + } + else + { + xf86ScrnToScreen(pScrn)->width = pMode->HDisplay; + xf86ScrnToScreen(pScrn)->height = pMode->VDisplay; + /* This prevents a crash in CentOS 3. I was unable to debug it to + * satisfaction, partly due to the lack of symbols. My guess is that + * pScrn->ModifyPixmapHeader() expects certain things to be set up when + * it sees pScrn->vtSema set to true which are not quite done at this + * point of the VT switch. */ + if (fEnterVTTime) + pScrn->vtSema = FALSE; + adjustScreenPixmap(pScrn, pMode->HDisplay, pMode->VDisplay); + if (fEnterVTTime) + pScrn->vtSema = TRUE; + } + if (pMode->HDisplay != 0 && pMode->VDisplay != 0 && pScrn->vtSema) + vbvxSetMode(pScrn, 0, cXPhysical, cYPhysical, 0, 0, true, true, &frameBuffer); + pScrn->currentMode = pMode; +} +#endif + +#ifdef VBOXVIDEO_13 +/* X.org 1.3+ mode-setting support ******************************************/ + +/** Set a video mode to the hardware, RandR 1.2 version. If this is the first + * screen, re-set the current mode for all others (the offset for the first + * screen is always treated as zero by the hardware, so all other screens need + * to be changed to compensate for any changes!). The mode to set is taken + * from the X.Org Crtc structure. */ +static void setModeRandR12(ScrnInfoPtr pScrn, unsigned cScreen) +{ + VBOXPtr pVBox = VBOXGetRec(pScrn); + unsigned i; + struct vbvxFrameBuffer frameBuffer = { pVBox->pScreens[0].paCrtcs->x, pVBox->pScreens[0].paCrtcs->y, pScrn->virtualX, + pScrn->virtualY, pScrn->bitsPerPixel }; + unsigned cFirst = cScreen; + unsigned cLast = cScreen != 0 ? cScreen + 1 : pVBox->cScreens; + int originalX, originalY; + + /* Check that this code cannot trigger the resizing bug in X.Org Server 1.3. + * See the work-around in ScreenInit. */ + xf86RandR12GetOriginalVirtualSize(pScrn, &originalX, &originalY); + AssertMsg(originalX == VBOX_VIDEO_MAX_VIRTUAL && originalY == VBOX_VIDEO_MAX_VIRTUAL, ("OriginalSize=%dx%d", + originalX, originalY)); + for (i = cFirst; i < cLast; ++i) + if (pVBox->pScreens[i].paCrtcs->mode.HDisplay != 0 && pVBox->pScreens[i].paCrtcs->mode.VDisplay != 0 && pScrn->vtSema) + vbvxSetMode(pScrn, i, pVBox->pScreens[i].paCrtcs->mode.HDisplay, pVBox->pScreens[i].paCrtcs->mode.VDisplay, + pVBox->pScreens[i].paCrtcs->x, pVBox->pScreens[i].paCrtcs->y, pVBox->pScreens[i].fPowerOn, + pVBox->pScreens[i].paOutputs->status == XF86OutputStatusConnected, &frameBuffer); +} + +/** Wrapper around setModeRandR12() to avoid exposing non-obvious semantics. + */ +static void setAllModesRandR12(ScrnInfoPtr pScrn) +{ + setModeRandR12(pScrn, 0); +} + +/* For descriptions of these functions and structures, see + hw/xfree86/modes/xf86Crtc.h and hw/xfree86/modes/xf86Modes.h in the + X.Org source tree. */ + +static Bool vbox_config_resize(ScrnInfoPtr pScrn, int cw, int ch) +{ + VBOXPtr pVBox = VBOXGetRec(pScrn); + Bool rc; + unsigned i; + + TRACE_LOG("width=%d, height=%d\n", cw, ch); + rc = adjustScreenPixmap(pScrn, cw, ch); + /* Power-on all screens (the server expects this) and set the new pitch to them. */ + for (i = 0; i < pVBox->cScreens; ++i) + pVBox->pScreens[i].fPowerOn = true; + setAllModesRandR12(pScrn); + vbvxSetSolarisMouseRange(cw, ch); + return rc; +} + +static const xf86CrtcConfigFuncsRec VBOXCrtcConfigFuncs = { + vbox_config_resize +}; + +static void +vbox_crtc_dpms(xf86CrtcPtr crtc, int mode) +{ + ScrnInfoPtr pScrn = crtc->scrn; + VBOXPtr pVBox = VBOXGetRec(pScrn); + unsigned cDisplay = (uintptr_t)crtc->driver_private; + + TRACE_LOG("mode=%d\n", mode); + pVBox->pScreens[cDisplay].fPowerOn = (mode != DPMSModeOff); + setModeRandR12(pScrn, cDisplay); +} + +static Bool +vbox_crtc_lock (xf86CrtcPtr crtc) +{ RT_NOREF(crtc); return FALSE; } + + +/* We use this function to check whether the X server owns the active virtual + * terminal before attempting a mode switch, since the RandR extension isn't + * very dilligent here, which can mean crashes if we are unlucky. This is + * not the way it the function is intended - it is meant for reporting modes + * which the hardware can't handle. I hope that this won't confuse any clients + * connecting to us. */ +static Bool +vbox_crtc_mode_fixup (xf86CrtcPtr crtc, DisplayModePtr mode, + DisplayModePtr adjusted_mode) +{ RT_NOREF(crtc, mode, adjusted_mode); return TRUE; } + +static void +vbox_crtc_stub (xf86CrtcPtr crtc) +{ RT_NOREF(crtc); } + +static void +vbox_crtc_mode_set (xf86CrtcPtr crtc, DisplayModePtr mode, + DisplayModePtr adjusted_mode, int x, int y) +{ + RT_NOREF(mode); + VBOXPtr pVBox = VBOXGetRec(crtc->scrn); + unsigned cDisplay = (uintptr_t)crtc->driver_private; + + TRACE_LOG("name=%s, HDisplay=%d, VDisplay=%d, x=%d, y=%d\n", adjusted_mode->name, + adjusted_mode->HDisplay, adjusted_mode->VDisplay, x, y); + pVBox->pScreens[cDisplay].fPowerOn = true; + pVBox->pScreens[cDisplay].aScreenLocation.cx = adjusted_mode->HDisplay; + pVBox->pScreens[cDisplay].aScreenLocation.cy = adjusted_mode->VDisplay; + pVBox->pScreens[cDisplay].aScreenLocation.x = x; + pVBox->pScreens[cDisplay].aScreenLocation.y = y; + setModeRandR12(crtc->scrn, cDisplay); +} + +static void +vbox_crtc_gamma_set (xf86CrtcPtr crtc, CARD16 *red, + CARD16 *green, CARD16 *blue, int size) +{ RT_NOREF(crtc, red, green, blue, size); } + +static void * +vbox_crtc_shadow_allocate (xf86CrtcPtr crtc, int width, int height) +{ RT_NOREF(crtc, width, height); return NULL; } + +static const xf86CrtcFuncsRec VBOXCrtcFuncs = { + .dpms = vbox_crtc_dpms, + .save = NULL, /* These two are never called by the server. */ + .restore = NULL, + .lock = vbox_crtc_lock, + .unlock = NULL, /* This will not be invoked if lock returns FALSE. */ + .mode_fixup = vbox_crtc_mode_fixup, + .prepare = vbox_crtc_stub, + .mode_set = vbox_crtc_mode_set, + .commit = vbox_crtc_stub, + .gamma_set = vbox_crtc_gamma_set, + .shadow_allocate = vbox_crtc_shadow_allocate, + .shadow_create = NULL, /* These two should not be invoked if allocate + returns NULL. */ + .shadow_destroy = NULL, + .set_cursor_colors = NULL, /* We are still using the old cursor API. */ + .set_cursor_position = NULL, + .show_cursor = NULL, + .hide_cursor = NULL, + .load_cursor_argb = NULL, + .destroy = vbox_crtc_stub +}; + +static void +vbox_output_stub (xf86OutputPtr output) +{ RT_NOREF(output); } + +static void +vbox_output_dpms (xf86OutputPtr output, int mode) +{ + RT_NOREF(output, mode); +} + +static int +vbox_output_mode_valid (xf86OutputPtr output, DisplayModePtr mode) +{ + return MODE_OK; +} + +static Bool +vbox_output_mode_fixup (xf86OutputPtr output, DisplayModePtr mode, + DisplayModePtr adjusted_mode) +{ RT_NOREF(output, mode, adjusted_mode); return TRUE; } + +static void +vbox_output_mode_set (xf86OutputPtr output, DisplayModePtr mode, + DisplayModePtr adjusted_mode) +{ RT_NOREF(output, mode, adjusted_mode); } + +static xf86OutputStatus +vbox_output_detect (xf86OutputPtr output) +{ + ScrnInfoPtr pScrn = output->scrn; + VBOXPtr pVBox = VBOXGetRec(pScrn); + uint32_t iScreen = (uintptr_t)output->driver_private; + return pVBox->pScreens[iScreen].afConnected + ? XF86OutputStatusConnected : XF86OutputStatusDisconnected; +} + +static DisplayModePtr vbox_output_add_mode(VBOXPtr pVBox, DisplayModePtr *pModes, const char *pszName, int x, int y, + Bool isPreferred, Bool isUserDef) +{ + TRACE_LOG("pszName=%s, x=%d, y=%d\n", pszName ? pszName : "(null)", x, y); + DisplayModePtr pMode = xnfcalloc(1, sizeof(DisplayModeRec)); + int cRefresh = 60; + + pMode->status = MODE_OK; + /* We don't ask the host whether it likes user defined modes, + * as we assume that the user really wanted that mode. */ + pMode->type = isUserDef ? M_T_USERDEF : M_T_BUILTIN; + if (isPreferred) + pMode->type |= M_T_PREFERRED; + /* Older versions of VBox only support screen widths which are a multiple + * of 8 */ + if (pVBox->fAnyX) + pMode->HDisplay = x; + else + pMode->HDisplay = x & ~7; + pMode->HSyncStart = pMode->HDisplay + 2; + pMode->HSyncEnd = pMode->HDisplay + 4; + pMode->HTotal = pMode->HDisplay + 6; + pMode->VDisplay = y; + pMode->VSyncStart = pMode->VDisplay + 2; + pMode->VSyncEnd = pMode->VDisplay + 4; + pMode->VTotal = pMode->VDisplay + 6; + pMode->Clock = pMode->HTotal * pMode->VTotal * cRefresh / 1000; /* kHz */ + if (NULL == pszName) { + xf86SetModeDefaultName(pMode); + } else { + pMode->name = xnfstrdup(pszName); + } + *pModes = xf86ModesAdd(*pModes, pMode); + return pMode; +} + +static DisplayModePtr +vbox_output_get_modes (xf86OutputPtr output) +{ + DisplayModePtr pModes = NULL; + DisplayModePtr pPreferred = NULL; + ScrnInfoPtr pScrn = output->scrn; + VBOXPtr pVBox = VBOXGetRec(pScrn); + + TRACE_ENTRY(); + uint32_t iScreen = (uintptr_t)output->driver_private; + pPreferred = vbox_output_add_mode(pVBox, &pModes, NULL, + RT_CLAMP(pVBox->pScreens[iScreen].aPreferredSize.cx, VBOX_VIDEO_MIN_SIZE, VBOX_VIDEO_MAX_VIRTUAL), + RT_CLAMP(pVBox->pScreens[iScreen].aPreferredSize.cy, VBOX_VIDEO_MIN_SIZE, VBOX_VIDEO_MAX_VIRTUAL), + TRUE, FALSE); + vbox_output_add_mode(pVBox, &pModes, NULL, 2560, 1600, FALSE, FALSE); + vbox_output_add_mode(pVBox, &pModes, NULL, 2560, 1440, FALSE, FALSE); + vbox_output_add_mode(pVBox, &pModes, NULL, 2048, 1536, FALSE, FALSE); + vbox_output_add_mode(pVBox, &pModes, NULL, 1920, 1600, FALSE, FALSE); + vbox_output_add_mode(pVBox, &pModes, NULL, 1920, 1080, FALSE, FALSE); + vbox_output_add_mode(pVBox, &pModes, NULL, 1680, 1050, FALSE, FALSE); + vbox_output_add_mode(pVBox, &pModes, NULL, 1600, 1200, FALSE, FALSE); + vbox_output_add_mode(pVBox, &pModes, NULL, 1400, 1050, FALSE, FALSE); + vbox_output_add_mode(pVBox, &pModes, NULL, 1280, 1024, FALSE, FALSE); + vbox_output_add_mode(pVBox, &pModes, NULL, 1024, 768, FALSE, FALSE); + vbox_output_add_mode(pVBox, &pModes, NULL, 800, 600, FALSE, FALSE); + vbox_output_add_mode(pVBox, &pModes, NULL, 640, 480, FALSE, FALSE); + VBOXEDIDSet(output, pPreferred); + TRACE_EXIT(); + return pModes; +} + +static const xf86OutputFuncsRec VBOXOutputFuncs = { + .create_resources = vbox_output_stub, + .dpms = vbox_output_dpms, + .save = NULL, /* These two are never called by the server. */ + .restore = NULL, + .mode_valid = vbox_output_mode_valid, + .mode_fixup = vbox_output_mode_fixup, + .prepare = vbox_output_stub, + .commit = vbox_output_stub, + .mode_set = vbox_output_mode_set, + .detect = vbox_output_detect, + .get_modes = vbox_output_get_modes, +#ifdef RANDR_12_INTERFACE + .set_property = NULL, +#endif + .destroy = vbox_output_stub +}; +#endif /* VBOXVIDEO_13 */ + +/* Module loader interface */ +static MODULESETUPPROTO(vboxSetup); + +static XF86ModuleVersionInfo vboxVersionRec = +{ + VBOX_DRIVER_NAME, + "Oracle Corporation", + MODINFOSTRING1, + MODINFOSTRING2, +#ifdef XORG_7X + XORG_VERSION_CURRENT, +#else + XF86_VERSION_CURRENT, +#endif + 1, /* Module major version. Xorg-specific */ + 0, /* Module minor version. Xorg-specific */ + 1, /* Module patchlevel. Xorg-specific */ + ABI_CLASS_VIDEODRV, /* This is a video driver */ + ABI_VIDEODRV_VERSION, + MOD_CLASS_VIDEODRV, + {0, 0, 0, 0} +}; + +/* + * This data is accessed by the loader. The name must be the module name + * followed by "ModuleData". + */ +#ifdef XORG_7X +_X_EXPORT +#endif +XF86ModuleData vboxvideoModuleData = { &vboxVersionRec, vboxSetup, NULL }; + +static pointer +vboxSetup(pointer Module, pointer Options, int *ErrorMajor, int *ErrorMinor) +{ + static Bool Initialised = FALSE; + RT_NOREF(Options, ErrorMinor); + + if (!Initialised) + { + Initialised = TRUE; +#ifdef PCIACCESS + xf86AddDriver(&VBOXVIDEO, Module, HaveDriverFuncs); +#else + xf86AddDriver(&VBOXVIDEO, Module, 0); +#endif +#ifndef XORG_7X + LoaderRefSymLists(fbSymbols, + shadowfbSymbols, + ramdacSymbols, + vgahwSymbols, + NULL); +#endif + xf86Msg(X_CONFIG, "Load address of symbol \"VBOXVIDEO\" is %p\n", + (void *)&VBOXVIDEO); + return (pointer)TRUE; + } + + if (ErrorMajor) + *ErrorMajor = LDR_ONCEONLY; + return (NULL); +} + + +static const OptionInfoRec * +VBOXAvailableOptions(int chipid, int busid) +{ + RT_NOREF(chipid, busid); + return (VBOXOptions); +} + +static void +VBOXIdentify(int flags) +{ + RT_NOREF(flags); + xf86PrintChipsets(VBOX_NAME, "guest driver for VirtualBox", VBOXChipsets); +} + +#ifndef XF86_SCRN_INTERFACE +# define SCRNINDEXAPI(pfn) pfn ## Index +static Bool VBOXScreenInitIndex(int scrnIndex, ScreenPtr pScreen, int argc, char **argv) +{ + RT_NOREF(scrnIndex); + return VBOXScreenInit(pScreen, argc, argv); +} + +static Bool VBOXEnterVTIndex(int scrnIndex, int flags) +{ RT_NOREF(flags); return VBOXEnterVT(xf86Screens[scrnIndex]); } + +static void VBOXLeaveVTIndex(int scrnIndex, int flags) +{ RT_NOREF(flags); VBOXLeaveVT(xf86Screens[scrnIndex]); } + +static Bool VBOXCloseScreenIndex(int scrnIndex, ScreenPtr pScreen) +{ RT_NOREF(scrnIndex); return VBOXCloseScreen(pScreen); } + +static Bool VBOXSwitchModeIndex(int scrnIndex, DisplayModePtr pMode, int flags) +{ RT_NOREF(flags); return VBOXSwitchMode(xf86Screens[scrnIndex], pMode); } + +static void VBOXAdjustFrameIndex(int scrnIndex, int x, int y, int flags) +{ RT_NOREF(flags); VBOXAdjustFrame(xf86Screens[scrnIndex], x, y); } + +static void VBOXFreeScreenIndex(int scrnIndex, int flags) +{ RT_NOREF(flags); VBOXFreeScreen(xf86Screens[scrnIndex]); } +# else +# define SCRNINDEXAPI(pfn) pfn +#endif /* XF86_SCRN_INTERFACE */ + +static void setScreenFunctions(ScrnInfoPtr pScrn, xf86ProbeProc pfnProbe) +{ + pScrn->driverVersion = VBOX_VERSION; + pScrn->driverName = VBOX_DRIVER_NAME; + pScrn->name = VBOX_NAME; + pScrn->Probe = pfnProbe; + pScrn->PreInit = VBOXPreInit; + pScrn->ScreenInit = SCRNINDEXAPI(VBOXScreenInit); + pScrn->SwitchMode = SCRNINDEXAPI(VBOXSwitchMode); + pScrn->AdjustFrame = SCRNINDEXAPI(VBOXAdjustFrame); + pScrn->EnterVT = SCRNINDEXAPI(VBOXEnterVT); + pScrn->LeaveVT = SCRNINDEXAPI(VBOXLeaveVT); + pScrn->FreeScreen = SCRNINDEXAPI(VBOXFreeScreen); +} + +/* + * One of these functions is called once, at the start of the first server + * generation to do a minimal probe for supported hardware. + */ + +#ifdef PCIACCESS +static Bool +VBOXPciProbe(DriverPtr drv, int entity_num, struct pci_device *dev, + intptr_t match_data) +{ + ScrnInfoPtr pScrn; + int drmFd; + + TRACE_ENTRY(); + + drmFd = open("/dev/dri/card0", O_RDWR, 0); + if (drmFd >= 0) + { + xf86Msg(X_INFO, "vboxvideo: kernel driver found, not loading.\n"); + close(drmFd); + return FALSE; + } + /* It is safe to call this, as the X server enables I/O access before + * calling the probe call-backs. */ + if (!xf86EnableIO()) + { + xf86Msg(X_INFO, "vboxvideo: this driver requires direct hardware access. You may wish to use the kernel driver instead.\n"); + return FALSE; + } + pScrn = xf86ConfigPciEntity(NULL, 0, entity_num, VBOXPCIchipsets, + NULL, NULL, NULL, NULL, NULL); + if (pScrn != NULL) { + VBOXPtr pVBox; + + VBOXSetRec(pScrn); + pVBox = VBOXGetRec(pScrn); + if (!pVBox) + return FALSE; + setScreenFunctions(pScrn, NULL); + pVBox->pciInfo = dev; + } + + TRACE_LOG("returning %s\n", pScrn == NULL ? "false" : "true"); + return (pScrn != NULL); +} +#endif + +#ifndef PCIACCESS +static Bool +VBOXProbe(DriverPtr drv, int flags) +{ + Bool foundScreen = FALSE; + int numDevSections; + GDevPtr *devSections; + + /* + * Find the config file Device sections that match this + * driver, and return if there are none. + */ + if ((numDevSections = xf86MatchDevice(VBOX_NAME, + &devSections)) <= 0) + return (FALSE); + + /* PCI BUS */ + if (xf86GetPciVideoInfo()) + { + int numUsed; + int *usedChips; + int i; + numUsed = xf86MatchPciInstances(VBOX_NAME, VBOX_VENDORID, + VBOXChipsets, VBOXPCIchipsets, + devSections, numDevSections, + drv, &usedChips); + if (numUsed > 0) + { + if (flags & PROBE_DETECT) + foundScreen = TRUE; + else + for (i = 0; i < numUsed; i++) + { + ScrnInfoPtr pScrn = NULL; + /* Allocate a ScrnInfoRec */ + if ((pScrn = xf86ConfigPciEntity(pScrn,0,usedChips[i], + VBOXPCIchipsets,NULL, + NULL,NULL,NULL,NULL))) + { + setScreenFunctions(pScrn, VBOXProbe); + foundScreen = TRUE; + } + } + free(usedChips); + } + } + free(devSections); + return (foundScreen); +} +#endif + + +/* + * QUOTE from the XFree86 DESIGN document: + * + * The purpose of this function is to find out all the information + * required to determine if the configuration is usable, and to initialise + * those parts of the ScrnInfoRec that can be set once at the beginning of + * the first server generation. + * + * (...) + * + * This includes probing for video memory, clocks, ramdac, and all other + * HW info that is needed. It includes determining the depth/bpp/visual + * and related info. It includes validating and determining the set of + * video modes that will be used (and anything that is required to + * determine that). + * + * This information should be determined in the least intrusive way + * possible. The state of the HW must remain unchanged by this function. + * Although video memory (including MMIO) may be mapped within this + * function, it must be unmapped before returning. + * + * END QUOTE + */ + +static Bool +VBOXPreInit(ScrnInfoPtr pScrn, int flags) +{ + VBOXPtr pVBox; + Gamma gzeros = {0.0, 0.0, 0.0}; + rgb rzeros = {0, 0, 0}; + + TRACE_ENTRY(); + /* Are we really starting the server, or is this just a dummy run? */ + if (flags & PROBE_DETECT) + return (FALSE); + + xf86DrvMsg(pScrn->scrnIndex, X_INFO, + "VirtualBox guest additions video driver version %d.%d\n", + VBOX_VERSION_MAJOR, VBOX_VERSION_MINOR); + + /* The ramdac module is needed for the hardware cursor. */ + if (!xf86LoadSubModule(pScrn, "ramdac")) + return FALSE; + + /* The framebuffer module. */ + if (!xf86LoadSubModule(pScrn, "fb")) + return (FALSE); + + if (!xf86LoadSubModule(pScrn, "shadowfb")) + return FALSE; + + if (!xf86LoadSubModule(pScrn, "vgahw")) + return FALSE; + + /* Get our private data from the ScrnInfoRec structure. */ + VBOXSetRec(pScrn); + pVBox = VBOXGetRec(pScrn); + if (!pVBox) + return FALSE; + + /* Entity information seems to mean bus information. */ + pVBox->pEnt = xf86GetEntityInfo(pScrn->entityList[0]); + +#ifndef PCIACCESS + if (pVBox->pEnt->location.type != BUS_PCI) + return FALSE; + + pVBox->pciInfo = xf86GetPciInfoForEntity(pVBox->pEnt->index); + pVBox->pciTag = pciTag(pVBox->pciInfo->bus, + pVBox->pciInfo->device, + pVBox->pciInfo->func); +#endif + + /* Set up our ScrnInfoRec structure to describe our virtual + capabilities to X. */ + + pScrn->chipset = "vbox"; + /** @note needed during colourmap initialisation */ + pScrn->rgbBits = 8; + + /* Let's create a nice, capable virtual monitor. */ + pScrn->monitor = pScrn->confScreen->monitor; + pScrn->monitor->DDC = NULL; + pScrn->monitor->nHsync = 1; + pScrn->monitor->hsync[0].lo = 1; + pScrn->monitor->hsync[0].hi = 10000; + pScrn->monitor->nVrefresh = 1; + pScrn->monitor->vrefresh[0].lo = 1; + pScrn->monitor->vrefresh[0].hi = 100; + + pScrn->progClock = TRUE; + + /* Using the PCI information caused problems with non-powers-of-two + sized video RAM configurations */ + pVBox->cbFBMax = VBoxVideoGetVRAMSize(); + pScrn->videoRam = pVBox->cbFBMax / 1024; + + /* Check if the chip restricts horizontal resolution or not. */ + pVBox->fAnyX = VBoxVideoAnyWidthAllowed(); + + /* Set up clock information that will support all modes we need. */ + pScrn->clockRanges = xnfcalloc(sizeof(ClockRange), 1); + pScrn->clockRanges->minClock = 1000; + pScrn->clockRanges->maxClock = 1000000000; + pScrn->clockRanges->clockIndex = -1; + pScrn->clockRanges->ClockMulFactor = 1; + pScrn->clockRanges->ClockDivFactor = 1; + + if (!xf86SetDepthBpp(pScrn, 24, 0, 0, Support32bppFb)) + return FALSE; + /* We only support 16 and 24 bits depth (i.e. 16 and 32bpp) */ + if (pScrn->bitsPerPixel != 32 && pScrn->bitsPerPixel != 16) + { + xf86DrvMsg(pScrn->scrnIndex, X_ERROR, + "The VBox additions only support 16 and 32bpp graphics modes\n"); + return FALSE; + } + xf86PrintDepthBpp(pScrn); + vboxAddModes(pScrn); + +#ifdef VBOXVIDEO_13 + pScrn->virtualX = VBOX_VIDEO_MAX_VIRTUAL; + pScrn->virtualY = VBOX_VIDEO_MAX_VIRTUAL; +#else + /* We don't validate with xf86ValidateModes and xf86PruneModes as we + * already know what we like and what we don't. */ + + pScrn->currentMode = pScrn->modes; + + /* Set the right virtual resolution. */ + pScrn->virtualX = pScrn->bitsPerPixel == 16 ? (pScrn->currentMode->HDisplay + 1) & ~1 : pScrn->currentMode->HDisplay; + pScrn->virtualY = pScrn->currentMode->VDisplay; + +#endif /* !VBOXVIDEO_13 */ + + pScrn->displayWidth = pScrn->virtualX; + + xf86PrintModes(pScrn); + + /* VGA hardware initialisation */ + if (!vgaHWGetHWRec(pScrn)) + return FALSE; + /* Must be called before any VGA registers are saved or restored */ + vgaHWSetStdFuncs(VGAHWPTR(pScrn)); + vgaHWGetIOBase(VGAHWPTR(pScrn)); + + /* Colour weight - we always call this, since we are always in + truecolour. */ + if (!xf86SetWeight(pScrn, rzeros, rzeros)) + return (FALSE); + + /* visual init */ + if (!xf86SetDefaultVisual(pScrn, -1)) + return (FALSE); + + xf86SetGamma(pScrn, gzeros); + + /* Set the DPI. Perhaps we should read this from the host? */ + xf86SetDpi(pScrn, 96, 96); + + if (pScrn->memPhysBase == 0) { +#ifdef PCIACCESS + pScrn->memPhysBase = pVBox->pciInfo->regions[0].base_addr; +#else + pScrn->memPhysBase = pVBox->pciInfo->memBase[0]; +#endif + pScrn->fbOffset = 0; + } + + TRACE_EXIT(); + return (TRUE); +} + +/** + * Dummy function for setting the colour palette, which we actually never + * touch. However, the server still requires us to provide this. + */ +static void +vboxLoadPalette(ScrnInfoPtr pScrn, int numColors, int *indices, + LOCO *colors, VisualPtr pVisual) +{ + RT_NOREF(pScrn, numColors, indices, colors, pVisual); +} + +/** Set the graphics and guest cursor support capabilities to the host if + * the user-space helper is running. */ +static void updateGraphicsCapability(ScrnInfoPtr pScrn, Bool hasVT) +{ + VBOXPtr pVBox = VBOXGetRec(pScrn); + + if (!pVBox->fHaveHGSMIModeHints) + return; + VBoxHGSMISendCapsInfo(&pVBox->guestCtx, hasVT + ? VBVACAPS_VIDEO_MODE_HINTS | VBVACAPS_DISABLE_CURSOR_INTEGRATION + : VBVACAPS_DISABLE_CURSOR_INTEGRATION); +} + +#ifndef VBOXVIDEO_13 + +#define PREFERRED_MODE_ATOM_NAME "VBOXVIDEO_PREFERRED_MODE" + +static void setSizesRandR11(ScrnInfoPtr pScrn) +{ + VBOXPtr pVBox = VBOXGetRec(pScrn); + DisplayModePtr pNewMode; + int32_t propertyValue; + + pNewMode = pScrn->modes != pScrn->currentMode ? pScrn->modes : pScrn->modes->next; + pNewMode->HDisplay = RT_CLAMP(pVBox->pScreens[0].aPreferredSize.cx, VBOX_VIDEO_MIN_SIZE, VBOX_VIDEO_MAX_VIRTUAL); + pNewMode->VDisplay = RT_CLAMP(pVBox->pScreens[0].aPreferredSize.cy, VBOX_VIDEO_MIN_SIZE, VBOX_VIDEO_MAX_VIRTUAL); + propertyValue = (pNewMode->HDisplay << 16) + pNewMode->VDisplay; + ChangeWindowProperty(ROOT_WINDOW(pScrn), MakeAtom(PREFERRED_MODE_ATOM_NAME, + sizeof(PREFERRED_MODE_ATOM_NAME) - 1, TRUE), XA_INTEGER, 32, + PropModeReplace, 1, &propertyValue, TRUE); +} + +#endif + +static void reprobeCursor(ScrnInfoPtr pScrn) +{ + if (ROOT_WINDOW(pScrn) == NULL) + return; +#ifdef XF86_SCRN_INTERFACE + pScrn->EnableDisableFBAccess(pScrn, FALSE); + pScrn->EnableDisableFBAccess(pScrn, TRUE); +#else + pScrn->EnableDisableFBAccess(pScrn->scrnIndex, FALSE); + pScrn->EnableDisableFBAccess(pScrn->scrnIndex, TRUE); +#endif +} + +static void setSizesAndCursorIntegration(ScrnInfoPtr pScrn, Bool fScreenInitTime) +{ + RT_NOREF(fScreenInitTime); + TRACE_LOG("fScreenInitTime=%d\n", (int)fScreenInitTime); +#ifdef VBOXVIDEO_13 +# if GET_ABI_MAJOR(ABI_VIDEODRV_VERSION) >= 5 + RRGetInfo(xf86ScrnToScreen(pScrn), TRUE); +# else + RRGetInfo(xf86ScrnToScreen(pScrn)); +# endif +#else + setSizesRandR11(pScrn); +#endif + /* This calls EnableDisableFBAccess(), so only use when switched in. */ + if (pScrn->vtSema) + reprobeCursor(pScrn); +} + +/* We update the size hints from the X11 property set by VBoxClient every time + * that the X server goes to sleep (to catch the property change request). + * Although this is far more often than necessary it should not have real-life + * performance consequences and allows us to simplify the code quite a bit. */ +static void vboxBlockHandler(pointer pData, +#if GET_ABI_MAJOR(ABI_VIDEODRV_VERSION) < 23 + OSTimePtr pTimeout, + pointer pReadmask +#else + void *pTimeout +#endif + ) +{ + ScrnInfoPtr pScrn = (ScrnInfoPtr)pData; + Bool fNeedUpdate = false; + + RT_NOREF(pTimeout); +#if GET_ABI_MAJOR(ABI_VIDEODRV_VERSION) < 23 + RT_NOREF(pReadmask); +#endif + if (pScrn->vtSema) + vbvxReadSizesAndCursorIntegrationFromHGSMI(pScrn, &fNeedUpdate); + if (fNeedUpdate) + setSizesAndCursorIntegration(pScrn, false); +} + +/* + * QUOTE from the XFree86 DESIGN document: + * + * This is called at the start of each server generation. + * + * (...) + * + * Decide which operations need to be placed under resource access + * control. (...) Map any video memory or other memory regions. (...) + * Save the video card state. (...) Initialise the initial video + * mode. + * + * End QUOTE. + */ +static Bool VBOXScreenInit(ScreenPtr pScreen, int argc, char **argv) +{ + ScrnInfoPtr pScrn = xf86ScreenToScrn(pScreen); + VBOXPtr pVBox = VBOXGetRec(pScrn); + VisualPtr visual; + RT_NOREF(argc, argv); + + TRACE_ENTRY(); + + if (!VBOXMapVidMem(pScrn)) + return (FALSE); + + /* save current video state */ + VBOXSaveMode(pScrn); + + /* mi layer - reset the visual list (?)*/ + miClearVisualTypes(); + if (!miSetVisualTypes(pScrn->depth, TrueColorMask, + pScrn->rgbBits, TrueColor)) + return (FALSE); + if (!miSetPixmapDepths()) + return (FALSE); + + if (!fbScreenInit(pScreen, pVBox->base, + pScrn->virtualX, pScrn->virtualY, + pScrn->xDpi, pScrn->yDpi, + pScrn->displayWidth, pScrn->bitsPerPixel)) + return (FALSE); + + /* Fixup RGB ordering */ + /** @note the X server uses this even in true colour. */ + visual = pScreen->visuals + pScreen->numVisuals; + while (--visual >= pScreen->visuals) { + if ((visual->class | DynamicClass) == DirectColor) { + visual->offsetRed = pScrn->offset.red; + visual->offsetGreen = pScrn->offset.green; + visual->offsetBlue = pScrn->offset.blue; + visual->redMask = pScrn->mask.red; + visual->greenMask = pScrn->mask.green; + visual->blueMask = pScrn->mask.blue; + } + } + + /* must be after RGB ordering fixed */ + fbPictureInit(pScreen, 0, 0); + + xf86SetBlackWhitePixels(pScreen); + pScrn->vtSema = TRUE; + +#if defined(VBOXVIDEO_13) && defined(RT_OS_LINUX) + vbvxSetUpLinuxACPI(pScreen); +#endif + + if (!VBoxHGSMIIsSupported()) + { + xf86DrvMsg(pScrn->scrnIndex, X_ERROR, "Graphics device too old to support.\n"); + return FALSE; + } + vbvxSetUpHGSMIHeapInGuest(pVBox, pScrn->videoRam * 1024); + pVBox->cScreens = VBoxHGSMIGetMonitorCount(&pVBox->guestCtx); + pVBox->pScreens = xnfcalloc(pVBox->cScreens, sizeof(*pVBox->pScreens)); + pVBox->paVBVAModeHints = xnfcalloc(pVBox->cScreens, sizeof(*pVBox->paVBVAModeHints)); + xf86DrvMsg(pScrn->scrnIndex, X_INFO, "Requested monitor count: %u\n", pVBox->cScreens); + vboxEnableVbva(pScrn); + /* Set up the dirty rectangle handler. It will be added into a function + * chain and gets removed when the screen is cleaned up. */ + if (ShadowFBInit2(pScreen, NULL, vbvxHandleDirtyRect) != TRUE) + return FALSE; + VBoxInitialiseSizeHints(pScrn); + +#ifdef VBOXVIDEO_13 + /* Initialise CRTC and output configuration for use with randr1.2. */ + xf86CrtcConfigInit(pScrn, &VBOXCrtcConfigFuncs); + + { + uint32_t i; + + for (i = 0; i < pVBox->cScreens; ++i) + { + char szOutput[256]; + + /* Setup our virtual CRTCs. */ + pVBox->pScreens[i].paCrtcs = xf86CrtcCreate(pScrn, &VBOXCrtcFuncs); + pVBox->pScreens[i].paCrtcs->driver_private = (void *)(uintptr_t)i; + + /* Set up our virtual outputs. */ + snprintf(szOutput, sizeof(szOutput), "VGA-%u", i); + pVBox->pScreens[i].paOutputs + = xf86OutputCreate(pScrn, &VBOXOutputFuncs, szOutput); + + /* We are not interested in the monitor section in the + * configuration file. */ + xf86OutputUseScreenMonitor(pVBox->pScreens[i].paOutputs, FALSE); + pVBox->pScreens[i].paOutputs->possible_crtcs = 1 << i; + pVBox->pScreens[i].paOutputs->possible_clones = 0; + pVBox->pScreens[i].paOutputs->driver_private = (void *)(uintptr_t)i; + TRACE_LOG("Created crtc (%p) and output %s (%p)\n", + (void *)pVBox->pScreens[i].paCrtcs, szOutput, + (void *)pVBox->pScreens[i].paOutputs); + } + } + + /* Set a sane minimum and maximum mode size to match what the hardware + * supports. */ + xf86CrtcSetSizeRange(pScrn, VBOX_VIDEO_MIN_SIZE, VBOX_VIDEO_MIN_SIZE, VBOX_VIDEO_MAX_VIRTUAL, VBOX_VIDEO_MAX_VIRTUAL); + + /* Now create our initial CRTC/output configuration. */ + if (!xf86InitialConfiguration(pScrn, TRUE)) { + xf86DrvMsg(pScrn->scrnIndex, X_ERROR, "Initial CRTC configuration failed!\n"); + return (FALSE); + } + + /* Work around a bug in the original X server modesetting code, which took + * the first valid values set to these two as maxima over the server + * lifetime. This bug was introduced on Feb 15 2007 and was fixed in commit + * fa877d7f three months later, so it was present in X.Org Server 1.3. */ + pScrn->virtualX = VBOX_VIDEO_MAX_VIRTUAL; + pScrn->virtualY = VBOX_VIDEO_MAX_VIRTUAL; + + /* Initialise randr 1.2 mode-setting functions. */ + if (!xf86CrtcScreenInit(pScreen)) { + return FALSE; + } + + /* set first video mode */ + if (!xf86SetDesiredModes(pScrn)) { + return FALSE; + } +#else /* !VBOXVIDEO_13 */ + /* set first video mode */ + setModeRandR11(pScrn, pScrn->currentMode, true, false, 0, 0); +#endif /* !VBOXVIDEO_13 */ + + /* Say that we support graphics. */ + updateGraphicsCapability(pScrn, TRUE); + +#if GET_ABI_MAJOR(ABI_VIDEODRV_VERSION) >= 23 +# define WakeupHandlerProcPtr ServerWakeupHandlerProcPtr +#endif + + /* Register block and wake-up handlers for getting new screen size hints. */ + RegisterBlockAndWakeupHandlers(vboxBlockHandler, (WakeupHandlerProcPtr)NoopDDA, (pointer)pScrn); + + /* software cursor */ + miDCInitialize(pScreen, xf86GetPointerScreenFuncs()); + + /* colourmap code */ + if (!miCreateDefColormap(pScreen)) + return (FALSE); + + if(!xf86HandleColormaps(pScreen, 256, 8, vboxLoadPalette, NULL, 0)) + return (FALSE); + + pVBox->CloseScreen = pScreen->CloseScreen; + pScreen->CloseScreen = SCRNINDEXAPI(VBOXCloseScreen); +#ifdef VBOXVIDEO_13 + pScreen->SaveScreen = xf86SaveScreen; +#else + pScreen->SaveScreen = VBOXSaveScreen; +#endif + +#ifdef VBOXVIDEO_13 + xf86DPMSInit(pScreen, xf86DPMSSet, 0); +#else + /* We probably do want to support power management - even if we just use + a dummy function. */ + xf86DPMSInit(pScreen, VBOXDisplayPowerManagementSet, 0); +#endif + + /* Report any unused options (only for the first generation) */ + if (serverGeneration == 1) + xf86ShowUnusedOptions(pScrn->scrnIndex, pScrn->options); + + if (vbvxCursorInit(pScreen) != TRUE) + xf86DrvMsg(pScrn->scrnIndex, X_ERROR, + "Unable to start the VirtualBox mouse pointer integration with the host system.\n"); + + return (TRUE); +} + +#define NO_VT_ATOM_NAME "VBOXVIDEO_NO_VT" + +static Bool VBOXEnterVT(ScrnInfoPtr pScrn) +{ + VBOXPtr pVBox = VBOXGetRec(pScrn); +#ifndef VBOXVIDEO_13 + /* If we got a mode request while we were switched out, temporarily override + * the physical mode set to the device while keeping things consistent from + * the server's point of view. */ + int cXOverRide = RT_CLAMP(pVBox->pScreens[0].aPreferredSize.cx, VBOX_VIDEO_MIN_SIZE, VBOX_VIDEO_MAX_VIRTUAL); + int cYOverRide = RT_CLAMP(pVBox->pScreens[0].aPreferredSize.cy, VBOX_VIDEO_MIN_SIZE, VBOX_VIDEO_MAX_VIRTUAL); +#endif + + TRACE_ENTRY(); + vbvxSetUpHGSMIHeapInGuest(pVBox, pScrn->videoRam * 1024); + vboxEnableVbva(pScrn); + /* Re-set video mode */ +#ifdef VBOXVIDEO_13 + if (!xf86SetDesiredModes(pScrn)) { + return FALSE; + } +#else + setModeRandR11(pScrn, pScrn->currentMode, false, true, cXOverRide, cYOverRide); + DeleteProperty(ROOT_WINDOW(pScrn), MakeAtom(NO_VT_ATOM_NAME, sizeof(NO_VT_ATOM_NAME) - 1, TRUE)); +#endif + updateGraphicsCapability(pScrn, TRUE); + return TRUE; +} + +static void VBOXLeaveVT(ScrnInfoPtr pScrn) +{ +#ifdef VBOXVIDEO_13 + VBOXPtr pVBox = VBOXGetRec(pScrn); + unsigned i; +#else + int32_t propertyValue = 0; +#endif + + TRACE_ENTRY(); +#ifdef VBOXVIDEO_13 + for (i = 0; i < pVBox->cScreens; ++i) + vbox_crtc_dpms(pVBox->pScreens[i].paCrtcs, DPMSModeOff); +#else + ChangeWindowProperty(ROOT_WINDOW(pScrn), MakeAtom(NO_VT_ATOM_NAME, sizeof(NO_VT_ATOM_NAME) - 1, FALSE), XA_INTEGER, 32, + PropModeReplace, 1, &propertyValue, TRUE); +#endif + updateGraphicsCapability(pScrn, FALSE); + vboxDisableVbva(pScrn); + vbvxClearVRAM(pScrn, ((size_t)pScrn->virtualX) * pScrn->virtualY * (pScrn->bitsPerPixel / 8), 0); + VBOXRestoreMode(pScrn); + TRACE_EXIT(); +} + +static Bool VBOXCloseScreen(ScreenPtr pScreen) +{ + ScrnInfoPtr pScrn = xf86ScreenToScrn(pScreen); + VBOXPtr pVBox = VBOXGetRec(pScrn); + BOOL ret; + + if (pScrn->vtSema) + { +#ifdef VBOXVIDEO_13 + unsigned i; + + for (i = 0; i < pVBox->cScreens; ++i) + vbox_crtc_dpms(pVBox->pScreens[i].paCrtcs, DPMSModeOff); +#endif + vboxDisableVbva(pScrn); + vbvxClearVRAM(pScrn, ((size_t)pScrn->virtualX) * pScrn->virtualY * (pScrn->bitsPerPixel / 8), 0); + } + if (pScrn->vtSema) + VBOXRestoreMode(pScrn); + if (pScrn->vtSema) + VBOXUnmapVidMem(pScrn); + pScrn->vtSema = FALSE; + + vbvxCursorTerm(pVBox); + + pScreen->CloseScreen = pVBox->CloseScreen; +#if defined(VBOXVIDEO_13) && defined(RT_OS_LINUX) + vbvxCleanUpLinuxACPI(pScreen); +#endif +#ifndef XF86_SCRN_INTERFACE + ret = pScreen->CloseScreen(pScreen->myNum, pScreen); +#else + ret = pScreen->CloseScreen(pScreen); +#endif + return ret; +} + +static Bool VBOXSwitchMode(ScrnInfoPtr pScrn, DisplayModePtr pMode) +{ + Bool rc = TRUE; + + TRACE_LOG("HDisplay=%d, VDisplay=%d\n", pMode->HDisplay, pMode->VDisplay); +#ifdef VBOXVIDEO_13 + rc = xf86SetSingleMode(pScrn, pMode, RR_Rotate_0); +#else + setModeRandR11(pScrn, pMode, false, false, 0, 0); +#endif + TRACE_LOG("returning %s\n", rc ? "TRUE" : "FALSE"); + return rc; +} + +static void VBOXAdjustFrame(ScrnInfoPtr pScrn, int x, int y) +{ RT_NOREF(pScrn, x, y); } + +static void VBOXFreeScreen(ScrnInfoPtr pScrn) +{ + /* Destroy the VGA hardware record */ + vgaHWFreeHWRec(pScrn); + /* And our private record */ + free(pScrn->driverPrivate); + pScrn->driverPrivate = NULL; +} + +static Bool +VBOXMapVidMem(ScrnInfoPtr pScrn) +{ + VBOXPtr pVBox = VBOXGetRec(pScrn); + Bool rc = TRUE; + + TRACE_ENTRY(); + if (!pVBox->base) + { +#ifdef PCIACCESS + (void) pci_device_map_range(pVBox->pciInfo, + pScrn->memPhysBase, + pScrn->videoRam * 1024, + PCI_DEV_MAP_FLAG_WRITABLE, + & pVBox->base); +#else + pVBox->base = xf86MapPciMem(pScrn->scrnIndex, + VIDMEM_FRAMEBUFFER, + pVBox->pciTag, pScrn->memPhysBase, + (unsigned) pScrn->videoRam * 1024); +#endif + if (!pVBox->base) + rc = FALSE; + } + TRACE_LOG("returning %s\n", rc ? "TRUE" : "FALSE"); + return rc; +} + +static void +VBOXUnmapVidMem(ScrnInfoPtr pScrn) +{ + VBOXPtr pVBox = VBOXGetRec(pScrn); + + TRACE_ENTRY(); + if (pVBox->base == NULL) + return; + +#ifdef PCIACCESS + (void) pci_device_unmap_range(pVBox->pciInfo, + pVBox->base, + pScrn->videoRam * 1024); +#else + xf86UnMapVidMem(pScrn->scrnIndex, pVBox->base, + (unsigned) pScrn->videoRam * 1024); +#endif + pVBox->base = NULL; + TRACE_EXIT(); +} + +#ifndef VBOXVIDEO_13 +static Bool +VBOXSaveScreen(ScreenPtr pScreen, int mode) +{ + RT_NOREF(pScreen, mode); + return TRUE; +} +#endif + +void +VBOXSaveMode(ScrnInfoPtr pScrn) +{ + VBOXPtr pVBox = VBOXGetRec(pScrn); + vgaRegPtr vgaReg; + + TRACE_ENTRY(); + vgaReg = &VGAHWPTR(pScrn)->SavedReg; + vgaHWSave(pScrn, vgaReg, VGA_SR_ALL); + pVBox->fSavedVBEMode = VBoxVideoGetModeRegisters(&pVBox->cSavedWidth, + &pVBox->cSavedHeight, + &pVBox->cSavedPitch, + &pVBox->cSavedBPP, + &pVBox->fSavedFlags); +} + +void +VBOXRestoreMode(ScrnInfoPtr pScrn) +{ + VBOXPtr pVBox = VBOXGetRec(pScrn); + vgaRegPtr vgaReg; + + TRACE_ENTRY(); + vgaReg = &VGAHWPTR(pScrn)->SavedReg; + vgaHWRestore(pScrn, vgaReg, VGA_SR_ALL); + if (pVBox->fSavedVBEMode) + VBoxVideoSetModeRegisters(pVBox->cSavedWidth, pVBox->cSavedHeight, + pVBox->cSavedPitch, pVBox->cSavedBPP, + pVBox->fSavedFlags, 0, 0); + else + VBoxVideoDisableVBE(); +} + +#ifndef VBOXVIDEO_13 +static void +VBOXDisplayPowerManagementSet(ScrnInfoPtr pScrn, int mode, int flags) +{ + RT_NOREF(pScrn, mode, flags); +} +#endif diff --git a/src/vboxvideo.h b/src/vboxvideo.h new file mode 100644 index 0000000..83f8f84 --- /dev/null +++ b/src/vboxvideo.h @@ -0,0 +1,240 @@ +/* $Id: vboxvideo.h 118369 2017-10-13 15:50:47Z michael $ */ +/** @file + * VirtualBox X11 Additions graphics driver + */ + +/* + * Copyright (C) 2006-2017 Oracle Corporation + * + * This code is based on: + * + * X11 VESA driver + * + * Copyright (c) 2000 by Conectiva S.A. (http://www.conectiva.com) + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NON-INFRINGEMENT. IN NO EVENT SHALL + * THE COPYRIGHT HOLDERS, AUTHORS AND/OR ITS SUPPLIERS BE LIABLE FOR ANY CLAIM, + * DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR + * OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE + * USE OR OTHER DEALINGS IN THE SOFTWARE. + * + * Except as contained in this notice, the name of Conectiva Linux shall + * not be used in advertising or otherwise to promote the sale, use or other + * dealings in this Software without prior written authorization from + * Conectiva Linux. + * + * Authors: Paulo César Pereira de Andrade <pcpa@conectiva.com.br> + * Michael Thayer <michael.thayer@oracle.com> + */ + +#ifndef _VBOXVIDEO_H_ +#define _VBOXVIDEO_H_ + +#include <VBoxVideoGuest.h> +#include <VBoxVideo.h> +#include "version-generated.h" + +#define VBOX_VENDORID 0x80EE +#define VBOX_DEVICEID 0xBEEF + +#ifndef VBVA_SCREEN_F_BLANK +# define VBVA_SCREEN_F_BLANK 0x0004 +#endif + +#include <VBoxVideoVBE.h> + +#include "xf86.h" +#include "xf86str.h" +#include "xf86Cursor.h" + +#ifdef DEBUG + +#define TRACE_ENTRY() do { xf86ErrorF("%s: entering\n", __func__); } while(0) +#define TRACE_EXIT() do { xf86ErrorF("%s: leaving\n", __func__); } while(0) +#define TRACE_LINE() \ + do { xf86ErrorF("%s: line\n", __func__, __LINE__); } while(0) +#define TRACE_LOG(...) \ +do { \ + xf86ErrorF("%s: ", __func__); \ + xf86ErrorF(__VA_ARGS__); \ +} while(0) + +#else /* !DEBUG */ + +#define TRACE_ENTRY() do { } while (0) +#define TRACE_EXIT() do { } while (0) +#define TRACE_LOG(...) do { } while (0) + +#endif /* !DEBUG */ + +#define VBOX_VERSION VBOX_VERSION_MAJOR * 10000 \ + + VBOX_VERSION_MINOR * 100 +#define VBOX_NAME "VBoxVideo" +#define VBOX_DRIVER_NAME "vboxvideo" + +#define VBOX_VIDEO_MAJOR VBOX_VERSION_MAJOR +#define VBOX_VIDEO_MINOR VBOX_VERSION_MINOR + +#define VBOX_VIDEO_MIN_SIZE 64 +#define VBOX_VIDEO_MAX_VIRTUAL (INT16_MAX - 1) + +#define VBOXPTR(p) ((VBOXPtr)((p)->driverPrivate)) + +/** Helper to work round different ways of getting the root window in different + * server versions. */ +#if defined(XORG_VERSION_CURRENT) && XORG_VERSION_CURRENT < 70000000 \ + && XORG_VERSION_CURRENT >= 10900000 +# define ROOT_WINDOW(pScrn) screenInfo.screens[(pScrn)->scrnIndex]->root +#else +# define ROOT_WINDOW(pScrn) WindowTable[(pScrn)->scrnIndex] +#endif + +/** ChangeWindowProperty for X.Org Server 1.19 and later */ +#if defined(XORG_VERSION_CURRENT) && XORG_VERSION_CURRENT < 70000000 \ + && XORG_VERSION_CURRENT >= 11900000 +# define ChangeWindowProperty(pWin, property, type, format, mode, \ + len, value, sendevent) \ + dixChangeWindowProperty(serverClient, pWin, property, type, format, \ + mode, len, value, sendevent) +#endif + +/** Structure containing all virtual monitor-specific information. */ +struct VBoxScreen +{ + /** Position information for each virtual screen for the purposes of + * sending dirty rectangle information to the right one. */ + RTRECT2 aScreenLocation; + /** Is this CRTC enabled or in DPMS off state? */ + Bool fPowerOn; +#ifdef VBOXVIDEO_13 + /** The virtual crtcs. */ + struct _xf86Crtc *paCrtcs; + /** The virtual outputs, logically not distinct from crtcs. */ + struct _xf86Output *paOutputs; +#endif + /** Offsets of VBVA buffers in video RAM */ + uint32_t aoffVBVABuffer; + /** Context information about the VBVA buffers for each screen */ + struct VBVABUFFERCONTEXT aVbvaCtx; + /** The current preferred resolution for the screen */ + RTRECTSIZE aPreferredSize; + /** The current preferred location for the screen. */ + RTPOINT aPreferredLocation; + /** Has this screen been enabled by the host? */ + Bool afConnected; + /** Does this screen have a preferred location? */ + Bool afHaveLocation; +}; + +typedef struct VBOXRec +{ + EntityInfoPtr pEnt; +#ifdef PCIACCESS + struct pci_device *pciInfo; +#else + pciVideoPtr pciInfo; + PCITAG pciTag; +#endif + void *base; + /** The amount of VRAM available for use as a framebuffer */ + unsigned long cbFBMax; + /** The size of the framebuffer and the VBVA buffers at the end of it. */ + unsigned long cbView; + /** Whether the pre-X-server mode was a VBE mode */ + Bool fSavedVBEMode; + /** Paramters of the saved pre-X-server VBE mode, invalid if there is none + */ + uint16_t cSavedWidth, cSavedHeight, cSavedPitch, cSavedBPP, fSavedFlags; + CloseScreenProcPtr CloseScreen; + /** Default X server procedure for enabling and disabling framebuffer access */ + xf86EnableDisableFBAccessProc *EnableDisableFBAccess; + OptionInfoPtr Options; + /** @todo we never actually free this */ + xf86CursorInfoPtr pCurs; + /** Do we currently want to use the host cursor? */ + Bool fUseHardwareCursor; + /** Number of screens attached */ + uint32_t cScreens; + /** Information about each virtual screen. */ + struct VBoxScreen *pScreens; + /** Can we get mode hint and cursor integration information from HGSMI? */ + Bool fHaveHGSMIModeHints; + /** Does the host support the screen blanking flag? */ + Bool fHostHasScreenBlankingFlag; + /** Array of structures for receiving mode hints. */ + VBVAMODEHINT *paVBVAModeHints; +#ifdef VBOXVIDEO_13 +# ifdef RT_OS_LINUX + /** Input device file descriptor for getting ACPI hot-plug events. */ + int fdACPIDevices; + /** Input handler handle for ACPI hot-plug listener. */ + void *hACPIEventHandler; +# endif +#endif + /** HGSMI guest heap context */ + HGSMIGUESTCOMMANDCONTEXT guestCtx; + /** Unrestricted horizontal resolution flag. */ + Bool fAnyX; +} VBOXRec, *VBOXPtr; + +#define VBOXGetRec(pScrn) ((VBOXPtr)(pScrn)->driverPrivate) + +/* setmode.c */ + +/** Structure describing the virtual frame buffer. It starts at the beginning + * of the video RAM. */ +struct vbvxFrameBuffer { + /** X offset of first screen in frame buffer. */ + int x0; + /** Y offset of first screen in frame buffer. */ + int y0; + /** Frame buffer virtual width. */ + unsigned cWidth; + /** Frame buffer virtual height. */ + unsigned cHeight; + /** Bits per pixel. */ + unsigned cBPP; +}; + +extern void vbvxClearVRAM(ScrnInfoPtr pScrn, size_t cbOldSize, size_t cbNewSize); +extern void vbvxSetMode(ScrnInfoPtr pScrn, unsigned cDisplay, unsigned cWidth, unsigned cHeight, int x, int y, Bool fEnabled, + Bool fConnected, struct vbvxFrameBuffer *pFrameBuffer); +extern void vbvxSetSolarisMouseRange(int width, int height); + +/* pointer.h */ +extern Bool vbvxCursorInit(ScreenPtr pScreen); +extern void vbvxCursorTerm(VBOXPtr pVBox); + +/* vbva.c */ +extern void vbvxHandleDirtyRect(ScrnInfoPtr pScrn, int iRects, BoxPtr aRects); +extern void vbvxSetUpHGSMIHeapInGuest(VBOXPtr pVBox, uint32_t cbVRAM); +extern Bool vboxEnableVbva(ScrnInfoPtr pScrn); +extern void vboxDisableVbva(ScrnInfoPtr pScrn); + +/* getmode.c */ +extern void vboxAddModes(ScrnInfoPtr pScrn); +extern void VBoxInitialiseSizeHints(ScrnInfoPtr pScrn); +extern void vbvxReadSizesAndCursorIntegrationFromProperties(ScrnInfoPtr pScrn, Bool *pfNeedUpdate); +extern void vbvxReadSizesAndCursorIntegrationFromHGSMI(ScrnInfoPtr pScrn, Bool *pfNeedUpdate); +extern void vbvxSetUpLinuxACPI(ScreenPtr pScreen); +extern void vbvxCleanUpLinuxACPI(ScreenPtr pScreen); + +/* EDID generation */ +#ifdef VBOXVIDEO_13 +extern Bool VBOXEDIDSet(struct _xf86Output *output, DisplayModePtr pmode); +#endif + +#endif /* _VBOXVIDEO_H_ */ + diff --git a/src/vbva.c b/src/vbva.c new file mode 100644 index 0000000..c927cae --- /dev/null +++ b/src/vbva.c @@ -0,0 +1,252 @@ +/* $Id: vbva.c 118368 2017-10-13 15:48:13Z michael $ */ +/** @file + * VirtualBox X11 Additions graphics driver 2D acceleration functions + */ + +/* + * Copyright (C) 2006-2017 Oracle Corporation + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NON-INFRINGEMENT. IN NO EVENT SHALL + * THE COPYRIGHT HOLDERS, AUTHORS AND/OR ITS SUPPLIERS BE LIABLE FOR ANY CLAIM, + * DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR + * OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE + * USE OR OTHER DEALINGS IN THE SOFTWARE. + */ + +#if defined(IN_XF86_MODULE) && !defined(NO_ANSIC) +# include "xf86_ansic.h" +#endif +#include "compiler.h" + +#include "vboxvideo.h" + +#ifdef XORG_7X +# include <stdlib.h> +# include <string.h> +#endif + +/************************************************************************** +* Main functions * +**************************************************************************/ + +/** + * Callback function called by the X server to tell us about dirty + * rectangles in the video buffer. + * + * @param pScrn pointer to the information structure for the current + * screen + * @param iRects Number of dirty rectangles to update + * @param aRects Array of structures containing the coordinates of the + * rectangles + */ +void vbvxHandleDirtyRect(ScrnInfoPtr pScrn, int iRects, BoxPtr aRects) +{ + VBVACMDHDR cmdHdr; + VBOXPtr pVBox; + int i; + unsigned j; + + pVBox = pScrn->driverPrivate; + if (!pScrn->vtSema) + return; + + for (j = 0; j < pVBox->cScreens; ++j) + { + /* Just continue quietly if VBVA is not currently active. */ + struct VBVABUFFER *pVBVA = pVBox->pScreens[j].aVbvaCtx.pVBVA; + if ( !pVBVA + || !(pVBVA->hostFlags.u32HostEvents & VBVA_F_MODE_ENABLED)) + continue; + for (i = 0; i < iRects; ++i) + { + if ( aRects[i].x1 > pVBox->pScreens[j].aScreenLocation.x + + pVBox->pScreens[j].aScreenLocation.cx + || aRects[i].y1 > pVBox->pScreens[j].aScreenLocation.y + + pVBox->pScreens[j].aScreenLocation.cy + || aRects[i].x2 < pVBox->pScreens[j].aScreenLocation.x + || aRects[i].y2 < pVBox->pScreens[j].aScreenLocation.y) + continue; + cmdHdr.x = (int16_t)aRects[i].x1 - pVBox->pScreens[0].aScreenLocation.x; + cmdHdr.y = (int16_t)aRects[i].y1 - pVBox->pScreens[0].aScreenLocation.y; + cmdHdr.w = (uint16_t)(aRects[i].x2 - aRects[i].x1); + cmdHdr.h = (uint16_t)(aRects[i].y2 - aRects[i].y1); + +#if 0 + TRACE_LOG("display=%u, x=%d, y=%d, w=%d, h=%d\n", + j, cmdHdr.x, cmdHdr.y, cmdHdr.w, cmdHdr.h); +#endif + + if (VBoxVBVABufferBeginUpdate(&pVBox->pScreens[j].aVbvaCtx, + &pVBox->guestCtx)) + { + VBoxVBVAWrite(&pVBox->pScreens[j].aVbvaCtx, &pVBox->guestCtx, &cmdHdr, + sizeof(cmdHdr)); + VBoxVBVABufferEndUpdate(&pVBox->pScreens[j].aVbvaCtx); + } + } + } +} + +static DECLCALLBACK(void *) hgsmiEnvAlloc(void *pvEnv, HGSMISIZE cb) +{ + RT_NOREF(pvEnv); + return calloc(1, cb); +} + +static DECLCALLBACK(void) hgsmiEnvFree(void *pvEnv, void *pv) +{ + RT_NOREF(pvEnv); + free(pv); +} + +static HGSMIENV g_hgsmiEnv = +{ + NULL, + hgsmiEnvAlloc, + hgsmiEnvFree +}; + +/** + * Calculate the location in video RAM of and initialise the heap for guest to + * host messages. + */ +void vbvxSetUpHGSMIHeapInGuest(VBOXPtr pVBox, uint32_t cbVRAM) +{ + int rc; + uint32_t offVRAMBaseMapping, offGuestHeapMemory, cbGuestHeapMemory; + void *pvGuestHeapMemory; + + VBoxHGSMIGetBaseMappingInfo(cbVRAM, &offVRAMBaseMapping, NULL, &offGuestHeapMemory, &cbGuestHeapMemory, NULL); + pvGuestHeapMemory = ((uint8_t *)pVBox->base) + offVRAMBaseMapping + offGuestHeapMemory; + rc = VBoxHGSMISetupGuestContext(&pVBox->guestCtx, pvGuestHeapMemory, cbGuestHeapMemory, + offVRAMBaseMapping + offGuestHeapMemory, &g_hgsmiEnv); + AssertMsg(RT_SUCCESS(rc), ("Failed to set up the guest-to-host message buffer heap, rc=%d\n", rc)); + pVBox->cbView = offVRAMBaseMapping; +} + +/** Callback to fill in the view structures */ +static DECLCALLBACK(int) vboxFillViewInfo(void *pvVBox, struct VBVAINFOVIEW *pViews, uint32_t cViews) +{ + VBOXPtr pVBox = (VBOXPtr)pvVBox; + unsigned i; + for (i = 0; i < cViews; ++i) + { + pViews[i].u32ViewIndex = i; + pViews[i].u32ViewOffset = 0; + pViews[i].u32ViewSize = pVBox->cbView; + pViews[i].u32MaxScreenSize = pVBox->cbFBMax; + } + return VINF_SUCCESS; +} + +/** + * Initialise VirtualBox's accelerated video extensions. + * + * @returns TRUE on success, FALSE on failure + */ +static Bool vboxSetupVRAMVbva(VBOXPtr pVBox) +{ + int rc = VINF_SUCCESS; + unsigned i; + + pVBox->cbFBMax = pVBox->cbView; + for (i = 0; i < pVBox->cScreens; ++i) + { + pVBox->cbFBMax -= VBVA_MIN_BUFFER_SIZE; + pVBox->pScreens[i].aoffVBVABuffer = pVBox->cbFBMax; + TRACE_LOG("VBVA buffer offset for screen %u: 0x%lx\n", i, + (unsigned long) pVBox->cbFBMax); + VBoxVBVASetupBufferContext(&pVBox->pScreens[i].aVbvaCtx, + pVBox->pScreens[i].aoffVBVABuffer, + VBVA_MIN_BUFFER_SIZE); + } + TRACE_LOG("Maximum framebuffer size: %lu (0x%lx)\n", + (unsigned long) pVBox->cbFBMax, + (unsigned long) pVBox->cbFBMax); + rc = VBoxHGSMISendViewInfo(&pVBox->guestCtx, pVBox->cScreens, + vboxFillViewInfo, (void *)pVBox); + AssertMsg(RT_SUCCESS(rc), ("Failed to send the view information to the host, rc=%d\n", rc)); + return TRUE; +} + +static Bool haveHGSMIModeHintAndCursorReportingInterface(VBOXPtr pVBox) +{ + uint32_t fModeHintReporting, fCursorReporting; + + return RT_SUCCESS(VBoxQueryConfHGSMI(&pVBox->guestCtx, VBOX_VBVA_CONF32_MODE_HINT_REPORTING, &fModeHintReporting)) + && RT_SUCCESS(VBoxQueryConfHGSMI(&pVBox->guestCtx, VBOX_VBVA_CONF32_GUEST_CURSOR_REPORTING, &fCursorReporting)) + && fModeHintReporting == VINF_SUCCESS + && fCursorReporting == VINF_SUCCESS; +} + +static Bool hostHasScreenBlankingFlag(VBOXPtr pVBox) +{ + uint32_t fScreenFlags; + + return RT_SUCCESS(VBoxQueryConfHGSMI(&pVBox->guestCtx, VBOX_VBVA_CONF32_SCREEN_FLAGS, &fScreenFlags)) + && fScreenFlags & VBVA_SCREEN_F_BLANK; +} + +/** + * Inform VBox that we will supply it with dirty rectangle information + * and install the dirty rectangle handler. + * + * @returns TRUE for success, FALSE for failure + * @param pScrn Pointer to a structure describing the X screen in use + */ +Bool +vboxEnableVbva(ScrnInfoPtr pScrn) +{ + Bool rc = TRUE; + unsigned i; + VBOXPtr pVBox = pScrn->driverPrivate; + + TRACE_ENTRY(); + if (!vboxSetupVRAMVbva(pVBox)) + return FALSE; + for (i = 0; i < pVBox->cScreens; ++i) + { + struct VBVABUFFER *pVBVA; + + pVBVA = (struct VBVABUFFER *) ( ((uint8_t *)pVBox->base) + + pVBox->pScreens[i].aoffVBVABuffer); + if (!VBoxVBVAEnable(&pVBox->pScreens[i].aVbvaCtx, &pVBox->guestCtx, + pVBVA, i)) + rc = FALSE; + } + AssertMsg(rc, ("Failed to enable screen update reporting for at least one virtual monitor.\n")); + pVBox->fHaveHGSMIModeHints = haveHGSMIModeHintAndCursorReportingInterface(pVBox); + pVBox->fHostHasScreenBlankingFlag = hostHasScreenBlankingFlag(pVBox); + return rc; +} + +/** + * Inform VBox that we will stop supplying it with dirty rectangle + * information. This function is intended to be called when an X + * virtual terminal is disabled, or the X server is terminated. + * + * @returns TRUE for success, FALSE for failure + * @param pScrn Pointer to a structure describing the X screen in use + */ +void +vboxDisableVbva(ScrnInfoPtr pScrn) +{ + unsigned i; + VBOXPtr pVBox = pScrn->driverPrivate; + + TRACE_ENTRY(); + for (i = 0; i < pVBox->cScreens; ++i) + VBoxVBVADisable(&pVBox->pScreens[i].aVbvaCtx, &pVBox->guestCtx, i); +} diff --git a/src/version-generated.h b/src/version-generated.h new file mode 100644 index 0000000..b2a3279 --- /dev/null +++ b/src/version-generated.h @@ -0,0 +1,14 @@ +#ifndef ___version_generated_h___ +#define ___version_generated_h___ + +#define VBOX_VERSION_MAJOR 5 +#define VBOX_VERSION_MINOR 2 +#define VBOX_VERSION_BUILD 0 +#define VBOX_VERSION_PRERELEASE "RC1" +#define VBOX_VERSION_STRING_RAW "5.2.0" +#define VBOX_VERSION_STRING "5.2.0_RC1" +#define VBOX_API_VERSION_STRING "5_2" + +#define VBOX_PRIVATE_BUILD_DESC "Private build by michael" + +#endif |