xref: /plan9/sys/src/9/pcboot/conf.c (revision 426f2a32150b219ea919a1d76cbd0c50d6d55686)
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