/* $XFree86: xc/programs/Xserver/hw/xfree86/int10/helper_exec.c,v 1.32 2006/03/16 20:43:35 dawes Exp $ */
/*
 *                   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 Linux's ioperm() in xf86EnableIO().
 * Otherwise we won't trap on PIO.
 */

#include "xf86.h"
#include "xf86_OSproc.h"
#include "xf86_ansic.h"
#include "compiler.h"
#define _INT10_PRIVATE
#include "int10Defines.h"
#include "xf86int10.h"

static int pciCfg1in(CARD16 addr, CARD32 *val);
static int pciCfg1inw(CARD16 addr, CARD16 *val);
static int pciCfg1inb(CARD16 addr, CARD8 *val);
static int pciCfg1out(CARD16 addr, CARD32 val);
static int pciCfg1outw(CARD16 addr, CARD16 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 == 0x21 && 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");
}

/* Unaligned accesses are anathema for some platforms */
static void
UnalignedIO(xf86Int10InfoPtr pInt, const char *rw, unsigned long address,
	    unsigned long length)
{
    xf86DrvMsgVerb(pInt->scrnIndex, X_WARNING, 4,
	"BIOS requested unaligned %ld-byte I/O %s access to %#lx\n",
	length, rw, address);
#ifdef PRINT_PORT
    if (xf86GetVerbosity() > 3) {
	dump_registers(pInt);
	stack_trace(pInt);
    }
#endif
}

int
port_rep_inb(xf86Int10InfoPtr pInt,
	     CARD16 port, CARD32 base, int d_f, CARD32 count)
{
    CARD32 dst;
    int inc;

    if (count == 0)
	return 0;

    dst = base;
    inc = d_f ? -1 : 1;

#ifdef PRINT_PORT
    ErrorF(" rep_insb(%#x) %d bytes at %#x %s\n",
	   (unsigned int)port, (unsigned int)count, (unsigned int)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)
{
    CARD32 dst;
    int inc;

    if (count == 0)
	return 0;

    dst = base;
    inc = d_f ? -2 : 2;

#ifdef PRINT_PORT
    ErrorF(" rep_insw(%#x) %d bytes at %#x %s\n",
	   (unsigned int)port, (unsigned int)count, (unsigned int)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)
{
    CARD32 dst;
    int inc;

    if (count == 0)
	return 0;

    dst = base;
    inc = d_f ? -4 : 4;

#ifdef PRINT_PORT
    ErrorF(" rep_insl(%#x) %d bytes at %#x %s\n",
	   (unsigned int)port, (unsigned int)count, (unsigned int)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)
{
    CARD32 dst;
    int inc;

    if (count == 0)
	return 0;

    dst = base;
    inc = d_f ? -1 : 1;

#ifdef PRINT_PORT
    ErrorF(" rep_outb(%#x) %d bytes at %#x %s\n",
	   (unsigned int)port, (unsigned int)count, (unsigned int)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)
{
    CARD32 dst;
    int inc;

    if (count == 0)
	return 0;

    dst = base;
    inc = d_f ? -2 : 2;

#ifdef PRINT_PORT
    ErrorF(" rep_outw(%#x) %d bytes at %#x %s\n",
	   (unsigned int)port, (unsigned int)count, (unsigned int)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)
{
    CARD32 dst;
    int inc;

    if (count == 0)
	return 0;

    dst = base;
    inc = d_f ? -4 : 4;

#ifdef PRINT_PORT
    ErrorF(" rep_outl(%#x) %d bytes at %#x %s\n",
	   (unsigned int)port, (unsigned int)count, (unsigned int)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 = 0;

    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)) {
	    IOADDRESS ioport = Int10Current->ioBase + port;
	    volatile int signo;
	    int iScreen = Int10Current->scrnIndex;
	    signed char attempt;

	    xf86InterceptSignals(&signo);
	    for (attempt = 0;  attempt >= 0;  attempt++) {
		signo = -1;
		val = inb(ioport);
		if (signo < 0)
		    break;
		xf86DrvMsg(iScreen, X_WARNING,
			   "Signal %d interrupted inb(%#x) attempt %d\n",
			   signo, port, attempt);
	    }
	    xf86InterceptSignals(NULL);
	}
#ifdef PRINT_PORT
	ErrorF(" inb(%#x) = %#2.2x\n", port, val);
#endif
    }

    return val;
}

CARD16
x_inw(CARD16 port)
{
    CARD16 val = 0;

    if (port & 0x0001U) {
	UnalignedIO(Int10Current, "read", port, 2);
	val = x_inb(port);
	val |= (CARD16)x_inb(port + 1) << 8;
    } else if (port == 0x5c) {
	/*
	 * Emulate a PC98's timer.  Typical resolution is 3.26 usec.
	 * Approximate this by dividing by 3.
	 */
	long sec, usec;
	(void)getsecs(&sec, &usec);
	val = (CARD16)(usec / 3);
#ifdef PRINT_PORT
	ErrorF(" inw(%#x) = %#4.4x\n", port, val);
#endif
    } else {
	if (!pciCfg1inw(port, &val)) {
	    IOADDRESS ioport = Int10Current->ioBase + port;
	    volatile int signo;
	    int iScreen = Int10Current->scrnIndex;
	    signed char attempt;

	    xf86InterceptSignals(&signo);
	    for (attempt = 0;  attempt >= 0;  attempt++) {
		signo = -1;
		val = inw(ioport);
		if (signo < 0)
		    break;
		xf86DrvMsg(iScreen, X_WARNING,
			   "Signal %d interrupted inw(%#x) attempt %d\n",
			   signo, port, attempt);
	    }
	    xf86InterceptSignals(NULL);
	}
#ifdef PRINT_PORT
	ErrorF(" inw(%#x) = %#4.4x\n", port, val);
#endif
    }

    return val;
}

CARD32
x_inl(CARD16 port)
{
    CARD32 val;

    if ((port & 0x0003U) && (port != Int10Current->vmwarePort)) {
	UnalignedIO(Int10Current, "read", port, 4);
	if (port & 0x0001U) {
	    val = x_inb(port);
	    val |= (CARD32)x_inw(port + 1) << 8;
	    val |= (CARD32)x_inb(port + 3) << 24;
	} else {
	    val = x_inw(port);
	    val |= (CARD32)x_inw(port + 2) << 16;
	}
    } else {
	if (!pciCfg1in(port, &val))
	{
	    IOADDRESS ioport = Int10Current->ioBase + port;
	    volatile int signo;
	    int iScreen = Int10Current->scrnIndex;
	    signed char attempt;

	    xf86InterceptSignals(&signo);
	    for (attempt = 0;  attempt >= 0;  attempt++) {
		signo = -1;
		val = inl(ioport);
		if (signo < 0)
		    break;
		xf86DrvMsg(iScreen, X_WARNING,
		    "Signal %d interrupted inl(%#x) attempt %d\n",
		    signo, port, attempt);
	    }
	    xf86InterceptSignals(NULL);
	}
#ifdef PRINT_PORT
	ErrorF(" inl(%#x) = %#8.8x\n", port, (unsigned int)val);
#endif
    }

    return val;
}

void
x_outb(CARD16 port, CARD8 val)
{
    if ((port == 0x43) && (val == 0)) {
	/*
	 * 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.
	 */
	long sec, usec;
	(void) getsecs(&sec, &usec);
	Int10Current->inb40time = (CARD16)(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 {
	if (!pciCfg1outb(port, val)) {
	    IOADDRESS ioport = Int10Current->ioBase + port;
	    volatile int signo;
	    int iScreen = Int10Current->scrnIndex;
	    signed char attempt;

	    xf86InterceptSignals(&signo);
	    for (attempt = 0;  attempt >= 0;  attempt++) {
		signo = -1;
		outb(ioport, val);
		if (signo < 0)
		    break;
		xf86DrvMsg(iScreen, X_WARNING,
			   "Signal %d interrupted outb(%#x, %#x) attempt %d\n",
			   signo, port, val, attempt);
	    }
	    xf86InterceptSignals(NULL);
	}
#ifdef PRINT_PORT
	ErrorF(" outb(%#x, %#2.2x)\n", port, val);
#endif
    }
}

void
x_outw(CARD16 port, CARD16 val)
{
    if (port & 0x0001U) {
	UnalignedIO(Int10Current, "write", port, 2);
	x_outb(port, val);
	x_outb(port + 1, val >> 8);
    } else {
	if (!pciCfg1outw(port, val)) {
	    IOADDRESS ioport = Int10Current->ioBase + port;
	    volatile int signo;
	    int iScreen = Int10Current->scrnIndex;
	    signed char attempt;

	    xf86InterceptSignals(&signo);
	    for (attempt = 0;  attempt >= 0;  attempt++) {
		signo = -1;
		outw(ioport, val);
		if (signo < 0)
		    break;
		xf86DrvMsg(iScreen, X_WARNING,
			   "Signal %d interrupted outw(%#x, %#x) attempt %d\n",
			   signo, port, val, attempt);
	    }
	}
	xf86InterceptSignals(NULL);
#ifdef PRINT_PORT
	ErrorF(" outw(%#x, %#4.4x)\n", port, val);
#endif
    }
}

void
x_outl(CARD16 port, CARD32 val)
{
    if ((port & 0x0003U) && (port != Int10Current->vmwarePort)) {
	UnalignedIO(Int10Current, "write", port, 4);
	if (port & 0x0001U) {
	    x_outb(port, val);
	    x_outw(port + 1, val >> 8);
	    x_outb(port + 3, val >> 24);
	} else {
	    x_outw(port, val);
	    x_outw(port + 2, val >> 16);
	}
    } else {
	if (!pciCfg1out(port, val))
	{
	    IOADDRESS ioport = Int10Current->ioBase + port;
	    volatile int signo;
	    int iScreen = Int10Current->scrnIndex;
	    signed char attempt;

	    xf86InterceptSignals(&signo);
	    for (attempt = 0;  attempt >= 0;  attempt++) {
		signo = -1;
		outl(ioport, val);
		if (signo < 0)
		    break;
		xf86DrvMsg(iScreen, X_WARNING,
		    "Signal %d interrupted outl(%#x, %#x) attempt %d\n",
		    signo, port, (unsigned int)val, attempt);
	    }
	    xf86InterceptSignals(NULL);
	}
#ifdef PRINT_PORT
	ErrorF(" outl(%#x, %#8.8x)\n", port, (unsigned int)val);
#endif
    }
}

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 TAG(Cfg1Addr) (Cfg1Addr & 0xffff00)
#define OFFSET(Cfg1Addr) (Cfg1Addr & 0xff)

static int
pciCfg1in(CARD16 addr, CARD32 *val)
{
    if (addr == 0xCF8) {
	*val = PciCfg1Addr;
	return 1;
    }

    if (addr == 0xCFC) {
	*val = pciReadLong(TAG(PciCfg1Addr), OFFSET(PciCfg1Addr));
	return 1;
    }

    return 0;
}

static int
pciCfg1out(CARD16 addr, CARD32 val)
{
    if (addr == 0xCF8) {
	PciCfg1Addr = val;
	return 1;
    }

    if (addr == 0xCFC) {
	pciWriteLong(TAG(PciCfg1Addr), OFFSET(PciCfg1Addr), val);
	return 1;
    }

    return 0;
}

static int
pciCfg1inw(CARD16 addr, CARD16 *val)
{
    if (addr == 0xCF8 || addr == 0xCFA) {
	int shift;
	CARD32 mask;

	shift = (addr - 0xCF8) * 8;
	mask = 0xffff << shift;
	*val = (PciCfg1Addr & mask) >> shift;
	return 1;
    }

    if (addr == 0xCFC || addr == 0xCFE) {
	int offset;

	offset = addr - 0xCFC;
	*val = pciReadWord(TAG(PciCfg1Addr), OFFSET(PciCfg1Addr) + offset);
	return 1;
    }

    return 0;
}

static int
pciCfg1outw(CARD16 addr, CARD16 val)
{
    if (addr == 0xCF8 || addr == 0xCFA) {
	int shift;
	CARD32 mask;

	shift = (addr - 0xCF8) * 8;
	mask = ~(0xffff << shift);
	PciCfg1Addr &= mask;
	PciCfg1Addr |= (val << shift);
	return 1;
    }

    if (addr == 0xCFC || addr == 0xCFE) {
	int offset;

	offset = addr - 0xCFC;
	pciWriteWord(TAG(PciCfg1Addr), OFFSET(PciCfg1Addr) + offset, val);
	return 1;
    }

    return 0;
}

static int
pciCfg1inb(CARD16 addr, CARD8 *val)
{
    if (addr >= 0xCF8 && addr <= 0xCFB) {
	int shift;
	CARD32 mask;

	shift = (addr - 0xCF8) * 8;
	mask = 0xff << shift;
	*val = (PciCfg1Addr & mask) >> shift;
	return 1;
    }

    if (addr >= 0xCFC && addr <= 0xCFF) {
	int offset;

	offset = addr - 0xCFC;
	*val = pciReadByte(TAG(PciCfg1Addr), OFFSET(PciCfg1Addr) + offset);
	return 1;
    }

    return 0;
}

static int
pciCfg1outb(CARD16 addr, CARD8 val)
{
    if (addr >= 0xCF8 && addr <= 0xCFB) {
	int shift;
	CARD32 mask;

	shift = (addr - 0xCF8) * 8;
	mask = ~(0xff << shift);
	PciCfg1Addr &= mask;
	PciCfg1Addr |= (val << shift);
	return 1;
    }

    if (addr >= 0xCFC && addr <= 0xCFF) {
	int offset;

	offset = addr - 0xCFC;
	pciWriteByte(TAG(PciCfg1Addr), OFFSET(PciCfg1Addr) + offset, val);
	return 1;
    }

    return 0;
}

CARD8
bios_checksum(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
 * initialisation and restore the original values afterwards.  In between 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 i, pagesize = getpagesize();
    unsigned char* base =
	xf86MapVidMem(pInt->scrnIndex, VIDMEM_MMIO, 0, pagesize);

    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 i, pagesize = getpagesize();
    unsigned char* base;

    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
