1 /*
2 * parse plan.ini or /cfg/pxe/%E file into low memory
3 */
4 #include "u.h"
5 #include "../port/lib.h"
6 #include "mem.h"
7 #include "dat.h"
8 #include "fns.h"
9 #include "io.h"
10 #include "ureg.h"
11 #include "pool.h"
12 #include "../port/netif.h"
13 #include "../ip/ip.h"
14 #include "pxe.h"
15
16 typedef struct {
17 char* name;
18 int start;
19 int end;
20 } Mblock;
21
22 typedef struct {
23 char* tag;
24 Mblock* mb;
25 } Mitem;
26
27 Chan *conschan;
28
29 static Mblock mblock[MAXCONF];
30 static int nmblock;
31 static Mitem mitem[MAXCONF];
32 static int nmitem;
33 static char* mdefault;
34 static char mdefaultbuf[10];
35 static int mtimeout;
36
37 static char*
comma(char * line,char ** residue)38 comma(char* line, char** residue)
39 {
40 char *q, *r;
41
42 if((q = strchr(line, ',')) != nil){
43 *q++ = 0;
44 if(*q == ' ')
45 q++;
46 }
47 *residue = q;
48
49 if((r = strchr(line, ' ')) != nil)
50 *r = 0;
51
52 if(*line == ' ')
53 line++;
54 return line;
55 }
56
57 static Mblock*
findblock(char * name,char ** residue)58 findblock(char* name, char** residue)
59 {
60 int i;
61 char *p;
62
63 p = comma(name, residue);
64 for(i = 0; i < nmblock; i++){
65 if(strcmp(p, mblock[i].name) == 0)
66 return &mblock[i];
67 }
68 return nil;
69 }
70
71 static Mitem*
finditem(char * name,char ** residue)72 finditem(char* name, char** residue)
73 {
74 int i;
75 char *p;
76
77 p = comma(name, residue);
78 for(i = 0; i < nmitem; i++){
79 if(strcmp(p, mitem[i].mb->name) == 0)
80 return &mitem[i];
81 }
82 return nil;
83 }
84
85 /* timeout is in seconds */
86 int
getstr(char * prompt,char * buf,int size,char * def,int timeout)87 getstr(char *prompt, char *buf, int size, char *def, int timeout)
88 {
89 int len, isdefault;
90 static char pbuf[PRINTSIZE];
91
92 if(conschan == nil)
93 panic("getstr: #c/cons not open");
94 buf[0] = 0;
95 isdefault = (def && *def);
96 if(isdefault == 0){
97 timeout = 0;
98 snprint(pbuf, sizeof pbuf, "%s: ", prompt);
99 }
100 else if(timeout)
101 snprint(pbuf, sizeof pbuf, "%s[default==%s (%ds timeout)]: ",
102 prompt, def, timeout);
103 else
104 snprint(pbuf, sizeof pbuf, "%s[default==%s]: ", prompt, def);
105 for (;;) {
106 print("%s", pbuf);
107 if (timeout > 0) {
108 for(timeout *= 1000; timeout > 0; timeout -= 100) {
109 if (qlen(kbdq) > 0) /* if input queued */
110 break;
111 tsleep(&up->sleep, return0, 0, 100);
112 }
113 if (timeout <= 0) { /* use default */
114 print("\n");
115 len = 0;
116 break;
117 }
118 }
119 buf[0] = '\0';
120 len = devtab[conschan->type]->read(conschan, buf, size - 1,
121 conschan->offset);
122 if(len >= 0)
123 buf[len] = '\0';
124 switch(len){
125 case 0: /* eof */
126 case 1: /* newline */
127 len = 0;
128 buf[len] = '\0';
129 if(!isdefault)
130 continue;
131 break;
132 }
133 if(len < size - 1)
134 break;
135 print("line too long\n");
136 }
137 if(len == 0 && isdefault)
138 strncpy(buf, def, size);
139 return 0;
140 }
141
142 void
askbootfile(char * buf,int len,char ** bootfp,int secs,char * def)143 askbootfile(char *buf, int len, char **bootfp, int secs, char *def)
144 {
145 getstr("\nBoot from", buf, len, def, secs);
146 trimnl(buf);
147 if (bootfp)
148 kstrdup(bootfp, buf);
149 }
150
151 int
isconf(char * name)152 isconf(char *name)
153 {
154 int i;
155
156 for(i = 0; i < nconf; i++)
157 if(cistrcmp(confname[i], name) == 0)
158 return 1;
159 return 0;
160 }
161
162 /* result is not malloced, unlike user-mode getenv() */
163 char*
getconf(char * name)164 getconf(char *name)
165 {
166 int i, n, nmatch;
167 char buf[120];
168
169 nmatch = 0;
170 for(i = 0; i < nconf; i++)
171 if(cistrcmp(confname[i], name) == 0)
172 nmatch++;
173
174 switch(nmatch) {
175 default:
176 print("\n");
177 nmatch = 0;
178 for(i = 0; i < nconf; i++)
179 if(cistrcmp(confname[i], name) == 0)
180 print("%d. %s\n", ++nmatch, confval[i]);
181 print("%d. none of the above\n", ++nmatch);
182 do {
183 getstr(name, buf, sizeof(buf), nil, 0);
184 n = atoi(buf);
185 } while(n < 1 || n > nmatch);
186
187 for(i = 0; i < nconf; i++)
188 if(cistrcmp(confname[i], name) == 0)
189 if(--n == 0)
190 return confval[i];
191 break;
192
193 case 1:
194 for(i = 0; i < nconf; i++)
195 if(cistrcmp(confname[i], name) == 0)
196 return confval[i];
197 break;
198
199 case 0:
200 break;
201 }
202 return nil;
203 }
204
205 static void
parsemenu(char * str,char * scratch,int len)206 parsemenu(char* str, char* scratch, int len)
207 {
208 Mitem *mi;
209 Mblock *mb, *menu;
210 char buf[20], *p, *q, *line[MAXCONF];
211 int i, inblock, n, show;
212
213 inblock = 0;
214 menu = nil;
215 memmove(scratch, str, len);
216 n = getfields(scratch, line, MAXCONF, 0, "\n");
217 if(n >= MAXCONF)
218 print("warning: possibly too many lines in plan9.ini\n");
219 for(i = 0; i < n; i++){
220 p = line[i];
221 if(inblock && *p == '['){
222 mblock[nmblock].end = i;
223 if(strcmp(mblock[nmblock].name, "menu") == 0)
224 menu = &mblock[nmblock];
225 nmblock++;
226 inblock = 0;
227 }
228 if(*p == '['){
229 if(nmblock == 0 && i != 0){
230 mblock[nmblock].name = "common";
231 mblock[nmblock].start = 0;
232 mblock[nmblock].end = i;
233 nmblock++;
234 }
235 q = strchr(p+1, ']');
236 if(q == nil || *(q+1) != 0){
237 print("malformed menu block header - %s\n", p);
238 return;
239 }
240 *q = 0;
241 mblock[nmblock].name = p+1;
242 mblock[nmblock].start = i+1;
243 inblock = 1;
244 }
245 }
246
247 if(inblock){
248 mblock[nmblock].end = i;
249 nmblock++;
250 }
251 if(menu == nil)
252 return;
253 if(nmblock < 2){
254 print("incomplete menu specification\n");
255 return;
256 }
257
258 for(i = menu->start; i < menu->end; i++){
259 p = line[i];
260 if(cistrncmp(p, "menuitem=", 9) == 0){
261 p += 9;
262 if((mb = findblock(p, &q)) == nil){
263 print("no block for menuitem %s\n", p);
264 return;
265 }
266 if(q != nil)
267 mitem[nmitem].tag = q;
268 else
269 mitem[nmitem].tag = mb->name;
270 mitem[nmitem].mb = mb;
271 nmitem++;
272 }
273 else if(cistrncmp(p, "menudefault=", 12) == 0){
274 p += 12;
275 if((mi = finditem(p, &q)) == nil){
276 print("no item for menudefault %s\n", p);
277 return;
278 }
279 if(q != nil)
280 mtimeout = strtol(q, 0, 0);
281 snprint(mdefaultbuf, sizeof mdefaultbuf, "%ld",
282 mi-mitem+1);
283 mdefault = mdefaultbuf;
284 }
285 else if(cistrncmp(p, "menuconsole=", 12) == 0){
286 p += 12;
287 p = comma(p, &q);
288 i8250config(p);
289 }
290 else{
291 print("invalid line in [menu] block - %s\n", p);
292 return;
293 }
294 }
295
296 again:
297 print("\nPlan 9 Startup Menu:\n====================\n");
298 for(i = 0; i < nmitem; i++)
299 print(" %d. %s\n", i+1, mitem[i].tag);
300 for(;;){
301 getstr("Selection", buf, sizeof(buf), mdefault, mtimeout);
302 mtimeout = 0;
303 i = strtol(buf, &p, 0)-1;
304 if(i < 0 || i >= nmitem)
305 goto again;
306 switch(*p){
307 case 'p':
308 case 'P':
309 show = 1;
310 print("\n");
311 break;
312 case 0:
313 case '\n':
314 show = 0;
315 break;
316 default:
317 continue;
318
319 }
320 mi = &mitem[i];
321
322 p = str;
323 p += snprint(p, len, "menuitem=%s\n", mi->mb->name);
324 for(i = 0; i < nmblock; i++){
325 mb = &mblock[i];
326 if(mi->mb != mb && cistrcmp(mb->name, "common") != 0)
327 continue;
328 for(n = mb->start; n < mb->end; n++)
329 p += snprint(p, &str[len] - p, "%s\n", line[n]);
330 }
331
332 if(show){
333 for(q = str; q < p; q += i){
334 if((i = print(q)) <= 0)
335 break;
336 }
337 goto again;
338 }
339 break;
340 }
341 print("\n");
342 }
343
344 /* dig out tables created by l16r.s in real mode */
345 void
readlsconf(void)346 readlsconf(void)
347 {
348 int i, n;
349 uchar *p;
350 MMap *map;
351 u64int addr, len;
352
353 /*
354 * we could be running above 1MB, so put bios tables in low memory,
355 * not after end.
356 */
357 p = (uchar*)KADDR(BIOSTABLES);
358 for(n = 0; n < nelem(mmap); n++){
359 if(*p == 0)
360 break;
361 if(memcmp(p, "APM\0", 4) == 0){
362 p += 20;
363 continue;
364 }
365 else if(memcmp(p, "MAP\0", 4) == 0){
366 map = (MMap*)p;
367
368 switch(map->type){
369 default:
370 if(v_flag)
371 print("type %ud", map->type);
372 break;
373 case 1:
374 if(v_flag)
375 print("Memory");
376 break;
377 case 2:
378 if(v_flag)
379 print("reserved");
380 break;
381 case 3:
382 if(v_flag)
383 print("ACPI Reclaim Memory");
384 break;
385 case 4:
386 if(v_flag)
387 print("ACPI NVS Memory");
388 break;
389 }
390 addr = (((u64int)map->base[1])<<32)|map->base[0];
391 len = (((u64int)map->length[1])<<32)|map->length[0];
392 if(v_flag)
393 print("\t%#16.16lluX %#16.16lluX (%llud)\n",
394 addr, addr+len, len);
395
396 if(nmmap < nelem(mmap)){
397 memmove(&mmap[nmmap], map, sizeof(MMap));
398 mmap[nmmap].size = 20;
399 nmmap++;
400 }
401 p += 24;
402 continue;
403 }
404 else{
405 /* ideally we shouldn't print here */
406 print("\nweird low-memory map at %#p:\n", p);
407 for(i = 0; i < 24; i++)
408 print(" %2.2uX", *(p+i));
409 print("\n");
410 delay(5000);
411 }
412 break;
413 }
414 }
415
416 void
addconf(char * fmt,...)417 addconf(char *fmt, ...)
418 {
419 va_list arg;
420
421 va_start(arg, fmt);
422 vseprint(BOOTARGS+strlen(BOOTARGS), BOOTARGS+BOOTARGSLEN, fmt, arg);
423 va_end(arg);
424 }
425
426 void
dumpbootargs(void)427 dumpbootargs(void)
428 {
429 char *p, *nl;
430
431 /* in the boot, we can only print PRINTSIZE (256) bytes at a time. */
432 print("boot args: ");
433 for (p = (char *)BOOTARGS; *p != '\0'; p = nl) {
434 nl = strchr(p, '\n');
435 if (nl != nil) {
436 ++nl;
437 print("%.*s", (int)(nl - p), p);
438 }
439 }
440 }
441
442 void
changeconf(char * fmt,...)443 changeconf(char *fmt, ...)
444 {
445 va_list arg;
446 char *p, *q, pref[20], buf[128];
447
448 va_start(arg, fmt);
449 vseprint(buf, buf+sizeof buf, fmt, arg);
450 va_end(arg);
451
452 pref[0] = '\n';
453 strncpy(pref+1, buf, 19);
454 pref[19] = '\0';
455 if(p = strchr(pref, '='))
456 *(p+1) = '\0';
457 else
458 print("warning: did not change %s in plan9.ini\n", buf);
459
460 /* find old line by looking for \nwhat= */
461 if(strncmp(BOOTARGS, pref+1, strlen(pref+1)) == 0)
462 p = BOOTARGS;
463 else if(p = strstr(BOOTARGS, pref))
464 p++;
465 else
466 p = nil;
467
468 /* move rest of args up, deleting what= line. */
469 if(p != nil && (q = strchr(p, '\n')) != nil)
470 memmove(p, q+1, strlen(q+1)+1);
471
472 /* add replacement to end */
473 addconf("%s", buf);
474 }
475
476 /*
477 * read configuration file
478 */
479 static char id[8] = "ZORT 0\r\n";
480
481 int
dotini(char * inibuf)482 dotini(char *inibuf)
483 {
484 int blankline, i, incomment, inspace, n;
485 char *cp, *p, *q, *line[MAXCONF];
486
487 cp = inibuf;
488
489 /*
490 * Strip out '\r', change '\t' -> ' '.
491 * Change runs of spaces into single spaces.
492 * Strip out trailing spaces, blank lines.
493 *
494 * We do this before we make the copy so that if we
495 * need to change the copy, it is already fairly clean.
496 * The main need is in the case when plan9.ini has been
497 * padded with lots of trailing spaces, as is the case
498 * for those created during a distribution install.
499 */
500 p = cp;
501 blankline = 1;
502 incomment = inspace = 0;
503 for(q = cp; *q; q++){
504 if(*q == '\r')
505 continue;
506 if(*q == '\t')
507 *q = ' ';
508 if(*q == ' '){
509 inspace = 1;
510 continue;
511 }
512 if(*q == '\n'){
513 if(!blankline){
514 if(!incomment)
515 *p++ = '\n';
516 blankline = 1;
517 }
518 incomment = inspace = 0;
519 continue;
520 }
521 if(inspace){
522 if(!blankline && !incomment)
523 *p++ = ' ';
524 inspace = 0;
525 }
526 if(blankline && *q == '#')
527 incomment = 1;
528 blankline = 0;
529 if(!incomment)
530 *p++ = *q;
531 }
532 if(p > cp && p[-1] != '\n')
533 *p++ = '\n';
534 *p++ = 0;
535 n = p-cp;
536
537 parsemenu(cp, BOOTARGS, n);
538
539 /*
540 * Keep a copy.
541 * We could change this to pass the parsed strings
542 * to the booted programme instead of the raw
543 * string, then it only gets done once.
544 */
545 if(strncmp(cp, id, sizeof(id))){
546 memmove(BOOTARGS, id, sizeof(id));
547 if(n+1+sizeof(id) >= BOOTARGSLEN)
548 n -= sizeof(id);
549 memmove(BOOTARGS+sizeof(id), cp, n+1);
550 }
551 else
552 memmove(BOOTARGS, cp, n+1);
553
554 n = getfields(cp, line, MAXCONF, 0, "\n");
555 for(i = 0; i < n; i++){
556 cp = strchr(line[i], '=');
557 if(cp == 0)
558 continue;
559 *cp++ = 0;
560 if(cp - line[i] >= NAMELEN+1)
561 *(line[i]+NAMELEN-1) = 0;
562 confname[nconf] = line[i];
563 confval[nconf] = cp;
564 nconf++;
565 }
566 return 0;
567 }
568