1 /*
2 * linux specific part of the int10 module
3 * Copyright 1999, 2000, 2001, 2002, 2003, 2004, 2008 Egbert Eich
4 */
5 #ifdef HAVE_XORG_CONFIG_H
6 #include <xorg-config.h>
7 #endif
9 #include "xf86.h"
10 #include "xf86_OSproc.h"
11 #include "xf86Pci.h"
12 #include "compiler.h"
13 #define _INT10_PRIVATE
14 #include "xf86int10.h"
15 #ifdef __sparc__
16 #define DEV_MEM "/dev/fb"
17 #else
18 #define DEV_MEM "/dev/mem"
19 #endif
20 #define ALLOC_ENTRIES(x) ((V_RAM / x) - 1)
21 #define SHMERRORPTR (pointer)(-1)
23 #include <fcntl.h>
24 #include <errno.h>
25 #include <sys/mman.h>
26 #include <sys/ipc.h>
27 #include <sys/shm.h>
28 #include <unistd.h>
29 #include <string.h>
31 static int counter = 0;
32 static unsigned long int10Generation = 0;
34 static CARD8 read_b(xf86Int10InfoPtr pInt, int addr);
35 static CARD16 read_w(xf86Int10InfoPtr pInt, int addr);
36 static CARD32 read_l(xf86Int10InfoPtr pInt, int addr);
37 static void write_b(xf86Int10InfoPtr pInt, int addr, CARD8 val);
38 static void write_w(xf86Int10InfoPtr pInt, int addr, CARD16 val);
39 static void write_l(xf86Int10InfoPtr pInt, int addr, CARD32 val);
41 int10MemRec linuxMem = {
42 read_b,
43 read_w,
44 read_l,
45 write_b,
46 write_w,
47 write_l
48 };
50 typedef struct {
51 int lowMem;
52 int highMem;
53 char* base;
54 char* base_high;
55 int screen;
56 char* alloc;
57 } linuxInt10Priv;
59 #if defined DoSubModules
61 typedef enum {
62 INT10_NOT_LOADED,
63 INT10_LOADED_VM86,
64 INT10_LOADED_X86EMU,
65 INT10_LOAD_FAILED
66 } Int10LinuxSubModuleState;
68 static Int10LinuxSubModuleState loadedSubModule = INT10_NOT_LOADED;
70 static Int10LinuxSubModuleState int10LinuxLoadSubModule(ScrnInfoPtr pScrn);
72 #endif /* DoSubModules */
74 xf86Int10InfoPtr
75 xf86ExtendedInitInt10(int entityIndex, int Flags)
76 {
77 xf86Int10InfoPtr pInt = NULL;
78 int screen;
79 int fd;
80 static void* vidMem = NULL;
81 static void* sysMem = NULL;
82 void* vMem = NULL;
83 void *options = NULL;
84 int low_mem;
85 int high_mem = -1;
86 char *base = SHMERRORPTR;
87 char *base_high = SHMERRORPTR;
88 int pagesize;
89 memType cs;
90 legacyVGARec vga;
91 Bool videoBiosMapped = FALSE;
93 if (int10Generation != serverGeneration) {
94 counter = 0;
95 int10Generation = serverGeneration;
96 }
98 screen = (xf86FindScreenForEntity(entityIndex))->scrnIndex;
100 options = xf86HandleInt10Options(xf86Screens[screen],entityIndex);
102 if (int10skip(options)) {
103 free(options);
104 return NULL;
105 }
107 #if defined DoSubModules
108 if (loadedSubModule == INT10_NOT_LOADED)
109 loadedSubModule = int10LinuxLoadSubModule(xf86Screens[screen]);
111 if (loadedSubModule == INT10_LOAD_FAILED)
112 return NULL;
113 #endif
115 if ((!vidMem) || (!sysMem)) {
116 if ((fd = open(DEV_MEM, O_RDWR, 0)) >= 0) {
117 if (!sysMem) {
118 DebugF("Mapping sys bios area\n");
119 if ((sysMem = mmap((void *)(SYS_BIOS), BIOS_SIZE,
120 PROT_READ | PROT_EXEC,
121 MAP_SHARED | MAP_FIXED, fd, SYS_BIOS))
122 == MAP_FAILED) {
123 xf86DrvMsg(screen, X_ERROR, "Cannot map SYS BIOS\n");
124 close(fd);
125 goto error0;
126 }
127 }
128 if (!vidMem) {
129 DebugF("Mapping VRAM area\n");
130 if ((vidMem = mmap((void *)(V_RAM), VRAM_SIZE,
131 PROT_READ | PROT_WRITE | PROT_EXEC,
132 MAP_SHARED | MAP_FIXED, fd, V_RAM))
133 == MAP_FAILED) {
134 xf86DrvMsg(screen, X_ERROR, "Cannot map V_RAM\n");
135 close(fd);
136 goto error0;
137 }
138 }
139 close(fd);
140 } else {
141 xf86DrvMsg(screen, X_ERROR, "Cannot open %s\n", DEV_MEM);
142 goto error0;
143 }
144 }
146 pInt = (xf86Int10InfoPtr)xnfcalloc(1, sizeof(xf86Int10InfoRec));
147 pInt->scrnIndex = screen;
148 pInt->entityIndex = entityIndex;
149 pInt->dev = xf86GetPciInfoForEntity(entityIndex);
151 if (!xf86Int10ExecSetup(pInt))
152 goto error0;
153 pInt->mem = &linuxMem;
154 pagesize = getpagesize();
155 pInt->private = (pointer)xnfcalloc(1, sizeof(linuxInt10Priv));
156 ((linuxInt10Priv*)pInt->private)->screen = screen;
157 ((linuxInt10Priv*)pInt->private)->alloc =
158 (pointer)xnfcalloc(1, ALLOC_ENTRIES(pagesize));
160 if (!xf86IsEntityPrimary(entityIndex)) {
161 DebugF("Mapping high memory area\n");
162 if ((high_mem = shmget(counter++, HIGH_MEM_SIZE,
163 IPC_CREAT | SHM_R | SHM_W)) == -1) {
164 if (errno == ENOSYS)
165 xf86DrvMsg(screen, X_ERROR, "shmget error\n Please reconfigure"
166 " your kernel to include System V IPC support\n");
167 else
168 xf86DrvMsg(screen, X_ERROR,
169 "shmget(highmem) error: %s\n",strerror(errno));
170 goto error1;
171 }
172 } else {
173 DebugF("Mapping Video BIOS\n");
174 videoBiosMapped = TRUE;
175 if ((fd = open(DEV_MEM, O_RDWR, 0)) >= 0) {
176 if ((vMem = mmap((void *)(V_BIOS), SYS_BIOS - V_BIOS,
177 PROT_READ | PROT_WRITE | PROT_EXEC,
178 MAP_SHARED | MAP_FIXED, fd, V_BIOS))
179 == MAP_FAILED) {
180 xf86DrvMsg(screen, X_ERROR, "Cannot map V_BIOS\n");
181 close(fd);
182 goto error1;
183 }
184 close (fd);
185 } else
186 goto error1;
187 }
188 ((linuxInt10Priv*)pInt->private)->highMem = high_mem;
190 DebugF("Mapping 640kB area\n");
191 if ((low_mem = shmget(counter++, V_RAM,
192 IPC_CREAT | SHM_R | SHM_W)) == -1) {
193 xf86DrvMsg(screen, X_ERROR,
194 "shmget(lowmem) error: %s\n",strerror(errno));
195 goto error2;
196 }
198 ((linuxInt10Priv*)pInt->private)->lowMem = low_mem;
199 base = shmat(low_mem, 0, 0);
200 if (base == SHMERRORPTR) {
201 xf86DrvMsg(screen, X_ERROR,
202 "shmat(low_mem) error: %s\n",strerror(errno));
203 goto error3;
204 }
205 ((linuxInt10Priv *)pInt->private)->base = base;
206 if (high_mem > -1) {
207 base_high = shmat(high_mem, 0, 0);
208 if (base_high == SHMERRORPTR) {
209 xf86DrvMsg(screen, X_ERROR,
210 "shmat(high_mem) error: %s\n",strerror(errno));
211 goto error3;
212 }
213 ((linuxInt10Priv*)pInt->private)->base_high = base_high;
214 } else
215 ((linuxInt10Priv*)pInt->private)->base_high = NULL;
217 if (!MapCurrentInt10(pInt))
218 goto error3;
220 Int10Current = pInt;
222 DebugF("Mapping int area\n");
223 if (xf86ReadBIOS(0, 0, (unsigned char *)0, LOW_PAGE_SIZE) < 0) {
224 xf86DrvMsg(screen, X_ERROR, "Cannot read int vect\n");
225 goto error3;
226 }
227 DebugF("done\n");
228 /*
229 * Read in everything between V_BIOS and SYS_BIOS as some system BIOSes
230 * have executable code there. Note that xf86ReadBIOS() can only bring in
231 * 64K bytes at a time.
232 */
233 if (!videoBiosMapped) {
234 memset((pointer)V_BIOS, 0, SYS_BIOS - V_BIOS);
235 DebugF("Reading BIOS\n");
236 for (cs = V_BIOS; cs < SYS_BIOS; cs += V_BIOS_SIZE)
237 if (xf86ReadBIOS(cs, 0, (pointer)cs, V_BIOS_SIZE) < V_BIOS_SIZE)
238 xf86DrvMsg(screen, X_WARNING,
239 "Unable to retrieve all of segment 0x%06lX.\n",
240 (long)cs);
241 DebugF("done\n");
242 }
244 if (xf86IsEntityPrimary(entityIndex) && !(initPrimary(options))) {
245 if (!xf86int10GetBiosSegment(pInt, NULL))
246 goto error3;
248 set_return_trap(pInt);
249 #ifdef _PC
250 pInt->Flags = Flags & (SET_BIOS_SCRATCH | RESTORE_BIOS_SCRATCH);
251 if (! (pInt->Flags & SET_BIOS_SCRATCH))
252 pInt->Flags &= ~RESTORE_BIOS_SCRATCH;
253 xf86Int10SaveRestoreBIOSVars(pInt, TRUE);
254 #endif
255 } else {
256 const BusType location_type = xf86int10GetBiosLocationType(pInt);
258 switch (location_type) {
259 case BUS_PCI: {
260 int err;
261 struct pci_device *rom_device =
262 xf86GetPciInfoForEntity(pInt->entityIndex);
264 #if HAVE_PCI_DEVICE_ENABLE
265 pci_device_enable(rom_device);
266 #endif
268 err = pci_device_read_rom(rom_device, (unsigned char *)(V_BIOS));
269 if (err) {
270 xf86DrvMsg(screen,X_ERROR,"Cannot read V_BIOS (%s)\n",
271 strerror(err));
272 goto error3;
273 }
275 pInt->BIOSseg = V_BIOS >> 4;
276 break;
277 }
278 default:
279 goto error3;
280 }
282 pInt->num = 0xe6;
283 reset_int_vect(pInt);
284 set_return_trap(pInt);
285 LockLegacyVGA(pInt, &vga);
286 xf86ExecX86int10(pInt);
287 UnlockLegacyVGA(pInt, &vga);
288 }
289 #ifdef DEBUG
290 dprint(0xc0000, 0x20);
291 #endif
293 free(options);
294 return pInt;
296 error3:
297 if (base_high)
298 shmdt(base_high);
299 shmdt(base);
300 shmdt(0);
301 if (base_high)
302 shmdt((char*)HIGH_MEM);
303 shmctl(low_mem, IPC_RMID, NULL);
304 Int10Current = NULL;
305 error2:
306 if (high_mem > -1)
307 shmctl(high_mem, IPC_RMID,NULL);
308 error1:
309 if (vMem)
310 munmap(vMem, SYS_BIOS - V_BIOS);
311 free(((linuxInt10Priv*)pInt->private)->alloc);
312 free(pInt->private);
313 error0:
314 free(options);
315 free(pInt);
316 return NULL;
317 }
319 Bool
320 MapCurrentInt10(xf86Int10InfoPtr pInt)
321 {
322 pointer addr;
323 int fd = -1;
325 if (Int10Current) {
326 shmdt(0);
327 if (((linuxInt10Priv*)Int10Current->private)->highMem >= 0)
328 shmdt((char*)HIGH_MEM);
329 else
330 munmap((pointer)V_BIOS, (SYS_BIOS - V_BIOS));
331 }
332 addr = shmat(((linuxInt10Priv*)pInt->private)->lowMem, (char*)1, SHM_RND);
333 if (addr == SHMERRORPTR) {
334 xf86DrvMsg(pInt->scrnIndex, X_ERROR, "Cannot shmat() low memory\n");
335 xf86DrvMsg(pInt->scrnIndex, X_ERROR,
336 "shmat(low_mem) error: %s\n",strerror(errno));
337 return FALSE;
338 }
339 if (mprotect((void*)0, V_RAM, PROT_READ|PROT_WRITE|PROT_EXEC) != 0)
340 xf86DrvMsg(pInt->scrnIndex, X_ERROR,
341 "Cannot set EXEC bit on low memory: %s\n", strerror(errno));
343 if (((linuxInt10Priv*)pInt->private)->highMem >= 0) {
344 addr = shmat(((linuxInt10Priv*)pInt->private)->highMem,
345 (char*)HIGH_MEM, 0);
346 if (addr == SHMERRORPTR) {
347 xf86DrvMsg(pInt->scrnIndex, X_ERROR,
348 "Cannot shmat() high memory\n");
349 xf86DrvMsg(pInt->scrnIndex, X_ERROR,
350 "shmget error: %s\n",strerror(errno));
351 return FALSE;
352 }
353 if (mprotect((void*)HIGH_MEM, HIGH_MEM_SIZE,
354 PROT_READ|PROT_WRITE|PROT_EXEC) != 0)
355 xf86DrvMsg(pInt->scrnIndex, X_ERROR,
356 "Cannot set EXEC bit on high memory: %s\n",
357 strerror(errno));
358 } else {
359 if ((fd = open(DEV_MEM, O_RDWR, 0)) >= 0) {
360 if (mmap((void *)(V_BIOS), SYS_BIOS - V_BIOS,
361 PROT_READ | PROT_WRITE | PROT_EXEC,
362 MAP_SHARED | MAP_FIXED, fd, V_BIOS)
363 == MAP_FAILED) {
364 xf86DrvMsg(pInt->scrnIndex, X_ERROR, "Cannot map V_BIOS\n");
365 close (fd);
366 return FALSE;
367 }
368 } else {
369 xf86DrvMsg(pInt->scrnIndex, X_ERROR, "Cannot open %s\n",DEV_MEM);
370 return FALSE;
371 }
372 close (fd);
373 }
375 return TRUE;
376 }
378 void
379 xf86FreeInt10(xf86Int10InfoPtr pInt)
380 {
381 if (!pInt)
382 return;
384 #ifdef _PC
385 xf86Int10SaveRestoreBIOSVars(pInt, FALSE);
386 #endif
387 if (Int10Current == pInt) {
388 shmdt(0);
389 if (((linuxInt10Priv*)pInt->private)->highMem >= 0)
390 shmdt((char*)HIGH_MEM);
391 else
392 munmap((pointer)V_BIOS, (SYS_BIOS - V_BIOS));
393 Int10Current = NULL;
394 }
396 if (((linuxInt10Priv*)pInt->private)->base_high)
397 shmdt(((linuxInt10Priv*)pInt->private)->base_high);
398 shmdt(((linuxInt10Priv*)pInt->private)->base);
399 shmctl(((linuxInt10Priv*)pInt->private)->lowMem, IPC_RMID, NULL);
400 if (((linuxInt10Priv*)pInt->private)->highMem >= 0)
401 shmctl(((linuxInt10Priv*)pInt->private)->highMem, IPC_RMID, NULL);
402 free(((linuxInt10Priv*)pInt->private)->alloc);
403 free(pInt->private);
404 free(pInt);
405 }
407 void *
408 xf86Int10AllocPages(xf86Int10InfoPtr pInt, int num, int *off)
409 {
410 int pagesize = getpagesize();
411 int num_pages = ALLOC_ENTRIES(pagesize);
412 int i, j;
414 for (i = 0; i < (num_pages - num); i++) {
415 if (((linuxInt10Priv*)pInt->private)->alloc[i] == 0) {
416 for (j = i; j < (num + i); j++)
417 if ((((linuxInt10Priv*)pInt->private)->alloc[j] != 0))
418 break;
419 if (j == (num + i))
420 break;
421 else
422 i = i + num;
423 }
424 }
425 if (i == (num_pages - num))
426 return NULL;
428 for (j = i; j < (i + num); j++)
429 ((linuxInt10Priv*)pInt->private)->alloc[j] = 1;
431 *off = (i + 1) * pagesize;
433 return ((linuxInt10Priv*)pInt->private)->base + ((i + 1) * pagesize);
434 }
436 void
437 xf86Int10FreePages(xf86Int10InfoPtr pInt, void *pbase, int num)
438 {
439 int pagesize = getpagesize();
440 int first = (((unsigned long)pbase
441 - (unsigned long)((linuxInt10Priv*)pInt->private)->base)
442 / pagesize) - 1;
443 int i;
445 for (i = first; i < (first + num); i++)
446 ((linuxInt10Priv*)pInt->private)->alloc[i] = 0;
447 }
449 static CARD8
450 read_b(xf86Int10InfoPtr pInt, int addr)
451 {
452 return *((CARD8 *)(memType)addr);
453 }
455 static CARD16
456 read_w(xf86Int10InfoPtr pInt, int addr)
457 {
458 return *((CARD16 *)(memType)addr);
459 }
461 static CARD32
462 read_l(xf86Int10InfoPtr pInt, int addr)
463 {
464 return *((CARD32 *)(memType)addr);
465 }
467 static void
468 write_b(xf86Int10InfoPtr pInt, int addr, CARD8 val)
469 {
470 *((CARD8 *)(memType)addr) = val;
471 }
473 static void
474 write_w(xf86Int10InfoPtr pInt, int addr, CARD16 val)
475 {
476 *((CARD16 *)(memType)addr) = val;
477 }
479 static
480 void write_l(xf86Int10InfoPtr pInt, int addr, CARD32 val)
481 {
482 *((CARD32 *)(memType) addr) = val;
483 }
485 pointer
486 xf86int10Addr(xf86Int10InfoPtr pInt, CARD32 addr)
487 {
488 if (addr < V_RAM)
489 return ((linuxInt10Priv*)pInt->private)->base + addr;
490 else if (addr < V_BIOS)
491 return (pointer)(memType)addr;
492 else if (addr < SYS_BIOS) {
493 if (((linuxInt10Priv*)pInt->private)->base_high)
494 return (pointer)(((linuxInt10Priv*)pInt->private)->base_high
495 - V_BIOS + addr);
496 else
497 return (pointer) (memType)addr;
498 } else
499 return (pointer) (memType)addr;
500 }
502 #if defined DoSubModules
504 static Bool
505 vm86_tst(void)
506 {
507 int __res;
509 #ifdef __PIC__
510 /* When compiling with -fPIC, we can't use asm constraint "b" because
511 %ebx is already taken by gcc. */
512 __asm__ __volatile__("pushl %%ebx\n\t"
513 "movl %2,%%ebx\n\t"
514 "movl %1,%%eax\n\t"
515 "int $0x80\n\t"
516 "popl %%ebx"
517 :"=a" (__res)
518 :"n" ((int)113), "r" (NULL));
519 #else
520 __asm__ __volatile__("int $0x80\n\t"
521 :"=a" (__res):"a" ((int)113),
522 "b" ((struct vm86_struct *)NULL));
523 #endif
525 if (__res < 0 && __res == -ENOSYS)
526 return FALSE;
528 return TRUE;
529 }
531 static Int10LinuxSubModuleState
532 int10LinuxLoadSubModule(ScrnInfoPtr pScrn)
533 {
534 if (vm86_tst()) {
535 if (xf86LoadSubModule(pScrn,"vm86"))
536 return INT10_LOADED_VM86;
537 }
538 if (xf86LoadSubModule(pScrn,"x86emu"))
539 return INT10_LOADED_X86EMU;
541 return INT10_LOAD_FAILED;
542 }
544 #endif /* DoSubModules */