xref: /plan9-contrib/sys/src/9/bcm/main.c (revision 1936bb650459bace06c38a45b60888b47e5cd459)
1 #include "u.h"
2 #include "tos.h"
3 #include "../port/lib.h"
4 #include "mem.h"
5 #include "dat.h"
6 #include "fns.h"
7 
8 #include "init.h"
9 #include <pool.h>
10 
11 #include "reboot.h"
12 
13 /* Firmware compatibility */
14 #define	Minfirmrev	326770
15 #define	Minfirmdate	"22 Jul 2012"
16 
17 /*
18  * Where configuration info is left for the loaded programme.
19  */
20 #define BOOTARGS	((char*)CONFADDR)
21 #define	BOOTARGSLEN	(MACHADDR-CONFADDR)
22 #define	MAXCONF		64
23 #define MAXCONFLINE	160
24 
25 uintptr kseg0 = KZERO;
26 Mach*	machaddr[MAXMACH];
27 Conf	conf;
28 ulong	memsize = 128*1024*1024;
29 
30 /*
31  * Option arguments from the command line.
32  * oargv[0] is the boot file.
33  */
34 static int oargc;
35 static char* oargv[20];
36 static char oargb[128];
37 static int oargblen;
38 
39 static uintptr sp;		/* XXX - must go - user stack of init proc */
40 
41 /* store plan9.ini contents here at least until we stash them in #ec */
42 static char confname[MAXCONF][KNAMELEN];
43 static char confval[MAXCONF][MAXCONFLINE];
44 static int nconf;
45 
46 typedef struct Atag Atag;
47 struct Atag {
48 	u32int	size;	/* size of atag in words, including this header */
49 	u32int	tag;	/* atag type */
50 	union {
51 		u32int	data[1];	/* actually [size-2] */
52 		/* AtagMem */
53 		struct {
54 			u32int	size;
55 			u32int	base;
56 		} mem;
57 		/* AtagCmdLine */
58 		char	cmdline[1];	/* actually [4*(size-2)] */
59 	};
60 };
61 
62 enum {
63 	AtagNone	= 0x00000000,
64 	AtagCore	= 0x54410001,
65 	AtagMem		= 0x54410002,
66 	AtagCmdline	= 0x54410009,
67 };
68 
69 static int
70 findconf(char *name)
71 {
72 	int i;
73 
74 	for(i = 0; i < nconf; i++)
75 		if(cistrcmp(confname[i], name) == 0)
76 			return i;
77 	return -1;
78 }
79 
80 char*
81 getconf(char *name)
82 {
83 	int i;
84 
85 	i = findconf(name);
86 	if(i >= 0)
87 		return confval[i];
88 	return nil;
89 }
90 
91 void
92 addconf(char *name, char *val)
93 {
94 	int i;
95 
96 	i = findconf(name);
97 	if(i < 0){
98 		if(val == nil || nconf >= MAXCONF)
99 			return;
100 		i = nconf++;
101 		strecpy(confname[i], confname[i]+sizeof(confname[i]), name);
102 	}
103 	strecpy(confval[i], confval[i]+sizeof(confval[i]), val);
104 }
105 
106 static void
107 writeconf(void)
108 {
109 	char *p, *q;
110 	int n;
111 
112 	p = getconfenv();
113 
114 	if(waserror()) {
115 		free(p);
116 		nexterror();
117 	}
118 
119 	/* convert to name=value\n format */
120 	for(q=p; *q; q++) {
121 		q += strlen(q);
122 		*q = '=';
123 		q += strlen(q);
124 		*q = '\n';
125 	}
126 	n = q - p + 1;
127 	if(n >= BOOTARGSLEN)
128 		error("kernel configuration too large");
129 	memmove(BOOTARGS, p, n);
130 	memset(BOOTARGS + n, '\n', BOOTARGSLEN - n);
131 	poperror();
132 	free(p);
133 }
134 
135 static void
136 plan9iniinit(char *s, int cmdline)
137 {
138 	char *toks[MAXCONF];
139 	int i, c, n;
140 	char *v;
141 
142 	if((c = *s) < ' ' || c >= 0x80)
143 		return;
144 	if(cmdline)
145 		n = tokenize(s, toks, MAXCONF);
146 	else
147 		n = getfields(s, toks, MAXCONF, 1, "\n");
148 	for(i = 0; i < n; i++){
149 		if(toks[i][0] == '#')
150 			continue;
151 		v = strchr(toks[i], '=');
152 		if(v == nil)
153 			continue;
154 		*v++ = '\0';
155 		addconf(toks[i], v);
156 	}
157 }
158 
159 static void
160 ataginit(Atag *a)
161 {
162 	int n;
163 
164 	if(a->tag != AtagCore){
165 		plan9iniinit((char*)a, 0);
166 		return;
167 	}
168 	while(a->tag != AtagNone){
169 		switch(a->tag){
170 		case AtagMem:
171 			/* use only first bank */
172 			if(conf.mem[0].limit == 0 && a->mem.size != 0){
173 				memsize = a->mem.size;
174 				conf.mem[0].base = a->mem.base;
175 				conf.mem[0].limit = a->mem.base + memsize;
176 			}
177 			break;
178 		case AtagCmdline:
179 			n = (a->size * sizeof(u32int)) - offsetof(Atag, cmdline[0]);
180 			if(a->cmdline + n < BOOTARGS + BOOTARGSLEN)
181 				a->cmdline[n] = 0;
182 			else
183 				BOOTARGS[BOOTARGSLEN-1] = 0;
184 			plan9iniinit(a->cmdline, 1);
185 			break;
186 		}
187 		a = (Atag*)((u32int*)a + a->size);
188 	}
189 }
190 
191 void
192 machinit(void)
193 {
194 	m->machno = 0;
195 	machaddr[m->machno] = m;
196 
197 	m->ticks = 1;
198 	m->perf.period = 1;
199 
200 	conf.nmach = 1;
201 
202 	active.machs = 1;
203 	active.exiting = 0;
204 
205 	up = nil;
206 }
207 
208 static void
209 optionsinit(char* s)
210 {
211 	strecpy(oargb, oargb+sizeof(oargb), s);
212 
213 	oargblen = strlen(oargb);
214 	oargc = tokenize(oargb, oargv, nelem(oargv)-1);
215 	oargv[oargc] = nil;
216 }
217 
218 void
219 main(void)
220 {
221 	extern char edata[], end[];
222 	uint rev;
223 
224 	okay(1);
225 	m = (Mach*)MACHADDR;
226 	memset(edata, 0, end - edata);	/* clear bss */
227 	machinit();
228 	mmuinit1();
229 
230 	optionsinit("/boot/boot boot");
231 	quotefmtinstall();
232 
233 	ataginit((Atag*)BOOTARGS);
234 	confinit();		/* figures out amount of memory */
235 	xinit();
236 	uartconsinit();
237 	screeninit();
238 
239 	print("\nPlan 9 from Bell Labs\n");
240 	rev = getfirmware();
241 	print("firmware: rev %d\n", rev);
242 	if(rev < Minfirmrev){
243 		print("Sorry, firmware (start.elf) must be at least rev %d (%s)\n",
244 			Minfirmrev, Minfirmdate);
245 		for(;;)
246 			;
247 	}
248 	trapinit();
249 	clockinit();
250 	printinit();
251 	timersinit();
252 	if(conf.monitor)
253 		swcursorinit();
254 	cpuidprint();
255 	archreset();
256 
257 	procinit0();
258 	initseg();
259 	links();
260 	chandevreset();			/* most devices are discovered here */
261 	pageinit();
262 	swapinit();
263 	userinit();
264 	schedinit();
265 	assert(0);			/* shouldn't have returned */
266 }
267 
268 /*
269  *  starting place for first process
270  */
271 void
272 init0(void)
273 {
274 	int i;
275 	char buf[2*KNAMELEN];
276 
277 	up->nerrlab = 0;
278 	coherence();
279 	spllo();
280 
281 	/*
282 	 * These are o.k. because rootinit is null.
283 	 * Then early kproc's will have a root and dot.
284 	 */
285 	up->slash = namec("#/", Atodir, 0, 0);
286 	pathclose(up->slash->path);
287 	up->slash->path = newpath("/");
288 	up->dot = cclone(up->slash);
289 
290 	chandevinit();
291 
292 	if(!waserror()){
293 		snprint(buf, sizeof(buf), "%s %s", "ARM", conffile);
294 		ksetenv("terminal", buf, 0);
295 		ksetenv("cputype", "arm", 0);
296 		if(cpuserver)
297 			ksetenv("service", "cpu", 0);
298 		else
299 			ksetenv("service", "terminal", 0);
300 		snprint(buf, sizeof(buf), "-a %s", getethermac());
301 		ksetenv("etherargs", buf, 0);
302 
303 		/* convert plan9.ini variables to #e and #ec */
304 		for(i = 0; i < nconf; i++) {
305 			ksetenv(confname[i], confval[i], 0);
306 			ksetenv(confname[i], confval[i], 1);
307 		}
308 		poperror();
309 	}
310 	kproc("alarm", alarmkproc, 0);
311 	touser(sp);
312 	assert(0);			/* shouldn't have returned */
313 }
314 
315 static void
316 bootargs(uintptr base)
317 {
318 	int i;
319 	ulong ssize;
320 	char **av, *p;
321 
322 	/*
323 	 * Push the boot args onto the stack.
324 	 * The initial value of the user stack must be such
325 	 * that the total used is larger than the maximum size
326 	 * of the argument list checked in syscall.
327 	 */
328 	i = oargblen+1;
329 	p = UINT2PTR(STACKALIGN(base + BY2PG - sizeof(Tos) - i));
330 	memmove(p, oargb, i);
331 
332 	/*
333 	 * Now push the argv pointers.
334 	 * The code jumped to by touser in lproc.s expects arguments
335 	 *	main(char* argv0, ...)
336 	 * and calls
337 	 * 	startboot("/boot/boot", &argv0)
338 	 * not the usual (int argc, char* argv[])
339 	 */
340 	av = (char**)(p - (oargc+1)*sizeof(char*));
341 	ssize = base + BY2PG - PTR2UINT(av);
342 	for(i = 0; i < oargc; i++)
343 		*av++ = (oargv[i] - oargb) + (p - base) + (USTKTOP - BY2PG);
344 	*av = nil;
345 	sp = USTKTOP - ssize;
346 }
347 
348 /*
349  *  create the first process
350  */
351 void
352 userinit(void)
353 {
354 	Proc *p;
355 	Segment *s;
356 	KMap *k;
357 	Page *pg;
358 
359 	/* no processes yet */
360 	up = nil;
361 
362 	p = newproc();
363 	p->pgrp = newpgrp();
364 	p->egrp = smalloc(sizeof(Egrp));
365 	p->egrp->ref = 1;
366 	p->fgrp = dupfgrp(nil);
367 	p->rgrp = newrgrp();
368 	p->procmode = 0640;
369 
370 	kstrdup(&eve, "");
371 	kstrdup(&p->text, "*init*");
372 	kstrdup(&p->user, eve);
373 
374 	/*
375 	 * Kernel Stack
376 	 */
377 	p->sched.pc = PTR2UINT(init0);
378 	p->sched.sp = PTR2UINT(p->kstack+KSTACK-sizeof(up->s.args)-sizeof(uintptr));
379 	p->sched.sp = STACKALIGN(p->sched.sp);
380 
381 	/*
382 	 * User Stack
383 	 *
384 	 * Technically, newpage can't be called here because it
385 	 * should only be called when in a user context as it may
386 	 * try to sleep if there are no pages available, but that
387 	 * shouldn't be the case here.
388 	 */
389 	s = newseg(SG_STACK, USTKTOP-USTKSIZE, USTKSIZE/BY2PG);
390 	s->flushme++;
391 	p->seg[SSEG] = s;
392 	pg = newpage(1, 0, USTKTOP-BY2PG);
393 	segpage(s, pg);
394 	k = kmap(pg);
395 	bootargs(VA(k));
396 	kunmap(k);
397 
398 	/*
399 	 * Text
400 	 */
401 	s = newseg(SG_TEXT, UTZERO, 1);
402 	p->seg[TSEG] = s;
403 	pg = newpage(1, 0, UTZERO);
404 	memset(pg->cachectl, PG_TXTFLUSH, sizeof(pg->cachectl));
405 	segpage(s, pg);
406 	k = kmap(s->map[0]->pages[0]);
407 	memmove(UINT2PTR(VA(k)), initcode, sizeof initcode);
408 	kunmap(k);
409 
410 	ready(p);
411 }
412 
413 void
414 confinit(void)
415 {
416 	int i;
417 	ulong kpages;
418 	uintptr pa;
419 	char *p;
420 
421 	if(0 && (p = getconf("service")) != nil){
422 		if(strcmp(p, "cpu") == 0)
423 			cpuserver = 1;
424 		else if(strcmp(p,"terminal") == 0)
425 			cpuserver = 0;
426 	}
427 	if((p = getconf("*maxmem")) != nil){
428 		memsize = strtoul(p, 0, 0) - PHYSDRAM;
429 		if (memsize < 16*MB)		/* sanity */
430 			memsize = 16*MB;
431 	}
432 
433 	getramsize(&conf.mem[0]);
434 	if(conf.mem[0].limit == 0){
435 		conf.mem[0].base = PHYSDRAM;
436 		conf.mem[0].limit = PHYSDRAM + memsize;
437 	}else if(p != nil)
438 		conf.mem[0].limit = conf.mem[0].base + memsize;
439 
440 	conf.npage = 0;
441 	pa = PADDR(PGROUND(PTR2UINT(end)));
442 
443 	/*
444 	 *  we assume that the kernel is at the beginning of one of the
445 	 *  contiguous chunks of memory and fits therein.
446 	 */
447 	for(i=0; i<nelem(conf.mem); i++){
448 		/* take kernel out of allocatable space */
449 		if(pa > conf.mem[i].base && pa < conf.mem[i].limit)
450 			conf.mem[i].base = pa;
451 
452 		conf.mem[i].npage = (conf.mem[i].limit - conf.mem[i].base)/BY2PG;
453 		conf.npage += conf.mem[i].npage;
454 	}
455 
456 	conf.upages = (conf.npage*80)/100;
457 	conf.ialloc = ((conf.npage-conf.upages)/2)*BY2PG;
458 
459 	/* only one processor */
460 	conf.nmach = 1;
461 
462 	/* set up other configuration parameters */
463 	conf.nproc = 100 + ((conf.npage*BY2PG)/MB)*5;
464 	if(cpuserver)
465 		conf.nproc *= 3;
466 	if(conf.nproc > 2000)
467 		conf.nproc = 2000;
468 	conf.nswap = conf.npage*3;
469 	conf.nswppo = 4096;
470 	conf.nimage = 200;
471 
472 	conf.copymode = 0;		/* copy on write */
473 
474 	/*
475 	 * Guess how much is taken by the large permanent
476 	 * datastructures. Mntcache and Mntrpc are not accounted for
477 	 * (probably ~300KB).
478 	 */
479 	kpages = conf.npage - conf.upages;
480 	kpages *= BY2PG;
481 	kpages -= conf.upages*sizeof(Page)
482 		+ conf.nproc*sizeof(Proc)
483 		+ conf.nimage*sizeof(Image)
484 		+ conf.nswap
485 		+ conf.nswppo*sizeof(Page);
486 	mainmem->maxsize = kpages;
487 	if(!cpuserver)
488 		/*
489 		 * give terminals lots of image memory, too; the dynamic
490 		 * allocation will balance the load properly, hopefully.
491 		 * be careful with 32-bit overflow.
492 		 */
493 		imagmem->maxsize = kpages;
494 
495 }
496 
497 static void
498 shutdown(int ispanic)
499 {
500 	int ms, once;
501 
502 	lock(&active);
503 	if(ispanic)
504 		active.ispanic = ispanic;
505 	else if(m->machno == 0 && (active.machs & (1<<m->machno)) == 0)
506 		active.ispanic = 0;
507 	once = active.machs & (1<<m->machno);
508 	active.machs &= ~(1<<m->machno);
509 	active.exiting = 1;
510 	unlock(&active);
511 
512 	if(once)
513 		iprint("cpu%d: exiting\n", m->machno);
514 	spllo();
515 	for(ms = 5*1000; ms > 0; ms -= TK2MS(2)){
516 		delay(TK2MS(2));
517 		if(active.machs == 0 && consactive() == 0)
518 			break;
519 	}
520 	delay(1000);
521 }
522 
523 /*
524  *  exit kernel either on a panic or user request
525  */
526 void
527 exit(int code)
528 {
529 	shutdown(code);
530 	splfhi();
531 	archreboot();
532 }
533 
534 /*
535  * stub for ../omap/devether.c
536  */
537 int
538 isaconfig(char *class, int ctlrno, ISAConf *isa)
539 {
540 	USED(ctlrno);
541 	USED(isa);
542 	return strcmp(class, "ether") == 0;
543 }
544 
545 /*
546  * the new kernel is already loaded at address `code'
547  * of size `size' and entry point `entry'.
548  */
549 void
550 reboot(void *entry, void *code, ulong size)
551 {
552 	void (*f)(ulong, ulong, ulong);
553 
554 	print("starting reboot...");
555 	writeconf();
556 	shutdown(0);
557 
558 	/*
559 	 * should be the only processor running now
560 	 */
561 
562 	print("reboot entry %#lux code %#lux size %ld\n",
563 		PADDR(entry), PADDR(code), size);
564 	delay(100);
565 
566 	/* turn off buffered serial console */
567 	serialoq = nil;
568 	kprintoq = nil;
569 	screenputs = nil;
570 
571 	/* shutdown devices */
572 	chandevshutdown();
573 
574 	/* stop the clock (and watchdog if any) */
575 	clockshutdown();
576 
577 	splfhi();
578 	intrsoff();
579 
580 	/* setup reboot trampoline function */
581 	f = (void*)REBOOTADDR;
582 	memmove(f, rebootcode, sizeof(rebootcode));
583 	cacheuwbinv();
584 
585 	/* off we go - never to return */
586 	(*f)(PADDR(entry), PADDR(code), size);
587 
588 	iprint("loaded kernel returned!\n");
589 	delay(1000);
590 	archreboot();
591 }
592 
593 int
594 cmpswap(long *addr, long old, long new)
595 {
596 	return cas32(addr, old, new);
597 }
598