freebsd-ports/graphics/utah-glx/files/patch-ag
Maxim Sobolev 1c63df741c Add MTRR support.
PR:		21901
Submitted by:	Alexander N. Kabaev <ak03@gte.com>
2000-11-08 11:19:39 +00:00

365 lines
7.3 KiB
Plaintext

--- servGL/hwglx/common/hw_mtrr.c.orig Mon Oct 9 21:42:30 2000
+++ servGL/hwglx/common/hw_mtrr.c Mon Oct 9 21:42:55 2000
@@ -1,4 +1,3 @@
-
#include <stdlib.h>
#include <errno.h>
#include <unistd.h>
@@ -7,13 +6,15 @@
#include <sys/stat.h>
#include <fcntl.h>
#include <signal.h>
+#include <sys/ioctl.h>
#include "glx_symbols.h"
#include "hwlog.h"
-
#ifndef NO_MTRR
+#ifdef linux
+
#define MTRR_NEED_STRINGS
#include <errno.h>
#include <asm/mtrr.h>
@@ -237,6 +238,339 @@
sentry.base, sentry.size );
}
}
+
+#endif
+
+#if defined(__FreeBSD__) || defined(__NetBSD__)
+
+#ifndef __NetBSD__
+#include <sys/memrange.h>
+#else
+#include "memrange.h"
+#endif
+#define X_MTRR_ID "XFree86"
+
+typedef struct x_RangeRec {
+ struct mem_range_desc mrd;
+ int wasWC;
+ struct x_RangeRec * next;
+} RangeRec, *RangePtr;
+
+static RangePtr savedRangeList = NULL;
+static int devMemFd = -1;
+
+/*
+ * getAllRanges returns the full list of memory ranges with attributes set.
+ */
+
+static struct mem_range_desc *
+getAllRanges(int *nmr)
+{
+ struct mem_range_desc *mrd;
+ struct mem_range_op mro;
+
+ /*
+ * Find how many ranges there are. If this fails, then the kernel
+ * probably doesn't have MTRR *support.
+ */
+ mro.mo_arg[0] = 0;
+ if (ioctl(devMemFd, MEMRANGE_GET, &mro))
+ return NULL;
+ *nmr = mro.mo_arg[0];
+ mrd = (struct mem_range_desc *)xalloc(*nmr * sizeof(struct mem_range_desc));
+ mro.mo_arg[0] = *nmr;
+ mro.mo_desc = mrd;
+ if (ioctl(devMemFd, MEMRANGE_GET, &mro)) {
+ xfree(mrd);
+ return NULL;
+ }
+ return mrd;
+}
+
+static void
+freeRangeList(RangePtr range)
+{
+ RangePtr rp;
+
+ while (range) {
+ rp = range;
+ range = rp->next;
+ xfree(rp);
+ }
+}
+
+static RangePtr
+dupRangeList(RangePtr list)
+{
+ RangePtr new = NULL, rp, p;
+
+ rp = list;
+ while (rp) {
+ p = (RangePtr)xalloc(sizeof(RangeRec));
+ *p = *rp;
+ p->next = new;
+ new = p;
+ rp = rp->next;
+ }
+ return new;
+}
+
+static RangePtr
+sortRangeList(RangePtr list)
+{
+ RangePtr rp1, rp2, copy, sorted = NULL, minp, prev, minprev;
+ unsigned long minBase;
+
+ /* Sort by base address */
+ rp1 = copy = dupRangeList(list);
+ while (rp1) {
+ minBase = rp1->mrd.mr_base;
+ minp = rp1;
+ minprev = NULL;
+ prev = rp1;
+ rp2 = rp1->next;
+ while (rp2) {
+ if (rp2->mrd.mr_base < minBase) {
+ minBase = rp2->mrd.mr_base;
+ minp = rp2;
+ minprev = prev;
+ }
+ prev = rp2;
+ rp2 = rp2->next;
+ }
+ if (minprev) {
+ minprev->next = minp->next;
+ rp1 = copy;
+ } else {
+ rp1 = minp->next;
+ }
+ minp->next = sorted;
+ sorted = minp;
+ }
+ return sorted;
+}
+
+/*
+ * findRanges returns a list of ranges that overlap the specified range.
+ */
+
+static void
+findRanges(unsigned long base, unsigned long size, RangePtr *ucp, RangePtr *wcp)
+{
+ struct mem_range_desc *mrd;
+ int nmr, i;
+ RangePtr rp, *p;
+
+ if (!(mrd = getAllRanges(&nmr)))
+ return;
+
+ for (i = 0; i < nmr; i++) {
+ if ((mrd[i].mr_flags & MDF_ACTIVE) &&
+ mrd[i].mr_base < base + size &&
+ mrd[i].mr_base + mrd[i].mr_len > base) {
+ if (mrd[i].mr_flags & MDF_WRITECOMBINE)
+ p = wcp;
+ else if (mrd[i].mr_flags & MDF_UNCACHEABLE)
+ p = ucp;
+ else
+ continue;
+ rp = (RangePtr)xalloc(sizeof(RangeRec));
+ rp->mrd = mrd[i];
+ rp->next = *p;
+ *p = rp;
+ }
+ }
+ xfree(mrd);
+}
+
+/*
+ * This checks if the existing overlapping ranges fully cover the requested
+ * range. Is this overkill?
+ */
+
+static int
+fullCoverage(unsigned long base, unsigned long size, RangePtr overlap)
+{
+ RangePtr rp1, sorted = NULL;
+ unsigned long end;
+
+ sorted = sortRangeList(overlap);
+ /* Look for gaps */
+ rp1 = sorted;
+ end = base + size;
+ while (rp1) {
+ if (rp1->mrd.mr_base > base) {
+ freeRangeList(sorted);
+ return FALSE;
+ } else {
+ base = rp1->mrd.mr_base + rp1->mrd.mr_len;
+ }
+ if (base >= end) {
+ freeRangeList(sorted);
+ return 1;
+ }
+ rp1 = rp1->next;
+ }
+ freeRangeList(sorted);
+ return 0;
+}
+
+static RangePtr
+addWC(unsigned long base, unsigned long size)
+{
+ RangePtr uc = NULL, wc = NULL, retlist = NULL;
+ struct mem_range_desc mrd;
+ struct mem_range_op mro;
+
+ findRanges(base, size, &uc, &wc);
+
+ /* See of the full range is already WC */
+ if (!uc && fullCoverage(base, size, wc)) {
+ hwMsg(1, "Write-combining range (0x%lx,0x%lx) was already set\n",
+ base, size);
+ return NULL;
+ }
+
+ /* Otherwise, try to add the new range */
+ mrd.mr_base = base;
+ mrd.mr_len = size;
+ strcpy(mrd.mr_owner, X_MTRR_ID);
+ mrd.mr_flags = MDF_WRITECOMBINE;
+ mro.mo_desc = &mrd;
+ mro.mo_arg[0] = MEMRANGE_SET_UPDATE;
+ if (ioctl(devMemFd, MEMRANGE_SET, &mro)) {
+ hwError("Failed to set write-combining range "
+ "(0x%lx,0x%lx)\n", base, size);
+ return NULL;
+ } else {
+ hwMsg(1, "Write-combining range (0x%lx,0x%lx)\n", base, size);
+ retlist = (RangePtr)xalloc(sizeof(RangeRec));
+ retlist->mrd = mrd;
+ retlist->wasWC = FALSE;
+ retlist->next = NULL;
+ return retlist;
+ }
+}
+
+static void
+undoWC(RangePtr rp)
+{
+ struct mem_range_op mro;
+ int failed;
+
+ while (rp) {
+ hwMsg(1,"Undo for (0x%lx,0x%lx), %d\n",
+ (unsigned long)rp->mrd.mr_base,
+ (unsigned long)rp->mrd.mr_len, rp->wasWC);
+ failed = FALSE;
+ if (rp->wasWC) {
+ mro.mo_arg[0] = MEMRANGE_SET_UPDATE;
+ rp->mrd.mr_flags = MDF_WRITECOMBINE;
+ strcpy(rp->mrd.mr_owner, "unknown");
+ } else {
+ mro.mo_arg[0] = MEMRANGE_SET_REMOVE;
+ }
+ mro.mo_desc = &rp->mrd;
+
+ if (ioctl(devMemFd, MEMRANGE_SET, &mro)) {
+ if (!rp->wasWC) {
+ mro.mo_arg[0] = MEMRANGE_SET_UPDATE;
+ rp->mrd.mr_flags = MDF_UNCACHEABLE;
+ strcpy(rp->mrd.mr_owner, "unknown");
+ if (ioctl(devMemFd, MEMRANGE_SET, &mro))
+ failed = TRUE;
+ } else
+ failed = TRUE;
+ }
+ if (failed) {
+ hwError("Failed to restore MTRR range (0x%lx,0x%lx)\n",
+ (unsigned long)rp->mrd.mr_base,
+ (unsigned long)rp->mrd.mr_len);
+ }
+ rp = rp->next;
+ }
+}
+
+static int
+cleanMTRR()
+{
+ struct mem_range_desc *mrd;
+ struct mem_range_op mro;
+ int nmr, i;
+
+ /* This shouldn't happen */
+ if (devMemFd < 0)
+ return FALSE;
+
+ if (!(mrd = getAllRanges(&nmr)))
+ return FALSE;
+
+ for (i = 0; i < nmr; i++) {
+ if (strcmp(mrd[i].mr_owner, X_MTRR_ID) == 0 &&
+ (mrd[i].mr_flags & MDF_ACTIVE)) {
+ hwMsg( 1,"Clean for (0x%lx,0x%lx)\n",
+ (unsigned long)mrd[i].mr_base,
+ (unsigned long)mrd[i].mr_len);
+ if (mrd[i].mr_flags & MDF_FIXACTIVE) {
+ mro.mo_arg[0] = MEMRANGE_SET_UPDATE;
+ mrd[i].mr_flags = MDF_UNCACHEABLE;
+ } else {
+ mro.mo_arg[0] = MEMRANGE_SET_REMOVE;
+ }
+ mro.mo_desc = mrd + i;
+ ioctl(devMemFd, MEMRANGE_SET, &mro);
+ }
+ }
+ xfree(mrd);
+ return TRUE;
+}
+
+static void CloseMTRR( void )
+{
+ undoWC(savedRangeList);
+ close(devMemFd);
+}
+
+static void OpenMTRR( void )
+{
+ if ( ( devMemFd = open( "/dev/mem", O_RDWR, 0 ) ) == -1 )
+ {
+ hwError( "Error opening /dev/mem: %s\n", strerror( errno ) );
+ hwError( "MTRR not enabled\n" );
+ return;
+ }
+
+ if ( !cleanMTRR() ) {
+ close(devMemFd);
+ devMemFd = -1;
+ return;
+ }
+
+ atexit( CloseMTRR );
+}
+
+void SetWriteCombining( long physical, int bytes )
+{
+ RangePtr rp;
+
+ if ( devMemFd < 0 ) {
+ OpenMTRR();
+ }
+
+ if ( devMemFd < 0 ) {
+ return;
+ }
+
+ rp = addWC(physical, bytes);
+
+ if ( NULL != rp ) {
+ rp->next = savedRangeList;
+ savedRangeList = rp;
+ }
+}
+
+
+#endif /* end of BSD MTRR support */
/*
* FlushWriteCombining