/*
* 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;
}
|