/* * XFree86 int10 module * execute BIOS int 10h calls in x86 real mode environment * Copyright 1999 Egbert Eich * * Part of this code was inspired by the VBIOS POSTing code in DOSEMU * developed by the "DOSEMU-Development-Team" */ /* * To debug port accesses define PRINT_PORT to 1. * Note! You also have to comment out ioperm() * in xf86EnableIO(). Otherwise we won't trap * on PIO. */ #ifdef HAVE_XORG_CONFIG_H #include #endif #define PRINT_PORT 0 #include #include #include "xf86.h" #include "xf86_OSproc.h" #include "compiler.h" #define _INT10_PRIVATE #include "int10Defines.h" #include "xf86int10.h" #include "Pci.h" #ifdef _X86EMU #include "x86emu/x86emui.h" #else #define DEBUG_IO_TRACE() 0 #endif #include static int pciCfg1in(CARD16 addr, CARD32 *val); static int pciCfg1out(CARD16 addr, CARD32 val); static int pciCfg1inw(CARD16 addr, CARD16 *val); static int pciCfg1outw(CARD16 addr, CARD16 val); static int pciCfg1inb(CARD16 addr, CARD8 *val); static int pciCfg1outb(CARD16 addr, CARD8 val); #if defined (_PC) static void SetResetBIOSVars(xf86Int10InfoPtr pInt, Bool set); #endif #define REG pInt _X_EXPORT int setup_int(xf86Int10InfoPtr pInt) { if (pInt != Int10Current) { if (!MapCurrentInt10(pInt)) return -1; Int10Current = pInt; } X86_EAX = (CARD32) pInt->ax; X86_EBX = (CARD32) pInt->bx; X86_ECX = (CARD32) pInt->cx; X86_EDX = (CARD32) pInt->dx; X86_ESI = (CARD32) pInt->si; X86_EDI = (CARD32) pInt->di; X86_EBP = (CARD32) pInt->bp; X86_ESP = 0x1000; X86_SS = pInt->stackseg >> 4; X86_EIP = 0x0600; X86_CS = 0x0; /* address of 'hlt' */ X86_DS = 0x40; /* standard pc ds */ X86_ES = pInt->es; X86_FS = 0; X86_GS = 0; X86_EFLAGS = X86_IF_MASK | X86_IOPL_MASK; #if defined (_PC) if (pInt->Flags & SET_BIOS_SCRATCH) SetResetBIOSVars(pInt, TRUE); #endif return xf86BlockSIGIO(); } _X_EXPORT void finish_int(xf86Int10InfoPtr pInt, int sig) { xf86UnblockSIGIO(sig); pInt->ax = (CARD32) X86_EAX; pInt->bx = (CARD32) X86_EBX; pInt->cx = (CARD32) X86_ECX; pInt->dx = (CARD32) X86_EDX; pInt->si = (CARD32) X86_ESI; pInt->di = (CARD32) X86_EDI; pInt->es = (CARD16) X86_ES; pInt->bp = (CARD32) X86_EBP; pInt->flags = (CARD32) X86_FLAGS; #if defined (_PC) if (pInt->Flags & RESTORE_BIOS_SCRATCH) SetResetBIOSVars(pInt, FALSE); #endif } /* general software interrupt handler */ _X_EXPORT CARD32 getIntVect(xf86Int10InfoPtr pInt,int num) { return MEM_RW(pInt, num << 2) + (MEM_RW(pInt, (num << 2) + 2) << 4); } _X_EXPORT void pushw(xf86Int10InfoPtr pInt, CARD16 val) { X86_ESP -= 2; MEM_WW(pInt, ((CARD32) X86_SS << 4) + X86_SP, val); } _X_EXPORT int run_bios_int(int num, xf86Int10InfoPtr pInt) { CARD32 eflags; #ifndef _PC /* check if bios vector is initialized */ if (MEM_RW(pInt, (num << 2) + 2) == (SYS_BIOS >> 4)) { /* SYS_BIOS_SEG ?*/ if (num == 21 && X86_AH == 0x4e) { xf86DrvMsg(pInt->scrnIndex, X_NOTICE, "Failing Find-Matching-File on non-PC" " (int 21, func 4e)\n"); X86_AX = 2; SET_FLAG(F_CF); return 1; } else { xf86DrvMsgVerb(pInt->scrnIndex, X_NOT_IMPLEMENTED, 2, "Ignoring int 0x%02x call\n", num); if (xf86GetVerbosity() > 3) { dump_registers(pInt); stack_trace(pInt); } return 1; } } #endif #ifdef PRINT_INT ErrorF("calling card BIOS at: "); #endif eflags = X86_EFLAGS; #if 0 eflags = eflags | IF_MASK; X86_EFLAGS = X86_EFLAGS & ~(VIF_MASK | TF_MASK | IF_MASK | NT_MASK); #endif pushw(pInt, eflags); pushw(pInt, X86_CS); pushw(pInt, X86_IP); X86_CS = MEM_RW(pInt, (num << 2) + 2); X86_IP = MEM_RW(pInt, num << 2); #ifdef PRINT_INT ErrorF("0x%x:%lx\n", X86_CS, X86_EIP); #endif return 1; } /* Debugging stuff */ _X_EXPORT void dump_code(xf86Int10InfoPtr pInt) { int i; unsigned long lina = SEG_ADR((CARD32), X86_CS, IP); xf86DrvMsgVerb(pInt->scrnIndex, X_INFO, 3, "code at 0x%8.8lx:\n", lina); for (i=0; i<0x10; i++) xf86ErrorFVerb(3, " %2.2x", MEM_RB(pInt, lina + i)); xf86ErrorFVerb(3, "\n"); for (; i<0x20; i++) xf86ErrorFVerb(3, " %2.2x", MEM_RB(pInt, lina + i)); xf86ErrorFVerb(3, "\n"); } _X_EXPORT void dump_registers(xf86Int10InfoPtr pInt) { xf86DrvMsgVerb(pInt->scrnIndex, X_INFO, 3, "EAX=0x%8.8lx, EBX=0x%8.8lx, ECX=0x%8.8lx, EDX=0x%8.8lx\n", (unsigned long)X86_EAX, (unsigned long)X86_EBX, (unsigned long)X86_ECX, (unsigned long)X86_EDX); xf86DrvMsgVerb(pInt->scrnIndex, X_INFO, 3, "ESP=0x%8.8lx, EBP=0x%8.8lx, ESI=0x%8.8lx, EDI=0x%8.8lx\n", (unsigned long)X86_ESP, (unsigned long)X86_EBP, (unsigned long)X86_ESI, (unsigned long)X86_EDI); xf86DrvMsgVerb(pInt->scrnIndex, X_INFO, 3, "CS=0x%4.4x, SS=0x%4.4x," " DS=0x%4.4x, ES=0x%4.4x, FS=0x%4.4x, GS=0x%4.4x\n", X86_CS, X86_SS, X86_DS, X86_ES, X86_FS, X86_GS); xf86DrvMsgVerb(pInt->scrnIndex, X_INFO, 3, "EIP=0x%8.8lx, EFLAGS=0x%8.8lx\n", (unsigned long)X86_EIP, (unsigned long)X86_EFLAGS); } _X_EXPORT void stack_trace(xf86Int10InfoPtr pInt) { int i = 0; unsigned long stack = SEG_ADR((CARD32), X86_SS, SP); unsigned long tail = (CARD32)((X86_SS << 4) + 0x1000); if (stack >= tail) return; xf86MsgVerb(X_INFO, 3, "stack at 0x%8.8lx:\n", stack); for (; stack < tail; stack++) { xf86ErrorFVerb(3, " %2.2x", MEM_RB(pInt, stack)); i = (i + 1) % 0x10; if (!i) xf86ErrorFVerb(3, "\n"); } if (i) xf86ErrorFVerb(3, "\n"); } _X_EXPORT int port_rep_inb(xf86Int10InfoPtr pInt, CARD16 port, CARD32 base, int d_f, CARD32 count) { register int inc = d_f ? -1 : 1; CARD32 dst = base; if (PRINT_PORT && DEBUG_IO_TRACE()) ErrorF(" rep_insb(%#x) %d bytes at %8.8x %s\n", port, count, base, d_f ? "up" : "down"); while (count--) { MEM_WB(pInt, dst, x_inb(port)); dst += inc; } return dst - base; } _X_EXPORT int port_rep_inw(xf86Int10InfoPtr pInt, CARD16 port, CARD32 base, int d_f, CARD32 count) { register int inc = d_f ? -2 : 2; CARD32 dst = base; if (PRINT_PORT && DEBUG_IO_TRACE()) ErrorF(" rep_insw(%#x) %d bytes at %8.8x %s\n", port, count, base, d_f ? "up" : "down"); while (count--) { MEM_WW(pInt, dst, x_inw(port)); dst += inc; } return dst - base; } _X_EXPORT int port_rep_inl(xf86Int10InfoPtr pInt, CARD16 port, CARD32 base, int d_f, CARD32 count) { register int inc = d_f ? -4 : 4; CARD32 dst = base; if (PRINT_PORT && DEBUG_IO_TRACE()) ErrorF(" rep_insl(%#x) %d bytes at %8.8x %s\n", port, count, base, d_f ? "up" : "down"); while (count--) { MEM_WL(pInt, dst, x_inl(port)); dst += inc; } return dst - base; } _X_EXPORT int port_rep_outb(xf86Int10InfoPtr pInt, CARD16 port, CARD32 base, int d_f, CARD32 count) { register int inc = d_f ? -1 : 1; CARD32 dst = base; if (PRINT_PORT && DEBUG_IO_TRACE()) ErrorF(" rep_outb(%#x) %d bytes at %8.8x %s\n", port, count, base, d_f ? "up" : "down"); while (count--) { x_outb(port, MEM_RB(pInt, dst)); dst += inc; } return dst - base; } _X_EXPORT int port_rep_outw(xf86Int10InfoPtr pInt, CARD16 port, CARD32 base, int d_f, CARD32 count) { register int inc = d_f ? -2 : 2; CARD32 dst = base; if (PRINT_PORT && DEBUG_IO_TRACE()) ErrorF(" rep_outw(%#x) %d bytes at %8.8x %s\n", port, count, base, d_f ? "up" : "down"); while (count--) { x_outw(port, MEM_RW(pInt, dst)); dst += inc; } return dst - base; } _X_EXPORT int port_rep_outl(xf86Int10InfoPtr pInt, CARD16 port, CARD32 base, int d_f, CARD32 count) { register int inc = d_f ? -4 : 4; CARD32 dst = base; if (PRINT_PORT && DEBUG_IO_TRACE()) ErrorF(" rep_outl(%#x) %d bytes at %8.8x %s\n", port, count, base, d_f ? "up" : "down"); while (count--) { x_outl(port, MEM_RL(pInt, dst)); dst += inc; } return dst - base; } _X_EXPORT CARD8 x_inb(CARD16 port) { CARD8 val; if (port == 0x40) { Int10Current->inb40time++; val = (CARD8)(Int10Current->inb40time >> ((Int10Current->inb40time & 1) << 3)); if (PRINT_PORT && DEBUG_IO_TRACE()) ErrorF(" inb(%#x) = %2.2x\n", port, val); #ifdef __NOT_YET__ } else if (port < 0x0100) { /* Don't interfere with mainboard */ val = 0; xf86DrvMsgVerb(Int10Current->scrnIndex, X_NOT_IMPLEMENTED, 2, "inb 0x%4.4x\n", port); if (xf86GetVerbosity() > 3) { dump_registers(Int10Current); stack_trace(Int10Current); } #endif /* __NOT_YET__ */ } else if (!pciCfg1inb(port, &val)) { val = inb(Int10Current->ioBase + port); if (PRINT_PORT && DEBUG_IO_TRACE()) ErrorF(" inb(%#x) = %2.2x\n", port, val); } return val; } _X_EXPORT CARD16 x_inw(CARD16 port) { CARD16 val; if (port == 0x5c) { struct timeval tv; /* * Emulate a PC98's timer. Typical resolution is 3.26 usec. * Approximate this by dividing by 3. */ X_GETTIMEOFDAY(&tv); val = (CARD16)(tv.tv_usec / 3); } else if (!pciCfg1inw(port, &val)) { val = inw(Int10Current->ioBase + port); if (PRINT_PORT && DEBUG_IO_TRACE()) ErrorF(" inw(%#x) = %4.4x\n", port, val); } return val; } _X_EXPORT void x_outb(CARD16 port, CARD8 val) { if ((port == 0x43) && (val == 0)) { struct timeval tv; /* * Emulate a PC's timer 0. Such timers typically have a resolution of * some .838 usec per tick, but this can only provide 1 usec per tick. * (Not that this matters much, given inherent emulation delays.) Use * the bottom bit as a byte select. See inb(0x40) above. */ X_GETTIMEOFDAY(&tv); Int10Current->inb40time = (CARD16)(tv.tv_usec | 1); if (PRINT_PORT && DEBUG_IO_TRACE()) ErrorF(" outb(%#x, %2.2x)\n", port, val); #ifdef __NOT_YET__ } else if (port < 0x0100) { /* Don't interfere with mainboard */ xf86DrvMsgVerb(Int10Current->scrnIndex, X_NOT_IMPLEMENTED, 2, "outb 0x%4.4x,0x%2.2x\n", port, val); if (xf86GetVerbosity() > 3) { dump_registers(Int10Current); stack_trace(Int10Current); } #endif /* __NOT_YET__ */ } else if (!pciCfg1outb(port, val)) { if (PRINT_PORT && DEBUG_IO_TRACE()) ErrorF(" outb(%#x, %2.2x)\n", port, val); outb(Int10Current->ioBase + port, val); } } _X_EXPORT void x_outw(CARD16 port, CARD16 val) { if (!pciCfg1outw(port, val)) { if (PRINT_PORT && DEBUG_IO_TRACE()) ErrorF(" outw(%#x, %4.4x)\n", port, val); outw(Int10Current->ioBase + port, val); } } _X_EXPORT CARD32 x_inl(CARD16 port) { CARD32 val; if (!pciCfg1in(port, &val)) { val = inl(Int10Current->ioBase + port); if (PRINT_PORT && DEBUG_IO_TRACE()) ErrorF(" inl(%#x) = %8.8x\n", port, val); } return val; } _X_EXPORT void x_outl(CARD16 port, CARD32 val) { if (!pciCfg1out(port, val)) { if (PRINT_PORT && DEBUG_IO_TRACE()) ErrorF(" outl(%#x, %8.8x)\n", port, val); outl(Int10Current->ioBase + port, val); } } _X_EXPORT CARD8 Mem_rb(CARD32 addr) { return (*Int10Current->mem->rb)(Int10Current, addr); } _X_EXPORT CARD16 Mem_rw(CARD32 addr) { return (*Int10Current->mem->rw)(Int10Current, addr); } _X_EXPORT CARD32 Mem_rl(CARD32 addr) { return (*Int10Current->mem->rl)(Int10Current, addr); } _X_EXPORT void Mem_wb(CARD32 addr, CARD8 val) { (*Int10Current->mem->wb)(Int10Current, addr, val); } _X_EXPORT void Mem_ww(CARD32 addr, CARD16 val) { (*Int10Current->mem->ww)(Int10Current, addr, val); } _X_EXPORT void Mem_wl(CARD32 addr, CARD32 val) { (*Int10Current->mem->wl)(Int10Current, addr, val); } static CARD32 PciCfg1Addr = 0; #define PCI_OFFSET(x) ((x) & 0x000000ff) #define PCI_TAG(x) ((x) & 0x7fffff00) static struct pci_device* pci_device_for_cfg_address (CARD32 addr) { struct pci_device *dev = NULL; PCITAG tag = PCI_TAG(addr); struct pci_slot_match slot_match = { .domain = PCI_DOM_FROM_TAG(tag), .bus = PCI_BUS_NO_DOMAIN(PCI_BUS_FROM_TAG(tag)), .dev = PCI_DEV_FROM_TAG(tag), .func = PCI_FUNC_FROM_TAG(tag), .match_data = 0 }; struct pci_device_iterator *iter = pci_slot_match_iterator_create (&slot_match); if (iter) dev = pci_device_next(iter); pci_iterator_destroy(iter); return dev; } static int pciCfg1in(CARD16 addr, CARD32 *val) { if (addr == 0xCF8) { *val = PciCfg1Addr; return 1; } if (addr == 0xCFC) { pci_device_cfg_read_u32(pci_device_for_cfg_address(PciCfg1Addr), val, PCI_OFFSET(PciCfg1Addr)); if (PRINT_PORT && DEBUG_IO_TRACE()) ErrorF(" cfg_inl(%#x) = %8.8x\n", PciCfg1Addr, *val); return 1; } return 0; } static int pciCfg1out(CARD16 addr, CARD32 val) { if (addr == 0xCF8) { PciCfg1Addr = val; return 1; } if (addr == 0xCFC) { if (PRINT_PORT && DEBUG_IO_TRACE()) ErrorF(" cfg_outl(%#x, %8.8x)\n", PciCfg1Addr, val); pci_device_cfg_write_u32(pci_device_for_cfg_address(PciCfg1Addr), val, PCI_OFFSET(PciCfg1Addr)); return 1; } return 0; } static int pciCfg1inw(CARD16 addr, CARD16 *val) { int shift; if ((addr >= 0xCF8) && (addr <= 0xCFB)) { shift = (addr - 0xCF8) * 8; *val = (PciCfg1Addr >> shift) & 0xffff; return 1; } if ((addr >= 0xCFC) && (addr <= 0xCFF)) { const unsigned offset = addr - 0xCFC; pci_device_cfg_read_u16(pci_device_for_cfg_address(PciCfg1Addr), val, PCI_OFFSET(PciCfg1Addr) + offset); if (PRINT_PORT && DEBUG_IO_TRACE()) ErrorF(" cfg_inw(%#x) = %4.4x\n", PciCfg1Addr + offset, *val); return 1; } return 0; } static int pciCfg1outw(CARD16 addr, CARD16 val) { int shift; if ((addr >= 0xCF8) && (addr <= 0xCFB)) { shift = (addr - 0xCF8) * 8; PciCfg1Addr &= ~(0xffff << shift); PciCfg1Addr |= ((CARD32) val) << shift; return 1; } if ((addr >= 0xCFC) && (addr <= 0xCFF)) { const unsigned offset = addr - 0xCFC; if (PRINT_PORT && DEBUG_IO_TRACE()) ErrorF(" cfg_outw(%#x, %4.4x)\n", PciCfg1Addr + offset, val); pci_device_cfg_write_u16(pci_device_for_cfg_address(PciCfg1Addr), val, PCI_OFFSET(PciCfg1Addr) + offset); return 1; } return 0; } static int pciCfg1inb(CARD16 addr, CARD8 *val) { int shift; if ((addr >= 0xCF8) && (addr <= 0xCFB)) { shift = (addr - 0xCF8) * 8; *val = (PciCfg1Addr >> shift) & 0xff; return 1; } if ((addr >= 0xCFC) && (addr <= 0xCFF)) { const unsigned offset = addr - 0xCFC; pci_device_cfg_read_u8(pci_device_for_cfg_address(PciCfg1Addr), val, PCI_OFFSET(PciCfg1Addr) + offset); if (PRINT_PORT && DEBUG_IO_TRACE()) ErrorF(" cfg_inb(%#x) = %2.2x\n", PciCfg1Addr + offset, *val); return 1; } return 0; } static int pciCfg1outb(CARD16 addr, CARD8 val) { int shift; if ((addr >= 0xCF8) && (addr <= 0xCFB)) { shift = (addr - 0xCF8) * 8; PciCfg1Addr &= ~(0xff << shift); PciCfg1Addr |= ((CARD32) val) << shift; return 1; } if ((addr >= 0xCFC) && (addr <= 0xCFF)) { const unsigned offset = addr - 0xCFC; if (PRINT_PORT && DEBUG_IO_TRACE()) ErrorF(" cfg_outb(%#x, %2.2x)\n", PciCfg1Addr + offset, val); pci_device_cfg_write_u8(pci_device_for_cfg_address(PciCfg1Addr), val, PCI_OFFSET(PciCfg1Addr) + offset); return 1; } return 0; } _X_EXPORT CARD8 bios_checksum(const CARD8 *start, int size) { CARD8 sum = 0; while (size-- > 0) sum += *start++; return sum; } /* * Lock/Unlock legacy VGA. Some Bioses try to be very clever and make * an attempt to detect a legacy ISA card. If they find one they might * act very strange: for example they might configure the card as a * monochrome card. This might cause some drivers to choke. * To avoid this we attempt legacy VGA by writing to all know VGA * disable registers before we call the BIOS initialization and * restore the original values afterwards. In beween we hold our * breath. To get to a (possibly exising) ISA card need to disable * our current PCI card. */ /* * This is just for booting: we just want to catch pure * legacy vga therefore we don't worry about mmio etc. * This stuff should really go into vgaHW.c. However then * the driver would have to load the vga-module prior to * doing int10. */ _X_EXPORT void LockLegacyVGA(xf86Int10InfoPtr pInt, legacyVGAPtr vga) { xf86SetCurrentAccess(FALSE, xf86Screens[pInt->scrnIndex]); vga->save_msr = inb(pInt->ioBase + 0x03CC); vga->save_vse = inb(pInt->ioBase + 0x03C3); #ifndef __ia64__ vga->save_46e8 = inb(pInt->ioBase + 0x46E8); #endif vga->save_pos102 = inb(pInt->ioBase + 0x0102); outb(pInt->ioBase + 0x03C2, ~(CARD8)0x03 & vga->save_msr); outb(pInt->ioBase + 0x03C3, ~(CARD8)0x01 & vga->save_vse); #ifndef __ia64__ outb(pInt->ioBase + 0x46E8, ~(CARD8)0x08 & vga->save_46e8); #endif outb(pInt->ioBase + 0x0102, ~(CARD8)0x01 & vga->save_pos102); xf86SetCurrentAccess(TRUE, xf86Screens[pInt->scrnIndex]); } _X_EXPORT void UnlockLegacyVGA(xf86Int10InfoPtr pInt, legacyVGAPtr vga) { xf86SetCurrentAccess(FALSE, xf86Screens[pInt->scrnIndex]); outb(pInt->ioBase + 0x0102, vga->save_pos102); #ifndef __ia64__ outb(pInt->ioBase + 0x46E8, vga->save_46e8); #endif outb(pInt->ioBase + 0x03C3, vga->save_vse); outb(pInt->ioBase + 0x03C2, vga->save_msr); xf86SetCurrentAccess(TRUE, xf86Screens[pInt->scrnIndex]); } #if defined (_PC) static void SetResetBIOSVars(xf86Int10InfoPtr pInt, Bool set) { int pagesize = getpagesize(); unsigned char* base = xf86MapVidMem(pInt->scrnIndex, VIDMEM_MMIO, 0, pagesize); int i; if (set) { for (i = BIOS_SCRATCH_OFF; i < BIOS_SCRATCH_END; i++) MEM_WW(pInt, i, *(base + i)); } else { for (i = BIOS_SCRATCH_OFF; i < BIOS_SCRATCH_END; i++) *(base + i) = MEM_RW(pInt, i); } xf86UnMapVidMem(pInt->scrnIndex,base,pagesize); } _X_EXPORT void xf86Int10SaveRestoreBIOSVars(xf86Int10InfoPtr pInt, Bool save) { int pagesize = getpagesize(); unsigned char* base; int i; if (!xf86IsEntityPrimary(pInt->entityIndex) || (!save && !pInt->BIOSScratch)) return; base = xf86MapVidMem(pInt->scrnIndex, VIDMEM_MMIO, 0, pagesize); base += BIOS_SCRATCH_OFF; if (save) { if ((pInt->BIOSScratch = xnfalloc(BIOS_SCRATCH_LEN))) for (i = 0; i < BIOS_SCRATCH_LEN; i++) *(((char*)pInt->BIOSScratch + i)) = *(base + i); } else { if (pInt->BIOSScratch) { for (i = 0; i < BIOS_SCRATCH_LEN; i++) *(base + i) = *(pInt->BIOSScratch + i); xfree(pInt->BIOSScratch); pInt->BIOSScratch = NULL; } } xf86UnMapVidMem(pInt->scrnIndex,base - BIOS_SCRATCH_OFF ,pagesize); } #endif _X_EXPORT xf86Int10InfoPtr xf86InitInt10(int entityIndex) { return xf86ExtendedInitInt10(entityIndex, 0); }