Commit | Line | Data |
---|---|---|
a09e091a JB |
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 | #ifdef __OpenBSD__ | |
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" | |
71 | #define SYSCTL_MSG2 \ | |
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" | |
75 | #endif | |
76 | ||
77 | /***************************************************************************/ | |
78 | /* Video Memory Mapping section */ | |
79 | /***************************************************************************/ | |
80 | ||
81 | static Bool useDevMem = FALSE; | |
82 | static int devMemFd = -1; | |
83 | ||
84 | #ifdef HAS_APERTURE_DRV | |
85 | #define DEV_APERTURE "/dev/xf86" | |
86 | #endif | |
87 | ||
88 | static pointer mapVidMem(int, unsigned long, unsigned long, int); | |
89 | static void unmapVidMem(int, pointer, unsigned long); | |
90 | ||
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); | |
95 | #endif | |
96 | #if defined(HAS_MTRR_BUILTIN) && defined(__NetBSD__) | |
97 | static pointer NetBSDsetWC(int, unsigned long, unsigned long, Bool, | |
98 | MessageType); | |
99 | static void NetBSDundoWC(int, pointer); | |
100 | #endif | |
101 | ||
102 | /* | |
103 | * Check if /dev/mem can be mmap'd. If it can't print a warning when | |
104 | * "warn" is TRUE. | |
105 | */ | |
106 | static void | |
107 | checkDevMem(Bool warn) | |
108 | { | |
109 | static Bool devMemChecked = FALSE; | |
110 | int fd; | |
111 | pointer base; | |
112 | ||
113 | if (devMemChecked) | |
114 | return; | |
115 | devMemChecked = TRUE; | |
116 | ||
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); | |
121 | ||
122 | if (base != MAP_FAILED) { | |
123 | munmap((caddr_t) base, 4096); | |
124 | devMemFd = fd; | |
125 | useDevMem = TRUE; | |
126 | return; | |
127 | } | |
128 | else { | |
129 | /* This should not happen */ | |
130 | if (warn) { | |
131 | xf86Msg(X_WARNING, "checkDevMem: failed to mmap %s (%s)\n", | |
132 | DEV_MEM, strerror(errno)); | |
133 | } | |
134 | useDevMem = FALSE; | |
135 | return; | |
136 | } | |
137 | } | |
138 | #ifndef HAS_APERTURE_DRV | |
139 | if (warn) { | |
140 | xf86Msg(X_WARNING, "checkDevMem: failed to open %s (%s)\n", | |
141 | DEV_MEM, strerror(errno)); | |
142 | } | |
143 | useDevMem = FALSE; | |
144 | return; | |
145 | #else | |
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); | |
151 | ||
152 | if (base != MAP_FAILED) { | |
153 | munmap((caddr_t) base, 4096); | |
154 | devMemFd = fd; | |
155 | useDevMem = TRUE; | |
156 | xf86Msg(X_INFO, "checkDevMem: using aperture driver %s\n", | |
157 | DEV_APERTURE); | |
158 | return; | |
159 | } | |
160 | else { | |
161 | ||
162 | if (warn) { | |
163 | xf86Msg(X_WARNING, "checkDevMem: failed to mmap %s (%s)\n", | |
164 | DEV_APERTURE, strerror(errno)); | |
165 | } | |
166 | } | |
167 | } | |
168 | else { | |
169 | if (warn) { | |
170 | #ifndef __OpenBSD__ | |
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), | |
176 | SYSCTL_MSG); | |
177 | #endif /* __OpenBSD__ */ | |
178 | } | |
179 | } | |
180 | ||
181 | useDevMem = FALSE; | |
182 | return; | |
183 | ||
184 | #endif | |
185 | } | |
186 | ||
187 | void | |
188 | xf86OSInitVidMem(VidMemInfoPtr pVidMem) | |
189 | { | |
190 | checkDevMem(TRUE); | |
191 | pVidMem->linearSupported = useDevMem; | |
192 | pVidMem->mapMem = mapVidMem; | |
193 | pVidMem->unmapMem = unmapVidMem; | |
194 | ||
195 | if (useDevMem) | |
196 | pci_system_init_dev_mem(devMemFd); | |
197 | ||
198 | #ifdef HAS_MTRR_SUPPORT | |
199 | if (useDevMem) { | |
200 | if (cleanMTRR()) { | |
201 | pVidMem->setWC = setWC; | |
202 | pVidMem->undoWC = undoWC; | |
203 | } | |
204 | } | |
205 | #endif | |
206 | #if defined(HAS_MTRR_BUILTIN) && defined(__NetBSD__) | |
207 | pVidMem->setWC = NetBSDsetWC; | |
208 | pVidMem->undoWC = NetBSDundoWC; | |
209 | #endif | |
210 | pVidMem->initialised = TRUE; | |
211 | } | |
212 | ||
213 | static pointer | |
214 | mapVidMem(int ScreenNum, unsigned long Base, unsigned long Size, int flags) | |
215 | { | |
216 | pointer base; | |
217 | ||
218 | checkDevMem(FALSE); | |
219 | ||
220 | if (useDevMem) { | |
221 | if (devMemFd < 0) { | |
222 | FatalError("xf86MapVidMem: failed to open %s (%s)", | |
223 | DEV_MEM, strerror(errno)); | |
224 | } | |
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)); | |
232 | } | |
233 | return base; | |
234 | } | |
235 | ||
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); | |
240 | } | |
241 | base = mmap(0, Size, | |
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)", | |
247 | strerror(errno)); | |
248 | } | |
249 | return base; | |
250 | } | |
251 | ||
252 | static void | |
253 | unmapVidMem(int ScreenNum, pointer Base, unsigned long Size) | |
254 | { | |
255 | munmap((caddr_t) Base, Size); | |
256 | } | |
257 | ||
258 | /* | |
259 | * Read BIOS via mmap()ing DEV_MEM | |
260 | */ | |
261 | ||
262 | int | |
263 | xf86ReadBIOS(unsigned long Base, unsigned long Offset, unsigned char *Buf, | |
264 | int Len) | |
265 | { | |
266 | unsigned char *ptr; | |
267 | int psize; | |
268 | int mlen; | |
269 | ||
270 | checkDevMem(TRUE); | |
271 | if (devMemFd == -1) { | |
272 | return -1; | |
273 | } | |
274 | ||
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) { | |
282 | xf86Msg(X_WARNING, | |
283 | "xf86ReadBIOS: %s mmap[s=%x,a=%lx,o=%lx] failed (%s)\n", | |
284 | DEV_MEM, Len, Base, Offset, strerror(errno)); | |
285 | #ifdef __OpenBSD__ | |
286 | if (Base < 0xa0000) { | |
287 | xf86Msg(X_WARNING, SYSCTL_MSG2); | |
288 | } | |
289 | #endif | |
290 | return -1; | |
291 | } | |
292 | #ifdef DEBUG | |
293 | ErrorF("xf86ReadBIOS: BIOS at 0x%08x has signature 0x%04x\n", | |
294 | Base, ptr[0] | (ptr[1] << 8)); | |
295 | #endif | |
296 | (void) memcpy(Buf, (void *) (ptr + Offset), Len); | |
297 | (void) munmap((caddr_t) ptr, mlen); | |
298 | #ifdef DEBUG | |
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]); | |
302 | #endif | |
303 | return Len; | |
304 | } | |
305 | ||
306 | #ifdef USE_I386_IOPL | |
307 | /***************************************************************************/ | |
308 | /* I/O Permissions section */ | |
309 | /***************************************************************************/ | |
310 | ||
311 | static Bool ExtendedEnabled = FALSE; | |
312 | ||
313 | Bool | |
314 | xf86EnableIO() | |
315 | { | |
316 | if (ExtendedEnabled) | |
317 | return TRUE; | |
318 | ||
319 | if (i386_iopl(TRUE) < 0) { | |
320 | #ifndef __OpenBSD__ | |
321 | xf86Msg(X_WARNING, "%s: Failed to set IOPL for extended I/O", | |
322 | "xf86EnableIO"); | |
323 | #else | |
324 | xf86Msg(X_WARNING, "%s: Failed to set IOPL for extended I/O\n%s", | |
325 | "xf86EnableIO", SYSCTL_MSG); | |
326 | #endif | |
327 | return FALSE; | |
328 | } | |
329 | ExtendedEnabled = TRUE; | |
330 | ||
331 | return TRUE; | |
332 | } | |
333 | ||
334 | void | |
335 | xf86DisableIO() | |
336 | { | |
337 | if (!ExtendedEnabled) | |
338 | return; | |
339 | ||
340 | i386_iopl(FALSE); | |
341 | ExtendedEnabled = FALSE; | |
342 | ||
343 | return; | |
344 | } | |
345 | ||
346 | #endif /* USE_I386_IOPL */ | |
347 | ||
348 | #ifdef USE_AMD64_IOPL | |
349 | /***************************************************************************/ | |
350 | /* I/O Permissions section */ | |
351 | /***************************************************************************/ | |
352 | ||
353 | static Bool ExtendedEnabled = FALSE; | |
354 | ||
355 | Bool | |
356 | xf86EnableIO() | |
357 | { | |
358 | if (ExtendedEnabled) | |
359 | return TRUE; | |
360 | ||
361 | if (amd64_iopl(TRUE) < 0) { | |
362 | #ifndef __OpenBSD__ | |
363 | xf86Msg(X_WARNING, "%s: Failed to set IOPL for extended I/O", | |
364 | "xf86EnableIO"); | |
365 | #else | |
366 | xf86Msg(X_WARNING, "%s: Failed to set IOPL for extended I/O\n%s", | |
367 | "xf86EnableIO", SYSCTL_MSG); | |
368 | #endif | |
369 | return FALSE; | |
370 | } | |
371 | ExtendedEnabled = TRUE; | |
372 | ||
373 | return TRUE; | |
374 | } | |
375 | ||
376 | void | |
377 | xf86DisableIO() | |
378 | { | |
379 | if (!ExtendedEnabled) | |
380 | return; | |
381 | ||
382 | if (amd64_iopl(FALSE) == 0) { | |
383 | ExtendedEnabled = FALSE; | |
384 | } | |
385 | /* Otherwise, the X server has revoqued its root uid, | |
386 | and thus cannot give up IO privileges any more */ | |
387 | ||
388 | return; | |
389 | } | |
390 | ||
391 | #endif /* USE_AMD64_IOPL */ | |
392 | ||
393 | #ifdef USE_DEV_IO | |
394 | static int IoFd = -1; | |
395 | ||
396 | Bool | |
397 | xf86EnableIO() | |
398 | { | |
399 | if (IoFd >= 0) | |
400 | return TRUE; | |
401 | ||
402 | if ((IoFd = open("/dev/io", O_RDWR)) == -1) { | |
403 | xf86Msg(X_WARNING, "xf86EnableIO: " | |
404 | "Failed to open /dev/io for extended I/O"); | |
405 | return FALSE; | |
406 | } | |
407 | return TRUE; | |
408 | } | |
409 | ||
410 | void | |
411 | xf86DisableIO() | |
412 | { | |
413 | if (IoFd < 0) | |
414 | return; | |
415 | ||
416 | close(IoFd); | |
417 | IoFd = -1; | |
418 | return; | |
419 | } | |
420 | ||
421 | #endif | |
422 | ||
423 | #ifdef __NetBSD__ | |
424 | /***************************************************************************/ | |
425 | /* Set TV output mode */ | |
426 | /***************************************************************************/ | |
427 | void | |
428 | xf86SetTVOut(int mode) | |
429 | { | |
430 | switch (xf86Info.consType) { | |
431 | #ifdef PCCONS_SUPPORT | |
432 | case PCCONS:{ | |
433 | ||
434 | if (ioctl(xf86Info.consoleFd, CONSOLE_X_TV_ON, &mode) < 0) { | |
435 | xf86Msg(X_WARNING, | |
436 | "xf86SetTVOut: Could not set console to TV output, %s\n", | |
437 | strerror(errno)); | |
438 | } | |
439 | } | |
440 | break; | |
441 | #endif /* PCCONS_SUPPORT */ | |
442 | ||
443 | default: | |
444 | FatalError("Xf86SetTVOut: Unsupported console"); | |
445 | break; | |
446 | } | |
447 | return; | |
448 | } | |
449 | ||
450 | void | |
451 | xf86SetRGBOut() | |
452 | { | |
453 | switch (xf86Info.consType) { | |
454 | #ifdef PCCONS_SUPPORT | |
455 | case PCCONS:{ | |
456 | ||
457 | if (ioctl(xf86Info.consoleFd, CONSOLE_X_TV_OFF, 0) < 0) { | |
458 | xf86Msg(X_WARNING, | |
459 | "xf86SetTVOut: Could not set console to RGB output, %s\n", | |
460 | strerror(errno)); | |
461 | } | |
462 | } | |
463 | break; | |
464 | #endif /* PCCONS_SUPPORT */ | |
465 | ||
466 | default: | |
467 | FatalError("Xf86SetTVOut: Unsupported console"); | |
468 | break; | |
469 | } | |
470 | return; | |
471 | } | |
472 | #endif | |
473 | ||
474 | #ifdef HAS_MTRR_SUPPORT | |
475 | /* memory range (MTRR) support for FreeBSD */ | |
476 | ||
477 | /* | |
478 | * This code is experimental. Some parts may be overkill, and other parts | |
479 | * may be incomplete. | |
480 | */ | |
481 | ||
482 | /* | |
483 | * getAllRanges returns the full list of memory ranges with attributes set. | |
484 | */ | |
485 | ||
486 | static struct mem_range_desc * | |
487 | getAllRanges(int *nmr) | |
488 | { | |
489 | struct mem_range_desc *mrd; | |
490 | struct mem_range_op mro; | |
491 | ||
492 | /* | |
493 | * Find how many ranges there are. If this fails, then the kernel | |
494 | * probably doesn't have MTRR support. | |
495 | */ | |
496 | mro.mo_arg[0] = 0; | |
497 | if (ioctl(devMemFd, MEMRANGE_GET, &mro)) | |
498 | return NULL; | |
499 | *nmr = mro.mo_arg[0]; | |
500 | mrd = xnfalloc(*nmr * sizeof(struct mem_range_desc)); | |
501 | mro.mo_arg[0] = *nmr; | |
502 | mro.mo_desc = mrd; | |
503 | if (ioctl(devMemFd, MEMRANGE_GET, &mro)) { | |
504 | free(mrd); | |
505 | return NULL; | |
506 | } | |
507 | return mrd; | |
508 | } | |
509 | ||
510 | /* | |
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. | |
514 | */ | |
515 | ||
516 | static Bool | |
517 | cleanMTRR() | |
518 | { | |
519 | struct mem_range_desc *mrd; | |
520 | struct mem_range_op mro; | |
521 | int nmr, i; | |
522 | ||
523 | /* This shouldn't happen */ | |
524 | if (devMemFd < 0) | |
525 | return FALSE; | |
526 | ||
527 | if (!(mrd = getAllRanges(&nmr))) | |
528 | return FALSE; | |
529 | ||
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)) { | |
533 | #ifdef DEBUG | |
534 | ErrorF("Clean for (0x%lx,0x%lx)\n", | |
535 | (unsigned long) mrd[i].mr_base, | |
536 | (unsigned long) mrd[i].mr_len); | |
537 | #endif | |
538 | if (mrd[i].mr_flags & MDF_FIXACTIVE) { | |
539 | mro.mo_arg[0] = MEMRANGE_SET_UPDATE; | |
540 | mrd[i].mr_flags = MDF_UNCACHEABLE; | |
541 | } | |
542 | else { | |
543 | mro.mo_arg[0] = MEMRANGE_SET_REMOVE; | |
544 | } | |
545 | mro.mo_desc = mrd + i; | |
546 | ioctl(devMemFd, MEMRANGE_SET, &mro); | |
547 | } | |
548 | } | |
549 | #ifdef DEBUG | |
550 | sleep(10); | |
551 | #endif | |
552 | free(mrd); | |
553 | return TRUE; | |
554 | } | |
555 | ||
556 | typedef struct x_RangeRec { | |
557 | struct mem_range_desc mrd; | |
558 | Bool wasWC; | |
559 | struct x_RangeRec *next; | |
560 | } RangeRec, *RangePtr; | |
561 | ||
562 | static void | |
563 | freeRangeList(RangePtr range) | |
564 | { | |
565 | RangePtr rp; | |
566 | ||
567 | while (range) { | |
568 | rp = range; | |
569 | range = rp->next; | |
570 | free(rp); | |
571 | } | |
572 | } | |
573 | ||
574 | static RangePtr | |
575 | dupRangeList(RangePtr list) | |
576 | { | |
577 | RangePtr new = NULL, rp, p; | |
578 | ||
579 | rp = list; | |
580 | while (rp) { | |
581 | p = xnfalloc(sizeof(RangeRec)); | |
582 | *p = *rp; | |
583 | p->next = new; | |
584 | new = p; | |
585 | rp = rp->next; | |
586 | } | |
587 | return new; | |
588 | } | |
589 | ||
590 | static RangePtr | |
591 | sortRangeList(RangePtr list) | |
592 | { | |
593 | RangePtr rp1, rp2, copy, sorted = NULL, minp, prev, minprev; | |
594 | unsigned long minBase; | |
595 | ||
596 | /* Sort by base address */ | |
597 | rp1 = copy = dupRangeList(list); | |
598 | while (rp1) { | |
599 | minBase = rp1->mrd.mr_base; | |
600 | minp = rp1; | |
601 | minprev = NULL; | |
602 | prev = rp1; | |
603 | rp2 = rp1->next; | |
604 | while (rp2) { | |
605 | if (rp2->mrd.mr_base < minBase) { | |
606 | minBase = rp2->mrd.mr_base; | |
607 | minp = rp2; | |
608 | minprev = prev; | |
609 | } | |
610 | prev = rp2; | |
611 | rp2 = rp2->next; | |
612 | } | |
613 | if (minprev) { | |
614 | minprev->next = minp->next; | |
615 | rp1 = copy; | |
616 | } | |
617 | else { | |
618 | rp1 = minp->next; | |
619 | } | |
620 | minp->next = sorted; | |
621 | sorted = minp; | |
622 | } | |
623 | return sorted; | |
624 | } | |
625 | ||
626 | /* | |
627 | * findRanges returns a list of ranges that overlap the specified range. | |
628 | */ | |
629 | ||
630 | static void | |
631 | findRanges(unsigned long base, unsigned long size, RangePtr * ucp, | |
632 | RangePtr * wcp) | |
633 | { | |
634 | struct mem_range_desc *mrd; | |
635 | int nmr, i; | |
636 | RangePtr rp, *p; | |
637 | ||
638 | if (!(mrd = getAllRanges(&nmr))) | |
639 | return; | |
640 | ||
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) | |
646 | p = wcp; | |
647 | else if (mrd[i].mr_flags & MDF_UNCACHEABLE) | |
648 | p = ucp; | |
649 | else | |
650 | continue; | |
651 | rp = xnfalloc(sizeof(RangeRec)); | |
652 | rp->mrd = mrd[i]; | |
653 | rp->next = *p; | |
654 | *p = rp; | |
655 | } | |
656 | } | |
657 | free(mrd); | |
658 | } | |
659 | ||
660 | /* | |
661 | * This checks if the existing overlapping ranges fully cover the requested | |
662 | * range. Is this overkill? | |
663 | */ | |
664 | ||
665 | static Bool | |
666 | fullCoverage(unsigned long base, unsigned long size, RangePtr overlap) | |
667 | { | |
668 | RangePtr rp1, sorted = NULL; | |
669 | unsigned long end; | |
670 | ||
671 | sorted = sortRangeList(overlap); | |
672 | /* Look for gaps */ | |
673 | rp1 = sorted; | |
674 | end = base + size; | |
675 | while (rp1) { | |
676 | if (rp1->mrd.mr_base > base) { | |
677 | freeRangeList(sorted); | |
678 | return FALSE; | |
679 | } | |
680 | else { | |
681 | base = rp1->mrd.mr_base + rp1->mrd.mr_len; | |
682 | } | |
683 | if (base >= end) { | |
684 | freeRangeList(sorted); | |
685 | return TRUE; | |
686 | } | |
687 | rp1 = rp1->next; | |
688 | } | |
689 | freeRangeList(sorted); | |
690 | return FALSE; | |
691 | } | |
692 | ||
693 | static pointer | |
694 | addWC(int screenNum, unsigned long base, unsigned long size, MessageType from) | |
695 | { | |
696 | RangePtr uc = NULL, wc = NULL, retlist = NULL; | |
697 | struct mem_range_desc mrd; | |
698 | struct mem_range_op mro; | |
699 | ||
700 | findRanges(base, size, &uc, &wc); | |
701 | ||
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", | |
706 | base, size); | |
707 | return NULL; | |
708 | } | |
709 | ||
710 | /* Otherwise, try to add the new range */ | |
711 | mrd.mr_base = base; | |
712 | mrd.mr_len = size; | |
713 | strcpy(mrd.mr_owner, X_MTRR_ID); | |
714 | mrd.mr_flags = MDF_WRITECOMBINE; | |
715 | mro.mo_desc = &mrd; | |
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); | |
721 | return NULL; | |
722 | } | |
723 | else { | |
724 | xf86DrvMsg(screenNum, from, | |
725 | "Write-combining range (0x%lx,0x%lx)\n", base, size); | |
726 | retlist = xnfalloc(sizeof(RangeRec)); | |
727 | retlist->mrd = mrd; | |
728 | retlist->wasWC = FALSE; | |
729 | retlist->next = NULL; | |
730 | return retlist; | |
731 | } | |
732 | } | |
733 | ||
734 | static pointer | |
735 | delWC(int screenNum, unsigned long base, unsigned long size, MessageType from) | |
736 | { | |
737 | RangePtr uc = NULL, wc = NULL, retlist = NULL; | |
738 | struct mem_range_desc mrd; | |
739 | struct mem_range_op mro; | |
740 | ||
741 | findRanges(base, size, &uc, &wc); | |
742 | ||
743 | /* | |
744 | * See of the full range is already not WC, or if there is full | |
745 | * coverage from UC ranges. | |
746 | */ | |
747 | if (!wc || fullCoverage(base, size, uc)) { | |
748 | xf86DrvMsg(screenNum, from, | |
749 | "Write-combining range (0x%lx,0x%lx) was already clear\n", | |
750 | base, size); | |
751 | return NULL; | |
752 | } | |
753 | ||
754 | /* Otherwise, try to add the new range */ | |
755 | mrd.mr_base = base; | |
756 | mrd.mr_len = size; | |
757 | strcpy(mrd.mr_owner, X_MTRR_ID); | |
758 | mrd.mr_flags = MDF_UNCACHEABLE; | |
759 | mro.mo_desc = &mrd; | |
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 */ | |
766 | return NULL; | |
767 | } | |
768 | else { | |
769 | xf86DrvMsg(screenNum, from, | |
770 | "Removed Write-combining range (0x%lx,0x%lx)\n", base, size); | |
771 | retlist = xnfalloc(sizeof(RangeRec)); | |
772 | retlist->mrd = mrd; | |
773 | retlist->wasWC = TRUE; | |
774 | retlist->next = NULL; | |
775 | return retlist; | |
776 | } | |
777 | } | |
778 | ||
779 | static pointer | |
780 | setWC(int screenNum, unsigned long base, unsigned long size, Bool enable, | |
781 | MessageType from) | |
782 | { | |
783 | if (enable) | |
784 | return addWC(screenNum, base, size, from); | |
785 | else | |
786 | return delWC(screenNum, base, size, from); | |
787 | } | |
788 | ||
789 | static void | |
790 | undoWC(int screenNum, pointer list) | |
791 | { | |
792 | RangePtr rp; | |
793 | struct mem_range_op mro; | |
794 | Bool failed; | |
795 | ||
796 | rp = list; | |
797 | while (rp) { | |
798 | #ifdef DEBUG | |
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); | |
802 | #endif | |
803 | failed = FALSE; | |
804 | if (rp->wasWC) { | |
805 | mro.mo_arg[0] = MEMRANGE_SET_UPDATE; | |
806 | rp->mrd.mr_flags = MDF_WRITECOMBINE; | |
807 | strcpy(rp->mrd.mr_owner, "unknown"); | |
808 | } | |
809 | else { | |
810 | mro.mo_arg[0] = MEMRANGE_SET_REMOVE; | |
811 | } | |
812 | mro.mo_desc = &rp->mrd; | |
813 | ||
814 | if (ioctl(devMemFd, MEMRANGE_SET, &mro)) { | |
815 | if (!rp->wasWC) { | |
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)) | |
820 | failed = TRUE; | |
821 | } | |
822 | else | |
823 | failed = TRUE; | |
824 | } | |
825 | if (failed) { | |
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); | |
830 | } | |
831 | rp = rp->next; | |
832 | } | |
833 | } | |
834 | ||
835 | #endif /* HAS_MTRR_SUPPORT */ | |
836 | ||
837 | #if defined(HAS_MTRR_BUILTIN) && defined(__NetBSD__) | |
838 | static pointer | |
839 | NetBSDsetWC(int screenNum, unsigned long base, unsigned long size, Bool enable, | |
840 | MessageType from) | |
841 | { | |
842 | struct mtrr *mtrrp; | |
843 | int n; | |
844 | ||
845 | xf86DrvMsg(screenNum, X_WARNING, | |
846 | "%s MTRR %lx - %lx\n", enable ? "set" : "remove", | |
847 | base, (base + size)); | |
848 | ||
849 | mtrrp = xnfalloc(sizeof(struct mtrr)); | |
850 | mtrrp->base = base; | |
851 | mtrrp->len = size; | |
852 | mtrrp->type = MTRR_TYPE_WC; | |
853 | ||
854 | /* | |
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. | |
858 | */ | |
859 | ||
860 | if (enable) | |
861 | mtrrp->flags = MTRR_VALID | MTRR_PRIVATE; | |
862 | else | |
863 | mtrrp->flags = 0; | |
864 | n = 1; | |
865 | ||
866 | if (i386_set_mtrr(mtrrp, &n) < 0) { | |
867 | free(mtrrp); | |
868 | return NULL; | |
869 | } | |
870 | return mtrrp; | |
871 | } | |
872 | ||
873 | static void | |
874 | NetBSDundoWC(int screenNum, pointer list) | |
875 | { | |
876 | struct mtrr *mtrrp = (struct mtrr *) list; | |
877 | int n; | |
878 | ||
879 | if (mtrrp == NULL) | |
880 | return; | |
881 | n = 1; | |
882 | mtrrp->flags &= ~MTRR_VALID; | |
883 | i386_set_mtrr(mtrrp, &n); | |
884 | free(mtrrp); | |
885 | } | |
886 | #endif |