2 * Copyright 1992 by Rich Murphey <Rich@Rice.edu>
3 * Copyright 1993 by David Wexelblat <dwex@goblin.org>
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
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.
26 #ifdef HAVE_XORG_CONFIG_H
27 #include <xorg-config.h>
37 #ifdef HAS_MTRR_SUPPORT
39 #include <sys/types.h>
40 #include <sys/memrange.h>
44 #define X_MTRR_ID "XFree86"
47 #if defined(HAS_MTRR_BUILTIN) && defined(__NetBSD__)
48 #include <machine/mtrr.h>
49 #include <machine/sysarch.h>
50 #include <sys/queue.h>
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
58 #include "xf86_OSlib.h"
59 #include "xf86OSpriv.h"
61 #if defined(__NetBSD__) && !defined(MAP_FILE)
62 #define MAP_FLAGS MAP_SHARED
64 #define MAP_FLAGS (MAP_FILE | MAP_SHARED)
68 #define SYSCTL_MSG "\tCheck that you have set 'machdep.allowaperture=1'\n"\
69 "\tin /etc/sysctl.conf and reboot your machine\n" \
70 "\trefer to xf86(4) for details"
72 "Check that you have set 'machdep.allowaperture=2'\n" \
73 "\tin /etc/sysctl.conf and reboot your machine\n" \
74 "\trefer to xf86(4) for details"
77 /***************************************************************************/
78 /* Video Memory Mapping section */
79 /***************************************************************************/
81 static Bool useDevMem
= FALSE
;
82 static int devMemFd
= -1;
84 #ifdef HAS_APERTURE_DRV
85 #define DEV_APERTURE "/dev/xf86"
88 static pointer
mapVidMem(int, unsigned long, unsigned long, int);
89 static void unmapVidMem(int, pointer
, unsigned long);
91 #ifdef HAS_MTRR_SUPPORT
92 static pointer
setWC(int, unsigned long, unsigned long, Bool
, MessageType
);
93 static void undoWC(int, pointer
);
94 static Bool
cleanMTRR(void);
96 #if defined(HAS_MTRR_BUILTIN) && defined(__NetBSD__)
97 static pointer
NetBSDsetWC(int, unsigned long, unsigned long, Bool
,
99 static void NetBSDundoWC(int, pointer
);
103 * Check if /dev/mem can be mmap'd. If it can't print a warning when
107 checkDevMem(Bool warn
)
109 static Bool devMemChecked
= FALSE
;
115 devMemChecked
= TRUE
;
117 if ((fd
= open(DEV_MEM
, O_RDWR
)) >= 0) {
118 /* Try to map a page at the VGA address */
119 base
= mmap((caddr_t
) 0, 4096, PROT_READ
| PROT_WRITE
,
120 MAP_FLAGS
, fd
, (off_t
) 0xA0000);
122 if (base
!= MAP_FAILED
) {
123 munmap((caddr_t
) base
, 4096);
129 /* This should not happen */
131 xf86Msg(X_WARNING
, "checkDevMem: failed to mmap %s (%s)\n",
132 DEV_MEM
, strerror(errno
));
138 #ifndef HAS_APERTURE_DRV
140 xf86Msg(X_WARNING
, "checkDevMem: failed to open %s (%s)\n",
141 DEV_MEM
, strerror(errno
));
146 /* Failed to open /dev/mem, try the aperture driver */
147 if ((fd
= open(DEV_APERTURE
, O_RDWR
)) >= 0) {
148 /* Try to map a page at the VGA address */
149 base
= mmap((caddr_t
) 0, 4096, PROT_READ
| PROT_WRITE
,
150 MAP_FLAGS
, fd
, (off_t
) 0xA0000);
152 if (base
!= MAP_FAILED
) {
153 munmap((caddr_t
) base
, 4096);
156 xf86Msg(X_INFO
, "checkDevMem: using aperture driver %s\n",
163 xf86Msg(X_WARNING
, "checkDevMem: failed to mmap %s (%s)\n",
164 DEV_APERTURE
, strerror(errno
));
171 xf86Msg(X_WARNING
, "checkDevMem: failed to open %s and %s\n"
172 "\t(%s)\n", DEV_MEM
, DEV_APERTURE
, strerror(errno
));
173 #else /* __OpenBSD__ */
174 xf86Msg(X_WARNING
, "checkDevMem: failed to open %s and %s\n"
175 "\t(%s)\n%s", DEV_MEM
, DEV_APERTURE
, strerror(errno
),
177 #endif /* __OpenBSD__ */
188 xf86OSInitVidMem(VidMemInfoPtr pVidMem
)
191 pVidMem
->linearSupported
= useDevMem
;
192 pVidMem
->mapMem
= mapVidMem
;
193 pVidMem
->unmapMem
= unmapVidMem
;
196 pci_system_init_dev_mem(devMemFd
);
198 #ifdef HAS_MTRR_SUPPORT
201 pVidMem
->setWC
= setWC
;
202 pVidMem
->undoWC
= undoWC
;
206 #if defined(HAS_MTRR_BUILTIN) && defined(__NetBSD__)
207 pVidMem
->setWC
= NetBSDsetWC
;
208 pVidMem
->undoWC
= NetBSDundoWC
;
210 pVidMem
->initialised
= TRUE
;
214 mapVidMem(int ScreenNum
, unsigned long Base
, unsigned long Size
, int flags
)
222 FatalError("xf86MapVidMem: failed to open %s (%s)",
223 DEV_MEM
, strerror(errno
));
225 base
= mmap((caddr_t
) 0, Size
,
226 (flags
& VIDMEM_READONLY
) ?
227 PROT_READ
: (PROT_READ
| PROT_WRITE
),
228 MAP_FLAGS
, devMemFd
, (off_t
) Base
);
229 if (base
== MAP_FAILED
) {
230 FatalError("%s: could not mmap %s [s=%lx,a=%lx] (%s)",
231 "xf86MapVidMem", DEV_MEM
, Size
, Base
, strerror(errno
));
236 /* else, mmap /dev/vga */
237 if ((unsigned long) Base
< 0xA0000 || (unsigned long) Base
>= 0xC0000) {
238 FatalError("%s: Address 0x%lx outside allowable range",
239 "xf86MapVidMem", Base
);
242 (flags
& VIDMEM_READONLY
) ?
243 PROT_READ
: (PROT_READ
| PROT_WRITE
),
244 MAP_FLAGS
, xf86Info
.consoleFd
, (unsigned long) Base
- 0xA0000);
245 if (base
== MAP_FAILED
) {
246 FatalError("xf86MapVidMem: Could not mmap /dev/vga (%s)",
253 unmapVidMem(int ScreenNum
, pointer Base
, unsigned long Size
)
255 munmap((caddr_t
) Base
, Size
);
259 * Read BIOS via mmap()ing DEV_MEM
263 xf86ReadBIOS(unsigned long Base
, unsigned long Offset
, unsigned char *Buf
,
271 if (devMemFd
== -1) {
275 psize
= getpagesize();
276 Offset
+= Base
& (psize
- 1);
277 Base
&= ~(psize
- 1);
278 mlen
= (Offset
+ Len
+ psize
- 1) & ~(psize
- 1);
279 ptr
= (unsigned char *) mmap((caddr_t
) 0, mlen
, PROT_READ
,
280 MAP_SHARED
, devMemFd
, (off_t
) Base
);
281 if ((long) ptr
== -1) {
283 "xf86ReadBIOS: %s mmap[s=%x,a=%lx,o=%lx] failed (%s)\n",
284 DEV_MEM
, Len
, Base
, Offset
, strerror(errno
));
286 if (Base
< 0xa0000) {
287 xf86Msg(X_WARNING
, SYSCTL_MSG2
);
293 ErrorF("xf86ReadBIOS: BIOS at 0x%08x has signature 0x%04x\n",
294 Base
, ptr
[0] | (ptr
[1] << 8));
296 (void) memcpy(Buf
, (void *) (ptr
+ Offset
), Len
);
297 (void) munmap((caddr_t
) ptr
, mlen
);
299 xf86MsgVerb(X_INFO
, 3, "xf86ReadBIOS(%x, %x, Buf, %x)"
300 "-> %02x %02x %02x %02x...\n",
301 Base
, Offset
, Len
, Buf
[0], Buf
[1], Buf
[2], Buf
[3]);
307 /***************************************************************************/
308 /* I/O Permissions section */
309 /***************************************************************************/
311 static Bool ExtendedEnabled
= FALSE
;
319 if (i386_iopl(TRUE
) < 0) {
321 xf86Msg(X_WARNING
, "%s: Failed to set IOPL for extended I/O",
324 xf86Msg(X_WARNING
, "%s: Failed to set IOPL for extended I/O\n%s",
325 "xf86EnableIO", SYSCTL_MSG
);
329 ExtendedEnabled
= TRUE
;
337 if (!ExtendedEnabled
)
341 ExtendedEnabled
= FALSE
;
346 #endif /* USE_I386_IOPL */
348 #ifdef USE_AMD64_IOPL
349 /***************************************************************************/
350 /* I/O Permissions section */
351 /***************************************************************************/
353 static Bool ExtendedEnabled
= FALSE
;
361 if (amd64_iopl(TRUE
) < 0) {
363 xf86Msg(X_WARNING
, "%s: Failed to set IOPL for extended I/O",
366 xf86Msg(X_WARNING
, "%s: Failed to set IOPL for extended I/O\n%s",
367 "xf86EnableIO", SYSCTL_MSG
);
371 ExtendedEnabled
= TRUE
;
379 if (!ExtendedEnabled
)
382 if (amd64_iopl(FALSE
) == 0) {
383 ExtendedEnabled
= FALSE
;
385 /* Otherwise, the X server has revoqued its root uid,
386 and thus cannot give up IO privileges any more */
391 #endif /* USE_AMD64_IOPL */
394 static int IoFd
= -1;
402 if ((IoFd
= open("/dev/io", O_RDWR
)) == -1) {
403 xf86Msg(X_WARNING
, "xf86EnableIO: "
404 "Failed to open /dev/io for extended I/O");
424 /***************************************************************************/
425 /* Set TV output mode */
426 /***************************************************************************/
428 xf86SetTVOut(int mode
)
430 switch (xf86Info
.consType
) {
431 #ifdef PCCONS_SUPPORT
434 if (ioctl(xf86Info
.consoleFd
, CONSOLE_X_TV_ON
, &mode
) < 0) {
436 "xf86SetTVOut: Could not set console to TV output, %s\n",
441 #endif /* PCCONS_SUPPORT */
444 FatalError("Xf86SetTVOut: Unsupported console");
453 switch (xf86Info
.consType
) {
454 #ifdef PCCONS_SUPPORT
457 if (ioctl(xf86Info
.consoleFd
, CONSOLE_X_TV_OFF
, 0) < 0) {
459 "xf86SetTVOut: Could not set console to RGB output, %s\n",
464 #endif /* PCCONS_SUPPORT */
467 FatalError("Xf86SetTVOut: Unsupported console");
474 #ifdef HAS_MTRR_SUPPORT
475 /* memory range (MTRR) support for FreeBSD */
478 * This code is experimental. Some parts may be overkill, and other parts
483 * getAllRanges returns the full list of memory ranges with attributes set.
486 static struct mem_range_desc
*
487 getAllRanges(int *nmr
)
489 struct mem_range_desc
*mrd
;
490 struct mem_range_op mro
;
493 * Find how many ranges there are. If this fails, then the kernel
494 * probably doesn't have MTRR support.
497 if (ioctl(devMemFd
, MEMRANGE_GET
, &mro
))
499 *nmr
= mro
.mo_arg
[0];
500 mrd
= xnfalloc(*nmr
* sizeof(struct mem_range_desc
));
501 mro
.mo_arg
[0] = *nmr
;
503 if (ioctl(devMemFd
, MEMRANGE_GET
, &mro
)) {
511 * cleanMTRR removes any memory attribute that may be left by a previous
512 * X server. Normally there won't be any, but this takes care of the
513 * case where a server crashed without being able finish cleaning up.
519 struct mem_range_desc
*mrd
;
520 struct mem_range_op mro
;
523 /* This shouldn't happen */
527 if (!(mrd
= getAllRanges(&nmr
)))
530 for (i
= 0; i
< nmr
; i
++) {
531 if (strcmp(mrd
[i
].mr_owner
, X_MTRR_ID
) == 0 &&
532 (mrd
[i
].mr_flags
& MDF_ACTIVE
)) {
534 ErrorF("Clean for (0x%lx,0x%lx)\n",
535 (unsigned long) mrd
[i
].mr_base
,
536 (unsigned long) mrd
[i
].mr_len
);
538 if (mrd
[i
].mr_flags
& MDF_FIXACTIVE
) {
539 mro
.mo_arg
[0] = MEMRANGE_SET_UPDATE
;
540 mrd
[i
].mr_flags
= MDF_UNCACHEABLE
;
543 mro
.mo_arg
[0] = MEMRANGE_SET_REMOVE
;
545 mro
.mo_desc
= mrd
+ i
;
546 ioctl(devMemFd
, MEMRANGE_SET
, &mro
);
556 typedef struct x_RangeRec
{
557 struct mem_range_desc mrd
;
559 struct x_RangeRec
*next
;
560 } RangeRec
, *RangePtr
;
563 freeRangeList(RangePtr range
)
575 dupRangeList(RangePtr list
)
577 RangePtr
new = NULL
, rp
, p
;
581 p
= xnfalloc(sizeof(RangeRec
));
591 sortRangeList(RangePtr list
)
593 RangePtr rp1
, rp2
, copy
, sorted
= NULL
, minp
, prev
, minprev
;
594 unsigned long minBase
;
596 /* Sort by base address */
597 rp1
= copy
= dupRangeList(list
);
599 minBase
= rp1
->mrd
.mr_base
;
605 if (rp2
->mrd
.mr_base
< minBase
) {
606 minBase
= rp2
->mrd
.mr_base
;
614 minprev
->next
= minp
->next
;
627 * findRanges returns a list of ranges that overlap the specified range.
631 findRanges(unsigned long base
, unsigned long size
, RangePtr
* ucp
,
634 struct mem_range_desc
*mrd
;
638 if (!(mrd
= getAllRanges(&nmr
)))
641 for (i
= 0; i
< nmr
; i
++) {
642 if ((mrd
[i
].mr_flags
& MDF_ACTIVE
) &&
643 mrd
[i
].mr_base
< base
+ size
&&
644 mrd
[i
].mr_base
+ mrd
[i
].mr_len
> base
) {
645 if (mrd
[i
].mr_flags
& MDF_WRITECOMBINE
)
647 else if (mrd
[i
].mr_flags
& MDF_UNCACHEABLE
)
651 rp
= xnfalloc(sizeof(RangeRec
));
661 * This checks if the existing overlapping ranges fully cover the requested
662 * range. Is this overkill?
666 fullCoverage(unsigned long base
, unsigned long size
, RangePtr overlap
)
668 RangePtr rp1
, sorted
= NULL
;
671 sorted
= sortRangeList(overlap
);
676 if (rp1
->mrd
.mr_base
> base
) {
677 freeRangeList(sorted
);
681 base
= rp1
->mrd
.mr_base
+ rp1
->mrd
.mr_len
;
684 freeRangeList(sorted
);
689 freeRangeList(sorted
);
694 addWC(int screenNum
, unsigned long base
, unsigned long size
, MessageType from
)
696 RangePtr uc
= NULL
, wc
= NULL
, retlist
= NULL
;
697 struct mem_range_desc mrd
;
698 struct mem_range_op mro
;
700 findRanges(base
, size
, &uc
, &wc
);
702 /* See of the full range is already WC */
703 if (!uc
&& fullCoverage(base
, size
, wc
)) {
704 xf86DrvMsg(screenNum
, from
,
705 "Write-combining range (0x%lx,0x%lx) was already set\n",
710 /* Otherwise, try to add the new range */
713 strcpy(mrd
.mr_owner
, X_MTRR_ID
);
714 mrd
.mr_flags
= MDF_WRITECOMBINE
;
716 mro
.mo_arg
[0] = MEMRANGE_SET_UPDATE
;
717 if (ioctl(devMemFd
, MEMRANGE_SET
, &mro
)) {
718 xf86DrvMsg(screenNum
, X_WARNING
,
719 "Failed to set write-combining range "
720 "(0x%lx,0x%lx)\n", base
, size
);
724 xf86DrvMsg(screenNum
, from
,
725 "Write-combining range (0x%lx,0x%lx)\n", base
, size
);
726 retlist
= xnfalloc(sizeof(RangeRec
));
728 retlist
->wasWC
= FALSE
;
729 retlist
->next
= NULL
;
735 delWC(int screenNum
, unsigned long base
, unsigned long size
, MessageType from
)
737 RangePtr uc
= NULL
, wc
= NULL
, retlist
= NULL
;
738 struct mem_range_desc mrd
;
739 struct mem_range_op mro
;
741 findRanges(base
, size
, &uc
, &wc
);
744 * See of the full range is already not WC, or if there is full
745 * coverage from UC ranges.
747 if (!wc
|| fullCoverage(base
, size
, uc
)) {
748 xf86DrvMsg(screenNum
, from
,
749 "Write-combining range (0x%lx,0x%lx) was already clear\n",
754 /* Otherwise, try to add the new range */
757 strcpy(mrd
.mr_owner
, X_MTRR_ID
);
758 mrd
.mr_flags
= MDF_UNCACHEABLE
;
760 mro
.mo_arg
[0] = MEMRANGE_SET_UPDATE
;
761 if (ioctl(devMemFd
, MEMRANGE_SET
, &mro
)) {
762 xf86DrvMsg(screenNum
, X_WARNING
,
763 "Failed to remove write-combining range "
764 "(0x%lx,0x%lx)\n", base
, size
);
765 /* XXX Should then remove all of the overlapping WC ranges */
769 xf86DrvMsg(screenNum
, from
,
770 "Removed Write-combining range (0x%lx,0x%lx)\n", base
, size
);
771 retlist
= xnfalloc(sizeof(RangeRec
));
773 retlist
->wasWC
= TRUE
;
774 retlist
->next
= NULL
;
780 setWC(int screenNum
, unsigned long base
, unsigned long size
, Bool enable
,
784 return addWC(screenNum
, base
, size
, from
);
786 return delWC(screenNum
, base
, size
, from
);
790 undoWC(int screenNum
, pointer list
)
793 struct mem_range_op mro
;
799 ErrorF("Undo for (0x%lx,0x%lx), %d\n",
800 (unsigned long) rp
->mrd
.mr_base
,
801 (unsigned long) rp
->mrd
.mr_len
, rp
->wasWC
);
805 mro
.mo_arg
[0] = MEMRANGE_SET_UPDATE
;
806 rp
->mrd
.mr_flags
= MDF_WRITECOMBINE
;
807 strcpy(rp
->mrd
.mr_owner
, "unknown");
810 mro
.mo_arg
[0] = MEMRANGE_SET_REMOVE
;
812 mro
.mo_desc
= &rp
->mrd
;
814 if (ioctl(devMemFd
, MEMRANGE_SET
, &mro
)) {
816 mro
.mo_arg
[0] = MEMRANGE_SET_UPDATE
;
817 rp
->mrd
.mr_flags
= MDF_UNCACHEABLE
;
818 strcpy(rp
->mrd
.mr_owner
, "unknown");
819 if (ioctl(devMemFd
, MEMRANGE_SET
, &mro
))
826 xf86DrvMsg(screenNum
, X_WARNING
,
827 "Failed to restore MTRR range (0x%lx,0x%lx)\n",
828 (unsigned long) rp
->mrd
.mr_base
,
829 (unsigned long) rp
->mrd
.mr_len
);
835 #endif /* HAS_MTRR_SUPPORT */
837 #if defined(HAS_MTRR_BUILTIN) && defined(__NetBSD__)
839 NetBSDsetWC(int screenNum
, unsigned long base
, unsigned long size
, Bool enable
,
845 xf86DrvMsg(screenNum
, X_WARNING
,
846 "%s MTRR %lx - %lx\n", enable
? "set" : "remove",
847 base
, (base
+ size
));
849 mtrrp
= xnfalloc(sizeof(struct mtrr
));
852 mtrrp
->type
= MTRR_TYPE_WC
;
855 * MTRR_PRIVATE will make this MTRR get reset automatically
856 * if this process exits, so we have no need for an explicit
857 * cleanup operation when starting a new server.
861 mtrrp
->flags
= MTRR_VALID
| MTRR_PRIVATE
;
866 if (i386_set_mtrr(mtrrp
, &n
) < 0) {
874 NetBSDundoWC(int screenNum
, pointer list
)
876 struct mtrr
*mtrrp
= (struct mtrr
*) list
;
882 mtrrp
->flags
&= ~MTRR_VALID
;
883 i386_set_mtrr(mtrrp
, &n
);