xserver-multidpi/hw/xfree86/int10/helper_exec.c
2006-11-03 18:59:51 +01:00

692 lines
16 KiB
C

/*
* XFree86 int10 module
* execute BIOS int 10h calls in x86 real mode environment
* Copyright 1999 Egbert Eich
*
* Part of this is based on code taken from DOSEMU
* (C) Copyright 1992, ..., 1999 the "DOSEMU-Development-Team"
*/
/*
* To debug port accesses define PRINT_PORT.
* Note! You also have to comment out ioperm()
* in xf86EnableIO(). Otherwise we won't trap
* on PIO.
*/
#ifdef HAVE_XORG_CONFIG_H
#include <xorg-config.h>
#endif
#include <unistd.h>
#include <X11/Xos.h>
#include "xf86.h"
#include "xf86_OSproc.h"
#include "compiler.h"
#define _INT10_PRIVATE
#include "int10Defines.h"
#include "xf86int10.h"
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
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();
}
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 */
CARD32
getIntVect(xf86Int10InfoPtr pInt,int num)
{
return MEM_RW(pInt, num << 2) + (MEM_RW(pInt, (num << 2) + 2) << 4);
}
void
pushw(xf86Int10InfoPtr pInt, CARD16 val)
{
X86_ESP -= 2;
MEM_WW(pInt, ((CARD32) X86_SS << 4) + X86_SP, val);
}
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 */
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");
}
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);
}
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");
}
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;
#ifdef PRINT_PORT
ErrorF(" rep_insb(%#x) %d bytes at %p %s\n",
port, count, base, d_f ? "up" : "down");
#endif
while (count--) {
MEM_WB(pInt, dst, x_inb(port));
dst += inc;
}
return dst - base;
}
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;
#ifdef PRINT_PORT
ErrorF(" rep_insw(%#x) %d bytes at %p %s\n",
port, count, base, d_f ? "up" : "down");
#endif
while (count--) {
MEM_WW(pInt, dst, x_inw(port));
dst += inc;
}
return dst - base;
}
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;
#ifdef PRINT_PORT
ErrorF(" rep_insl(%#x) %d bytes at %p %s\n",
port, count, base, d_f ? "up" : "down");
#endif
while (count--) {
MEM_WL(pInt, dst, x_inl(port));
dst += inc;
}
return dst - base;
}
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;
#ifdef PRINT_PORT
ErrorF(" rep_outb(%#x) %d bytes at %p %s\n",
port, count, base, d_f ? "up" : "down");
#endif
while (count--) {
x_outb(port, MEM_RB(pInt, dst));
dst += inc;
}
return dst - base;
}
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;
#ifdef PRINT_PORT
ErrorF(" rep_outw(%#x) %d bytes at %p %s\n",
port, count, base, d_f ? "up" : "down");
#endif
while (count--) {
x_outw(port, MEM_RW(pInt, dst));
dst += inc;
}
return dst - base;
}
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;
#ifdef PRINT_PORT
ErrorF(" rep_outl(%#x) %d bytes at %p %s\n",
port, count, base, d_f ? "up" : "down");
#endif
while (count--) {
x_outl(port, MEM_RL(pInt, dst));
dst += inc;
}
return dst - base;
}
CARD8
x_inb(CARD16 port)
{
CARD8 val;
if (port == 0x40) {
Int10Current->inb40time++;
val = (CARD8)(Int10Current->inb40time >>
((Int10Current->inb40time & 1) << 3));
#ifdef PRINT_PORT
ErrorF(" inb(%#x) = %2.2x\n", port, val);
#endif
#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);
#ifdef PRINT_PORT
ErrorF(" inb(%#x) = %2.2x\n", port, val);
#endif
}
return val;
}
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);
}
#ifdef PRINT_PORT
ErrorF(" inw(%#x) = %4.4x\n", port, val);
#endif
return val;
}
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);
#ifdef PRINT_PORT
ErrorF(" outb(%#x, %2.2x)\n", port, val);
#endif
#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 {
#ifdef PRINT_PORT
ErrorF(" outb(%#x, %2.2x)\n", port, val);
#endif
if (!pciCfg1outb(port, val))
outb(Int10Current->ioBase + port, val);
}
}
void
x_outw(CARD16 port, CARD16 val)
{
#ifdef PRINT_PORT
ErrorF(" outw(%#x, %4.4x)\n", port, val);
#endif
if (!pciCfg1outw(port, val))
outw(Int10Current->ioBase + port, val);
}
CARD32
x_inl(CARD16 port)
{
CARD32 val;
if (!pciCfg1in(port, &val))
val = inl(Int10Current->ioBase + port);
#ifdef PRINT_PORT
ErrorF(" inl(%#x) = %8.8x\n", port, val);
#endif
return val;
}
void
x_outl(CARD16 port, CARD32 val)
{
#ifdef PRINT_PORT
ErrorF(" outl(%#x, %8.8x)\n", port, val);
#endif
if (!pciCfg1out(port, val))
outl(Int10Current->ioBase + port, val);
}
CARD8
Mem_rb(CARD32 addr)
{
return (*Int10Current->mem->rb)(Int10Current, addr);
}
CARD16
Mem_rw(CARD32 addr)
{
return (*Int10Current->mem->rw)(Int10Current, addr);
}
CARD32
Mem_rl(CARD32 addr)
{
return (*Int10Current->mem->rl)(Int10Current, addr);
}
void
Mem_wb(CARD32 addr, CARD8 val)
{
(*Int10Current->mem->wb)(Int10Current, addr, val);
}
void
Mem_ww(CARD32 addr, CARD16 val)
{
(*Int10Current->mem->ww)(Int10Current, addr, val);
}
void
Mem_wl(CARD32 addr, CARD32 val)
{
(*Int10Current->mem->wl)(Int10Current, addr, val);
}
static CARD32 PciCfg1Addr = 0;
#define OFFSET(Cfg1Addr) (Cfg1Addr & 0xff)
static int
pciCfg1in(CARD16 addr, CARD32 *val)
{
if (addr == 0xCF8) {
*val = PciCfg1Addr;
return 1;
}
if (addr == 0xCFC) {
*val = pciReadLong(Int10Current->Tag, OFFSET(PciCfg1Addr));
return 1;
}
return 0;
}
static int
pciCfg1out(CARD16 addr, CARD32 val)
{
if (addr == 0xCF8) {
PciCfg1Addr = val;
return 1;
}
if (addr == 0xCFC) {
pciWriteLong(Int10Current->Tag, OFFSET(PciCfg1Addr), val);
return 1;
}
return 0;
}
static int
pciCfg1inw(CARD16 addr, CARD16 *val)
{
int offset, shift;
if ((addr >= 0xCF8) && (addr <= 0xCFB)) {
shift = (addr - 0xCF8) * 8;
*val = (PciCfg1Addr >> shift) & 0xffff;
return 1;
}
if ((addr >= 0xCFC) && (addr <= 0xCFF)) {
offset = addr - 0xCFC;
*val = pciReadWord(Int10Current->Tag, OFFSET(PciCfg1Addr) + offset);
return 1;
}
return 0;
}
static int
pciCfg1outw(CARD16 addr, CARD16 val)
{
int offset, shift;
if ((addr >= 0xCF8) && (addr <= 0xCFB)) {
shift = (addr - 0xCF8) * 8;
PciCfg1Addr &= ~(0xffff << shift);
PciCfg1Addr |= ((CARD32) val) << shift;
return 1;
}
if ((addr >= 0xCFC) && (addr <= 0xCFF)) {
offset = addr - 0xCFC;
pciWriteWord(Int10Current->Tag, OFFSET(PciCfg1Addr) + offset, val);
return 1;
}
return 0;
}
static int
pciCfg1inb(CARD16 addr, CARD8 *val)
{
int offset, shift;
if ((addr >= 0xCF8) && (addr <= 0xCFB)) {
shift = (addr - 0xCF8) * 8;
*val = (PciCfg1Addr >> shift) & 0xff;
return 1;
}
if ((addr >= 0xCFC) && (addr <= 0xCFF)) {
offset = addr - 0xCFC;
*val = pciReadByte(Int10Current->Tag, OFFSET(PciCfg1Addr) + offset);
return 1;
}
return 0;
}
static int
pciCfg1outb(CARD16 addr, CARD8 val)
{
int offset, shift;
if ((addr >= 0xCF8) && (addr <= 0xCFB)) {
shift = (addr - 0xCF8) * 8;
PciCfg1Addr &= ~(0xff << shift);
PciCfg1Addr |= ((CARD32) val) << shift;
return 1;
}
if ((addr >= 0xCFC) && (addr <= 0xCFF)) {
offset = addr - 0xCFC;
pciWriteByte(Int10Current->Tag, OFFSET(PciCfg1Addr) + offset, val);
return 1;
}
return 0;
}
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.
*/
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]);
}
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);
}
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
xf86Int10InfoPtr
xf86InitInt10(int entityIndex)
{
return xf86ExtendedInitInt10(entityIndex, 0);
}