385 lines
9.2 KiB
C
385 lines
9.2 KiB
C
/**********************************************************
|
|
* 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.
|
|
*
|
|
**********************************************************/
|
|
|
|
/*
|
|
* gmr.c --
|
|
*
|
|
* Utilities for creating and Guest Memory Regions (GMRs)
|
|
*/
|
|
|
|
#include "svga.h"
|
|
#include "gmr.h"
|
|
|
|
/*
|
|
* Global data
|
|
*/
|
|
|
|
static uint32 heapTop;
|
|
GMRState gGMR;
|
|
|
|
|
|
/*
|
|
*-----------------------------------------------------------------------------
|
|
*
|
|
* Heap_Reset --
|
|
* Heap_Alloc --
|
|
* Heap_AllocPages --
|
|
* Heap_Discard --
|
|
* Heap_DiscardPages --
|
|
* Heap_ProbeMem --
|
|
*
|
|
* Trivial memory heap with 32-bit aligned and page-aligned
|
|
* allocation. Memory can't be freed, but the entire heap
|
|
* can be reset.
|
|
*
|
|
* On 'discard', we don't actually free memory- we just write
|
|
* over it to ensure its values aren't still being used.
|
|
*
|
|
* We insert padding pages between each individual memory
|
|
* allocation, to ensure that separate allocations are not
|
|
* accidentally contiguous.
|
|
*
|
|
*-----------------------------------------------------------------------------
|
|
*/
|
|
|
|
void
|
|
Heap_Reset(void)
|
|
{
|
|
extern uint8 _end[];
|
|
heapTop = (uint32) _end;
|
|
}
|
|
|
|
void
|
|
Heap_ProbeMem(volatile uint32 *addr, uint32 size)
|
|
{
|
|
const uint32 probe = 0x55AA55AA;
|
|
while (size > sizeof *addr) {
|
|
*addr = probe;
|
|
if (*addr != probe) {
|
|
goto error;
|
|
}
|
|
*addr = ~probe;
|
|
if (*addr != ~probe) {
|
|
goto error;
|
|
}
|
|
size -= sizeof *addr;
|
|
addr++;
|
|
}
|
|
return;
|
|
error:
|
|
SVGA_Panic("Out of physical memory.\n\n"
|
|
"Increase the amount of memory allocated to this VM.\n"
|
|
"128MB of RAM is recommended.\n");
|
|
}
|
|
|
|
void *
|
|
Heap_Alloc(uint32 bytes)
|
|
{
|
|
const uint32 padding = 16;
|
|
void *result;
|
|
|
|
heapTop = (heapTop + 3) & ~3;
|
|
result = (void*) heapTop;
|
|
|
|
bytes += padding;
|
|
heapTop += bytes;
|
|
Heap_ProbeMem(result, bytes);
|
|
|
|
return result;
|
|
}
|
|
|
|
PPN
|
|
Heap_AllocPages(uint32 numPages)
|
|
{
|
|
const uint32 padding = 1;
|
|
PPN result;
|
|
uint32 bytes;
|
|
|
|
heapTop = (heapTop + PAGE_MASK) & ~PAGE_MASK;
|
|
result = heapTop / PAGE_SIZE;
|
|
|
|
numPages += padding;
|
|
bytes = numPages * PAGE_SIZE;
|
|
heapTop += bytes;
|
|
Heap_ProbeMem(PPN_POINTER(result), bytes);
|
|
|
|
return result;
|
|
}
|
|
|
|
void
|
|
Heap_Discard(void *data, uint32 bytes)
|
|
{
|
|
memset(data, 0xAA, bytes);
|
|
}
|
|
|
|
void
|
|
Heap_DiscardPages(PPN firstPage, uint32 numPages)
|
|
{
|
|
memset(PPN_POINTER(firstPage), 0xAA, numPages * PAGE_SIZE);
|
|
}
|
|
|
|
|
|
/*
|
|
*-----------------------------------------------------------------------------
|
|
*
|
|
* GMR_AllocDescriptor --
|
|
*
|
|
* Given a flat array of descriptors, allocate physical pages as
|
|
* necessary and copy the descriptors into a linked list of
|
|
* pages. The result is a PPN which is suitable to give directly to
|
|
* the SVGA device's GMR_DESCRIPTOR register.
|
|
*
|
|
* Results:
|
|
* If numDescriptors is zero, returns zero. otherwise, returns
|
|
* a physical page number.
|
|
*
|
|
* Side effects:
|
|
* Allocates (and never frees) memory for the GMR descriptor.
|
|
*
|
|
*-----------------------------------------------------------------------------
|
|
*/
|
|
|
|
PPN
|
|
GMR_AllocDescriptor(SVGAGuestMemDescriptor *descArray,
|
|
uint32 numDescriptors)
|
|
{
|
|
const uint32 descPerPage = PAGE_SIZE / sizeof(SVGAGuestMemDescriptor) - 1;
|
|
SVGAGuestMemDescriptor *desc = NULL;
|
|
PPN firstPage = 0;
|
|
PPN page = 0;
|
|
int i = 0;
|
|
|
|
while (numDescriptors) {
|
|
if (!firstPage) {
|
|
firstPage = page = Heap_AllocPages(1);
|
|
}
|
|
|
|
desc = PPN_POINTER(page);
|
|
|
|
if (i == descPerPage) {
|
|
/*
|
|
* Terminate this page with a pointer to the next one.
|
|
*/
|
|
page = Heap_AllocPages(1);
|
|
desc[i].ppn = page;
|
|
desc[i].numPages = 0;
|
|
i = 0;
|
|
continue;
|
|
}
|
|
|
|
desc[i] = *descArray;
|
|
i++;
|
|
descArray++;
|
|
numDescriptors--;
|
|
}
|
|
|
|
if (desc) {
|
|
/* Terminate the end of the descriptor list. */
|
|
desc[i].ppn = 0;
|
|
desc[i].numPages = 0;
|
|
}
|
|
|
|
return firstPage;
|
|
}
|
|
|
|
|
|
/*
|
|
*-----------------------------------------------------------------------------
|
|
*
|
|
* GMR_Define --
|
|
*
|
|
* Initialize a GMR, using a flat array of descriptors. The array
|
|
* is copied into a set of physically discontiguous pages, which we
|
|
* give the host.
|
|
*
|
|
* Results:
|
|
* None.
|
|
*
|
|
* Side effects:
|
|
* Defines or redefines a GMR in the SVGA device.
|
|
* Allocates (and never frees) memory for the GMR descriptor.
|
|
*
|
|
*-----------------------------------------------------------------------------
|
|
*/
|
|
|
|
void
|
|
GMR_Define(uint32 gmrId,
|
|
SVGAGuestMemDescriptor *descArray,
|
|
uint32 numDescriptors)
|
|
{
|
|
PPN desc = GMR_AllocDescriptor(descArray, numDescriptors);
|
|
|
|
/*
|
|
* Define/undefine the GMR. Defining an empty GMR is equivalent to
|
|
* undefining a GMR.
|
|
*/
|
|
|
|
SVGA_WriteReg(SVGA_REG_GMR_ID, gmrId);
|
|
SVGA_WriteReg(SVGA_REG_GMR_DESCRIPTOR, desc);
|
|
|
|
if (desc) {
|
|
/*
|
|
* Clobber the first page, to verify that the device reads our
|
|
* descriptors synchronously when we write the GMR registers.
|
|
*/
|
|
Heap_DiscardPages(desc, 1);
|
|
}
|
|
}
|
|
|
|
|
|
/*
|
|
*-----------------------------------------------------------------------------
|
|
*
|
|
* GMR_DefineContiguous --
|
|
*
|
|
* Allocate and define a physically contiguous GMR, consisting of a
|
|
* single SVGAGuestMemDescriptor.
|
|
*
|
|
* Results:
|
|
* Returns the first PPN of the allocated GMR region. All of
|
|
* 'numPages' subsequent PPNs are also part of the GMR.
|
|
*
|
|
* Side effects:
|
|
* Allocates and never frees memory for the GMR descriptor and for
|
|
* the GMR contents itself.
|
|
*
|
|
*-----------------------------------------------------------------------------
|
|
*/
|
|
|
|
PPN
|
|
GMR_DefineContiguous(uint32 gmrId, uint32 numPages)
|
|
{
|
|
SVGAGuestMemDescriptor desc = {
|
|
.ppn = Heap_AllocPages(numPages),
|
|
.numPages = numPages,
|
|
};
|
|
|
|
GMR_Define(gmrId, &desc, 1);
|
|
|
|
return desc.ppn;
|
|
}
|
|
|
|
|
|
/*
|
|
*-----------------------------------------------------------------------------
|
|
*
|
|
* GMR_DefineEvenPages --
|
|
*
|
|
* Define a worst-case discontiguous GMR, in which we map only the
|
|
* even pages. This allocates twice numPages, and creates a GMR
|
|
* which skips every other page. To be extra annoying, we also
|
|
* make sure that descriptors are allocated on physically
|
|
* discontiguous pages.
|
|
*
|
|
* Results:
|
|
* Returns the first PPN of the allocated GMR region. All of
|
|
* 'numPages*2' subsequent PPNs have been allocated, but only
|
|
* the even-numbered pages in that sequence are mapped into the GMR.
|
|
*
|
|
* Side effects:
|
|
* Allocates and never frees memory for the GMR descriptor and for
|
|
* the GMR contents itself.
|
|
*
|
|
*-----------------------------------------------------------------------------
|
|
*/
|
|
|
|
PPN
|
|
GMR_DefineEvenPages(uint32 gmrId, uint32 numPages)
|
|
{
|
|
SVGAGuestMemDescriptor *desc;
|
|
PPN region = Heap_AllocPages(numPages * 2);
|
|
int i;
|
|
|
|
desc = Heap_Alloc(sizeof *desc * numPages);
|
|
|
|
for (i = 0; i < numPages; i++) {
|
|
desc[i].ppn = region + i*2;
|
|
desc[i].numPages = 1;
|
|
}
|
|
|
|
GMR_Define(gmrId, desc, numPages);
|
|
|
|
return region;
|
|
}
|
|
|
|
|
|
/*
|
|
*-----------------------------------------------------------------------------
|
|
*
|
|
* GMR_FreeAll --
|
|
*
|
|
* Undefine all GMRs.
|
|
*
|
|
* This frees device resources, but it doesn't actually free
|
|
* any memory from our trivial heap. If you are done with the
|
|
* heap, call Heap_Reset() separately.
|
|
*
|
|
* Results:
|
|
* None.
|
|
*
|
|
* Side effects:
|
|
* Undefines GMRs.
|
|
*
|
|
*-----------------------------------------------------------------------------
|
|
*/
|
|
|
|
void
|
|
GMR_FreeAll(void)
|
|
{
|
|
uint32 id;
|
|
|
|
for (id = 0; id < gGMR.maxIds; id++) {
|
|
GMR_Define(id, NULL, 0);
|
|
}
|
|
}
|
|
|
|
|
|
/*
|
|
*-----------------------------------------------------------------------------
|
|
*
|
|
* GMR_Init --
|
|
*
|
|
* Read GMR capabilities, or panic of GMRs aren't supported.
|
|
*
|
|
* Results:
|
|
* None.
|
|
*
|
|
* Side effects:
|
|
* Fills in 'gGMR'.
|
|
*
|
|
*-----------------------------------------------------------------------------
|
|
*/
|
|
|
|
void
|
|
GMR_Init(void)
|
|
{
|
|
if (gSVGA.capabilities & SVGA_CAP_GMR) {
|
|
gGMR.maxIds = SVGA_ReadReg(SVGA_REG_GMR_MAX_IDS);
|
|
gGMR.maxDescriptorLen = SVGA_ReadReg(SVGA_REG_GMR_MAX_DESCRIPTOR_LENGTH);
|
|
} else {
|
|
SVGA_Panic("Virtual device does not have Guest Memory Region (GMR) support.");
|
|
}
|
|
}
|