1/*
2 * Copyright 1992 by Rich Murphey <Rich@Rice.edu>
3 * Copyright 1993 by David Wexelblat <dwex@goblin.org>
4 *
5 * Permission to use, copy, modify, distribute, and sell this software and its
6 * documentation for any purpose is hereby granted without fee, provided that
7 * the above copyright notice appear in all copies and that both that
8 * copyright notice and this permission notice appear in supporting
9 * documentation, and that the names of Rich Murphey and David Wexelblat
10 * not be used in advertising or publicity pertaining to distribution of
11 * the software without specific, written prior permission.  Rich Murphey and
12 * David Wexelblat make no representations about the suitability of this
13 * software for any purpose.  It is provided "as is" without express or
14 * implied warranty.
15 *
16 * RICH MURPHEY AND DAVID WEXELBLAT DISCLAIM ALL WARRANTIES WITH REGARD TO
17 * THIS SOFTWARE, INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND
18 * FITNESS, IN NO EVENT SHALL RICH MURPHEY OR DAVID WEXELBLAT BE LIABLE FOR
19 * ANY SPECIAL, INDIRECT OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER
20 * RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF
21 * CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN
22 * CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
23 *
24 */
25
26#ifdef HAVE_XORG_CONFIG_H
27#include <xorg-config.h>
28#endif
29
30#include <X11/X.h>
31#include "xf86.h"
32#include "xf86Priv.h"
33
34#include <errno.h>
35#include <sys/mman.h>
36
37#ifdef HAS_MTRR_SUPPORT
38#ifndef __NetBSD__
39#include <sys/types.h>
40#include <sys/memrange.h>
41#else
42#include "memrange.h"
43#endif
44#define X_MTRR_ID "XFree86"
45#endif
46
47#if defined(HAS_MTRR_BUILTIN) && defined(__NetBSD__)
48#include <machine/mtrr.h>
49#include <machine/sysarch.h>
50#include <sys/queue.h>
51#ifdef __x86_64__
52#define i386_set_mtrr x86_64_set_mtrr
53#define i386_get_mtrr x86_64_get_mtrr
54#define i386_iopl x86_64_iopl
55#endif
56#endif
57
58#include "xf86_OSlib.h"
59#include "xf86OSpriv.h"
60
61#if defined(__NetBSD__) && !defined(MAP_FILE)
62#define MAP_FLAGS MAP_SHARED
63#else
64#define MAP_FLAGS (MAP_FILE | MAP_SHARED)
65#endif
66
67#ifndef MAP_FAILED
68#define MAP_FAILED ((caddr_t)-1)
69#endif
70
71#ifdef __OpenBSD__
72#define SYSCTL_MSG "\tCheck that you have set 'machdep.allowaperture=1'\n"\
73		   "\tin /etc/sysctl.conf and reboot your machine\n" \
74		   "\trefer to xf86(4) for details"
75#define SYSCTL_MSG2 \
76		"Check that you have set 'machdep.allowaperture=2'\n" \
77		"\tin /etc/sysctl.conf and reboot your machine\n" \
78		"\trefer to xf86(4) for details"
79#endif
80
81/***************************************************************************/
82/* Video Memory Mapping section                                            */
83/***************************************************************************/
84
85static Bool useDevMem = FALSE;
86static int  devMemFd = -1;
87
88#ifdef HAS_APERTURE_DRV
89#define DEV_APERTURE "/dev/xf86"
90#endif
91#define DEV_MEM "/dev/mem"
92
93static pointer mapVidMem(int, unsigned long, unsigned long, int);
94static void unmapVidMem(int, pointer, unsigned long);
95
96#ifdef HAS_MTRR_SUPPORT
97static pointer setWC(int, unsigned long, unsigned long, Bool, MessageType);
98static void undoWC(int, pointer);
99static Bool cleanMTRR(void);
100#endif
101#if defined(HAS_MTRR_BUILTIN) && defined(__NetBSD__)
102static pointer NetBSDsetWC(int, unsigned long, unsigned long, Bool,
103			   MessageType);
104static void NetBSDundoWC(int, pointer);
105#endif
106
107/*
108 * Check if /dev/mem can be mmap'd.  If it can't print a warning when
109 * "warn" is TRUE.
110 */
111static void
112checkDevMem(Bool warn)
113{
114	static Bool devMemChecked = FALSE;
115	int fd;
116	pointer base;
117
118	if (devMemChecked)
119	    return;
120	devMemChecked = TRUE;
121
122	if ((fd = open(DEV_MEM, O_RDWR)) >= 0)
123	{
124	    /* Try to map a page at the VGA address */
125	    base = mmap((caddr_t)0, 4096, PROT_READ | PROT_WRITE,
126				 MAP_FLAGS, fd, (off_t)0xA0000);
127
128	    if (base != MAP_FAILED)
129	    {
130		munmap((caddr_t)base, 4096);
131		devMemFd = fd;
132		useDevMem = TRUE;
133		return;
134	    } else {
135		/* This should not happen */
136		if (warn)
137		{
138		    xf86Msg(X_WARNING, "checkDevMem: failed to mmap %s (%s)\n",
139			    DEV_MEM, strerror(errno));
140		}
141		useDevMem = FALSE;
142		return;
143	    }
144	}
145#ifndef HAS_APERTURE_DRV
146	if (warn)
147	{
148	    xf86Msg(X_WARNING, "checkDevMem: failed to open %s (%s)\n",
149		    DEV_MEM, strerror(errno));
150	}
151	useDevMem = FALSE;
152	return;
153#else
154	/* Failed to open /dev/mem, try the aperture driver */
155	if ((fd = open(DEV_APERTURE, O_RDWR)) >= 0)
156	{
157	    /* Try to map a page at the VGA address */
158	    base = mmap((caddr_t)0, 4096, PROT_READ | PROT_WRITE,
159			     MAP_FLAGS, fd, (off_t)0xA0000);
160
161	    if (base != MAP_FAILED)
162	    {
163		munmap((caddr_t)base, 4096);
164		devMemFd = fd;
165		useDevMem = TRUE;
166		xf86Msg(X_INFO, "checkDevMem: using aperture driver %s\n",
167		        DEV_APERTURE);
168		return;
169	    } else {
170
171		if (warn)
172		{
173		    xf86Msg(X_WARNING, "checkDevMem: failed to mmap %s (%s)\n",
174			    DEV_APERTURE, strerror(errno));
175		}
176	    }
177	} else {
178	    if (warn)
179	    {
180#ifndef __OpenBSD__
181		xf86Msg(X_WARNING, "checkDevMem: failed to open %s and %s\n"
182			"\t(%s)\n", DEV_MEM, DEV_APERTURE, strerror(errno));
183#else /* __OpenBSD__ */
184		xf86Msg(X_WARNING, "checkDevMem: failed to open %s and %s\n"
185			"\t(%s)\n%s", DEV_MEM, DEV_APERTURE, strerror(errno),
186			SYSCTL_MSG);
187#endif /* __OpenBSD__ */
188	    }
189	}
190
191	useDevMem = FALSE;
192	return;
193
194#endif
195}
196
197void
198xf86OSInitVidMem(VidMemInfoPtr pVidMem)
199{
200	checkDevMem(TRUE);
201	pVidMem->linearSupported = useDevMem;
202	pVidMem->mapMem = mapVidMem;
203	pVidMem->unmapMem = unmapVidMem;
204
205#if HAVE_PCI_SYSTEM_INIT_DEV_MEM
206	if (useDevMem)
207		pci_system_init_dev_mem(devMemFd);
208#endif
209
210#ifdef HAS_MTRR_SUPPORT
211	if (useDevMem) {
212		if (cleanMTRR()) {
213			pVidMem->setWC = setWC;
214			pVidMem->undoWC = undoWC;
215		}
216	}
217#endif
218#if defined(HAS_MTRR_BUILTIN) && defined(__NetBSD__)
219	pVidMem->setWC = NetBSDsetWC;
220	pVidMem->undoWC = NetBSDundoWC;
221#endif
222	pVidMem->initialised = TRUE;
223}
224
225static pointer
226mapVidMem(int ScreenNum, unsigned long Base, unsigned long Size, int flags)
227{
228	pointer base;
229
230	checkDevMem(FALSE);
231
232	if (useDevMem)
233	{
234	    if (devMemFd < 0)
235	    {
236		FatalError("xf86MapVidMem: failed to open %s (%s)",
237			   DEV_MEM, strerror(errno));
238	    }
239	    base = mmap((caddr_t)0, Size,
240			(flags & VIDMEM_READONLY) ?
241			 PROT_READ : (PROT_READ | PROT_WRITE),
242			MAP_FLAGS, devMemFd, (off_t)Base);
243	    if (base == MAP_FAILED)
244	    {
245		FatalError("%s: could not mmap %s [s=%lx,a=%lx] (%s)",
246			   "xf86MapVidMem", DEV_MEM, Size, Base,
247			   strerror(errno));
248	    }
249	    return base;
250	}
251
252	/* else, mmap /dev/vga */
253	if ((unsigned long)Base < 0xA0000 || (unsigned long)Base >= 0xC0000)
254	{
255		FatalError("%s: Address 0x%lx outside allowable range",
256			   "xf86MapVidMem", Base);
257	}
258	base = mmap(0, Size,
259		    (flags & VIDMEM_READONLY) ?
260		     PROT_READ : (PROT_READ | PROT_WRITE),
261		    MAP_FLAGS, xf86Info.screenFd,
262		    (unsigned long)Base - 0xA0000
263	    );
264	if (base == MAP_FAILED)
265	{
266	    FatalError("xf86MapVidMem: Could not mmap /dev/vga (%s)",
267		       strerror(errno));
268	}
269	return base;
270}
271
272static void
273unmapVidMem(int ScreenNum, pointer Base, unsigned long Size)
274{
275	munmap((caddr_t)Base, Size);
276}
277
278/*
279 * Read BIOS via mmap()ing DEV_MEM
280 */
281
282int
283xf86ReadBIOS(unsigned long Base, unsigned long Offset, unsigned char *Buf,
284	     int Len)
285{
286	unsigned char *ptr;
287	int psize;
288	int mlen;
289
290	checkDevMem(TRUE);
291	if (devMemFd == -1) {
292	    return -1;
293	}
294
295	psize = getpagesize();
296	Offset += Base & (psize - 1);
297	Base &= ~(psize - 1);
298	mlen = (Offset + Len + psize - 1) & ~(psize - 1);
299	ptr = (unsigned char *)mmap((caddr_t)0, mlen, PROT_READ,
300					MAP_SHARED, devMemFd, (off_t)Base);
301	if ((long)ptr == -1)
302	{
303		xf86Msg(X_WARNING,
304			"xf86ReadBIOS: %s mmap[s=%x,a=%lx,o=%lx] failed (%s)\n",
305			DEV_MEM, Len, Base, Offset, strerror(errno));
306#ifdef __OpenBSD__
307		if (Base < 0xa0000) {
308		    xf86Msg(X_WARNING, SYSCTL_MSG2);
309		}
310#endif
311		return -1;
312	}
313#ifdef DEBUG
314	ErrorF("xf86ReadBIOS: BIOS at 0x%08x has signature 0x%04x\n",
315		Base, ptr[0] | (ptr[1] << 8));
316#endif
317	(void)memcpy(Buf, (void *)(ptr + Offset), Len);
318	(void)munmap((caddr_t)ptr, mlen);
319#ifdef DEBUG
320	xf86MsgVerb(X_INFO, 3, "xf86ReadBIOS(%x, %x, Buf, %x)"
321		"-> %02x %02x %02x %02x...\n",
322		Base, Offset, Len, Buf[0], Buf[1], Buf[2], Buf[3]);
323#endif
324	return Len;
325}
326
327#ifdef USE_I386_IOPL
328/***************************************************************************/
329/* I/O Permissions section                                                 */
330/***************************************************************************/
331
332static Bool ExtendedEnabled = FALSE;
333
334Bool
335xf86EnableIO()
336{
337	if (ExtendedEnabled)
338		return TRUE;
339
340	if (i386_iopl(TRUE) < 0)
341	{
342#ifndef __OpenBSD__
343		xf86Msg(X_WARNING,"%s: Failed to set IOPL for extended I/O",
344			   "xf86EnableIO");
345#else
346		xf86Msg(X_WARNING,"%s: Failed to set IOPL for extended I/O\n%s",
347			   "xf86EnableIO", SYSCTL_MSG);
348#endif
349		return FALSE;
350	}
351	ExtendedEnabled = TRUE;
352
353	return TRUE;
354}
355
356void
357xf86DisableIO()
358{
359	if (!ExtendedEnabled)
360		return;
361
362	i386_iopl(FALSE);
363	ExtendedEnabled = FALSE;
364
365	return;
366}
367
368#endif /* USE_I386_IOPL */
369
370#ifdef USE_AMD64_IOPL
371/***************************************************************************/
372/* I/O Permissions section                                                 */
373/***************************************************************************/
374
375static Bool ExtendedEnabled = FALSE;
376
377Bool
378xf86EnableIO()
379{
380	if (ExtendedEnabled)
381		return TRUE;
382
383	if (amd64_iopl(TRUE) < 0)
384	{
385#ifndef __OpenBSD__
386		xf86Msg(X_WARNING,"%s: Failed to set IOPL for extended I/O",
387			   "xf86EnableIO");
388#else
389		xf86Msg(X_WARNING,"%s: Failed to set IOPL for extended I/O\n%s",
390			   "xf86EnableIO", SYSCTL_MSG);
391#endif
392		return FALSE;
393	}
394	ExtendedEnabled = TRUE;
395
396	return TRUE;
397}
398
399void
400xf86DisableIO()
401{
402	if (!ExtendedEnabled)
403		return;
404
405	if (amd64_iopl(FALSE) == 0) {
406		ExtendedEnabled = FALSE;
407	}
408	/* Otherwise, the X server has revoqued its root uid,
409	   and thus cannot give up IO privileges any more */
410
411	return;
412}
413
414#endif /* USE_AMD64_IOPL */
415
416#ifdef USE_DEV_IO
417static int IoFd = -1;
418
419Bool
420xf86EnableIO()
421{
422	if (IoFd >= 0)
423		return TRUE;
424
425	if ((IoFd = open("/dev/io", O_RDWR)) == -1)
426	{
427		xf86Msg(X_WARNING,"xf86EnableIO: "
428				"Failed to open /dev/io for extended I/O");
429		return FALSE;
430	}
431	return TRUE;
432}
433
434void
435xf86DisableIO()
436{
437	if (IoFd < 0)
438		return;
439
440	close(IoFd);
441	IoFd = -1;
442	return;
443}
444
445#endif
446
447#ifdef __NetBSD__
448/***************************************************************************/
449/* Set TV output mode                                                      */
450/***************************************************************************/
451void
452xf86SetTVOut(int mode)
453{
454    switch (xf86Info.consType)
455    {
456#ifdef PCCONS_SUPPORT
457	case PCCONS:{
458
459	    if (ioctl (xf86Info.consoleFd, CONSOLE_X_TV_ON, &mode) < 0)
460	    {
461		xf86Msg(X_WARNING,
462		    "xf86SetTVOut: Could not set console to TV output, %s\n",
463		    strerror(errno));
464	    }
465	}
466	break;
467#endif /* PCCONS_SUPPORT */
468
469	default:
470	    FatalError("Xf86SetTVOut: Unsupported console");
471	    break;
472    }
473    return;
474}
475
476void
477xf86SetRGBOut()
478{
479    switch (xf86Info.consType)
480    {
481#ifdef PCCONS_SUPPORT
482	case PCCONS:{
483
484	    if (ioctl (xf86Info.consoleFd, CONSOLE_X_TV_OFF, 0) < 0)
485	    {
486		xf86Msg(X_WARNING,
487		    "xf86SetTVOut: Could not set console to RGB output, %s\n",
488		    strerror(errno));
489	    }
490	}
491	break;
492#endif /* PCCONS_SUPPORT */
493
494	default:
495	    FatalError("Xf86SetTVOut: Unsupported console");
496	    break;
497    }
498    return;
499}
500#endif
501
502#ifdef HAS_MTRR_SUPPORT
503/* memory range (MTRR) support for FreeBSD */
504
505/*
506 * This code is experimental.  Some parts may be overkill, and other parts
507 * may be incomplete.
508 */
509
510/*
511 * getAllRanges returns the full list of memory ranges with attributes set.
512 */
513
514static struct mem_range_desc *
515getAllRanges(int *nmr)
516{
517	struct mem_range_desc *mrd;
518	struct mem_range_op mro;
519
520	/*
521	 * Find how many ranges there are.  If this fails, then the kernel
522	 * probably doesn't have MTRR support.
523	 */
524	mro.mo_arg[0] = 0;
525	if (ioctl(devMemFd, MEMRANGE_GET, &mro))
526		return NULL;
527	*nmr = mro.mo_arg[0];
528	mrd = xnfalloc(*nmr * sizeof(struct mem_range_desc));
529	mro.mo_arg[0] = *nmr;
530	mro.mo_desc = mrd;
531	if (ioctl(devMemFd, MEMRANGE_GET, &mro)) {
532		free(mrd);
533		return NULL;
534	}
535	return mrd;
536}
537
538/*
539 * cleanMTRR removes any memory attribute that may be left by a previous
540 * X server.  Normally there won't be any, but this takes care of the
541 * case where a server crashed without being able finish cleaning up.
542 */
543
544static Bool
545cleanMTRR()
546{
547	struct mem_range_desc *mrd;
548	struct mem_range_op mro;
549	int nmr, i;
550
551	/* This shouldn't happen */
552	if (devMemFd < 0)
553		return FALSE;
554
555	if (!(mrd = getAllRanges(&nmr)))
556		return FALSE;
557
558	for (i = 0; i < nmr; i++) {
559		if (strcmp(mrd[i].mr_owner, X_MTRR_ID) == 0 &&
560		    (mrd[i].mr_flags & MDF_ACTIVE)) {
561#ifdef DEBUG
562			ErrorF("Clean for (0x%lx,0x%lx)\n",
563				(unsigned long)mrd[i].mr_base,
564				(unsigned long)mrd[i].mr_len);
565#endif
566			if (mrd[i].mr_flags & MDF_FIXACTIVE) {
567				mro.mo_arg[0] = MEMRANGE_SET_UPDATE;
568				mrd[i].mr_flags = MDF_UNCACHEABLE;
569			} else {
570				mro.mo_arg[0] = MEMRANGE_SET_REMOVE;
571			}
572			mro.mo_desc = mrd + i;
573			ioctl(devMemFd, MEMRANGE_SET, &mro);
574		}
575	}
576#ifdef DEBUG
577	sleep(10);
578#endif
579	free(mrd);
580	return TRUE;
581}
582
583typedef struct x_RangeRec {
584	struct mem_range_desc	mrd;
585	Bool			wasWC;
586	struct x_RangeRec *	next;
587} RangeRec, *RangePtr;
588
589static void
590freeRangeList(RangePtr range)
591{
592	RangePtr rp;
593
594	while (range) {
595		rp = range;
596		range = rp->next;
597		free(rp);
598	}
599}
600
601static RangePtr
602dupRangeList(RangePtr list)
603{
604	RangePtr new = NULL, rp, p;
605
606	rp = list;
607	while (rp) {
608		p = xnfalloc(sizeof(RangeRec));
609		*p = *rp;
610		p->next = new;
611		new = p;
612		rp = rp->next;
613	}
614	return new;
615}
616
617static RangePtr
618sortRangeList(RangePtr list)
619{
620	RangePtr rp1, rp2, copy, sorted = NULL, minp, prev, minprev;
621	unsigned long minBase;
622
623	/* Sort by base address */
624	rp1 = copy = dupRangeList(list);
625	while (rp1) {
626		minBase = rp1->mrd.mr_base;
627		minp = rp1;
628		minprev = NULL;
629		prev = rp1;
630		rp2 = rp1->next;
631		while (rp2) {
632			if (rp2->mrd.mr_base < minBase) {
633				minBase = rp2->mrd.mr_base;
634				minp = rp2;
635				minprev = prev;
636			}
637			prev = rp2;
638			rp2 = rp2->next;
639		}
640		if (minprev) {
641			minprev->next = minp->next;
642			rp1 = copy;
643		} else {
644			rp1 = minp->next;
645		}
646		minp->next = sorted;
647		sorted = minp;
648	}
649	return sorted;
650}
651
652/*
653 * findRanges returns a list of ranges that overlap the specified range.
654 */
655
656static void
657findRanges(unsigned long base, unsigned long size, RangePtr *ucp, RangePtr *wcp)
658{
659	struct mem_range_desc *mrd;
660	int nmr, i;
661	RangePtr rp, *p;
662
663	if (!(mrd = getAllRanges(&nmr)))
664		return;
665
666	for (i = 0; i < nmr; i++) {
667		if ((mrd[i].mr_flags & MDF_ACTIVE) &&
668		    mrd[i].mr_base < base + size &&
669		    mrd[i].mr_base + mrd[i].mr_len > base) {
670			if (mrd[i].mr_flags & MDF_WRITECOMBINE)
671				p = wcp;
672			else if (mrd[i].mr_flags & MDF_UNCACHEABLE)
673				p = ucp;
674			else
675				continue;
676			rp = xnfalloc(sizeof(RangeRec));
677			rp->mrd = mrd[i];
678			rp->next = *p;
679			*p = rp;
680		}
681	}
682	free(mrd);
683}
684
685/*
686 * This checks if the existing overlapping ranges fully cover the requested
687 * range.  Is this overkill?
688 */
689
690static Bool
691fullCoverage(unsigned long base, unsigned long size, RangePtr overlap)
692{
693	RangePtr rp1, sorted = NULL;
694	unsigned long end;
695
696	sorted = sortRangeList(overlap);
697	/* Look for gaps */
698	rp1 = sorted;
699	end = base + size;
700	while (rp1) {
701		if (rp1->mrd.mr_base > base) {
702			freeRangeList(sorted);
703			return FALSE;
704		} else {
705			base = rp1->mrd.mr_base + rp1->mrd.mr_len;
706		}
707		if (base >= end) {
708			freeRangeList(sorted);
709			return TRUE;
710		}
711		rp1 = rp1->next;
712	}
713	freeRangeList(sorted);
714	return FALSE;
715}
716
717static pointer
718addWC(int screenNum, unsigned long base, unsigned long size, MessageType from)
719{
720	RangePtr uc = NULL, wc = NULL, retlist = NULL;
721	struct mem_range_desc mrd;
722	struct mem_range_op mro;
723
724	findRanges(base, size, &uc, &wc);
725
726	/* See of the full range is already WC */
727	if (!uc && fullCoverage(base, size, wc)) {
728		xf86DrvMsg(screenNum, from,
729		   "Write-combining range (0x%lx,0x%lx) was already set\n",
730		    base, size);
731		return NULL;
732	}
733
734	/* Otherwise, try to add the new range */
735	mrd.mr_base = base;
736	mrd.mr_len = size;
737	strcpy(mrd.mr_owner, X_MTRR_ID);
738	mrd.mr_flags = MDF_WRITECOMBINE;
739	mro.mo_desc = &mrd;
740	mro.mo_arg[0] = MEMRANGE_SET_UPDATE;
741	if (ioctl(devMemFd, MEMRANGE_SET, &mro)) {
742		xf86DrvMsg(screenNum, X_WARNING,
743			   "Failed to set write-combining range "
744			   "(0x%lx,0x%lx)\n", base, size);
745		return NULL;
746	} else {
747		xf86DrvMsg(screenNum, from,
748			   "Write-combining range (0x%lx,0x%lx)\n", base, size);
749		retlist = xnfalloc(sizeof(RangeRec));
750		retlist->mrd = mrd;
751		retlist->wasWC = FALSE;
752		retlist->next = NULL;
753		return retlist;
754	}
755}
756
757static pointer
758delWC(int screenNum, unsigned long base, unsigned long size, MessageType from)
759{
760	RangePtr uc = NULL, wc = NULL, retlist = NULL;
761	struct mem_range_desc mrd;
762	struct mem_range_op mro;
763
764	findRanges(base, size, &uc, &wc);
765
766	/*
767	 * See of the full range is already not WC, or if there is full
768	 * coverage from UC ranges.
769	 */
770	if (!wc || fullCoverage(base, size, uc)) {
771		xf86DrvMsg(screenNum, from,
772		   "Write-combining range (0x%lx,0x%lx) was already clear\n",
773		    base, size);
774		return NULL;
775	}
776
777	/* Otherwise, try to add the new range */
778	mrd.mr_base = base;
779	mrd.mr_len = size;
780	strcpy(mrd.mr_owner, X_MTRR_ID);
781	mrd.mr_flags = MDF_UNCACHEABLE;
782	mro.mo_desc = &mrd;
783	mro.mo_arg[0] = MEMRANGE_SET_UPDATE;
784	if (ioctl(devMemFd, MEMRANGE_SET, &mro)) {
785		xf86DrvMsg(screenNum, X_WARNING,
786			   "Failed to remove write-combining range "
787			   "(0x%lx,0x%lx)\n", base, size);
788		/* XXX Should then remove all of the overlapping WC ranges */
789		return NULL;
790	} else {
791		xf86DrvMsg(screenNum, from,
792			   "Removed Write-combining range (0x%lx,0x%lx)\n",
793			   base, size);
794		retlist = xnfalloc(sizeof(RangeRec));
795		retlist->mrd = mrd;
796		retlist->wasWC = TRUE;
797		retlist->next = NULL;
798		return retlist;
799	}
800}
801
802static pointer
803setWC(int screenNum, unsigned long base, unsigned long size, Bool enable,
804	MessageType from)
805{
806	if (enable)
807		return addWC(screenNum, base, size, from);
808	else
809		return delWC(screenNum, base, size, from);
810}
811
812static void
813undoWC(int screenNum, pointer list)
814{
815	RangePtr rp;
816	struct mem_range_op mro;
817	Bool failed;
818
819	rp = list;
820	while (rp) {
821#ifdef DEBUG
822		ErrorF("Undo for (0x%lx,0x%lx), %d\n",
823			(unsigned long)rp->mrd.mr_base,
824			(unsigned long)rp->mrd.mr_len, rp->wasWC);
825#endif
826		failed = FALSE;
827		if (rp->wasWC) {
828			mro.mo_arg[0] = MEMRANGE_SET_UPDATE;
829			rp->mrd.mr_flags = MDF_WRITECOMBINE;
830			strcpy(rp->mrd.mr_owner, "unknown");
831		} else {
832			mro.mo_arg[0] = MEMRANGE_SET_REMOVE;
833		}
834		mro.mo_desc = &rp->mrd;
835
836		if (ioctl(devMemFd, MEMRANGE_SET, &mro)) {
837			if (!rp->wasWC) {
838				mro.mo_arg[0] = MEMRANGE_SET_UPDATE;
839				rp->mrd.mr_flags = MDF_UNCACHEABLE;
840				strcpy(rp->mrd.mr_owner, "unknown");
841				if (ioctl(devMemFd, MEMRANGE_SET, &mro))
842					failed = TRUE;
843			} else
844				failed = TRUE;
845		}
846		if (failed) {
847			xf86DrvMsg(screenNum, X_WARNING,
848				"Failed to restore MTRR range (0x%lx,0x%lx)\n",
849				(unsigned long)rp->mrd.mr_base,
850				(unsigned long)rp->mrd.mr_len);
851		}
852		rp = rp->next;
853	}
854}
855
856#endif /* HAS_MTRR_SUPPORT */
857
858
859#if defined(HAS_MTRR_BUILTIN) && defined(__NetBSD__)
860static pointer
861NetBSDsetWC(int screenNum, unsigned long base, unsigned long size, Bool enable,
862	    MessageType from)
863{
864	struct mtrr *mtrrp;
865	int n;
866
867	xf86DrvMsg(screenNum, X_WARNING,
868		   "%s MTRR %lx - %lx\n", enable ? "set" : "remove",
869		   base, (base + size));
870
871	mtrrp = xnfalloc(sizeof (struct mtrr));
872	mtrrp->base = base;
873	mtrrp->len = size;
874	mtrrp->type = MTRR_TYPE_WC;
875
876	/*
877	 * MTRR_PRIVATE will make this MTRR get reset automatically
878	 * if this process exits, so we have no need for an explicit
879	 * cleanup operation when starting a new server.
880	 */
881
882	if (enable)
883		mtrrp->flags = MTRR_VALID | MTRR_PRIVATE;
884	else
885		mtrrp->flags = 0;
886	n = 1;
887
888	if (i386_set_mtrr(mtrrp, &n) < 0) {
889		free(mtrrp);
890		return NULL;
891	}
892	return mtrrp;
893}
894
895static void
896NetBSDundoWC(int screenNum, pointer list)
897{
898	struct mtrr *mtrrp = (struct mtrr *)list;
899	int n;
900
901	if (mtrrp == NULL)
902		return;
903	n = 1;
904	mtrrp->flags &= ~MTRR_VALID;
905	i386_set_mtrr(mtrrp, &n);
906	free(mtrrp);
907}
908#endif
909