Subversion Repositories planix.SVN

Rev

Blame | Last modification | View Log | RSS feed

#include        "u.h"
#include        "tos.h"
#include        "../port/lib.h"
#include        "mem.h"
#include        "dat.h"
#include        "fns.h"
#include        "io.h"
#include        "ureg.h"
#include        "../port/error.h"

/*
 * Back the processor into real mode to run a BIOS call,
 * then return.  This must be used carefully, since it 
 * completely disables hardware interrupts (e.g., the i8259)
 * while running.  It is *not* using VM86 mode. 
 * Maybe that's really the right answer, but real mode
 * is fine for now.  We don't expect to use this very much --
 * just for VGA and APM.
 */
#define realmoderegs (*(Ureg*)RMUADDR)

#define LORMBUF (RMBUF-KZERO)

static Ureg rmu;
static Lock rmlock;

void
realmode(Ureg *ureg)
{
        int s;
        ulong cr3;
        extern void realmode0(void);    /* in l.s */

        if(getconf("*norealmode"))
                return;

        lock(&rmlock);
        realmoderegs = *ureg;

        /* copy l.s so that it can be run from 16-bit mode */
        memmove((void*)RMCODE, (void*)KTZERO, 0x1000);

        s = splhi();
        m->pdb[PDX(0)] = m->pdb[PDX(KZERO)];    /* identity map low */
        cr3 = getcr3();
        putcr3(PADDR(m->pdb));
        if (arch)
                arch->introff();
        else
                i8259off();
        realmode0();
        if(m->tss){
                /*
                 * Called from memory.c before initialization of mmu.
                 * Don't turn interrupts on before the kernel is ready!
                 */
                if (arch)
                        arch->intron();
                else
                        i8259on();
        }
        m->pdb[PDX(0)] = 0;     /* remove low mapping */
        putcr3(cr3);
        splx(s);
        *ureg = realmoderegs;
        unlock(&rmlock);
}

static long
rtrapread(Chan*, void *a, long n, vlong off)
{
        if(off < 0)
                error("badarg");
        if(n+off > sizeof rmu)
                n = sizeof rmu - off;
        if(n <= 0)
                return 0;
        memmove(a, (char*)&rmu+off, n);
        return n;
}

static long
rtrapwrite(Chan*, void *a, long n, vlong off)
{
        if(off || n != sizeof rmu)
                error("write a Ureg");
        memmove(&rmu, a, sizeof rmu);
        /*
         * Sanity check
         */
        if(rmu.trap == 0x10){   /* VBE */
                rmu.es = (LORMBUF>>4)&0xF000;
                rmu.di = LORMBUF&0xFFFF;
        }else
                error("invalid trap arguments");
        realmode(&rmu);
        return n;
}

static long
rmemrw(int isr, void *a, long n, vlong off)
{
        if(off < 0 || n < 0)
                error("bad offset/count");
        if(isr){
                if(off >= MB)
                        return 0;
                if(off+n >= MB)
                        n = MB - off;
                memmove(a, KADDR((ulong)off), n);
        }else{
                /* realmode buf page ok, allow vga framebuf's access */
                if(off >= MB || off+n > MB ||
                    (off < LORMBUF || off+n > LORMBUF+BY2PG) &&
                    (off < 0xA0000 || off+n > 0xB0000+0x10000))
                        error("bad offset/count in write");
                memmove(KADDR((ulong)off), a, n);
        }
        return n;
}

static long
rmemread(Chan*, void *a, long n, vlong off)
{
        return rmemrw(1, a, n, off);
}

static long
rmemwrite(Chan*, void *a, long n, vlong off)
{
        return rmemrw(0, a, n, off);
}

void
realmodelink(void)
{
        addarchfile("realmode", 0660, rtrapread, rtrapwrite);
        addarchfile("realmodemem", 0660, rmemread, rmemwrite);
}