2009-04-13 02:05:42 -05:00
|
|
|
/**********************************************************
|
|
|
|
* Copyright 2008-2009 VMware, 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 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.
|
|
|
|
*
|
|
|
|
**********************************************************/
|
|
|
|
|
|
|
|
/*
|
|
|
|
* svga.c --
|
|
|
|
*
|
|
|
|
* This is a simple example driver for the VMware SVGA device.
|
|
|
|
* It handles initialization, register accesses, low-level
|
|
|
|
* command FIFO writes, and host/guest synchronization.
|
|
|
|
*/
|
|
|
|
|
|
|
|
#include "svga.h"
|
|
|
|
#include "pci.h"
|
|
|
|
#include "console_vga.h"
|
|
|
|
#include "io.h"
|
|
|
|
#include "intr.h"
|
|
|
|
#include "svga_reg.h"
|
|
|
|
|
|
|
|
SVGADevice gSVGA;
|
|
|
|
|
|
|
|
static void SVGAFIFOFull(void);
|
|
|
|
static void SVGAInterruptHandler(int vector);
|
|
|
|
|
|
|
|
|
|
|
|
/*
|
|
|
|
*-----------------------------------------------------------------------------
|
|
|
|
*
|
|
|
|
* SVGA_Init --
|
|
|
|
*
|
|
|
|
* Initialize the global SVGA device. This locates it on the PCI bus,
|
|
|
|
* negotiates device version, and maps the command FIFO and framebuffer
|
|
|
|
* memory.
|
|
|
|
*
|
|
|
|
* Intr_Init() must have already been called. If the SVGA device
|
|
|
|
* supports interrupts, this will initalize them.
|
|
|
|
*
|
|
|
|
* Does not switch video modes.
|
|
|
|
*
|
|
|
|
* Results:
|
|
|
|
* void.
|
|
|
|
*
|
|
|
|
* Side effects:
|
|
|
|
* Steals various IOspace and memory regions.
|
|
|
|
* In this example code they're constant addresses, but in reality
|
|
|
|
* you'll need to negotiate these with the operating system.
|
|
|
|
*
|
|
|
|
*-----------------------------------------------------------------------------
|
|
|
|
*/
|
|
|
|
|
|
|
|
void
|
|
|
|
SVGA_Init(void)
|
|
|
|
{
|
|
|
|
if (!PCI_FindDevice(PCI_VENDOR_ID_VMWARE, PCI_DEVICE_ID_VMWARE_SVGA2,
|
|
|
|
&gSVGA.pciAddr)) {
|
|
|
|
Console_Panic("No VMware SVGA device found.");
|
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
|
|
* Use the default base address for each memory region.
|
|
|
|
* We must map at least ioBase before using ReadReg/WriteReg.
|
|
|
|
*/
|
|
|
|
|
|
|
|
PCI_SetMemEnable(&gSVGA.pciAddr, TRUE);
|
|
|
|
gSVGA.ioBase = PCI_GetBARAddr(&gSVGA.pciAddr, 0);
|
|
|
|
gSVGA.fbMem = (void*) PCI_GetBARAddr(&gSVGA.pciAddr, 1);
|
|
|
|
gSVGA.fifoMem = (void*) PCI_GetBARAddr(&gSVGA.pciAddr, 2);
|
|
|
|
|
|
|
|
/*
|
|
|
|
* Version negotiation:
|
|
|
|
*
|
|
|
|
* 1. Write to SVGA_REG_ID the maximum ID supported by this driver.
|
|
|
|
* 2. Read from SVGA_REG_ID
|
|
|
|
* a. If we read back the same value, this ID is supported. We're done.
|
|
|
|
* b. If not, decrement the ID and repeat.
|
|
|
|
*/
|
|
|
|
|
|
|
|
gSVGA.deviceVersionId = SVGA_ID_2;
|
|
|
|
do {
|
|
|
|
SVGA_WriteReg(SVGA_REG_ID, gSVGA.deviceVersionId);
|
|
|
|
if (SVGA_ReadReg(SVGA_REG_ID) == gSVGA.deviceVersionId) {
|
|
|
|
break;
|
|
|
|
} else {
|
|
|
|
gSVGA.deviceVersionId--;
|
|
|
|
}
|
|
|
|
} while (gSVGA.deviceVersionId >= SVGA_ID_0);
|
|
|
|
|
|
|
|
if (gSVGA.deviceVersionId < SVGA_ID_0) {
|
|
|
|
Console_Panic("Error negotiating SVGA device version.");
|
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
|
|
* We must determine the FIFO and FB size after version
|
|
|
|
* negotiation, since the default version (SVGA_ID_0)
|
|
|
|
* does not support the FIFO buffer at all.
|
|
|
|
*/
|
|
|
|
|
|
|
|
gSVGA.fbSize = SVGA_ReadReg(SVGA_REG_FB_SIZE);
|
|
|
|
gSVGA.fifoSize = SVGA_ReadReg(SVGA_REG_MEM_SIZE);
|
|
|
|
|
|
|
|
/*
|
|
|
|
* Sanity-check the FIFO and framebuffer sizes.
|
|
|
|
* These are arbitrary values.
|
|
|
|
*/
|
|
|
|
|
|
|
|
if (gSVGA.fbSize < 0x100000) {
|
|
|
|
SVGA_Panic("FB size very small, probably incorrect.");
|
|
|
|
}
|
|
|
|
if (gSVGA.fifoSize < 0x20000) {
|
|
|
|
SVGA_Panic("FIFO size very small, probably incorrect.");
|
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
|
|
* If the device is new enough to support capability flags, get the
|
|
|
|
* capabilities register.
|
|
|
|
*/
|
|
|
|
|
|
|
|
if (gSVGA.deviceVersionId >= SVGA_ID_1) {
|
|
|
|
gSVGA.capabilities = SVGA_ReadReg(SVGA_REG_CAPABILITIES);
|
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
|
|
* Optional interrupt initialization.
|
|
|
|
*
|
|
|
|
* This uses the default IRQ that was assigned to our
|
|
|
|
* device by the BIOS.
|
|
|
|
*/
|
|
|
|
|
|
|
|
if (gSVGA.capabilities & SVGA_CAP_IRQMASK) {
|
|
|
|
uint8 irq = PCI_ConfigRead8(&gSVGA.pciAddr, offsetof(PCIConfigSpace, intrLine));
|
|
|
|
|
|
|
|
/* Start out with all SVGA IRQs masked */
|
|
|
|
SVGA_WriteReg(SVGA_REG_IRQMASK, 0);
|
|
|
|
|
|
|
|
/* Clear all pending IRQs stored by the device */
|
|
|
|
IO_Out32(gSVGA.ioBase + SVGA_IRQSTATUS_PORT, 0xFF);
|
|
|
|
|
|
|
|
/* Clear all pending IRQs stored by us */
|
|
|
|
SVGA_ClearIRQ();
|
|
|
|
|
|
|
|
/* Enable the IRQ */
|
|
|
|
Intr_SetHandler(IRQ_VECTOR(irq), SVGAInterruptHandler);
|
|
|
|
Intr_SetMask(irq, TRUE);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
/*
|
|
|
|
*-----------------------------------------------------------------------------
|
|
|
|
*
|
|
|
|
* SVGA_SetMode --
|
|
|
|
*
|
|
|
|
* This switches to SVGA video mode, and enables the command FIFO.
|
|
|
|
*
|
|
|
|
* Results:
|
|
|
|
* void.
|
|
|
|
*
|
|
|
|
* Side effects:
|
|
|
|
* Switches modes. Disables legacy video modes (VGA, VBE).
|
|
|
|
* Clears the command FIFO, and asks the host to start processing it.
|
|
|
|
*
|
|
|
|
*-----------------------------------------------------------------------------
|
|
|
|
*/
|
|
|
|
|
|
|
|
void
|
|
|
|
SVGA_SetMode(uint32 width, // IN
|
|
|
|
uint32 height, // IN
|
|
|
|
uint32 bpp) // IN
|
|
|
|
{
|
|
|
|
gSVGA.width = width;
|
|
|
|
gSVGA.height = height;
|
|
|
|
gSVGA.bpp = bpp;
|
|
|
|
|
|
|
|
SVGA_WriteReg(SVGA_REG_WIDTH, width);
|
|
|
|
SVGA_WriteReg(SVGA_REG_HEIGHT, height);
|
|
|
|
SVGA_WriteReg(SVGA_REG_BITS_PER_PIXEL, bpp);
|
|
|
|
SVGA_WriteReg(SVGA_REG_ENABLE, TRUE);
|
|
|
|
gSVGA.pitch = SVGA_ReadReg(SVGA_REG_BYTES_PER_LINE);
|
|
|
|
|
|
|
|
/*
|
|
|
|
* Initialize the command FIFO. The beginning of FIFO memory is
|
|
|
|
* used for an additional set of registers, the "FIFO registers".
|
|
|
|
* These are higher-performance memory mapped registers which
|
|
|
|
* happen to live in the same space as the FIFO. The driver is
|
|
|
|
* responsible for allocating space for these registers, according
|
|
|
|
* to the maximum number of registers supported by this driver
|
|
|
|
* release.
|
|
|
|
*/
|
|
|
|
|
|
|
|
gSVGA.fifoMem[SVGA_FIFO_MIN] = SVGA_FIFO_NUM_REGS * sizeof(uint32);
|
|
|
|
gSVGA.fifoMem[SVGA_FIFO_MAX] = gSVGA.fifoSize;
|
|
|
|
gSVGA.fifoMem[SVGA_FIFO_NEXT_CMD] = gSVGA.fifoMem[SVGA_FIFO_MIN];
|
|
|
|
gSVGA.fifoMem[SVGA_FIFO_STOP] = gSVGA.fifoMem[SVGA_FIFO_MIN];
|
|
|
|
|
|
|
|
/*
|
|
|
|
* Prep work for 3D version negotiation. See SVGA3D_Init for
|
|
|
|
* details, but we have to give the host our 3D protocol version
|
|
|
|
* before enabling the FIFO.
|
|
|
|
*/
|
|
|
|
|
|
|
|
if (SVGA_HasFIFOCap(SVGA_CAP_EXTENDED_FIFO) &&
|
|
|
|
SVGA_IsFIFORegValid(SVGA_FIFO_GUEST_3D_HWVERSION)) {
|
|
|
|
|
|
|
|
gSVGA.fifoMem[SVGA_FIFO_GUEST_3D_HWVERSION] = SVGA3D_HWVERSION_CURRENT;
|
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
|
|
* Enable the FIFO.
|
|
|
|
*/
|
|
|
|
|
|
|
|
SVGA_WriteReg(SVGA_REG_CONFIG_DONE, TRUE);
|
|
|
|
|
|
|
|
/*
|
|
|
|
* Now that the FIFO is initialized, we can do an IRQ sanity check.
|
|
|
|
* This makes sure that the VM's chipset and our own IRQ code
|
|
|
|
* works. Better to find out now if something's wrong, than to
|
|
|
|
* deadlock later.
|
|
|
|
*
|
|
|
|
* This inserts a FIFO fence, does a legacy sync to drain the FIFO,
|
|
|
|
* then ensures that we received all applicable interrupts.
|
|
|
|
*/
|
|
|
|
|
|
|
|
if (gSVGA.capabilities & SVGA_CAP_IRQMASK) {
|
|
|
|
|
|
|
|
SVGA_WriteReg(SVGA_REG_IRQMASK, SVGA_IRQFLAG_ANY_FENCE);
|
|
|
|
SVGA_ClearIRQ();
|
|
|
|
|
|
|
|
SVGA_InsertFence();
|
|
|
|
|
|
|
|
SVGA_WriteReg(SVGA_REG_SYNC, 1);
|
|
|
|
while (SVGA_ReadReg(SVGA_REG_BUSY) != FALSE);
|
|
|
|
|
|
|
|
SVGA_WriteReg(SVGA_REG_IRQMASK, 0);
|
|
|
|
|
|
|
|
/* Check whether the interrupt occurred without blocking. */
|
|
|
|
if ((gSVGA.irq.pending & SVGA_IRQFLAG_ANY_FENCE) == 0) {
|
|
|
|
SVGA_Panic("SVGA IRQ appears to be present but broken.");
|
|
|
|
}
|
|
|
|
|
|
|
|
SVGA_WaitForIRQ();
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
/*
|
|
|
|
*-----------------------------------------------------------------------------
|
|
|
|
*
|
|
|
|
* SVGA_Disable --
|
|
|
|
*
|
|
|
|
* Disable the SVGA portion of the video adapter, switching back
|
|
|
|
* to VGA mode.
|
|
|
|
*
|
|
|
|
* Results:
|
|
|
|
* void.
|
|
|
|
*
|
|
|
|
* Side effects:
|
|
|
|
* Switches modes. Disables the command FIFO, without flushing it.
|
|
|
|
* (Any commands still in the FIFO will be lost.)
|
|
|
|
*
|
|
|
|
*-----------------------------------------------------------------------------
|
|
|
|
*/
|
|
|
|
|
|
|
|
void
|
|
|
|
SVGA_Disable(void)
|
|
|
|
{
|
|
|
|
SVGA_WriteReg(SVGA_REG_ENABLE, FALSE);
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
/*
|
|
|
|
*-----------------------------------------------------------------------------
|
|
|
|
*
|
|
|
|
* SVGA_Panic --
|
|
|
|
*
|
|
|
|
* This is a Panic handler which works in SVGA mode. It disables SVGA,
|
|
|
|
* then executes the Console's panic handler.
|
|
|
|
*
|
|
|
|
* Results:
|
|
|
|
* Never returns.
|
|
|
|
*
|
|
|
|
* Side effects:
|
|
|
|
* Disables SVGA mode, initializes VGA text mode.
|
|
|
|
*
|
|
|
|
*-----------------------------------------------------------------------------
|
|
|
|
*/
|
|
|
|
|
|
|
|
void
|
|
|
|
SVGA_Panic(const char *msg) // IN
|
|
|
|
{
|
|
|
|
SVGA_Disable();
|
|
|
|
ConsoleVGA_Init();
|
|
|
|
Console_Panic(msg);
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
/*
|
|
|
|
*-----------------------------------------------------------------------------
|
|
|
|
*
|
|
|
|
* SVGA_DefaultFaultHandler --
|
|
|
|
*
|
|
|
|
* This is a default fault handler which works in SVGA mode. It
|
|
|
|
* disables SVGA, then executes the Console's default fault
|
|
|
|
* handler.
|
|
|
|
*
|
|
|
|
* Results:
|
|
|
|
* Never returns.
|
|
|
|
*
|
|
|
|
* Side effects:
|
|
|
|
* Disables SVGA mode, initializes VGA text mode.
|
|
|
|
*
|
|
|
|
*-----------------------------------------------------------------------------
|
|
|
|
*/
|
|
|
|
|
|
|
|
void
|
|
|
|
SVGA_DefaultFaultHandler(int vector) // IN
|
|
|
|
{
|
|
|
|
SVGA_Disable();
|
|
|
|
Console_UnhandledFault(vector);
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
/*
|
|
|
|
*-----------------------------------------------------------------------------
|
|
|
|
*
|
|
|
|
* SVGA_ReadReg --
|
|
|
|
*
|
|
|
|
* Read an SVGA device register (SVGA_REG_*).
|
|
|
|
* This must be called after the PCI device's IOspace has been mapped.
|
|
|
|
*
|
|
|
|
* Results:
|
|
|
|
* 32-bit register value.
|
|
|
|
*
|
|
|
|
* Side effects:
|
|
|
|
* Depends on the register value. Some special registers
|
|
|
|
* like SVGA_REG_BUSY have significant side-effects.
|
|
|
|
*
|
|
|
|
*-----------------------------------------------------------------------------
|
|
|
|
*/
|
|
|
|
|
|
|
|
uint32
|
|
|
|
SVGA_ReadReg(uint32 index) // IN
|
|
|
|
{
|
|
|
|
IO_Out32(gSVGA.ioBase + SVGA_INDEX_PORT, index);
|
|
|
|
return IO_In32(gSVGA.ioBase + SVGA_VALUE_PORT);
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
/*
|
|
|
|
*-----------------------------------------------------------------------------
|
|
|
|
*
|
|
|
|
* SVGA_WriteReg --
|
|
|
|
*
|
|
|
|
* Write an SVGA device register (SVGA_REG_*).
|
|
|
|
* This must be called after the PCI device's IOspace has been mapped.
|
|
|
|
*
|
|
|
|
* Results:
|
|
|
|
* void.
|
|
|
|
*
|
|
|
|
* Side effects:
|
|
|
|
* Depends on the register value.
|
|
|
|
*
|
|
|
|
*-----------------------------------------------------------------------------
|
|
|
|
*/
|
|
|
|
|
|
|
|
void
|
|
|
|
SVGA_WriteReg(uint32 index, // IN
|
|
|
|
uint32 value) // IN
|
|
|
|
{
|
|
|
|
IO_Out32(gSVGA.ioBase + SVGA_INDEX_PORT, index);
|
|
|
|
IO_Out32(gSVGA.ioBase + SVGA_VALUE_PORT, value);
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
/*
|
|
|
|
*-----------------------------------------------------------------------------
|
|
|
|
*
|
|
|
|
* SVGA_IsFIFORegValid --
|
|
|
|
*
|
|
|
|
* Check whether space has been allocated for a particular FIFO register.
|
|
|
|
*
|
|
|
|
* This isn't particularly useful for our simple examples, since the
|
|
|
|
* same binary is responsible for both allocating and using the FIFO,
|
|
|
|
* but a real driver may need to check this if the module which handles
|
|
|
|
* initialization and mode switches is separate from the module which
|
|
|
|
* actually writes to the FIFO.
|
|
|
|
*
|
|
|
|
* Results:
|
|
|
|
* TRUE if the register has been allocated, FALSE if the register
|
|
|
|
* does not exist.
|
|
|
|
*
|
|
|
|
* Side effects:
|
|
|
|
* None.
|
|
|
|
*
|
|
|
|
*-----------------------------------------------------------------------------
|
|
|
|
*/
|
|
|
|
|
|
|
|
Bool
|
|
|
|
SVGA_IsFIFORegValid(int reg)
|
|
|
|
{
|
|
|
|
return gSVGA.fifoMem[SVGA_FIFO_MIN] > (reg << 2);
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
/*
|
|
|
|
*-----------------------------------------------------------------------------
|
|
|
|
*
|
|
|
|
* SVGA_HasFIFOCap --
|
|
|
|
*
|
|
|
|
* Check whether the SVGA device has a particular FIFO capability bit.
|
|
|
|
*
|
|
|
|
* Results:
|
|
|
|
* TRUE if the capability is present, FALSE if it's absent.
|
|
|
|
*
|
|
|
|
* Side effects:
|
|
|
|
* None.
|
|
|
|
*
|
|
|
|
*-----------------------------------------------------------------------------
|
|
|
|
*/
|
|
|
|
|
|
|
|
Bool
|
|
|
|
SVGA_HasFIFOCap(int cap)
|
|
|
|
{
|
|
|
|
return (gSVGA.fifoMem[SVGA_FIFO_CAPABILITIES] & cap) != 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
/*
|
|
|
|
*-----------------------------------------------------------------------------
|
|
|
|
*
|
|
|
|
* SVGA_FIFOReserve --
|
|
|
|
*
|
|
|
|
* Begin writing a command to the FIFO buffer. There are several
|
|
|
|
* examples floating around which show how to write to the FIFO
|
|
|
|
* buffer, but this is the preferred method: write directly to
|
|
|
|
* FIFO memory in the common case, but if the command would not
|
|
|
|
* be contiguous, use a bounce buffer.
|
|
|
|
*
|
|
|
|
* This method is easy to use, and quite fast. The X.org driver
|
|
|
|
* does not yet use this method, but recent Windows drivers use
|
|
|
|
* it.
|
|
|
|
*
|
|
|
|
* The main principles here are:
|
|
|
|
*
|
|
|
|
* - There are multiple code paths. In the best case, we write
|
|
|
|
* directly to the FIFO. In the next-best case, we use a
|
|
|
|
* static bounce buffer. If you need to support arbitrarily
|
|
|
|
* large commands, you can have a worst case in which you use
|
|
|
|
* a dynamically sized bounce buffer.
|
|
|
|
*
|
|
|
|
* - We must tell the host that we're reserving FIFO
|
|
|
|
* space. This is important because the device doesn't
|
|
|
|
* guarantee it will preserve the contents of FIFO memory
|
|
|
|
* which hasn't been reserved. If we write to a totally
|
|
|
|
* unused portion of the FIFO and the VM is suspended, on
|
|
|
|
* resume that data will no longer exist.
|
|
|
|
*
|
|
|
|
* This function is not re-entrant. If your driver is
|
|
|
|
* multithreaded or may be used from multiple processes
|
|
|
|
* concurrently, you must make sure to serialize all FIFO
|
|
|
|
* commands.
|
|
|
|
*
|
|
|
|
* The caller must pair this command with SVGA_FIFOCommit or
|
|
|
|
* SVGA_FIFOCommitAll.
|
|
|
|
*
|
|
|
|
* Results:
|
|
|
|
* Returns a pointer to the location where the FIFO command can
|
|
|
|
* be written. There will be room for at least 'bytes' bytes of
|
|
|
|
* data.
|
|
|
|
*
|
|
|
|
* Side effects:
|
|
|
|
* Begins a FIFO command, reserves space in the FIFO.
|
|
|
|
* May block (in SVGAFIFOFull) if the FIFO is full.
|
|
|
|
*
|
|
|
|
*-----------------------------------------------------------------------------
|
|
|
|
*/
|
|
|
|
|
|
|
|
void *
|
|
|
|
SVGA_FIFOReserve(uint32 bytes) // IN
|
|
|
|
{
|
|
|
|
volatile uint32 *fifo = gSVGA.fifoMem;
|
|
|
|
uint32 max = fifo[SVGA_FIFO_MAX];
|
|
|
|
uint32 min = fifo[SVGA_FIFO_MIN];
|
|
|
|
uint32 nextCmd = fifo[SVGA_FIFO_NEXT_CMD];
|
|
|
|
Bool reserveable = SVGA_HasFIFOCap(SVGA_FIFO_CAP_RESERVE);
|
|
|
|
|
|
|
|
/*
|
|
|
|
* This example implementation uses only a statically allocated
|
|
|
|
* buffer. If you want to support arbitrarily large commands,
|
|
|
|
* dynamically allocate a buffer if and only if it's necessary.
|
|
|
|
*/
|
|
|
|
|
|
|
|
if (bytes > sizeof gSVGA.fifo.bounceBuffer ||
|
|
|
|
bytes > (max - min)) {
|
|
|
|
SVGA_Panic("FIFO command too large");
|
|
|
|
}
|
|
|
|
|
|
|
|
if (bytes % sizeof(uint32)) {
|
|
|
|
SVGA_Panic("FIFO command length not 32-bit aligned");
|
|
|
|
}
|
|
|
|
|
|
|
|
if (gSVGA.fifo.reservedSize != 0) {
|
|
|
|
SVGA_Panic("FIFOReserve before FIFOCommit");
|
|
|
|
}
|
|
|
|
|
|
|
|
gSVGA.fifo.reservedSize = bytes;
|
|
|
|
|
|
|
|
while (1) {
|
|
|
|
uint32 stop = fifo[SVGA_FIFO_STOP];
|
|
|
|
Bool reserveInPlace = FALSE;
|
|
|
|
Bool needBounce = FALSE;
|
|
|
|
|
|
|
|
/*
|
|
|
|
* Find a strategy for dealing with "bytes" of data:
|
|
|
|
* - reserve in place, if there's room and the FIFO supports it
|
|
|
|
* - reserve in bounce buffer, if there's room in FIFO but not
|
|
|
|
* contiguous or FIFO can't safely handle reservations
|
|
|
|
* - otherwise, sync the FIFO and try again.
|
|
|
|
*/
|
|
|
|
|
|
|
|
if (nextCmd >= stop) {
|
|
|
|
/* There is no valid FIFO data between nextCmd and max */
|
|
|
|
|
|
|
|
if (nextCmd + bytes < max ||
|
|
|
|
(nextCmd + bytes == max && stop > min)) {
|
|
|
|
/*
|
|
|
|
* Fastest path 1: There is already enough contiguous space
|
|
|
|
* between nextCmd and max (the end of the buffer).
|
|
|
|
*
|
|
|
|
* Note the edge case: If the "<" path succeeds, we can
|
|
|
|
* quickly return without performing any other tests. If
|
|
|
|
* we end up on the "==" path, we're writing exactly up to
|
|
|
|
* the top of the FIFO and we still need to make sure that
|
|
|
|
* there is at least one unused DWORD at the bottom, in
|
|
|
|
* order to be sure we don't fill the FIFO entirely.
|
|
|
|
*
|
|
|
|
* If the "==" test succeeds, but stop <= min (the FIFO
|
|
|
|
* would be completely full if we were to reserve this
|
|
|
|
* much space) we'll end up hitting the FIFOFull path below.
|
|
|
|
*/
|
|
|
|
reserveInPlace = TRUE;
|
|
|
|
} else if ((max - nextCmd) + (stop - min) <= bytes) {
|
|
|
|
/*
|
|
|
|
* We have to split the FIFO command into two pieces,
|
|
|
|
* but there still isn't enough total free space in
|
|
|
|
* the FIFO to store it.
|
|
|
|
*
|
|
|
|
* Note the "<=". We need to keep at least one DWORD
|
|
|
|
* of the FIFO free at all times, or we won't be able
|
|
|
|
* to tell the difference between full and empty.
|
|
|
|
*/
|
|
|
|
SVGAFIFOFull();
|
|
|
|
} else {
|
|
|
|
/*
|
|
|
|
* Data fits in FIFO but only if we split it.
|
|
|
|
* Need to bounce to guarantee contiguous buffer.
|
|
|
|
*/
|
|
|
|
needBounce = TRUE;
|
|
|
|
}
|
|
|
|
|
|
|
|
} else {
|
|
|
|
/* There is FIFO data between nextCmd and max */
|
|
|
|
|
|
|
|
if (nextCmd + bytes < stop) {
|
|
|
|
/*
|
|
|
|
* Fastest path 2: There is already enough contiguous space
|
|
|
|
* between nextCmd and stop.
|
|
|
|
*/
|
|
|
|
reserveInPlace = TRUE;
|
|
|
|
} else {
|
|
|
|
/*
|
|
|
|
* There isn't enough room between nextCmd and stop.
|
|
|
|
* The FIFO is too full to accept this command.
|
|
|
|
*/
|
|
|
|
SVGAFIFOFull();
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
|
|
* If we decided we can write directly to the FIFO, make sure
|
|
|
|
* the VMX can safely support this.
|
|
|
|
*/
|
|
|
|
if (reserveInPlace) {
|
|
|
|
if (reserveable || bytes <= sizeof(uint32)) {
|
|
|
|
gSVGA.fifo.usingBounceBuffer = FALSE;
|
|
|
|
if (reserveable) {
|
|
|
|
fifo[SVGA_FIFO_RESERVED] = bytes;
|
|
|
|
}
|
|
|
|
return nextCmd + (uint8*) fifo;
|
|
|
|
} else {
|
|
|
|
/*
|
|
|
|
* Need to bounce because we can't trust the VMX to safely
|
|
|
|
* handle uncommitted data in FIFO.
|
|
|
|
*/
|
|
|
|
needBounce = TRUE;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
|
|
* If we reach here, either we found a full FIFO, called
|
|
|
|
* SVGAFIFOFull to make more room, and want to try again, or we
|
|
|
|
* decided to use a bounce buffer instead.
|
|
|
|
*/
|
|
|
|
if (needBounce) {
|
|
|
|
gSVGA.fifo.usingBounceBuffer = TRUE;
|
|
|
|
return gSVGA.fifo.bounceBuffer;
|
|
|
|
}
|
|
|
|
} /* while (1) */
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
/*
|
|
|
|
*-----------------------------------------------------------------------------
|
|
|
|
*
|
|
|
|
* SVGA_FIFOCommit --
|
|
|
|
*
|
|
|
|
* Commit a block of FIFO data which was placed in the buffer
|
|
|
|
* returned by SVGA_FIFOReserve. Every Reserve must be paired
|
|
|
|
* with exactly one Commit, but the sizes don't have to match.
|
|
|
|
* The caller is free to commit less space than they
|
|
|
|
* reserved. This can be used if the command size isn't known in
|
|
|
|
* advance, but it is reasonable to make a worst-case estimate.
|
|
|
|
*
|
|
|
|
* The commit size does not have to match the size of a single
|
|
|
|
* FIFO command. This can be used to write a partial command, or
|
|
|
|
* to write multiple commands at once.
|
|
|
|
*
|
|
|
|
* Results:
|
|
|
|
* None.
|
|
|
|
*
|
|
|
|
* Side effects:
|
|
|
|
* None.
|
|
|
|
*
|
|
|
|
*-----------------------------------------------------------------------------
|
|
|
|
*/
|
|
|
|
|
|
|
|
void
|
|
|
|
SVGA_FIFOCommit(uint32 bytes) // IN
|
|
|
|
{
|
|
|
|
volatile uint32 *fifo = gSVGA.fifoMem;
|
|
|
|
uint32 nextCmd = fifo[SVGA_FIFO_NEXT_CMD];
|
|
|
|
uint32 max = fifo[SVGA_FIFO_MAX];
|
|
|
|
uint32 min = fifo[SVGA_FIFO_MIN];
|
|
|
|
Bool reserveable = SVGA_HasFIFOCap(SVGA_FIFO_CAP_RESERVE);
|
|
|
|
|
|
|
|
if (gSVGA.fifo.reservedSize == 0) {
|
|
|
|
SVGA_Panic("FIFOCommit before FIFOReserve");
|
|
|
|
}
|
|
|
|
gSVGA.fifo.reservedSize = 0;
|
|
|
|
|
|
|
|
if (gSVGA.fifo.usingBounceBuffer) {
|
|
|
|
/*
|
|
|
|
* Slow paths: copy out of a bounce buffer.
|
|
|
|
*/
|
|
|
|
uint8 *buffer = gSVGA.fifo.bounceBuffer;
|
|
|
|
|
|
|
|
if (reserveable) {
|
|
|
|
/*
|
|
|
|
* Slow path: bulk copy out of a bounce buffer in two chunks.
|
|
|
|
*
|
|
|
|
* Note that the second chunk may be zero-length if the reserved
|
|
|
|
* size was large enough to wrap around but the commit size was
|
|
|
|
* small enough that everything fit contiguously into the FIFO.
|
|
|
|
*
|
|
|
|
* Note also that we didn't need to tell the FIFO about the
|
|
|
|
* reservation in the bounce buffer, but we do need to tell it
|
|
|
|
* about the data we're bouncing from there into the FIFO.
|
|
|
|
*/
|
|
|
|
|
|
|
|
uint32 chunkSize = MIN(bytes, max - nextCmd);
|
|
|
|
fifo[SVGA_FIFO_RESERVED] = bytes;
|
|
|
|
memcpy(nextCmd + (uint8*) fifo, buffer, chunkSize);
|
|
|
|
memcpy(min + (uint8*) fifo, buffer + chunkSize, bytes - chunkSize);
|
|
|
|
|
|
|
|
} else {
|
|
|
|
/*
|
|
|
|
* Slowest path: copy one dword at a time, updating NEXT_CMD as
|
|
|
|
* we go, so that we bound how much data the guest has written
|
|
|
|
* and the host doesn't know to checkpoint.
|
|
|
|
*/
|
|
|
|
|
|
|
|
uint32 *dword = (uint32 *)buffer;
|
|
|
|
|
|
|
|
while (bytes > 0) {
|
|
|
|
fifo[nextCmd / sizeof *dword] = *dword++;
|
|
|
|
nextCmd += sizeof *dword;
|
|
|
|
if (nextCmd == max) {
|
|
|
|
nextCmd = min;
|
|
|
|
}
|
|
|
|
fifo[SVGA_FIFO_NEXT_CMD] = nextCmd;
|
|
|
|
bytes -= sizeof *dword;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
|
|
* Atomically update NEXT_CMD, if we didn't already
|
|
|
|
*/
|
|
|
|
if (!gSVGA.fifo.usingBounceBuffer || reserveable) {
|
|
|
|
nextCmd += bytes;
|
|
|
|
if (nextCmd >= max) {
|
|
|
|
nextCmd -= max - min;
|
|
|
|
}
|
|
|
|
fifo[SVGA_FIFO_NEXT_CMD] = nextCmd;
|
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
|
|
* Clear the reservation in the FIFO.
|
|
|
|
*/
|
|
|
|
if (reserveable) {
|
|
|
|
fifo[SVGA_FIFO_RESERVED] = 0;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
/*
|
|
|
|
*-----------------------------------------------------------------------------
|
|
|
|
*
|
|
|
|
* SVGA_FIFOCommitAll --
|
|
|
|
*
|
|
|
|
* This is a convenience wrapper for SVGA_FIFOCommit(), which
|
|
|
|
* always commits the last reserved block in its entirety.
|
|
|
|
*
|
|
|
|
* Results:
|
|
|
|
* None.
|
|
|
|
*
|
|
|
|
* Side effects:
|
|
|
|
* SVGA_FIFOCommit.
|
|
|
|
*
|
|
|
|
*-----------------------------------------------------------------------------
|
|
|
|
*/
|
|
|
|
|
|
|
|
void
|
|
|
|
SVGA_FIFOCommitAll(void)
|
|
|
|
{
|
|
|
|
SVGA_FIFOCommit(gSVGA.fifo.reservedSize);
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
/*
|
|
|
|
*-----------------------------------------------------------------------------
|
|
|
|
*
|
|
|
|
* SVGA_FIFOReserveCmd --
|
|
|
|
*
|
|
|
|
* This is a convenience wrapper around SVGA_FIFOReserve, which
|
|
|
|
* prefixes the reserved memory block with a uint32 that
|
|
|
|
* indicates the command type.
|
|
|
|
*
|
|
|
|
* Results:
|
|
|
|
* Always returns a pointer to 'bytes' bytes of reserved space in the FIFO.
|
|
|
|
*
|
|
|
|
* Side effects:
|
|
|
|
* Begins a FIFO command, reserves space in the FIFO. Writes a
|
|
|
|
* 1-word header into the FIFO. May block (in SVGAFIFOFull) if
|
|
|
|
* the FIFO is full.
|
|
|
|
*
|
|
|
|
*-----------------------------------------------------------------------------
|
|
|
|
*/
|
|
|
|
|
|
|
|
void *
|
|
|
|
SVGA_FIFOReserveCmd(uint32 type, // IN
|
|
|
|
uint32 bytes) // IN
|
|
|
|
{
|
|
|
|
uint32 *cmd = SVGA_FIFOReserve(bytes + sizeof type);
|
|
|
|
cmd[0] = type;
|
|
|
|
return cmd + 1;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
/*
|
|
|
|
*-----------------------------------------------------------------------------
|
|
|
|
*
|
|
|
|
* SVGA_FIFOReserveEscape --
|
|
|
|
*
|
|
|
|
* This is a convenience wrapper around SVGA_FIFOReserve, which
|
|
|
|
* prefixes the reserved memory block with an ESCAPE command header.
|
|
|
|
*
|
|
|
|
* ESCAPE commands are a way of encoding extensible and
|
|
|
|
* variable-length packets within the basic FIFO protocol
|
|
|
|
* itself. ESCAPEs are used for some SVGA device functionality,
|
|
|
|
* like video overlays, for VMware's internal debugging tools,
|
|
|
|
* and for communicating with third party code that can load into
|
|
|
|
* the SVGA device.
|
|
|
|
*
|
|
|
|
* Results:
|
|
|
|
* Always returns a pointer to 'bytes' bytes of reserved space in the FIFO.
|
|
|
|
*
|
|
|
|
* Side effects:
|
|
|
|
* Begins a FIFO command, reserves space in the FIFO. Writes a
|
|
|
|
* 3-word header into the FIFO. May block (in SVGAFIFOFull) if
|
|
|
|
* the FIFO is full.
|
|
|
|
*
|
|
|
|
*-----------------------------------------------------------------------------
|
|
|
|
*/
|
|
|
|
|
|
|
|
void *
|
|
|
|
SVGA_FIFOReserveEscape(uint32 nsid, // IN
|
|
|
|
uint32 bytes) // IN
|
|
|
|
{
|
|
|
|
uint32 paddedBytes = (bytes + 3) & ~3UL;
|
|
|
|
struct {
|
|
|
|
uint32 cmd;
|
|
|
|
uint32 nsid;
|
|
|
|
uint32 size;
|
|
|
|
} __attribute__ ((__packed__)) *header = SVGA_FIFOReserve(paddedBytes
|
|
|
|
+ sizeof *header);
|
|
|
|
|
|
|
|
header->cmd = SVGA_CMD_ESCAPE;
|
|
|
|
header->nsid = nsid;
|
|
|
|
header->size = bytes;
|
|
|
|
|
|
|
|
return header + 1;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
/*
|
|
|
|
*-----------------------------------------------------------------------------
|
|
|
|
*
|
|
|
|
* SVGAFIFOFull --
|
|
|
|
*
|
|
|
|
* This function is called repeatedly as long as the FIFO has too
|
|
|
|
* little free space for us to continue.
|
|
|
|
*
|
|
|
|
* The simplest implementation of this function is a no-op. This
|
|
|
|
* will just burn guest CPU until space is available. (That's a
|
|
|
|
* bad idea, since the host probably needs that CPU in order to
|
|
|
|
* make progress on emptying the FIFO.)
|
|
|
|
*
|
|
|
|
* A better implementation would sleep until a FIFO progress
|
|
|
|
* interrupt occurs. Depending on the OS you're writing drivers
|
|
|
|
* for, this may deschedule the calling task or it may simply put
|
|
|
|
* the CPU to sleep.
|
|
|
|
*
|
|
|
|
* Results:
|
|
|
|
* None.
|
|
|
|
*
|
|
|
|
* Side effects:
|
|
|
|
* None.
|
|
|
|
*
|
|
|
|
*-----------------------------------------------------------------------------
|
|
|
|
*/
|
|
|
|
|
|
|
|
void
|
|
|
|
SVGAFIFOFull(void)
|
|
|
|
{
|
|
|
|
if (SVGA_IsFIFORegValid(SVGA_FIFO_FENCE_GOAL) &&
|
|
|
|
(gSVGA.capabilities & SVGA_CAP_IRQMASK)) {
|
|
|
|
|
|
|
|
/*
|
|
|
|
* On hosts which support interrupts, we can sleep until the
|
|
|
|
* FIFO_PROGRESS interrupt occurs. This is the most efficient
|
|
|
|
* thing to do when the FIFO fills up.
|
|
|
|
*
|
|
|
|
* As with the IRQ-based SVGA_SyncToFence(), this will only work
|
|
|
|
* on Workstation 6.5 virtual machines and later.
|
|
|
|
*/
|
|
|
|
|
|
|
|
SVGA_WriteReg(SVGA_REG_IRQMASK, SVGA_IRQFLAG_FIFO_PROGRESS);
|
|
|
|
SVGA_ClearIRQ();
|
|
|
|
SVGA_RingDoorbell();
|
|
|
|
SVGA_WaitForIRQ();
|
|
|
|
SVGA_WriteReg(SVGA_REG_IRQMASK, 0);
|
|
|
|
|
|
|
|
} else {
|
|
|
|
|
|
|
|
/*
|
|
|
|
* Fallback implementation: Perform one iteration of the
|
|
|
|
* legacy-style sync. This synchronously processes FIFO commands
|
|
|
|
* for an arbitrary amount of time, then returns control back to
|
|
|
|
* the guest CPU.
|
|
|
|
*/
|
|
|
|
|
|
|
|
SVGA_WriteReg(SVGA_REG_SYNC, 1);
|
|
|
|
SVGA_ReadReg(SVGA_REG_BUSY);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
/*
|
|
|
|
*-----------------------------------------------------------------------------
|
|
|
|
*
|
|
|
|
* SVGA_InsertFence --
|
|
|
|
*
|
|
|
|
* Write a new fence value to the FIFO.
|
|
|
|
*
|
|
|
|
* Fences are the basis of the SVGA device's synchronization
|
|
|
|
* model. A fence is a marker inserted into the FIFO by the
|
|
|
|
* guest. The host processes all FIFO commands in order. Once the
|
|
|
|
* fence is reached, the host does two things:
|
|
|
|
*
|
|
|
|
* - The fence value is written to the SVGA_FIFO_FENCE register.
|
|
|
|
* - Optionally, an interrupt is raised.
|
|
|
|
*
|
|
|
|
* There are multiple ways to use fences for synchronization. See
|
|
|
|
* SVGA_SyncToFence and SVGA_HasFencePassed.
|
|
|
|
*
|
|
|
|
* Results:
|
|
|
|
*
|
|
|
|
* Returns the value of the fence we inserted. Fence values
|
|
|
|
* increment, wrapping around at 32 bits. Fence value zero is
|
|
|
|
* reserved to mean "no fence". This function never returns zero.
|
|
|
|
*
|
|
|
|
* Certain very old versions of the VMware SVGA device do not
|
|
|
|
* support fences. On these devices, we always return 1. On these
|
|
|
|
* devices, SyncToFence will always do a full Sync and
|
|
|
|
* SyncToFence will always return FALSE, so the actual fence
|
|
|
|
* value we use is unimportant. Code written to use fences will
|
|
|
|
* run inefficiently, but it will still be correct.
|
|
|
|
*
|
|
|
|
* Side effects:
|
|
|
|
* Writes to the FIFO. Increments our nextFence.
|
|
|
|
*
|
|
|
|
*-----------------------------------------------------------------------------
|
|
|
|
*/
|
|
|
|
|
|
|
|
uint32
|
|
|
|
SVGA_InsertFence(void)
|
|
|
|
{
|
|
|
|
uint32 fence;
|
|
|
|
|
|
|
|
struct {
|
|
|
|
uint32 id;
|
|
|
|
uint32 fence;
|
|
|
|
} __attribute__ ((__packed__)) *cmd;
|
|
|
|
|
|
|
|
if (!SVGA_HasFIFOCap(SVGA_FIFO_CAP_FENCE)) {
|
|
|
|
return 1;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (gSVGA.fifo.nextFence == 0) {
|
|
|
|
gSVGA.fifo.nextFence = 1;
|
|
|
|
}
|
|
|
|
fence = gSVGA.fifo.nextFence++;
|
|
|
|
|
|
|
|
cmd = SVGA_FIFOReserve(sizeof *cmd);
|
|
|
|
cmd->id = SVGA_CMD_FENCE;
|
|
|
|
cmd->fence = fence;
|
|
|
|
SVGA_FIFOCommitAll();
|
|
|
|
|
|
|
|
return fence;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
/*
|
|
|
|
*-----------------------------------------------------------------------------
|
|
|
|
*
|
|
|
|
* SVGA_SyncToFence --
|
|
|
|
*
|
|
|
|
* Sleep until the SVGA device has processed all FIFO commands
|
|
|
|
* prior to the insertion point of the specified fence.
|
|
|
|
*
|
|
|
|
* This is the most important way to maintain synchronization
|
|
|
|
* between the driver and the SVGA3D device itself. It can be
|
|
|
|
* used to provide flow control between the host and the guest,
|
|
|
|
* or to ensure that DMA operations have completed before reusing
|
|
|
|
* guest memory.
|
|
|
|
*
|
|
|
|
* If the provided fence is zero or it has already passed,
|
|
|
|
* this is a no-op.
|
|
|
|
*
|
|
|
|
* If the SVGA device and virtual machine hardware version are
|
|
|
|
* both new enough (Workstation 6.5 or later), this will use an
|
|
|
|
* efficient interrupt-driven mechanism to sleep until just after
|
|
|
|
* the host processes the fence.
|
|
|
|
*
|
|
|
|
* If not, this will use a less efficient synchronization
|
|
|
|
* mechanism which may require the host to process significantly
|
|
|
|
* more of the FIFO than is necessary.
|
|
|
|
*
|
|
|
|
* Results:
|
|
|
|
* void.
|
|
|
|
*
|
|
|
|
* Side effects:
|
|
|
|
* None.
|
|
|
|
*
|
|
|
|
*-----------------------------------------------------------------------------
|
|
|
|
*/
|
|
|
|
|
|
|
|
void
|
|
|
|
SVGA_SyncToFence(uint32 fence) // IN
|
|
|
|
{
|
|
|
|
if (!fence) {
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (!SVGA_HasFIFOCap(SVGA_FIFO_CAP_FENCE)) {
|
|
|
|
/*
|
|
|
|
* Fall back on the legacy sync if the host does not support
|
|
|
|
* fences. This is the old sync mechanism that has been
|
|
|
|
* supported in the SVGA device pretty much since the dawn of
|
|
|
|
* time: write to the SYNC register, then read from BUSY until
|
|
|
|
* it's nonzero. This will drain the entire FIFO.
|
|
|
|
*
|
|
|
|
* The parameter we write to SVGA_REG_SYNC is an arbitrary
|
|
|
|
* nonzero value which can be used for debugging, but which is
|
|
|
|
* ignored by release builds of VMware products.
|
|
|
|
*/
|
|
|
|
|
|
|
|
SVGA_WriteReg(SVGA_REG_SYNC, 1);
|
|
|
|
while (SVGA_ReadReg(SVGA_REG_BUSY) != FALSE);
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (SVGA_HasFencePassed(fence)) {
|
|
|
|
/*
|
|
|
|
* Nothing to do
|
|
|
|
*/
|
|
|
|
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (SVGA_IsFIFORegValid(SVGA_FIFO_FENCE_GOAL) &&
|
|
|
|
(gSVGA.capabilities & SVGA_CAP_IRQMASK)) {
|
|
|
|
|
|
|
|
/*
|
|
|
|
* On hosts which support interrupts and which support the
|
|
|
|
* FENCE_GOAL interrupt, we can use our preferred
|
|
|
|
* synchronization mechanism.
|
|
|
|
*
|
|
|
|
* This provides low latency notification both from guest to
|
|
|
|
* host and from host to guest, and it doesn't block the guest
|
|
|
|
* while we're waiting.
|
|
|
|
*
|
|
|
|
* This will only work on Workstation 6.5 virtual machines
|
|
|
|
* or later. Older virtual machines did not allocate an IRQ
|
|
|
|
* for the SVGA device, and the IRQMASK capability will be
|
|
|
|
* unset.
|
|
|
|
*/
|
|
|
|
|
|
|
|
/*
|
|
|
|
* Set the fence goal. This asks the host to send an interrupt
|
|
|
|
* when this specific fence has been reached.
|
|
|
|
*/
|
|
|
|
|
|
|
|
gSVGA.fifoMem[SVGA_FIFO_FENCE_GOAL] = fence;
|
|
|
|
SVGA_WriteReg(SVGA_REG_IRQMASK, SVGA_IRQFLAG_FENCE_GOAL);
|
|
|
|
|
|
|
|
SVGA_ClearIRQ();
|
|
|
|
|
|
|
|
/*
|
|
|
|
* Must check again, in case we reached the fence between the
|
|
|
|
* first HasFencePassed and when we set up the IRQ.
|
|
|
|
*
|
|
|
|
* As a small performance optimization, we check yet again after
|
|
|
|
* RingDoorbell, since there's a chance that RingDoorbell will
|
|
|
|
* deschedule this VM and process some SVGA FIFO commands in a
|
|
|
|
* way that appears synchronous from the VM's point of view.
|
|
|
|
*/
|
|
|
|
|
|
|
|
if (!SVGA_HasFencePassed(fence)) {
|
|
|
|
/*
|
|
|
|
* We're about to go to sleep. Make sure the host is awake.
|
|
|
|
*/
|
|
|
|
SVGA_RingDoorbell();
|
|
|
|
|
|
|
|
if (!SVGA_HasFencePassed(fence)) {
|
|
|
|
SVGA_WaitForIRQ();
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
SVGA_WriteReg(SVGA_REG_IRQMASK, 0);
|
|
|
|
|
|
|
|
} else {
|
|
|
|
/*
|
|
|
|
* Sync-to-fence mechanism for older hosts. Wake up the host,
|
|
|
|
* and spin on BUSY until we've reached the fence. This
|
|
|
|
* processes FIFO commands synchronously, blocking the VM's
|
|
|
|
* execution entirely until it's done.
|
|
|
|
*/
|
|
|
|
|
|
|
|
Bool busy = TRUE;
|
|
|
|
|
|
|
|
SVGA_WriteReg(SVGA_REG_SYNC, 1);
|
|
|
|
|
|
|
|
while (!SVGA_HasFencePassed(fence) && busy) {
|
|
|
|
busy = (SVGA_ReadReg(SVGA_REG_BUSY) != 0);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
if (!SVGA_HasFencePassed(fence)) {
|
|
|
|
/*
|
|
|
|
* This shouldn't happen. If it does, there might be a bug in
|
|
|
|
* the SVGA device.
|
|
|
|
*/
|
|
|
|
SVGA_Panic("SyncToFence failed!");
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
/*
|
|
|
|
*-----------------------------------------------------------------------------
|
|
|
|
*
|
|
|
|
* SVGA_HasFencePassed --
|
|
|
|
*
|
|
|
|
* Test whether the host has processed all FIFO commands prior to
|
|
|
|
* the insertion point of the specified fence.
|
|
|
|
*
|
|
|
|
* This function tolerates fence wrap-around, but it will return
|
|
|
|
* the wrong result if 'fence' is more than 2^31 fences old. It
|
|
|
|
* is recommended that callers don't allow fence values to
|
|
|
|
* persist indefinitely. Once we notice that a fence has been
|
|
|
|
* passed, that fence variable should be set to zero so we don't
|
|
|
|
* test it in the future.
|
|
|
|
*
|
|
|
|
* Results:
|
|
|
|
* TRUE if the fence has been passed,
|
|
|
|
* TRUE if fence==0 (no fence),
|
|
|
|
* FALSE if the fence has not been passed,
|
|
|
|
* FALSE if the SVGA device does not support fences.
|
|
|
|
*
|
|
|
|
* Side effects:
|
|
|
|
* None.
|
|
|
|
*
|
|
|
|
*-----------------------------------------------------------------------------
|
|
|
|
*/
|
|
|
|
|
|
|
|
Bool
|
|
|
|
SVGA_HasFencePassed(uint32 fence) // IN
|
|
|
|
{
|
|
|
|
if (!fence) {
|
|
|
|
return TRUE;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (!SVGA_HasFIFOCap(SVGA_FIFO_CAP_FENCE)) {
|
|
|
|
return FALSE;
|
|
|
|
}
|
|
|
|
|
|
|
|
return ((int32)(gSVGA.fifoMem[SVGA_FIFO_FENCE] - fence)) >= 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
/*
|
|
|
|
*-----------------------------------------------------------------------------
|
|
|
|
*
|
|
|
|
* SVGA_RingDoorbell --
|
|
|
|
*
|
|
|
|
* FIFO fences are fundamentally a host-to-guest notification
|
|
|
|
* mechanism. This is the opposite: we can explicitly wake up
|
|
|
|
* the host when we know there is work for it to do.
|
|
|
|
*
|
|
|
|
* Background: The host processes the SVGA command FIFO in a
|
|
|
|
* separate thread which runs asynchronously with the virtual
|
|
|
|
* machine's CPU and other I/O devices. When the SVGA device is
|
|
|
|
* idle, this thread is sleeping. It periodically wakes up to
|
|
|
|
* poll for new commands. This polling must occur for various
|
|
|
|
* reasons, but it's mostly related to the historical way in
|
|
|
|
* which the SVGA device processes 2D updates.
|
|
|
|
*
|
|
|
|
* This polling introduces significant latency between when the
|
|
|
|
* first new command is placed in an empty FIFO, and when the
|
|
|
|
* host begins processing it. Normally this isn't a huge problem
|
|
|
|
* since the host and guest run fairly asynchronously, but in
|
|
|
|
* a synchronization-heavy workload this can be a bottleneck.
|
|
|
|
*
|
|
|
|
* For example, imagine an application with a worst-case
|
|
|
|
* synchronization bottleneck: The guest enqueues a single FIFO
|
|
|
|
* command, then waits for that command to finish using
|
|
|
|
* SyncToFence, then the guest spends a little time doing
|
|
|
|
* CPU-intensive processing before the cycle repeats. The
|
|
|
|
* workload may be latency-bound if the host-to-guest or
|
|
|
|
* guest-to-host notifications ever block.
|
|
|
|
*
|
|
|
|
* One solution would be for the guest to explicitly wake up the
|
|
|
|
* SVGA3D device any time a command is enqueued. This would solve
|
|
|
|
* the latency bottleneck above, but it would be inefficient on
|
|
|
|
* single-CPU host machines. One could easily imagine a situation
|
|
|
|
* in which we wake up the host after enqueueing one FIFO
|
|
|
|
* command, the physical CPU context switches to the SVGA
|
|
|
|
* device's thread, the single command is processed, then we
|
|
|
|
* context switch back to running guest code.
|
|
|
|
*
|
|
|
|
* Our recommended solution is to wake up the host only either:
|
|
|
|
*
|
|
|
|
* - After a "significant" amount of work has been enqueued into
|
|
|
|
* the FIFO. For example, at least one 3D drawing command.
|
|
|
|
*
|
|
|
|
* - After a complete frame has been rendered.
|
|
|
|
*
|
|
|
|
* - Just before the guest sleeps for any reason.
|
|
|
|
*
|
|
|
|
* This function implements the above guest wakeups. It uses the
|
|
|
|
* SVGA_FIFO_BUSY register to quickly assess whether the SVGA
|
|
|
|
* device may be idle. If so, it asynchronously wakes up the host
|
|
|
|
* by writing to SVGA_REG_SYNC.
|
|
|
|
*
|
|
|
|
* Results:
|
|
|
|
* None.
|
|
|
|
*
|
|
|
|
* Side effects:
|
|
|
|
* May wake up the SVGA3D device.
|
|
|
|
*
|
|
|
|
*-----------------------------------------------------------------------------
|
|
|
|
*/
|
|
|
|
|
|
|
|
void
|
|
|
|
SVGA_RingDoorbell(void)
|
|
|
|
{
|
|
|
|
if (SVGA_IsFIFORegValid(SVGA_FIFO_BUSY) &&
|
|
|
|
gSVGA.fifoMem[SVGA_FIFO_BUSY] == FALSE) {
|
|
|
|
|
|
|
|
/* Remember that we already rang the doorbell. */
|
|
|
|
gSVGA.fifoMem[SVGA_FIFO_BUSY] = TRUE;
|
|
|
|
|
|
|
|
/*
|
|
|
|
* Asynchronously wake up the SVGA3D device. The second
|
|
|
|
* parameter is an arbitrary nonzero 'sync reason' which can be
|
|
|
|
* used for debugging, but which isn't part of the SVGA3D
|
|
|
|
* protocol proper and which isn't used by release builds of
|
|
|
|
* VMware products.
|
|
|
|
*/
|
|
|
|
SVGA_WriteReg(SVGA_REG_SYNC, 1);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
/*
|
|
|
|
*-----------------------------------------------------------------------------
|
|
|
|
*
|
|
|
|
* SVGA_ClearIRQ --
|
|
|
|
*
|
|
|
|
* Clear all pending IRQs. Any IRQs which occurred prior to this
|
|
|
|
* function call will be ignored by the next SVGA_WaitForIRQ()
|
|
|
|
* call.
|
|
|
|
*
|
|
|
|
* Does not affect the current IRQ mask. This function is not
|
|
|
|
* useful unless the SVGA device has IRQ support.
|
|
|
|
*
|
|
|
|
* Results:
|
|
|
|
* Returns a mask of all the interrupt flags that were set prior
|
|
|
|
* to the clear.
|
|
|
|
*
|
|
|
|
* Side effects:
|
|
|
|
* None.
|
|
|
|
*
|
|
|
|
*-----------------------------------------------------------------------------
|
|
|
|
*/
|
|
|
|
|
|
|
|
uint32
|
|
|
|
SVGA_ClearIRQ(void)
|
|
|
|
{
|
|
|
|
uint32 flags = 0;
|
|
|
|
Atomic_Exchange(gSVGA.irq.pending, flags);
|
|
|
|
return flags;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
/*
|
|
|
|
*-----------------------------------------------------------------------------
|
|
|
|
*
|
|
|
|
* SVGA_WaitForIRQ --
|
|
|
|
*
|
|
|
|
* Sleep until an SVGA interrupt occurs. When we wake up, return
|
|
|
|
* a mask of all SVGA IRQs which occurred while we were asleep.
|
|
|
|
*
|
|
|
|
* The design of this function will be different for every OS,
|
|
|
|
* but it is imperative that you ensure it will work properly
|
|
|
|
* no matter when the interrupt actually occurs. The IRQ could have
|
|
|
|
* already happened (any time since the last ClearIRQ, it could
|
|
|
|
* happen while this function is running, or it could happen
|
|
|
|
* after we decide to sleep.
|
|
|
|
*
|
|
|
|
* In this example:
|
|
|
|
*
|
|
|
|
* 1. If the IRQ occurred before this function was called,
|
|
|
|
* the bit will already be set in irq.pending. We can return.
|
|
|
|
*
|
|
|
|
* 2. If the IRQ occurs while this function is executing, we
|
|
|
|
* ask our IRQ handler to do a light-weight context switch
|
|
|
|
* back to the beginning of this function, so we can
|
|
|
|
* re-examine irq.pending before sleeping.
|
|
|
|
*
|
|
|
|
* 3. If the IRQ occurs while we're sleeping, we wake up
|
|
|
|
* from the HLT instruction and re-test irq.pending.
|
|
|
|
*
|
|
|
|
* Results:
|
|
|
|
* Returns a mask of all the interrupt flags that were set prior
|
|
|
|
* to the clear. This will always be nonzero.
|
|
|
|
*
|
|
|
|
* Side effects:
|
|
|
|
* Clears the irq.pending flags for exactly the set of IRQs we return.
|
|
|
|
*
|
|
|
|
*-----------------------------------------------------------------------------
|
|
|
|
*/
|
|
|
|
|
|
|
|
static __attribute__ ((noinline)) uint32
|
|
|
|
SVGAWaitForIRQInternal(void *arg, ...)
|
|
|
|
{
|
|
|
|
uint32 flags = 0;
|
|
|
|
|
Hopefully fix Metalkit refdriver SVGA_WaitForIRQ hangs
There's been a longstanding bug in the IRQ handling code
in the Metalkit version of the SVGA reference driver, which
occasionally caused tests to hang.
I looked at the disassembly of SVGA_WaitForIRQ and friends, and
I found one big problem and one smaller problem:
The 'switchContext' flag is used to tell the IRQ handler
whether it should branch to the saved context or not. This
value *must* be written before we halt, or we can deadlock.
Unfortunately, the value wasn't marked as 'volatile', and the
compiler was optimizing out this assignment entirely! This
means that 'flags' will never get set, and we'll be in an
infinite loop.
Given this bug, I'm not sure why WaitForIRQ ever worked.
It's possible that we were fine if the IRQ had already arrived
by the time WaitForIRQ was called, but we'd deadlock if we
actually ended up waiting. If this is the case, it also means
that fence-stress isn't doing a very good job of stressing the
IRQ code.
The other, much less severe problem: The stack frames for
WaitForIRQInternal and SaveContext may have been partially
overlapping, since gcc was allocating 0x10 bytes on the
stack before calling SaveContext. The proper solution is
probably just to rewrite all of this in assembly, but for
now I just increased the number of padding words on the
stack by adding extra NULL parameters to WaitForIRQInternal.
2009-05-07 17:35:49 -05:00
|
|
|
/*
|
|
|
|
* switchContext must be set to TRUE before we halt, or we'll deadlock.
|
|
|
|
* This variable is marked as volatile, plus we use a compiler memory barrier.
|
|
|
|
*/
|
2009-04-13 02:05:42 -05:00
|
|
|
gSVGA.irq.switchContext = TRUE;
|
Hopefully fix Metalkit refdriver SVGA_WaitForIRQ hangs
There's been a longstanding bug in the IRQ handling code
in the Metalkit version of the SVGA reference driver, which
occasionally caused tests to hang.
I looked at the disassembly of SVGA_WaitForIRQ and friends, and
I found one big problem and one smaller problem:
The 'switchContext' flag is used to tell the IRQ handler
whether it should branch to the saved context or not. This
value *must* be written before we halt, or we can deadlock.
Unfortunately, the value wasn't marked as 'volatile', and the
compiler was optimizing out this assignment entirely! This
means that 'flags' will never get set, and we'll be in an
infinite loop.
Given this bug, I'm not sure why WaitForIRQ ever worked.
It's possible that we were fine if the IRQ had already arrived
by the time WaitForIRQ was called, but we'd deadlock if we
actually ended up waiting. If this is the case, it also means
that fence-stress isn't doing a very good job of stressing the
IRQ code.
The other, much less severe problem: The stack frames for
WaitForIRQInternal and SaveContext may have been partially
overlapping, since gcc was allocating 0x10 bytes on the
stack before calling SaveContext. The proper solution is
probably just to rewrite all of this in assembly, but for
now I just increased the number of padding words on the
stack by adding extra NULL parameters to WaitForIRQInternal.
2009-05-07 17:35:49 -05:00
|
|
|
asm volatile ("" ::: "memory");
|
2009-04-13 02:05:42 -05:00
|
|
|
|
|
|
|
Atomic_Exchange(gSVGA.irq.pending, flags);
|
|
|
|
|
|
|
|
if (!flags) {
|
|
|
|
Intr_Halt();
|
|
|
|
}
|
|
|
|
|
Hopefully fix Metalkit refdriver SVGA_WaitForIRQ hangs
There's been a longstanding bug in the IRQ handling code
in the Metalkit version of the SVGA reference driver, which
occasionally caused tests to hang.
I looked at the disassembly of SVGA_WaitForIRQ and friends, and
I found one big problem and one smaller problem:
The 'switchContext' flag is used to tell the IRQ handler
whether it should branch to the saved context or not. This
value *must* be written before we halt, or we can deadlock.
Unfortunately, the value wasn't marked as 'volatile', and the
compiler was optimizing out this assignment entirely! This
means that 'flags' will never get set, and we'll be in an
infinite loop.
Given this bug, I'm not sure why WaitForIRQ ever worked.
It's possible that we were fine if the IRQ had already arrived
by the time WaitForIRQ was called, but we'd deadlock if we
actually ended up waiting. If this is the case, it also means
that fence-stress isn't doing a very good job of stressing the
IRQ code.
The other, much less severe problem: The stack frames for
WaitForIRQInternal and SaveContext may have been partially
overlapping, since gcc was allocating 0x10 bytes on the
stack before calling SaveContext. The proper solution is
probably just to rewrite all of this in assembly, but for
now I just increased the number of padding words on the
stack by adding extra NULL parameters to WaitForIRQInternal.
2009-05-07 17:35:49 -05:00
|
|
|
/*
|
|
|
|
* Must set switchContext to FALSE before this stack frame is deallocated.
|
|
|
|
*/
|
2009-04-13 02:05:42 -05:00
|
|
|
gSVGA.irq.switchContext = FALSE;
|
Hopefully fix Metalkit refdriver SVGA_WaitForIRQ hangs
There's been a longstanding bug in the IRQ handling code
in the Metalkit version of the SVGA reference driver, which
occasionally caused tests to hang.
I looked at the disassembly of SVGA_WaitForIRQ and friends, and
I found one big problem and one smaller problem:
The 'switchContext' flag is used to tell the IRQ handler
whether it should branch to the saved context or not. This
value *must* be written before we halt, or we can deadlock.
Unfortunately, the value wasn't marked as 'volatile', and the
compiler was optimizing out this assignment entirely! This
means that 'flags' will never get set, and we'll be in an
infinite loop.
Given this bug, I'm not sure why WaitForIRQ ever worked.
It's possible that we were fine if the IRQ had already arrived
by the time WaitForIRQ was called, but we'd deadlock if we
actually ended up waiting. If this is the case, it also means
that fence-stress isn't doing a very good job of stressing the
IRQ code.
The other, much less severe problem: The stack frames for
WaitForIRQInternal and SaveContext may have been partially
overlapping, since gcc was allocating 0x10 bytes on the
stack before calling SaveContext. The proper solution is
probably just to rewrite all of this in assembly, but for
now I just increased the number of padding words on the
stack by adding extra NULL parameters to WaitForIRQInternal.
2009-05-07 17:35:49 -05:00
|
|
|
asm volatile ("" ::: "memory");
|
|
|
|
|
2009-04-13 02:05:42 -05:00
|
|
|
return flags;
|
|
|
|
}
|
|
|
|
|
|
|
|
uint32
|
|
|
|
SVGA_WaitForIRQ(void)
|
|
|
|
{
|
|
|
|
uint32 flags;
|
|
|
|
|
|
|
|
/*
|
|
|
|
* Store this CPU context, and tell the IRQ handler to return
|
|
|
|
* here if an IRQ occurs. We use IntrContext here just like a
|
|
|
|
* setjmp/longjmp that works in ISRs.
|
|
|
|
*
|
|
|
|
* This must be above the irq.pending check, so that we re-test
|
|
|
|
* irq.pending if an IRQ occurs after testing irq.pending but
|
|
|
|
* before we halt. Without this, we could deadlock if an IRQ
|
|
|
|
* comes in before we actually halt but after we've decided to
|
|
|
|
* halt.
|
|
|
|
*/
|
|
|
|
|
Hopefully fix Metalkit refdriver SVGA_WaitForIRQ hangs
There's been a longstanding bug in the IRQ handling code
in the Metalkit version of the SVGA reference driver, which
occasionally caused tests to hang.
I looked at the disassembly of SVGA_WaitForIRQ and friends, and
I found one big problem and one smaller problem:
The 'switchContext' flag is used to tell the IRQ handler
whether it should branch to the saved context or not. This
value *must* be written before we halt, or we can deadlock.
Unfortunately, the value wasn't marked as 'volatile', and the
compiler was optimizing out this assignment entirely! This
means that 'flags' will never get set, and we'll be in an
infinite loop.
Given this bug, I'm not sure why WaitForIRQ ever worked.
It's possible that we were fine if the IRQ had already arrived
by the time WaitForIRQ was called, but we'd deadlock if we
actually ended up waiting. If this is the case, it also means
that fence-stress isn't doing a very good job of stressing the
IRQ code.
The other, much less severe problem: The stack frames for
WaitForIRQInternal and SaveContext may have been partially
overlapping, since gcc was allocating 0x10 bytes on the
stack before calling SaveContext. The proper solution is
probably just to rewrite all of this in assembly, but for
now I just increased the number of padding words on the
stack by adding extra NULL parameters to WaitForIRQInternal.
2009-05-07 17:35:49 -05:00
|
|
|
Intr_SaveContext((IntrContext*) &gSVGA.irq.newContext);
|
2009-04-13 02:05:42 -05:00
|
|
|
|
|
|
|
do {
|
|
|
|
/*
|
|
|
|
* XXX: The arguments here are a kludge to prevent the interrupt
|
|
|
|
* stack frame from overlapping the stack frame referenced
|
|
|
|
* by Intr_SaveContext().
|
Hopefully fix Metalkit refdriver SVGA_WaitForIRQ hangs
There's been a longstanding bug in the IRQ handling code
in the Metalkit version of the SVGA reference driver, which
occasionally caused tests to hang.
I looked at the disassembly of SVGA_WaitForIRQ and friends, and
I found one big problem and one smaller problem:
The 'switchContext' flag is used to tell the IRQ handler
whether it should branch to the saved context or not. This
value *must* be written before we halt, or we can deadlock.
Unfortunately, the value wasn't marked as 'volatile', and the
compiler was optimizing out this assignment entirely! This
means that 'flags' will never get set, and we'll be in an
infinite loop.
Given this bug, I'm not sure why WaitForIRQ ever worked.
It's possible that we were fine if the IRQ had already arrived
by the time WaitForIRQ was called, but we'd deadlock if we
actually ended up waiting. If this is the case, it also means
that fence-stress isn't doing a very good job of stressing the
IRQ code.
The other, much less severe problem: The stack frames for
WaitForIRQInternal and SaveContext may have been partially
overlapping, since gcc was allocating 0x10 bytes on the
stack before calling SaveContext. The proper solution is
probably just to rewrite all of this in assembly, but for
now I just increased the number of padding words on the
stack by adding extra NULL parameters to WaitForIRQInternal.
2009-05-07 17:35:49 -05:00
|
|
|
*
|
|
|
|
* Be sure to look at the generated assembly and compare the
|
|
|
|
* size of this argument list to the amount of stack space
|
|
|
|
* allocated for Intr_SaveContext.
|
2009-04-13 02:05:42 -05:00
|
|
|
*/
|
Hopefully fix Metalkit refdriver SVGA_WaitForIRQ hangs
There's been a longstanding bug in the IRQ handling code
in the Metalkit version of the SVGA reference driver, which
occasionally caused tests to hang.
I looked at the disassembly of SVGA_WaitForIRQ and friends, and
I found one big problem and one smaller problem:
The 'switchContext' flag is used to tell the IRQ handler
whether it should branch to the saved context or not. This
value *must* be written before we halt, or we can deadlock.
Unfortunately, the value wasn't marked as 'volatile', and the
compiler was optimizing out this assignment entirely! This
means that 'flags' will never get set, and we'll be in an
infinite loop.
Given this bug, I'm not sure why WaitForIRQ ever worked.
It's possible that we were fine if the IRQ had already arrived
by the time WaitForIRQ was called, but we'd deadlock if we
actually ended up waiting. If this is the case, it also means
that fence-stress isn't doing a very good job of stressing the
IRQ code.
The other, much less severe problem: The stack frames for
WaitForIRQInternal and SaveContext may have been partially
overlapping, since gcc was allocating 0x10 bytes on the
stack before calling SaveContext. The proper solution is
probably just to rewrite all of this in assembly, but for
now I just increased the number of padding words on the
stack by adding extra NULL parameters to WaitForIRQInternal.
2009-05-07 17:35:49 -05:00
|
|
|
flags = SVGAWaitForIRQInternal(NULL, NULL, NULL, NULL, NULL, NULL);
|
2009-04-13 02:05:42 -05:00
|
|
|
|
|
|
|
} while (flags == 0);
|
|
|
|
|
|
|
|
return flags;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
/*
|
|
|
|
*-----------------------------------------------------------------------------
|
|
|
|
*
|
|
|
|
* SVGAInterruptHandler --
|
|
|
|
*
|
|
|
|
* This is the ISR for the SVGA device's interrupt. We ask the
|
|
|
|
* SVGA device which interrupt occurred, and clear its flag.
|
|
|
|
*
|
|
|
|
* To report this IRQ to the rest of the driver, we:
|
|
|
|
*
|
|
|
|
* 1. Atomically remember the IRQ in the irq.pending bitmask.
|
|
|
|
*
|
|
|
|
* 2. Optionally switch to a different light-weight thread
|
|
|
|
* or a different place in this thread upon returning.
|
|
|
|
* This is analogous to waking up a sleeping thread in
|
|
|
|
* a driver for a real operating system. See SVGA_WaitForIRQ
|
|
|
|
* for details.
|
|
|
|
*
|
|
|
|
* Results:
|
|
|
|
* void.
|
|
|
|
*
|
|
|
|
* Side effects:
|
|
|
|
* Sets bits in pendingIRQs. Reads and clears the device's IRQ flags.
|
|
|
|
* May switch execution contexts.
|
|
|
|
*
|
|
|
|
*-----------------------------------------------------------------------------
|
|
|
|
*/
|
|
|
|
|
|
|
|
void
|
|
|
|
SVGAInterruptHandler(int vector) // IN (unused)
|
|
|
|
{
|
2009-07-01 16:02:40 -05:00
|
|
|
IntrContext *context = Intr_GetContext(vector);
|
2009-04-13 02:05:42 -05:00
|
|
|
|
|
|
|
/*
|
|
|
|
* The SVGA_IRQSTATUS_PORT is a separate I/O port, not a register.
|
|
|
|
* Reading from it gives us the set of IRQ flags which are
|
|
|
|
* set. Writing a '1' to any bit in this register will clear the
|
|
|
|
* corresponding flag.
|
|
|
|
*
|
|
|
|
* Here, we read then clear all flags.
|
|
|
|
*/
|
|
|
|
|
|
|
|
uint16 port = gSVGA.ioBase + SVGA_IRQSTATUS_PORT;
|
|
|
|
uint32 irqFlags = IO_In32(port);
|
|
|
|
IO_Out32(port, irqFlags);
|
|
|
|
|
|
|
|
gSVGA.irq.count++;
|
|
|
|
|
|
|
|
if (!irqFlags) {
|
|
|
|
SVGA_Panic("Spurious SVGA IRQ");
|
|
|
|
}
|
|
|
|
|
|
|
|
Atomic_Or(gSVGA.irq.pending, irqFlags);
|
|
|
|
|
|
|
|
if (gSVGA.irq.switchContext) {
|
2009-07-01 16:02:40 -05:00
|
|
|
memcpy((void*) &gSVGA.irq.oldContext, context, sizeof *context);
|
|
|
|
memcpy(context, (void*) &gSVGA.irq.newContext, sizeof *context);
|
2009-04-13 02:05:42 -05:00
|
|
|
gSVGA.irq.switchContext = FALSE;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
/*
|
|
|
|
*-----------------------------------------------------------------------------
|
|
|
|
*
|
|
|
|
* SVGA_Update --
|
|
|
|
*
|
|
|
|
* Send a 2D update rectangle through the FIFO.
|
|
|
|
*
|
|
|
|
* Results:
|
|
|
|
* None.
|
|
|
|
*
|
|
|
|
* Side effects:
|
|
|
|
* None.
|
|
|
|
*
|
|
|
|
*-----------------------------------------------------------------------------
|
|
|
|
*/
|
|
|
|
|
|
|
|
void
|
|
|
|
SVGA_Update(uint32 x, // IN
|
|
|
|
uint32 y, // IN
|
|
|
|
uint32 width, // IN
|
|
|
|
uint32 height) // IN
|
|
|
|
{
|
|
|
|
SVGAFifoCmdUpdate *cmd = SVGA_FIFOReserveCmd(SVGA_CMD_UPDATE, sizeof *cmd);
|
|
|
|
cmd->x = x;
|
|
|
|
cmd->y = y;
|
|
|
|
cmd->width = width;
|
|
|
|
cmd->height = height;
|
|
|
|
SVGA_FIFOCommitAll();
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
/*
|
|
|
|
*-----------------------------------------------------------------------------
|
|
|
|
*
|
|
|
|
* SVGA_BeginDefineCursor --
|
|
|
|
*
|
|
|
|
* Begin an SVGA_CMD_DEFINE_CURSOR command. This copies the command header
|
|
|
|
* into the FIFO, and reserves enough space for the cursor image itself.
|
|
|
|
* We return pointers to FIFO memory where the AND/XOR masks can be written.
|
|
|
|
* When finished, the caller must invoke SVGA_FIFOCommitAll().
|
|
|
|
*
|
|
|
|
* Results:
|
|
|
|
* Returns pointers to memory where the caller can store the AND/XOR masks.
|
|
|
|
*
|
|
|
|
* Side effects:
|
|
|
|
* Reserves space in the FIFO.
|
|
|
|
*
|
|
|
|
*-----------------------------------------------------------------------------
|
|
|
|
*/
|
|
|
|
|
|
|
|
void
|
|
|
|
SVGA_BeginDefineCursor(const SVGAFifoCmdDefineCursor *cursorInfo, // IN
|
|
|
|
void **andMask, // OUT
|
|
|
|
void **xorMask) // OUT
|
|
|
|
{
|
|
|
|
uint32 andPitch = ((cursorInfo->andMaskDepth * cursorInfo->width + 31) >> 5) << 2;
|
|
|
|
uint32 andSize = andPitch * cursorInfo->height;
|
|
|
|
uint32 xorPitch = ((cursorInfo->xorMaskDepth * cursorInfo->width + 31) >> 5) << 2;
|
|
|
|
uint32 xorSize = xorPitch * cursorInfo->height;
|
|
|
|
|
|
|
|
SVGAFifoCmdDefineCursor *cmd = SVGA_FIFOReserveCmd(SVGA_CMD_DEFINE_CURSOR,
|
|
|
|
sizeof *cmd + andSize + xorSize);
|
|
|
|
*cmd = *cursorInfo;
|
|
|
|
*andMask = (void*) (cmd + 1);
|
|
|
|
*xorMask = (void*) (andSize + (uint8*) *andMask);
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
/*
|
|
|
|
*-----------------------------------------------------------------------------
|
|
|
|
*
|
|
|
|
* SVGA_BeginDefineAlphaCursor --
|
|
|
|
*
|
|
|
|
* Begin an SVGA_CMD_DEFINE_ALPHA_CURSOR command. This copies the
|
|
|
|
* command header into the FIFO, and reserves enough space for
|
|
|
|
* the cursor image itself. We return a pointer to the FIFO
|
|
|
|
* memory where the caller should write a pre-multiplied BGRA
|
|
|
|
* image. When finished, the caller must invoke SVGA_FIFOCommitAll().
|
|
|
|
*
|
|
|
|
* Results:
|
|
|
|
* Returns pointers to memory where the caller can store the image.
|
|
|
|
*
|
|
|
|
* Side effects:
|
|
|
|
* Reserves space in the FIFO.
|
|
|
|
*
|
|
|
|
*-----------------------------------------------------------------------------
|
|
|
|
*/
|
|
|
|
|
|
|
|
void
|
|
|
|
SVGA_BeginDefineAlphaCursor(const SVGAFifoCmdDefineAlphaCursor *cursorInfo, // IN
|
|
|
|
void **data) // OUT
|
|
|
|
{
|
|
|
|
uint32 imageSize = cursorInfo->width * cursorInfo->height * sizeof(uint32);
|
|
|
|
SVGAFifoCmdDefineAlphaCursor *cmd = SVGA_FIFOReserveCmd(SVGA_CMD_DEFINE_ALPHA_CURSOR,
|
|
|
|
sizeof *cmd + imageSize);
|
|
|
|
*cmd = *cursorInfo;
|
|
|
|
*data = (void*) (cmd + 1);
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
/*
|
|
|
|
*-----------------------------------------------------------------------------
|
|
|
|
*
|
|
|
|
* SVGA_MoveCursor --
|
|
|
|
*
|
|
|
|
* Change the position or visibility of the hardware cursor, using
|
|
|
|
* the Cursor Bypass 3 protocol.
|
|
|
|
*
|
|
|
|
* Results:
|
|
|
|
* None.
|
|
|
|
*
|
|
|
|
* Side effects:
|
|
|
|
* Modifies FIFO registers.
|
|
|
|
*
|
|
|
|
*-----------------------------------------------------------------------------
|
|
|
|
*/
|
|
|
|
|
|
|
|
void
|
|
|
|
SVGA_MoveCursor(uint32 visible, // IN
|
|
|
|
uint32 x, // IN
|
|
|
|
uint32 y) // IN
|
|
|
|
{
|
|
|
|
if (SVGA_HasFIFOCap(SVGA_FIFO_CAP_CURSOR_BYPASS_3)) {
|
|
|
|
gSVGA.fifoMem[SVGA_FIFO_CURSOR_ON] = visible;
|
|
|
|
gSVGA.fifoMem[SVGA_FIFO_CURSOR_X] = x;
|
|
|
|
gSVGA.fifoMem[SVGA_FIFO_CURSOR_Y] = y;
|
|
|
|
gSVGA.fifoMem[SVGA_FIFO_CURSOR_COUNT]++;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
/*
|
|
|
|
*-----------------------------------------------------------------------------
|
|
|
|
*
|
|
|
|
* SVGA_BeginVideoSetRegs --
|
|
|
|
*
|
|
|
|
* Begin an SVGA_ESCAPE_VMWARE_VIDEO_SET_REGS command. This can be
|
|
|
|
* used to set an arbitrary number of video registers atomically.
|
|
|
|
*
|
|
|
|
* Results:
|
|
|
|
* Returns a pointer to the command. The caller must fill in (*setRegs)->items.
|
|
|
|
*
|
|
|
|
* Side effects:
|
|
|
|
* Reserves space in the FIFO.
|
|
|
|
*
|
|
|
|
*-----------------------------------------------------------------------------
|
|
|
|
*/
|
|
|
|
|
|
|
|
void
|
|
|
|
SVGA_BeginVideoSetRegs(uint32 streamId, // IN
|
|
|
|
uint32 numItems, // IN
|
|
|
|
SVGAEscapeVideoSetRegs **setRegs) // OUT
|
|
|
|
{
|
|
|
|
SVGAEscapeVideoSetRegs *cmd;
|
|
|
|
uint32 cmdSize = (sizeof *cmd
|
|
|
|
- sizeof cmd->items
|
|
|
|
+ numItems * sizeof cmd->items[0]);
|
|
|
|
|
|
|
|
cmd = SVGA_FIFOReserveEscape(SVGA_ESCAPE_NSID_VMWARE, cmdSize);
|
|
|
|
cmd->header.cmdType = SVGA_ESCAPE_VMWARE_VIDEO_SET_REGS;
|
|
|
|
cmd->header.streamId = streamId;
|
|
|
|
|
|
|
|
*setRegs = cmd;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
/*
|
|
|
|
*-----------------------------------------------------------------------------
|
|
|
|
*
|
|
|
|
* SVGA_VideoSetReg --
|
|
|
|
*
|
|
|
|
* Atomically set all registers in a video overlay unit.
|
|
|
|
*
|
|
|
|
* 'maxReg' should be the highest video overlay register that the
|
|
|
|
* caller understands. We will not set any registers with IDs
|
|
|
|
* higher than maxReg. This prevents older applications compiled
|
|
|
|
* with newer headers from sending improper values to new overlay
|
|
|
|
* registers.
|
|
|
|
*
|
|
|
|
* Each video overlay unit (specified by streamId) has a collection
|
|
|
|
* of 32-bit registers which control the operation of that overlay.
|
|
|
|
* Changes to these registers take effect at the next Flush.
|
|
|
|
*
|
|
|
|
* Results:
|
|
|
|
* None.
|
|
|
|
*
|
|
|
|
* Side effects:
|
|
|
|
* Writes a FIFO command.
|
|
|
|
*
|
|
|
|
*-----------------------------------------------------------------------------
|
|
|
|
*/
|
|
|
|
|
|
|
|
void
|
|
|
|
SVGA_VideoSetAllRegs(uint32 streamId, // IN
|
|
|
|
SVGAOverlayUnit *regs, // IN
|
|
|
|
uint32 maxReg) // IN
|
|
|
|
{
|
|
|
|
uint32 *regArray = (uint32*) regs;
|
|
|
|
const uint32 numRegs = maxReg + 1;
|
|
|
|
SVGAEscapeVideoSetRegs *setRegs;
|
|
|
|
uint32 i;
|
|
|
|
|
|
|
|
SVGA_BeginVideoSetRegs(streamId, numRegs, &setRegs);
|
|
|
|
|
|
|
|
for (i = 0; i < numRegs; i++) {
|
|
|
|
setRegs->items[i].registerId = i;
|
|
|
|
setRegs->items[i].value = regArray[i];
|
|
|
|
}
|
|
|
|
|
|
|
|
SVGA_FIFOCommitAll();
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
/*
|
|
|
|
*-----------------------------------------------------------------------------
|
|
|
|
*
|
|
|
|
* SVGA_VideoSetReg --
|
|
|
|
*
|
|
|
|
* Set a single video overlay register.
|
|
|
|
*
|
|
|
|
* Each video overlay unit (specified by streamId) has a collection
|
|
|
|
* of 32-bit registers which control the operation of that overlay.
|
|
|
|
* Changes to these registers take effect at the next Flush.
|
|
|
|
*
|
|
|
|
* Results:
|
|
|
|
* None.
|
|
|
|
*
|
|
|
|
* Side effects:
|
|
|
|
* Writes a FIFO command.
|
|
|
|
*
|
|
|
|
*-----------------------------------------------------------------------------
|
|
|
|
*/
|
|
|
|
|
|
|
|
void
|
|
|
|
SVGA_VideoSetReg(uint32 streamId, // IN
|
|
|
|
uint32 registerId, // IN
|
|
|
|
uint32 value) // IN
|
|
|
|
{
|
|
|
|
SVGAEscapeVideoSetRegs *setRegs;
|
|
|
|
|
|
|
|
SVGA_BeginVideoSetRegs(streamId, 1, &setRegs);
|
|
|
|
setRegs->items[0].registerId = registerId;
|
|
|
|
setRegs->items[0].value = value;
|
|
|
|
SVGA_FIFOCommitAll();
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
/*
|
|
|
|
*-----------------------------------------------------------------------------
|
|
|
|
*
|
|
|
|
* SVGA_VideoFlush --
|
|
|
|
*
|
|
|
|
* Update a video overlay. On a VideoFlush, all register changes
|
|
|
|
* take effect, and the device will perform a DMA transfer to
|
|
|
|
* copy the next frame of video from guest memory.
|
|
|
|
*
|
|
|
|
* The SVGA device will generally perform a DMA transfer from
|
|
|
|
* overlay memory only during a VideoFlush. The DMA operation
|
|
|
|
* will usually be completed before the host moves to the next
|
|
|
|
* FIFO command. However, this is not guaranteed. In unusual
|
|
|
|
* conditions, the device may re-perform the DMA operation at any
|
|
|
|
* time. This means that, as long as an overlay is enabled, its
|
|
|
|
* corresponding guest memory must be valid.
|
|
|
|
*
|
|
|
|
* Results:
|
|
|
|
* None.
|
|
|
|
*
|
|
|
|
* Side effects:
|
|
|
|
* Writes a FIFO command. May cause an asynchronous DMA transfer.
|
|
|
|
*
|
|
|
|
*-----------------------------------------------------------------------------
|
|
|
|
*/
|
|
|
|
|
|
|
|
void
|
|
|
|
SVGA_VideoFlush(uint32 streamId) // IN
|
|
|
|
{
|
|
|
|
SVGAEscapeVideoFlush *cmd;
|
|
|
|
|
|
|
|
cmd = SVGA_FIFOReserveEscape(SVGA_ESCAPE_NSID_VMWARE, sizeof *cmd);
|
|
|
|
cmd->cmdType = SVGA_ESCAPE_VMWARE_VIDEO_FLUSH;
|
|
|
|
cmd->streamId = streamId;
|
|
|
|
SVGA_FIFOCommitAll();
|
|
|
|
}
|