Subversion Repositories planix.SVN

Rev

Blame | Last modification | View Log | RSS feed

/*
 * expand gzipped boot loader appended to this binary and execute it.
 *
 * due to Russ Cox, rsc@swtch.com.
 * see http://plan9.bell-labs.com/wiki/plan9/Replacing_9load
 */
#include <u.h>
#include <libc.h>
#include <a.out.h>
#include <flate.h>
#include "mem.h"
#include "expand.h"

#include "inflate.guts.c"

#define KB              1024
#define MB              (1024*1024)

extern char edata[];

/* ldecomp.s */
void mb586(void);
void splhi(void);
void wbinvd(void);

/* inflate.guts.c */
int gunzip(uchar*, int, uchar*, int);

int isexec(void*);
int isgzip(uchar*);
void run(void*);

#pragma varargck type "d" ulong
#pragma varargck type "x" ulong

static uchar *kernel = (uchar*)Bootkernaddr;
static char *dbrk = (char*)Mallocbase;

ulong
swap(ulong p)
{
        return p<<24 | p>>24 | (p<<8)&0x00FF0000 | (p>>8)&0x0000FF00;
}

enum {
        /* keyboard controller ports & cmds */
        Data=           0x60,           /* data port */
        Status=         0x64,           /* status port */
         Inready=       0x01,           /*  input character ready */
         Outbusy=       0x02,           /*  output busy */
        Cmd=            0x64,           /* command port (write only) */

        /* system control port a */
        Sysctla=        0x92,
         Sysctlreset=   1<<0,
         Sysctla20ena=  1<<1,
};

static int
isa20on(void)
{
        int r;
        ulong o;
        ulong *zp, *mb1p;

        zp = 0;
        mb1p = (ulong *)MB;
        o = *zp;

        *zp = 0x1234;
        *mb1p = 0x8765;
        mb586();
        wbinvd();
        r = *zp != *mb1p;

        *zp = o;
        return r;
}

static void
delay(uint ms)                          /* approximate */
{
        int i;

        while(ms-- > 0)
                for(i = 1000*1000; i > 0; i--)
                        ;
}

static int
kbdinit(void)
{
        int c, try;

        /* wait for a quiescent controller */
        try = 500;                      /* was 1000 */
        while(try-- > 0 && (c = inb(Status)) & (Outbusy | Inready)) {
                if(c & Inready)
                        inb(Data);
                delay(1);
        }
        return try <= 0? -1: 0;
}

/*
 *  wait for output no longer busy (but not forever,
 *  there might not be a keyboard controller).
 */
static void
outready(void)
{
        int i;

        for (i = 1000; i > 0 && inb(Status) & Outbusy; i--)
                delay(1);
}

/*
 *  ask 8042 to enable the use of address bit 20
 */
int
i8042a20(void)
{
        if (kbdinit() < 0)
                return -1;
        outready();
        outb(Cmd, 0xD1);
        outready();
        outb(Data, 0xDF);
        outready();
        return 0;
}

void
a20init(void)
{
        int b;

        if (isa20on())
                return;
        if (i8042a20() < 0) {           /* original method, via kbd ctlr */
                /* newer method, last resort */
                b = inb(Sysctla);
                if (!(b & Sysctla20ena))
                        outb(Sysctla, (b & ~Sysctlreset) | Sysctla20ena);
        }
        if (!isa20on()){
                print("a20 didn't come on!\n");
                for(;;)
                        ;
        }
}

void
_main(void)
{
        int ksize;
        Exec *exec;

        splhi();
        a20init();              /* don't wrap addresses at 1MB boundaries */
        ksize = Lowmemsz - (ulong)edata;        /* payload size */
        memmove(kernel, edata, ksize);
        memset(edata, 0, end - edata);

        cgainit();
        if(isgzip(kernel)) {
                print("gz...");
                memmove((uchar*)Unzipbuf, kernel, ksize);

                /* we have to uncompress the entire kernel to get OK status */
                if(gunzip(kernel, Bootkernmax, (uchar*)Unzipbuf, ksize) < 0){
                        print("gzip failed.");
                        exits(0);
                }
        }
        if(isexec(kernel))
                run(kernel);

        exec = (Exec *)kernel;
        print("unrecognized program; magic # 0x%x\n", swap(exec->magic));
        exits(0);
}

int
isexec(void *v)
{
        Exec *exec;

        exec = v;
        return swap(exec->magic) == I_MAGIC || swap(exec->magic) == S_MAGIC;
}

void
run(void *v)
{
        ulong entry, text, data;
        uchar *base;
        Exec *exec;

        base = v;
        exec = v;
        entry = swap(exec->entry) & ~KSEGM;
        text = swap(exec->text);
        data = swap(exec->data);
        /*
         * align data segment on the expected page boundary.
         * sizeof(Exec)+text is offset from base to data segment.
         */
        memmove(base+PGROUND(sizeof(Exec)+text), base+sizeof(Exec)+text, data);

        print("starting protected-mode loader at 0x%x\n", entry);
        ((void(*)(void))entry)();

        print("exec failed");
        exits(0);
}

int
isgzip(uchar *p)
{
        return p[0] == 0x1F && p[1] == 0x8B;
}

void*
malloc(ulong n)
{
        void *v;

        v = dbrk;
        dbrk += ROUND(n, BY2WD);
        return v;
}

void
free(void*)
{
}

void
puts(char *s)
{
        for(; *s; s++)
                cgaputc(*s);
}

int
print(char *fmt, ...)
{
        int sign;
        long d;
        ulong x;
        char *p, *s, buf[20];
        va_list arg;
        static char *hex = "0123456789abcdef";

        va_start(arg, fmt);
        for(p = fmt; *p; p++){
                if(*p != '%') {
                        cgaputc(*p);
                        continue;
                }
                SET(s);
                switch(*++p){
                case 'p':
                case 'x':
                        x = va_arg(arg, ulong);
                        if(x == 0){
                                s = "0";
                                break;
                        }
                        s = buf+sizeof buf;
                        *--s = 0;
                        while(x > 0){
                                *--s = hex[x&15];
                                x /= 16;
                        }
                        if(s == buf+sizeof buf)
                                *--s = '0';
                        break;
                case 'd':
                        d = va_arg(arg, ulong);
                        if(d == 0){
                                s = "0";
                                break;
                        }
                        if(d < 0){
                                d = -d;
                                sign = -1;
                        }else
                                sign = 1;
                        s = buf+sizeof buf;
                        *--s = 0;
                        while(d > 0){
                                *--s = (d%10)+'0';
                                d /= 10;
                        }
                        if(sign < 0)
                                *--s = '-';
                        break;
                case 's':
                        s = va_arg(arg, char*);
                        break;
                case 0:
                        return 0;
                }
                puts(s);
        }
        return 0;
}

void
exits(char*)
{
        for(;;)
                ;
}