diff options
Diffstat (limited to 'src/VBox/Additions/common/VBoxVideo')
| -rw-r--r-- | src/VBox/Additions/common/VBoxVideo/HGSMIBase.cpp | 579 | ||||
| -rw-r--r-- | src/VBox/Additions/common/VBoxVideo/Modesetting.cpp | 171 | ||||
| -rw-r--r-- | src/VBox/Additions/common/VBoxVideo/VBVABase.cpp | 390 |
3 files changed, 1140 insertions, 0 deletions
diff --git a/src/VBox/Additions/common/VBoxVideo/HGSMIBase.cpp b/src/VBox/Additions/common/VBoxVideo/HGSMIBase.cpp new file mode 100644 index 000000000..4ab42f26a --- /dev/null +++ b/src/VBox/Additions/common/VBoxVideo/HGSMIBase.cpp @@ -0,0 +1,579 @@ +/* $Id: HGSMIBase.cpp 35118 2010-12-15 10:48:29Z vboxsync $ */ +/** @file + * VirtualBox Video driver, common code - HGSMI initialisation and helper + * functions. + */ + +/* + * Copyright (C) 2006-2010 Oracle Corporation + * + * This file is part of VirtualBox Open Source Edition (OSE), as + * available from http://www.virtualbox.org. This file is free software; + * you can redistribute it and/or modify it under the terms of the GNU + * General Public License (GPL) as published by the Free Software + * Foundation, in version 2 as it comes in the "COPYING" file of the + * VirtualBox OSE distribution. VirtualBox OSE is distributed in the + * hope that it will be useful, but WITHOUT ANY WARRANTY of any kind. + */ + +#include <VBox/VBoxVideoGuest.h> +#include <VBox/VBoxVideo.h> +#include <VBox/VBoxGuest.h> +#include <VBox/Hardware/VBoxVideoVBE.h> +#include <VBox/VMMDev.h> + +#include <iprt/asm.h> +#include <iprt/log.h> + +#include <string.h> + +/** Send completion notification to the host for the command located at offset + * @a offt into the host command buffer. */ +static void HGSMINotifyHostCmdComplete(PHGSMIHOSTCOMMANDCONTEXT pCtx, HGSMIOFFSET offt) +{ + VBoxVideoCmnPortWriteUlong(pCtx->port, offt); +} + + +/** + * Inform the host that a command has been handled. + * + * @param pCtx the context containing the heap to be used + * @param pvMem pointer into the heap as mapped in @a pCtx to the command to + * be completed + */ +RTDECL(void) VBoxHGSMIHostCmdComplete(PHGSMIHOSTCOMMANDCONTEXT pCtx, + void *pvMem) +{ + HGSMIBUFFERHEADER *pHdr = HGSMIBufferHeaderFromData(pvMem); + HGSMIOFFSET offMem = HGSMIPointerToOffset(&pCtx->areaCtx, pHdr); + Assert(offMem != HGSMIOFFSET_VOID); + if(offMem != HGSMIOFFSET_VOID) + { + HGSMINotifyHostCmdComplete(pCtx, offMem); + } +} + + +/** Submit an incoming host command to the appropriate handler. */ +static void hgsmiHostCmdProcess(PHGSMIHOSTCOMMANDCONTEXT pCtx, + HGSMIOFFSET offBuffer) +{ + int rc = HGSMIBufferProcess(&pCtx->areaCtx, &pCtx->channels, offBuffer); + Assert(!RT_FAILURE(rc)); + if(RT_FAILURE(rc)) + { + /* failure means the command was not submitted to the handler for some reason + * it's our responsibility to notify its completion in this case */ + HGSMINotifyHostCmdComplete(pCtx, offBuffer); + } + /* if the cmd succeeded it's responsibility of the callback to complete it */ +} + +/** Get the next command from the host. */ +static HGSMIOFFSET hgsmiGetHostBuffer(PHGSMIHOSTCOMMANDCONTEXT pCtx) +{ + return VBoxVideoCmnPortReadUlong(pCtx->port); +} + + +/** Get and handle the next command from the host. */ +static void hgsmiHostCommandQueryProcess(PHGSMIHOSTCOMMANDCONTEXT pCtx) +{ + HGSMIOFFSET offset = hgsmiGetHostBuffer(pCtx); + AssertReturnVoid(offset != HGSMIOFFSET_VOID); + hgsmiHostCmdProcess(pCtx, offset); +} + + +/** Drain the host command queue. */ +RTDECL(void) VBoxHGSMIProcessHostQueue(PHGSMIHOSTCOMMANDCONTEXT pCtx) +{ + while (pCtx->pfHostFlags->u32HostFlags & HGSMIHOSTFLAGS_COMMANDS_PENDING) + { + if (!ASMAtomicCmpXchgBool(&pCtx->fHostCmdProcessing, true, false)) + return; + hgsmiHostCommandQueryProcess(pCtx); + ASMAtomicWriteBool(&pCtx->fHostCmdProcessing, false); + } +} + + +/** Detect whether HGSMI is supported by the host. */ +RTDECL(bool) VBoxHGSMIIsSupported(void) +{ + uint16_t DispiId; + + VBoxVideoCmnPortWriteUshort(VBE_DISPI_IOPORT_INDEX, VBE_DISPI_INDEX_ID); + VBoxVideoCmnPortWriteUshort(VBE_DISPI_IOPORT_DATA, VBE_DISPI_ID_HGSMI); + + DispiId = VBoxVideoCmnPortReadUshort(VBE_DISPI_IOPORT_DATA); + + return (DispiId == VBE_DISPI_ID_HGSMI); +} + + +/** + * 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 + */ +RTDECL(void *) VBoxHGSMIBufferAlloc(PHGSMIGUESTCOMMANDCONTEXT pCtx, + HGSMISIZE cbData, + uint8_t u8Ch, + uint16_t u16Op) +{ +#ifdef VBOX_WITH_WDDM + /* @todo: add synchronization */ +#endif + return HGSMIHeapAlloc (&pCtx->heapCtx, cbData, u8Ch, u16Op); +} + + +/** + * Free a descriptor allocated by @a VBoxHGSMIBufferAlloc. + * + * @param pCtx the context containing the heap used + * @param pvBuffer the pointer returned by @a VBoxHGSMIBufferAlloc + */ +RTDECL(void) VBoxHGSMIBufferFree(PHGSMIGUESTCOMMANDCONTEXT pCtx, + void *pvBuffer) +{ +#ifdef VBOX_WITH_WDDM + /* @todo: add synchronization */ +#endif + HGSMIHeapFree (&pCtx->heapCtx, pvBuffer); +} + + +/** + * Submit a command descriptor allocated by @a VBoxHGSMIBufferAlloc. + * + * @param pCtx the context containing the heap used + * @param pvBuffer the pointer returned by @a VBoxHGSMIBufferAlloc + */ +RTDECL(int) VBoxHGSMIBufferSubmit(PHGSMIGUESTCOMMANDCONTEXT pCtx, + void *pvBuffer) +{ + /* Initialize the buffer and get the offset for port IO. */ + HGSMIOFFSET offBuffer = HGSMIHeapBufferOffset (&pCtx->heapCtx, pvBuffer); + + Assert(offBuffer != HGSMIOFFSET_VOID); + if (offBuffer != HGSMIOFFSET_VOID) + { + /* Submit the buffer to the host. */ + VBoxVideoCmnPortWriteUlong(pCtx->port, offBuffer); + return VINF_SUCCESS; + } + + return VERR_INVALID_PARAMETER; +} + + +/** Inform the host of the location of the host flags in VRAM via an HGSMI + * command. */ +static int vboxHGSMIReportFlagsLocation(PHGSMIGUESTCOMMANDCONTEXT pCtx, + HGSMIOFFSET offLocation) +{ + HGSMIBUFFERLOCATION *p; + int rc = VINF_SUCCESS; + + /* Allocate the IO buffer. */ + p = (HGSMIBUFFERLOCATION *)HGSMIHeapAlloc(&pCtx->heapCtx, + sizeof(HGSMIBUFFERLOCATION), + HGSMI_CH_HGSMI, + HGSMI_CC_HOST_FLAGS_LOCATION); + if (p) + { + /* Prepare data to be sent to the host. */ + p->offLocation = offLocation; + p->cbLocation = sizeof(HGSMIHOSTFLAGS); + rc = VBoxHGSMIBufferSubmit(pCtx, p); + /* Free the IO buffer. */ + HGSMIHeapFree (&pCtx->heapCtx, p); + } + else + rc = VERR_NO_MEMORY; + return rc; +} + + +/** Notify the host of HGSMI-related guest capabilities via an HGSMI command. + */ +static int vboxHGSMISendCapsInfo(PHGSMIGUESTCOMMANDCONTEXT pCtx, + uint32_t fCaps) +{ + VBVACAPS *pCaps; + int rc = VINF_SUCCESS; + + /* Allocate the IO buffer. */ + pCaps = (VBVACAPS *)HGSMIHeapAlloc(&pCtx->heapCtx, + sizeof(VBVACAPS), HGSMI_CH_VBVA, + VBVA_INFO_CAPS); + + if (pCaps) + { + /* Prepare data to be sent to the host. */ + pCaps->rc = VERR_NOT_IMPLEMENTED; + pCaps->fCaps = fCaps; + rc = VBoxHGSMIBufferSubmit(pCtx, pCaps); + if (RT_SUCCESS(rc)) + { + AssertRC(pCaps->rc); + rc = pCaps->rc; + } + /* Free the IO buffer. */ + HGSMIHeapFree(&pCtx->heapCtx, pCaps); + } + else + rc = VERR_NO_MEMORY; + return rc; +} + + +/** Tell the host about the location of the area of VRAM set aside for the host + * heap. */ +static int vboxHGSMIReportHostArea(PHGSMIGUESTCOMMANDCONTEXT pCtx, + uint32_t u32AreaOffset, uint32_t u32AreaSize) +{ + VBVAINFOHEAP *p; + int rc = VINF_SUCCESS; + + /* Allocate the IO buffer. */ + p = (VBVAINFOHEAP *)HGSMIHeapAlloc(&pCtx->heapCtx, + sizeof (VBVAINFOHEAP), HGSMI_CH_VBVA, + VBVA_INFO_HEAP); + if (p) + { + /* Prepare data to be sent to the host. */ + p->u32HeapOffset = u32AreaOffset; + p->u32HeapSize = u32AreaSize; + rc = VBoxHGSMIBufferSubmit(pCtx, p); + /* Free the IO buffer. */ + HGSMIHeapFree(&pCtx->heapCtx, p); + } + else + rc = VERR_NO_MEMORY; + return 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 + */ +RTDECL(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); +} + + +/** + * 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 + */ +RTDECL(int) VBoxHGSMISetupGuestContext(PHGSMIGUESTCOMMANDCONTEXT pCtx, + void *pvGuestHeapMemory, + uint32_t cbGuestHeapMemory, + uint32_t offVRAMGuestHeapMemory) +{ + /** @todo should we be using a fixed ISA port value here? */ + pCtx->port = (RTIOPORT)VGA_PORT_HGSMI_GUEST; + return HGSMIHeapSetup(&pCtx->heapCtx, pvGuestHeapMemory, + cbGuestHeapMemory, offVRAMGuestHeapMemory, + false /*fOffsetBased*/); +} + + +/** + * Get the information needed to map the area used by the host to send back + * requests. + * + * @param pCtx the context containing the heap to use + * @param cbVRAM how much video RAM is allocated to the device + * @param offVRAMBaseMapping the offset of the basic communication structures + * into the guest's VRAM + * @param poffVRAMHostArea where to store the offset into VRAM of the host + * heap area + * @param pcbHostArea where to store the size of the host heap area + */ +RTDECL(void) VBoxHGSMIGetHostAreaMapping(PHGSMIGUESTCOMMANDCONTEXT pCtx, + uint32_t cbVRAM, + uint32_t offVRAMBaseMapping, + uint32_t *poffVRAMHostArea, + uint32_t *pcbHostArea) +{ + uint32_t offVRAMHostArea = offVRAMBaseMapping, cbHostArea = 0; + + AssertPtrReturnVoid(poffVRAMHostArea); + AssertPtrReturnVoid(pcbHostArea); + VBoxQueryConfHGSMI(pCtx, VBOX_VBVA_CONF32_HOST_HEAP_SIZE, &cbHostArea); + if (cbHostArea != 0) + { + uint32_t cbHostAreaMaxSize = cbVRAM / 4; + /** @todo what is the idea of this? */ + if (cbHostAreaMaxSize >= VBVA_ADAPTER_INFORMATION_SIZE) + { + cbHostAreaMaxSize -= VBVA_ADAPTER_INFORMATION_SIZE; + } + if (cbHostArea > cbHostAreaMaxSize) + { + cbHostArea = cbHostAreaMaxSize; + } + /* Round up to 4096 bytes. */ + cbHostArea = (cbHostArea + 0xFFF) & ~0xFFF; + offVRAMHostArea = offVRAMBaseMapping - cbHostArea; + } + + *pcbHostArea = cbHostArea; + *poffVRAMHostArea = offVRAMHostArea; + LogFunc(("offVRAMHostArea = 0x%08X, cbHostArea = 0x%08X\n", + offVRAMHostArea, cbHostArea)); +} + + +/** + * Initialise the host context structure. + * + * @param pCtx the context structure to initialise + * @param pvBaseMapping where the basic HGSMI structures are mapped at + * @param offHostFlags the offset of the host flags into the basic HGSMI + * structures + * @param pvHostAreaMapping where the area for the host heap is mapped at + * @param offVRAMHostArea offset of the host heap area into VRAM + * @param cbHostArea size in bytes of the host heap area + */ +RTDECL(void) VBoxHGSMISetupHostContext(PHGSMIHOSTCOMMANDCONTEXT pCtx, + void *pvBaseMapping, + uint32_t offHostFlags, + void *pvHostAreaMapping, + uint32_t offVRAMHostArea, + uint32_t cbHostArea) +{ + uint8_t *pu8HostFlags = ((uint8_t *)pvBaseMapping) + offHostFlags; + pCtx->pfHostFlags = (HGSMIHOSTFLAGS *)pu8HostFlags; + /** @todo should we really be using a fixed ISA port value here? */ + pCtx->port = (RTIOPORT)VGA_PORT_HGSMI_HOST; + HGSMIAreaInitialize(&pCtx->areaCtx, pvHostAreaMapping, cbHostArea, + offVRAMHostArea); +} + + +/** + * Tell the host about the ways it can use to communicate back to us via an + * HGSMI command + * + * @returns iprt status value + * @param pCtx the context containing the heap to use + * @param offVRAMFlagsLocation where we wish the host to place its flags + * relative to the start of the VRAM + * @param fCaps additions HGSMI capabilities the guest + * supports + * @param offVRAMHostArea offset into VRAM of the host heap area + * @param cbHostArea size in bytes of the host heap area + */ +RTDECL(int) VBoxHGSMISendHostCtxInfo(PHGSMIGUESTCOMMANDCONTEXT pCtx, + HGSMIOFFSET offVRAMFlagsLocation, + uint32_t fCaps, + uint32_t offVRAMHostArea, + uint32_t cbHostArea) +{ + Log(("VBoxVideo::vboxSetupAdapterInfo\n")); + + /* setup the flags first to ensure they are initialized by the time the + * host heap is ready */ + int rc = vboxHGSMIReportFlagsLocation(pCtx, offVRAMFlagsLocation); + AssertRC(rc); + if (RT_SUCCESS(rc) && fCaps) + { + /* Inform about caps */ + rc = vboxHGSMISendCapsInfo(pCtx, fCaps); + AssertRC(rc); + } + if (RT_SUCCESS (rc)) + { + /* Report the host heap location. */ + rc = vboxHGSMIReportHostArea(pCtx, offVRAMHostArea, cbHostArea); + AssertRC(rc); + } + Log(("VBoxVideo::vboxSetupAdapterInfo finished rc = %d\n", rc)); + return rc; +} + + +/** + * 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 + */ +RTDECL(int) VBoxQueryConfHGSMI(PHGSMIGUESTCOMMANDCONTEXT pCtx, + uint32_t u32Index, uint32_t *pulValue) +{ + int rc = VINF_SUCCESS; + VBVACONF32 *p; + LogFunc(("u32Index = %d\n", u32Index)); + + /* Allocate the IO buffer. */ + p = (VBVACONF32 *)HGSMIHeapAlloc(&pCtx->heapCtx, + sizeof(VBVACONF32), HGSMI_CH_VBVA, + VBVA_QUERY_CONF32); + if (p) + { + /* Prepare data to be sent to the host. */ + p->u32Index = u32Index; + p->u32Value = 0; + rc = VBoxHGSMIBufferSubmit(pCtx, p); + if (RT_SUCCESS(rc)) + { + *pulValue = p->u32Value; + LogFunc(("u32Value = %d\n", p->u32Value)); + } + /* Free the IO buffer. */ + HGSMIHeapFree(&pCtx->heapCtx, p); + } + else + rc = VERR_NO_MEMORY; + LogFunc(("rc = %d\n", rc)); + return rc; +} + + +/** + * Pass the host a new mouse pointer shape via an HGSMI command. + * + * @returns success or failure + * @todo why not return an iprt status code? + * @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 + */ +RTDECL(bool) 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 cbData = 0; + int rc = VINF_SUCCESS; + + if (fFlags & VBOX_MOUSE_POINTER_SHAPE) + { + /* Size of the pointer data: sizeof (AND mask) + sizeof (XOR_MASK) */ + cbData = ((((cWidth + 7) / 8) * cHeight + 3) & ~3) + + cWidth * 4 * cHeight; + /* If shape is supplied, then always create the pointer visible. + * See comments in 'vboxUpdatePointerShape' + */ + fFlags |= VBOX_MOUSE_POINTER_VISIBLE; + } + LogFlowFunc(("cbData %d, %dx%d\n", cbData, cWidth, cHeight)); + if (cbData > cbLength) + { + LogFunc(("calculated pointer data size is too big (%d bytes, limit %d)\n", + cbData, cbLength)); + return false; + } + /* Allocate the IO buffer. */ + p = (VBVAMOUSEPOINTERSHAPE *)HGSMIHeapAlloc(&pCtx->heapCtx, + sizeof(VBVAMOUSEPOINTERSHAPE) + + cbData, + HGSMI_CH_VBVA, + VBVA_MOUSE_POINTER_SHAPE); + if (p) + { + /* 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 (p->fu32Flags & VBOX_MOUSE_POINTER_SHAPE) + /* Copy the actual pointer data. */ + memcpy (p->au8Data, pPixels, cbData); + rc = VBoxHGSMIBufferSubmit(pCtx, p); + if (RT_SUCCESS(rc)) + rc = p->i32Result; + /* Free the IO buffer. */ + HGSMIHeapFree(&pCtx->heapCtx, p); + } + else + rc = VERR_NO_MEMORY; + LogFlowFunc(("rc %d\n", rc)); + return RT_SUCCESS(rc); +} + + +/** @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/VBox/Additions/common/VBoxVideo/Modesetting.cpp b/src/VBox/Additions/common/VBoxVideo/Modesetting.cpp new file mode 100644 index 000000000..a61fa79eb --- /dev/null +++ b/src/VBox/Additions/common/VBoxVideo/Modesetting.cpp @@ -0,0 +1,171 @@ +/* $Id: Modesetting.cpp 35150 2010-12-15 16:33:59Z vboxsync $ */ +/** @file + * VirtualBox Video driver, common code - HGSMI initialisation and helper + * functions. + */ + +/* + * Copyright (C) 2006-2010 Oracle Corporation + * + * This file is part of VirtualBox Open Source Edition (OSE), as + * available from http://www.virtualbox.org. This file is free software; + * you can redistribute it and/or modify it under the terms of the GNU + * General Public License (GPL) as published by the Free Software + * Foundation, in version 2 as it comes in the "COPYING" file of the + * VirtualBox OSE distribution. VirtualBox OSE is distributed in the + * hope that it will be useful, but WITHOUT ANY WARRANTY of any kind. + */ + +#include <VBox/VBoxVideoGuest.h> +#include <VBox/VBoxVideo.h> +#include <VBox/VBoxGuest.h> +#include <VBox/Hardware/VBoxVideoVBE.h> +#include <VBox/VMMDev.h> + +#include <iprt/asm.h> +#include <iprt/log.h> + +/** + * 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 + */ +RTDECL(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; +} + + +/** + * 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 + * @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 + */ +RTDECL(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 cBPP the colour depth of the mode + * @param cx the horizontal panning offset + * @param cy the vertical panning offset + */ +RTDECL(void) VBoxVideoSetModeRegisters(uint16_t cWidth, uint16_t cHeight, + uint16_t cVirtWidth, uint16_t cBPP, + uint16_t cx, uint16_t cy) +{ + /* set the mode characteristics */ + VBoxVideoCmnPortWriteUshort(VBE_DISPI_IOPORT_INDEX, VBE_DISPI_INDEX_XRES); + VBoxVideoCmnPortWriteUshort(VBE_DISPI_IOPORT_DATA, cWidth); + VBoxVideoCmnPortWriteUshort(VBE_DISPI_IOPORT_INDEX, VBE_DISPI_INDEX_YRES); + VBoxVideoCmnPortWriteUshort(VBE_DISPI_IOPORT_DATA, cHeight); + VBoxVideoCmnPortWriteUshort(VBE_DISPI_IOPORT_INDEX, VBE_DISPI_INDEX_VIRT_WIDTH); + VBoxVideoCmnPortWriteUshort(VBE_DISPI_IOPORT_DATA, cVirtWidth); + VBoxVideoCmnPortWriteUshort(VBE_DISPI_IOPORT_INDEX, VBE_DISPI_INDEX_BPP); + VBoxVideoCmnPortWriteUshort(VBE_DISPI_IOPORT_DATA, cBPP); + /* enable the mode */ + VBoxVideoCmnPortWriteUshort(VBE_DISPI_IOPORT_INDEX, VBE_DISPI_INDEX_ENABLE); + VBoxVideoCmnPortWriteUshort(VBE_DISPI_IOPORT_DATA, VBE_DISPI_ENABLED | VBE_DISPI_LFB_ENABLED); + /* Panning registers */ + VBoxVideoCmnPortWriteUshort(VBE_DISPI_IOPORT_INDEX, VBE_DISPI_INDEX_X_OFFSET); + VBoxVideoCmnPortWriteUshort(VBE_DISPI_IOPORT_DATA, cx); + VBoxVideoCmnPortWriteUshort(VBE_DISPI_IOPORT_INDEX, VBE_DISPI_INDEX_Y_OFFSET); + VBoxVideoCmnPortWriteUshort(VBE_DISPI_IOPORT_DATA, cy); + /** @todo read from the port to see if the mode switch was successful */ +} + + +/** + * 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 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 + */ +RTDECL(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); + } +} diff --git a/src/VBox/Additions/common/VBoxVideo/VBVABase.cpp b/src/VBox/Additions/common/VBoxVideo/VBVABase.cpp new file mode 100644 index 000000000..6f8398ae8 --- /dev/null +++ b/src/VBox/Additions/common/VBoxVideo/VBVABase.cpp @@ -0,0 +1,390 @@ +/* $Id: VBVABase.cpp 35292 2010-12-22 11:56:59Z vboxsync $ */ +/** @file + * VirtualBox Video driver, common code - VBVA initialisation and helper + * functions. + */ + +/* + * Copyright (C) 2006-2010 Oracle Corporation + * + * This file is part of VirtualBox Open Source Edition (OSE), as + * available from http://www.virtualbox.org. This file is free software; + * you can redistribute it and/or modify it under the terms of the GNU + * General Public License (GPL) as published by the Free Software + * Foundation, in version 2 as it comes in the "COPYING" file of the + * VirtualBox OSE distribution. VirtualBox OSE is distributed in the + * hope that it will be useful, but WITHOUT ANY WARRANTY of any kind. + */ + +#include <VBox/VBoxVideoGuest.h> +#include <VBox/VBoxVideo.h> +#include <VBox/err.h> +#include <VBox/log.h> +#include <iprt/assert.h> + +#include <string.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. + */ +RTDECL(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; +} + +RTDECL(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; +} + +RTDECL(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; +} + +RTDECL(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. + */ +RTDECL(bool) VBoxVBVAWrite(PVBVABUFFERCONTEXT pCtx, + PHGSMIGUESTCOMMANDCONTEXT pHGSMICtx, + const void *pv, uint32_t cb) +{ + return vboxHwBufferWrite (pCtx, pHGSMICtx, pv, cb); +} + +RTDECL(bool) VBoxVBVAOrderSupported(PVBVABUFFERCONTEXT pCtx, unsigned code) +{ + VBVABUFFER *pVBVA = pCtx->pVBVA; + + if (!pVBVA) + { + return false; + } + + if (pVBVA->hostFlags.u32SupportedOrders & (1 << code)) + { + return true; + } + + return false; +} + +RTDECL(void) VBoxVBVASetupBufferContext(PVBVABUFFERCONTEXT pCtx, + uint32_t offVRAMBuffer, + uint32_t cbBuffer) +{ + pCtx->offVRAMBuffer = offVRAMBuffer; + pCtx->cbBuffer = cbBuffer; +} |
