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