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