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