Subversion Repositories planix.SVN

Rev

Blame | Last modification | View Log | RSS feed

/*
 * parse plan.ini or /cfg/pxe/%E file into low memory
 */
#include        "u.h"
#include        "../port/lib.h"
#include        "mem.h"
#include        "dat.h"
#include        "fns.h"
#include        "io.h"
#include        "ureg.h"
#include        "pool.h"
#include        "../port/netif.h"
#include        "../ip/ip.h"
#include        "pxe.h"

typedef struct {
        char*   name;
        int     start;
        int     end;
} Mblock;

typedef struct {
        char*   tag;
        Mblock* mb;
} Mitem;

Chan *conschan;

static Mblock mblock[MAXCONF];
static int nmblock;
static Mitem mitem[MAXCONF];
static int nmitem;
static char* mdefault;
static char mdefaultbuf[10];
static int mtimeout;

static char*
comma(char* line, char** residue)
{
        char *q, *r;

        if((q = strchr(line, ',')) != nil){
                *q++ = 0;
                if(*q == ' ')
                        q++;
        }
        *residue = q;

        if((r = strchr(line, ' ')) != nil)
                *r = 0;

        if(*line == ' ')
                line++;
        return line;
}

static Mblock*
findblock(char* name, char** residue)
{
        int i;
        char *p;

        p = comma(name, residue);
        for(i = 0; i < nmblock; i++){
                if(strcmp(p, mblock[i].name) == 0)
                        return &mblock[i];
        }
        return nil;
}

static Mitem*
finditem(char* name, char** residue)
{
        int i;
        char *p;

        p = comma(name, residue);
        for(i = 0; i < nmitem; i++){
                if(strcmp(p, mitem[i].mb->name) == 0)
                        return &mitem[i];
        }
        return nil;
}

/* timeout is in seconds */
int
getstr(char *prompt, char *buf, int size, char *def, int timeout)
{
        int len, isdefault;
        static char pbuf[PRINTSIZE];

        if(conschan == nil)
                panic("getstr: #c/cons not open");
        buf[0] = 0;
        isdefault = (def && *def);
        if(isdefault == 0){
                timeout = 0;
                snprint(pbuf, sizeof pbuf, "%s: ", prompt);
        }
        else if(timeout)
                snprint(pbuf, sizeof pbuf, "%s[default==%s (%ds timeout)]: ",
                        prompt, def, timeout);
        else
                snprint(pbuf, sizeof pbuf, "%s[default==%s]: ", prompt, def);
        for (;;) {
                print("%s", pbuf);
                if (timeout > 0) {
                        for(timeout *= 1000; timeout > 0; timeout -= 100) {
                                if (qlen(kbdq) > 0)     /* if input queued */
                                        break; 
                                tsleep(&up->sleep, return0, 0, 100);
                        }
                        if (timeout <= 0) {             /* use default */
                                print("\n");
                                len = 0;
                                break;
                        }
                }
                buf[0] = '\0';
                len = devtab[conschan->type]->read(conschan, buf, size - 1,
                        conschan->offset);
                if(len >= 0)
                        buf[len] = '\0';
                switch(len){
                case 0:                          /* eof */
                case 1:                         /* newline */
                        len = 0;
                        buf[len] = '\0';
                        if(!isdefault)
                                continue;
                        break;
                }
                if(len < size - 1)
                        break;
                print("line too long\n");
        }
        if(len == 0 && isdefault)
                strncpy(buf, def, size);
        return 0;
}

void
askbootfile(char *buf, int len, char **bootfp, int secs, char *def)
{
        getstr("\nBoot from", buf, len, def, secs);
        trimnl(buf);
        if (bootfp)
                kstrdup(bootfp, buf);
}

int
isconf(char *name)
{
        int i;

        for(i = 0; i < nconf; i++)
                if(cistrcmp(confname[i], name) == 0)
                        return 1;
        return 0;
}

/* result is not malloced, unlike user-mode getenv() */
char*
getconf(char *name)
{
        int i, n, nmatch;
        char buf[120];

        nmatch = 0;
        for(i = 0; i < nconf; i++)
                if(cistrcmp(confname[i], name) == 0)
                        nmatch++;

        switch(nmatch) {
        default:
                print("\n");
                nmatch = 0;
                for(i = 0; i < nconf; i++)
                        if(cistrcmp(confname[i], name) == 0)
                                print("%d. %s\n", ++nmatch, confval[i]);
                print("%d. none of the above\n", ++nmatch);
                do {
                        getstr(name, buf, sizeof(buf), nil, 0);
                        n = atoi(buf);
                } while(n < 1 || n > nmatch);

                for(i = 0; i < nconf; i++)
                        if(cistrcmp(confname[i], name) == 0)
                                if(--n == 0)
                                        return confval[i];
                break;

        case 1:
                for(i = 0; i < nconf; i++)
                        if(cistrcmp(confname[i], name) == 0)
                                return confval[i];
                break;

        case 0:
                break;
        }
        return nil;
}

static void
parsemenu(char* str, char* scratch, int len)
{
        Mitem *mi;
        Mblock *mb, *menu;
        char buf[20], *p, *q, *line[MAXCONF];
        int i, inblock, n, show;

        inblock = 0;
        menu = nil;
        memmove(scratch, str, len);
        n = getfields(scratch, line, MAXCONF, 0, "\n");
        if(n >= MAXCONF)
                print("warning: possibly too many lines in plan9.ini\n");
        for(i = 0; i < n; i++){
                p = line[i];
                if(inblock && *p == '['){
                        mblock[nmblock].end = i;
                        if(strcmp(mblock[nmblock].name, "menu") == 0)
                                menu = &mblock[nmblock];
                        nmblock++;
                        inblock = 0;
                }
                if(*p == '['){
                        if(nmblock == 0 && i != 0){
                                mblock[nmblock].name = "common";
                                mblock[nmblock].start = 0;
                                mblock[nmblock].end = i;
                                nmblock++;
                        }
                        q = strchr(p+1, ']');
                        if(q == nil || *(q+1) != 0){
                                print("malformed menu block header - %s\n", p);
                                return;
                        }
                        *q = 0;
                        mblock[nmblock].name = p+1;
                        mblock[nmblock].start = i+1;
                        inblock = 1;
                }
        }

        if(inblock){
                mblock[nmblock].end = i;
                nmblock++;
        }
        if(menu == nil)
                return;
        if(nmblock < 2){
                print("incomplete menu specification\n");
                return;
        }

        for(i = menu->start; i < menu->end; i++){
                p = line[i];
                if(cistrncmp(p, "menuitem=", 9) == 0){
                        p += 9;
                        if((mb = findblock(p, &q)) == nil){
                                print("no block for menuitem %s\n", p);
                                return;
                        }
                        if(q != nil)
                                mitem[nmitem].tag = q;
                        else
                                mitem[nmitem].tag = mb->name;
                        mitem[nmitem].mb = mb;
                        nmitem++;
                }
                else if(cistrncmp(p, "menudefault=", 12) == 0){
                        p += 12;
                        if((mi = finditem(p, &q)) == nil){
                                print("no item for menudefault %s\n", p);
                                return;
                        }
                        if(q != nil)
                                mtimeout = strtol(q, 0, 0);
                        snprint(mdefaultbuf, sizeof mdefaultbuf, "%ld",
                                mi-mitem+1);
                        mdefault = mdefaultbuf;
                }
                else if(cistrncmp(p, "menuconsole=", 12) == 0){
                        p += 12;
                        p = comma(p, &q);
                        i8250config(p);
                }
                else{
                        print("invalid line in [menu] block - %s\n", p);
                        return;
                }
        }

again:
        print("\nPlan 9 Startup Menu:\n====================\n");
        for(i = 0; i < nmitem; i++)
                print("    %d. %s\n", i+1, mitem[i].tag);
        for(;;){
                getstr("Selection", buf, sizeof(buf), mdefault, mtimeout);
                mtimeout = 0;
                i = strtol(buf, &p, 0)-1;
                if(i < 0 || i >= nmitem)
                        goto again;
                switch(*p){
                case 'p':
                case 'P':
                        show = 1;
                        print("\n");
                        break;
                case 0:
                case '\n':
                        show = 0;
                        break;
                default:
                        continue;
                        
                }
                mi = &mitem[i];
        
                p = str;
                p += snprint(p, len, "menuitem=%s\n", mi->mb->name);
                for(i = 0; i < nmblock; i++){
                        mb = &mblock[i];
                        if(mi->mb != mb && cistrcmp(mb->name, "common") != 0)
                                continue;
                        for(n = mb->start; n < mb->end; n++)
                                p += snprint(p, &str[len] - p, "%s\n", line[n]);
                }

                if(show){
                        for(q = str; q < p; q += i){
                                if((i = print(q)) <= 0)
                                        break;
                        }
                        goto again;
                }
                break;
        }
        print("\n");
}

/* dig out tables created by l16r.s in real mode */
void
readlsconf(void)
{
        int i, n;
        uchar *p;
        MMap *map;
        u64int addr, len;

        /*
         * we could be running above 1MB, so put bios tables in low memory,
         * not after end.
         */
        p = (uchar*)KADDR(BIOSTABLES);
        for(n = 0; n < nelem(mmap); n++){
                if(*p == 0)
                        break;
                if(memcmp(p, "APM\0", 4) == 0){
                        p += 20;
                        continue;
                }
                else if(memcmp(p, "MAP\0", 4) == 0){
                        map = (MMap*)p;

                        switch(map->type){
                        default:
                                if(v_flag)
                                        print("type %ud", map->type);
                                break;
                        case 1:
                                if(v_flag)
                                        print("Memory");
                                break;
                        case 2:
                                if(v_flag)
                                        print("reserved");
                                break;
                        case 3:
                                if(v_flag)
                                        print("ACPI Reclaim Memory");
                                break;
                        case 4:
                                if(v_flag)
                                        print("ACPI NVS Memory");
                                break;
                        }
                        addr = (((u64int)map->base[1])<<32)|map->base[0];
                        len = (((u64int)map->length[1])<<32)|map->length[0];
                        if(v_flag)
                                print("\t%#16.16lluX %#16.16lluX (%llud)\n",
                                        addr, addr+len, len);

                        if(nmmap < nelem(mmap)){
                                memmove(&mmap[nmmap], map, sizeof(MMap));
                                mmap[nmmap].size = 20;
                                nmmap++;
                        }
                        p += 24;
                        continue;
                }
                else{
                         /* ideally we shouldn't print here */
                        print("\nweird low-memory map at %#p:\n", p);
                        for(i = 0; i < 24; i++)
                                print(" %2.2uX", *(p+i));
                        print("\n");
                        delay(5000);
                }
                break;
        }
}

void
addconf(char *fmt, ...)
{
        va_list arg;

        va_start(arg, fmt);
        vseprint(BOOTARGS+strlen(BOOTARGS), BOOTARGS+BOOTARGSLEN, fmt, arg);
        va_end(arg);
}

void
dumpbootargs(void)
{
        char *p, *nl;

        /* in the boot, we can only print PRINTSIZE (256) bytes at a time. */
        print("boot args: ");
        for (p = (char *)BOOTARGS; *p != '\0'; p = nl) {
                nl = strchr(p, '\n');
                if (nl != nil) {
                        ++nl;
                        print("%.*s", (int)(nl - p), p);
                }
        }
}

void
changeconf(char *fmt, ...)
{
        va_list arg;
        char *p, *q, pref[20], buf[128];

        va_start(arg, fmt);
        vseprint(buf, buf+sizeof buf, fmt, arg);
        va_end(arg);

        pref[0] = '\n';
        strncpy(pref+1, buf, 19);
        pref[19] = '\0';
        if(p = strchr(pref, '='))
                *(p+1) = '\0';
        else
                print("warning: did not change %s in plan9.ini\n", buf);

        /* find old line by looking for \nwhat= */
        if(strncmp(BOOTARGS, pref+1, strlen(pref+1)) == 0)
                p = BOOTARGS;
        else if(p = strstr(BOOTARGS, pref))
                p++;
        else
                p = nil;

        /* move rest of args up, deleting what= line. */
        if(p != nil && (q = strchr(p, '\n')) != nil)
                memmove(p, q+1, strlen(q+1)+1);

        /* add replacement to end */
        addconf("%s", buf);
}

/*
 *  read configuration file
 */
static char id[8] = "ZORT 0\r\n";

int
dotini(char *inibuf)
{
        int blankline, i, incomment, inspace, n;
        char *cp, *p, *q, *line[MAXCONF];

        cp = inibuf;

        /*
         * Strip out '\r', change '\t' -> ' '.
         * Change runs of spaces into single spaces.
         * Strip out trailing spaces, blank lines.
         *
         * We do this before we make the copy so that if we 
         * need to change the copy, it is already fairly clean.
         * The main need is in the case when plan9.ini has been
         * padded with lots of trailing spaces, as is the case 
         * for those created during a distribution install.
         */
        p = cp;
        blankline = 1;
        incomment = inspace = 0;
        for(q = cp; *q; q++){
                if(*q == '\r')
                        continue;
                if(*q == '\t')
                        *q = ' ';
                if(*q == ' '){
                        inspace = 1;
                        continue;
                }
                if(*q == '\n'){
                        if(!blankline){
                                if(!incomment)
                                        *p++ = '\n';
                                blankline = 1;
                        }
                        incomment = inspace = 0;
                        continue;
                }
                if(inspace){
                        if(!blankline && !incomment)
                                *p++ = ' ';
                        inspace = 0;
                }
                if(blankline && *q == '#')
                        incomment = 1;
                blankline = 0;
                if(!incomment)
                        *p++ = *q;      
        }
        if(p > cp && p[-1] != '\n')
                *p++ = '\n';
        *p++ = 0;
        n = p-cp;

        parsemenu(cp, BOOTARGS, n);

        /*
         * Keep a copy.
         * We could change this to pass the parsed strings
         * to the booted programme instead of the raw
         * string, then it only gets done once.
         */
        if(strncmp(cp, id, sizeof(id))){
                memmove(BOOTARGS, id, sizeof(id));
                if(n+1+sizeof(id) >= BOOTARGSLEN)
                        n -= sizeof(id);
                memmove(BOOTARGS+sizeof(id), cp, n+1);
        }
        else
                memmove(BOOTARGS, cp, n+1);

        n = getfields(cp, line, MAXCONF, 0, "\n");
        for(i = 0; i < n; i++){
                cp = strchr(line[i], '=');
                if(cp == 0)
                        continue;
                *cp++ = 0;
                if(cp - line[i] >= NAMELEN+1)
                        *(line[i]+NAMELEN-1) = 0;
                confname[nconf] = line[i];
                confval[nconf] = cp;
                nconf++;
        }
        return 0;
}