/* * linux specific part of the int10 module * Copyright 1999, 2000, 2001, 2002, 2003, 2004, 2008 Egbert Eich */ #ifdef HAVE_XORG_CONFIG_H #include #endif #include "xf86.h" #include "xf86_OSproc.h" #include "xf86Pci.h" #include "compiler.h" #define _INT10_PRIVATE #include "xf86int10.h" #ifdef __sparc__ #define DEV_MEM "/dev/fb" #else #define DEV_MEM "/dev/mem" #endif #define ALLOC_ENTRIES(x) ((V_RAM / x) - 1) #define SHMERRORPTR (void *)(-1) #include #include #include #include #include #include #include static int counter = 0; static unsigned long int10Generation = 0; static CARD8 read_b(xf86Int10InfoPtr pInt, int addr); static CARD16 read_w(xf86Int10InfoPtr pInt, int addr); static CARD32 read_l(xf86Int10InfoPtr pInt, int addr); static void write_b(xf86Int10InfoPtr pInt, int addr, CARD8 val); static void write_w(xf86Int10InfoPtr pInt, int addr, CARD16 val); static void write_l(xf86Int10InfoPtr pInt, int addr, CARD32 val); int10MemRec linuxMem = { read_b, read_w, read_l, write_b, write_w, write_l }; typedef struct { int lowMem; int highMem; char *base; char *base_high; char *alloc; } linuxInt10Priv; #if defined DoSubModules typedef enum { INT10_NOT_LOADED, INT10_LOADED_VM86, INT10_LOADED_X86EMU, INT10_LOAD_FAILED } Int10LinuxSubModuleState; static Int10LinuxSubModuleState loadedSubModule = INT10_NOT_LOADED; static Int10LinuxSubModuleState int10LinuxLoadSubModule(ScrnInfoPtr pScrn); #endif /* DoSubModules */ static Bool readLegacy(struct pci_device *dev, unsigned char *buf, int base, int len) { void *map; if (!pci_device_map_legacy(dev, base, len, 0, &map)) return FALSE; memcpy(buf, map, len); pci_device_unmap_legacy(dev, man, len); return TRUE; } xf86Int10InfoPtr xf86ExtendedInitInt10(int entityIndex, int Flags) { xf86Int10InfoPtr pInt = NULL; int screen; int fd; static void *vidMem = NULL; static void *sysMem = NULL; void *vMem = NULL; void *options = NULL; int low_mem; int high_mem = -1; char *base = SHMERRORPTR; char *base_high = SHMERRORPTR; int pagesize; memType cs; legacyVGARec vga; Bool videoBiosMapped = FALSE; ScrnInfoPtr pScrn; if (int10Generation != serverGeneration) { counter = 0; int10Generation = serverGeneration; } pScrn = xf86FindScreenForEntity(entityIndex); screen = pScrn->scrnIndex; options = xf86HandleInt10Options(pScrn, entityIndex); if (int10skip(options)) { free(options); return NULL; } #if defined DoSubModules if (loadedSubModule == INT10_NOT_LOADED) loadedSubModule = int10LinuxLoadSubModule(pScrn); if (loadedSubModule == INT10_LOAD_FAILED) return NULL; #endif if ((!vidMem) || (!sysMem)) { if ((fd = open(DEV_MEM, O_RDWR, 0)) >= 0) { if (!sysMem) { DebugF("Mapping sys bios area\n"); if ((sysMem = mmap((void *) (SYS_BIOS), BIOS_SIZE, PROT_READ | PROT_EXEC, MAP_SHARED | MAP_FIXED, fd, SYS_BIOS)) == MAP_FAILED) { xf86DrvMsg(screen, X_ERROR, "Cannot map SYS BIOS\n"); close(fd); goto error0; } } if (!vidMem) { DebugF("Mapping VRAM area\n"); if ((vidMem = mmap((void *) (V_RAM), VRAM_SIZE, PROT_READ | PROT_WRITE | PROT_EXEC, MAP_SHARED | MAP_FIXED, fd, V_RAM)) == MAP_FAILED) { xf86DrvMsg(screen, X_ERROR, "Cannot map V_RAM\n"); close(fd); goto error0; } } close(fd); } else { xf86DrvMsg(screen, X_ERROR, "Cannot open %s\n", DEV_MEM); goto error0; } } pInt = (xf86Int10InfoPtr) xnfcalloc(1, sizeof(xf86Int10InfoRec)); pInt->pScrn = pScrn; pInt->entityIndex = entityIndex; pInt->dev = xf86GetPciInfoForEntity(entityIndex); if (!xf86Int10ExecSetup(pInt)) goto error0; pInt->mem = &linuxMem; pagesize = getpagesize(); pInt->private = (void *) xnfcalloc(1, sizeof(linuxInt10Priv)); ((linuxInt10Priv *) pInt->private)->alloc = (void *) xnfcalloc(1, ALLOC_ENTRIES(pagesize)); if (!xf86IsEntityPrimary(entityIndex)) { DebugF("Mapping high memory area\n"); if ((high_mem = shmget(counter++, HIGH_MEM_SIZE, IPC_CREAT | SHM_R | SHM_W)) == -1) { if (errno == ENOSYS) xf86DrvMsg(screen, X_ERROR, "shmget error\n Please reconfigure" " your kernel to include System V IPC support\n"); else xf86DrvMsg(screen, X_ERROR, "shmget(highmem) error: %s\n", strerror(errno)); goto error1; } } else { DebugF("Mapping Video BIOS\n"); videoBiosMapped = TRUE; if ((fd = open(DEV_MEM, O_RDWR, 0)) >= 0) { if ((vMem = mmap((void *) (V_BIOS), SYS_BIOS - V_BIOS, PROT_READ | PROT_WRITE | PROT_EXEC, MAP_SHARED | MAP_FIXED, fd, V_BIOS)) == MAP_FAILED) { xf86DrvMsg(screen, X_ERROR, "Cannot map V_BIOS\n"); close(fd); goto error1; } close(fd); } else goto error1; } ((linuxInt10Priv *) pInt->private)->highMem = high_mem; DebugF("Mapping 640kB area\n"); if ((low_mem = shmget(counter++, V_RAM, IPC_CREAT | SHM_R | SHM_W)) == -1) { xf86DrvMsg(screen, X_ERROR, "shmget(lowmem) error: %s\n", strerror(errno)); goto error2; } ((linuxInt10Priv *) pInt->private)->lowMem = low_mem; base = shmat(low_mem, 0, 0); if (base == SHMERRORPTR) { xf86DrvMsg(screen, X_ERROR, "shmat(low_mem) error: %s\n", strerror(errno)); goto error3; } ((linuxInt10Priv *) pInt->private)->base = base; if (high_mem > -1) { base_high = shmat(high_mem, 0, 0); if (base_high == SHMERRORPTR) { xf86DrvMsg(screen, X_ERROR, "shmat(high_mem) error: %s\n", strerror(errno)); goto error3; } ((linuxInt10Priv *) pInt->private)->base_high = base_high; } else ((linuxInt10Priv *) pInt->private)->base_high = NULL; if (!MapCurrentInt10(pInt)) goto error3; Int10Current = pInt; DebugF("Mapping int area\n"); /* note: yes, we really are writing the 0 page here */ if (!readLegacy(pInt->dev, (unsigned char *) 0, 0, LOW_PAGE_SIZE)) { xf86DrvMsg(screen, X_ERROR, "Cannot read int vect\n"); goto error3; } DebugF("done\n"); /* * Read in everything between V_BIOS and SYS_BIOS as some system BIOSes * have executable code there. Note that xf86ReadBIOS() can only bring in * 64K bytes at a time. */ if (!videoBiosMapped) { memset((void *) V_BIOS, 0, SYS_BIOS - V_BIOS); DebugF("Reading BIOS\n"); for (cs = V_BIOS; cs < SYS_BIOS; cs += V_BIOS_SIZE) if (!readLegacy(pInt->dev, (void *)cs, cs, V_BIOS_SIZE)) xf86DrvMsg(screen, X_WARNING, "Unable to retrieve all of segment 0x%06lX.\n", (long) cs); DebugF("done\n"); } if (xf86IsEntityPrimary(entityIndex) && !(initPrimary(options))) { if (!xf86int10GetBiosSegment(pInt, NULL)) goto error3; set_return_trap(pInt); #ifdef _PC pInt->Flags = Flags & (SET_BIOS_SCRATCH | RESTORE_BIOS_SCRATCH); if (!(pInt->Flags & SET_BIOS_SCRATCH)) pInt->Flags &= ~RESTORE_BIOS_SCRATCH; xf86Int10SaveRestoreBIOSVars(pInt, TRUE); #endif } else { const BusType location_type = xf86int10GetBiosLocationType(pInt); switch (location_type) { case BUS_PCI:{ int err; struct pci_device *rom_device = xf86GetPciInfoForEntity(pInt->entityIndex); pci_device_enable(rom_device); err = pci_device_read_rom(rom_device, (unsigned char *) (V_BIOS)); if (err) { xf86DrvMsg(screen, X_ERROR, "Cannot read V_BIOS (%s)\n", strerror(err)); goto error3; } pInt->BIOSseg = V_BIOS >> 4; break; } default: goto error3; } pInt->num = 0xe6; reset_int_vect(pInt); set_return_trap(pInt); LockLegacyVGA(pInt, &vga); xf86ExecX86int10(pInt); UnlockLegacyVGA(pInt, &vga); } #ifdef DEBUG dprint(0xc0000, 0x20); #endif free(options); return pInt; error3: if (base_high) shmdt(base_high); shmdt(base); shmdt(0); if (base_high) shmdt((char *) HIGH_MEM); shmctl(low_mem, IPC_RMID, NULL); Int10Current = NULL; error2: if (high_mem > -1) shmctl(high_mem, IPC_RMID, NULL); error1: if (vMem) munmap(vMem, SYS_BIOS - V_BIOS); free(((linuxInt10Priv *) pInt->private)->alloc); free(pInt->private); error0: free(options); free(pInt); return NULL; } Bool MapCurrentInt10(xf86Int10InfoPtr pInt) { void *addr; int fd = -1; if (Int10Current) { shmdt(0); if (((linuxInt10Priv *) Int10Current->private)->highMem >= 0) shmdt((char *) HIGH_MEM); else munmap((void *) V_BIOS, (SYS_BIOS - V_BIOS)); } addr = shmat(((linuxInt10Priv *) pInt->private)->lowMem, (char *) 1, SHM_RND); if (addr == SHMERRORPTR) { xf86DrvMsg(pInt->pScrn->scrnIndex, X_ERROR, "Cannot shmat() low memory\n"); xf86DrvMsg(pInt->pScrn->scrnIndex, X_ERROR, "shmat(low_mem) error: %s\n", strerror(errno)); return FALSE; } if (mprotect((void *) 0, V_RAM, PROT_READ | PROT_WRITE | PROT_EXEC) != 0) xf86DrvMsg(pInt->pScrn->scrnIndex, X_ERROR, "Cannot set EXEC bit on low memory: %s\n", strerror(errno)); if (((linuxInt10Priv *) pInt->private)->highMem >= 0) { addr = shmat(((linuxInt10Priv *) pInt->private)->highMem, (char *) HIGH_MEM, 0); if (addr == SHMERRORPTR) { xf86DrvMsg(pInt->pScrn->scrnIndex, X_ERROR, "Cannot shmat() high memory\n"); xf86DrvMsg(pInt->pScrn->scrnIndex, X_ERROR, "shmget error: %s\n", strerror(errno)); return FALSE; } if (mprotect((void *) HIGH_MEM, HIGH_MEM_SIZE, PROT_READ | PROT_WRITE | PROT_EXEC) != 0) xf86DrvMsg(pInt->pScrn->scrnIndex, X_ERROR, "Cannot set EXEC bit on high memory: %s\n", strerror(errno)); } else { if ((fd = open(DEV_MEM, O_RDWR, 0)) >= 0) { if (mmap((void *) (V_BIOS), SYS_BIOS - V_BIOS, PROT_READ | PROT_WRITE | PROT_EXEC, MAP_SHARED | MAP_FIXED, fd, V_BIOS) == MAP_FAILED) { xf86DrvMsg(pInt->pScrn->scrnIndex, X_ERROR, "Cannot map V_BIOS\n"); close(fd); return FALSE; } } else { xf86DrvMsg(pInt->pScrn->scrnIndex, X_ERROR, "Cannot open %s\n", DEV_MEM); return FALSE; } close(fd); } return TRUE; } void xf86FreeInt10(xf86Int10InfoPtr pInt) { if (!pInt) return; #ifdef _PC xf86Int10SaveRestoreBIOSVars(pInt, FALSE); #endif if (Int10Current == pInt) { shmdt(0); if (((linuxInt10Priv *) pInt->private)->highMem >= 0) shmdt((char *) HIGH_MEM); else munmap((void *) V_BIOS, (SYS_BIOS - V_BIOS)); Int10Current = NULL; } if (((linuxInt10Priv *) pInt->private)->base_high) shmdt(((linuxInt10Priv *) pInt->private)->base_high); shmdt(((linuxInt10Priv *) pInt->private)->base); shmctl(((linuxInt10Priv *) pInt->private)->lowMem, IPC_RMID, NULL); if (((linuxInt10Priv *) pInt->private)->highMem >= 0) shmctl(((linuxInt10Priv *) pInt->private)->highMem, IPC_RMID, NULL); free(((linuxInt10Priv *) pInt->private)->alloc); free(pInt->private); free(pInt); } void * xf86Int10AllocPages(xf86Int10InfoPtr pInt, int num, int *off) { int pagesize = getpagesize(); int num_pages = ALLOC_ENTRIES(pagesize); int i, j; for (i = 0; i < (num_pages - num); i++) { if (((linuxInt10Priv *) pInt->private)->alloc[i] == 0) { for (j = i; j < (num + i); j++) if ((((linuxInt10Priv *) pInt->private)->alloc[j] != 0)) break; if (j == (num + i)) break; else i = i + num; } } if (i == (num_pages - num)) return NULL; for (j = i; j < (i + num); j++) ((linuxInt10Priv *) pInt->private)->alloc[j] = 1; *off = (i + 1) * pagesize; return ((linuxInt10Priv *) pInt->private)->base + ((i + 1) * pagesize); } void xf86Int10FreePages(xf86Int10InfoPtr pInt, void *pbase, int num) { int pagesize = getpagesize(); int first = (((unsigned long) pbase - (unsigned long) ((linuxInt10Priv *) pInt->private)->base) / pagesize) - 1; int i; for (i = first; i < (first + num); i++) ((linuxInt10Priv *) pInt->private)->alloc[i] = 0; } static CARD8 read_b(xf86Int10InfoPtr pInt, int addr) { return *((CARD8 *) (memType) addr); } static CARD16 read_w(xf86Int10InfoPtr pInt, int addr) { return *((CARD16 *) (memType) addr); } static CARD32 read_l(xf86Int10InfoPtr pInt, int addr) { return *((CARD32 *) (memType) addr); } static void write_b(xf86Int10InfoPtr pInt, int addr, CARD8 val) { *((CARD8 *) (memType) addr) = val; } static void write_w(xf86Int10InfoPtr pInt, int addr, CARD16 val) { *((CARD16 *) (memType) addr) = val; } static void write_l(xf86Int10InfoPtr pInt, int addr, CARD32 val) { *((CARD32 *) (memType) addr) = val; } void * xf86int10Addr(xf86Int10InfoPtr pInt, CARD32 addr) { if (addr < V_RAM) return ((linuxInt10Priv *) pInt->private)->base + addr; else if (addr < V_BIOS) return (void *) (memType) addr; else if (addr < SYS_BIOS) { if (((linuxInt10Priv *) pInt->private)->base_high) return (void *) (((linuxInt10Priv *) pInt->private)->base_high - V_BIOS + addr); else return (void *) (memType) addr; } else return (void *) (memType) addr; } #if defined DoSubModules static Bool vm86_tst(void) { int __res; #ifdef __PIC__ /* When compiling with -fPIC, we can't use asm constraint "b" because %ebx is already taken by gcc. */ __asm__ __volatile__("pushl %%ebx\n\t" "movl %2,%%ebx\n\t" "movl %1,%%eax\n\t" "int $0x80\n\t" "popl %%ebx":"=a"(__res) :"n"((int) 113), "r"(NULL)); #else __asm__ __volatile__("int $0x80\n\t":"=a"(__res):"a"((int) 113), "b"((struct vm86_struct *) NULL)); #endif if (__res < 0 && __res == -ENOSYS) return FALSE; return TRUE; } static Int10LinuxSubModuleState int10LinuxLoadSubModule(ScrnInfoPtr pScrn) { if (vm86_tst()) { if (xf86LoadSubModule(pScrn, "vm86")) return INT10_LOADED_VM86; } if (xf86LoadSubModule(pScrn, "x86emu")) return INT10_LOADED_X86EMU; return INT10_LOAD_FAILED; } #endif /* DoSubModules */