xref: /plan9-contrib/sys/src/9/bcm/main.c (revision 5c47fe09a0cc86dfb02c0ea4a2b6aec7eda2361f)
1 #include "u.h"
2 #include "tos.h"
3 #include "../port/lib.h"
4 #include "mem.h"
5 #include "dat.h"
6 #include "io.h"
7 #include "fns.h"
8 
9 #include "init.h"
10 #include <pool.h>
11 
12 #include "reboot.h"
13 
14 enum {
15 	/* space for syscall args, return PC, top-of-stack struct */
16 	Ustkheadroom	= sizeof(Sargs) + sizeof(uintptr) + sizeof(Tos),
17 };
18 
19 /* Firmware compatibility */
20 #define	Minfirmrev	326770
21 #define	Minfirmdate	"19 Aug 2013"
22 
23 /*
24  * Where configuration info is left for the loaded programme.
25  */
26 #define BOOTARGS	((char*)CONFADDR)
27 #define	BOOTARGSLEN	(MACHADDR-CONFADDR)
28 #define	MAXCONF		64
29 #define MAXCONFLINE	160
30 
31 uintptr kseg0 = KZERO;
32 Mach*	machaddr[MAXMACH];
33 Conf	conf;
34 ulong	memsize = 128*1024*1024;
35 
36 /*
37  * Option arguments from the command line.
38  * oargv[0] is the boot file.
39  */
40 static int oargc;
41 static char* oargv[20];
42 static char oargb[128];
43 static int oargblen;
44 
45 static uintptr sp;		/* XXX - must go - user stack of init proc */
46 
47 /* store plan9.ini contents here at least until we stash them in #ec */
48 static char confname[MAXCONF][KNAMELEN];
49 static char confval[MAXCONF][MAXCONFLINE];
50 static int nconf;
51 
52 typedef struct Atag Atag;
53 struct Atag {
54 	u32int	size;	/* size of atag in words, including this header */
55 	u32int	tag;	/* atag type */
56 	union {
57 		u32int	data[1];	/* actually [size-2] */
58 		/* AtagMem */
59 		struct {
60 			u32int	size;
61 			u32int	base;
62 		} mem;
63 		/* AtagCmdLine */
64 		char	cmdline[1];	/* actually [4*(size-2)] */
65 	};
66 };
67 
68 enum {
69 	AtagNone	= 0x00000000,
70 	AtagCore	= 0x54410001,
71 	AtagMem		= 0x54410002,
72 	AtagCmdline	= 0x54410009,
73 };
74 
75 static int
findconf(char * name)76 findconf(char *name)
77 {
78 	int i;
79 
80 	for(i = 0; i < nconf; i++)
81 		if(cistrcmp(confname[i], name) == 0)
82 			return i;
83 	return -1;
84 }
85 
86 char*
getconf(char * name)87 getconf(char *name)
88 {
89 	int i;
90 
91 	i = findconf(name);
92 	if(i >= 0)
93 		return confval[i];
94 	return nil;
95 }
96 
97 void
addconf(char * name,char * val)98 addconf(char *name, char *val)
99 {
100 	int i;
101 
102 	i = findconf(name);
103 	if(i < 0){
104 		if(val == nil || nconf >= MAXCONF)
105 			return;
106 		i = nconf++;
107 		strecpy(confname[i], confname[i]+sizeof(confname[i]), name);
108 	}
109 	strecpy(confval[i], confval[i]+sizeof(confval[i]), val);
110 }
111 
112 static void
writeconf(void)113 writeconf(void)
114 {
115 	char *p, *q;
116 	int n;
117 
118 	p = getconfenv();
119 
120 	if(waserror()) {
121 		free(p);
122 		nexterror();
123 	}
124 
125 	/* convert to name=value\n format */
126 	for(q=p; *q; q++) {
127 		q += strlen(q);
128 		*q = '=';
129 		q += strlen(q);
130 		*q = '\n';
131 	}
132 	n = q - p + 1;
133 	if(n >= BOOTARGSLEN)
134 		error("kernel configuration too large");
135 	memmove(BOOTARGS, p, n);
136 	memset(BOOTARGS + n, '\n', BOOTARGSLEN - n);
137 	poperror();
138 	free(p);
139 }
140 
141 static void
plan9iniinit(char * s,int cmdline)142 plan9iniinit(char *s, int cmdline)
143 {
144 	char *toks[MAXCONF];
145 	int i, c, n;
146 	char *v;
147 
148 	if((c = *s) < ' ' || c >= 0x80)
149 		return;
150 	if(cmdline)
151 		n = tokenize(s, toks, MAXCONF);
152 	else
153 		n = getfields(s, toks, MAXCONF, 1, "\n");
154 	for(i = 0; i < n; i++){
155 		if(toks[i][0] == '#')
156 			continue;
157 		v = strchr(toks[i], '=');
158 		if(v == nil)
159 			continue;
160 		*v++ = '\0';
161 		addconf(toks[i], v);
162 	}
163 }
164 
165 static void
ataginit(Atag * a)166 ataginit(Atag *a)
167 {
168 	int n;
169 
170 	if(a->tag != AtagCore){
171 		plan9iniinit((char*)a, 0);
172 		return;
173 	}
174 	while(a->tag != AtagNone){
175 		switch(a->tag){
176 		case AtagMem:
177 			/* use only first bank */
178 			if(conf.mem[0].limit == 0 && a->mem.size != 0){
179 				memsize = a->mem.size;
180 				conf.mem[0].base = a->mem.base;
181 				conf.mem[0].limit = a->mem.base + memsize;
182 			}
183 			break;
184 		case AtagCmdline:
185 			n = (a->size * sizeof(u32int)) - offsetof(Atag, cmdline[0]);
186 			if(a->cmdline + n < BOOTARGS + BOOTARGSLEN)
187 				a->cmdline[n] = 0;
188 			else
189 				BOOTARGS[BOOTARGSLEN-1] = 0;
190 			plan9iniinit(a->cmdline, 1);
191 			break;
192 		}
193 		a = (Atag*)((u32int*)a + a->size);
194 	}
195 }
196 
197 /* enable scheduling of this cpu */
198 void
machon(uint cpu)199 machon(uint cpu)
200 {
201 	ulong cpubit;
202 
203 	cpubit = 1 << cpu;
204 	lock(&active);
205 	if ((active.machs & cpubit) == 0) {	/* currently off? */
206 		conf.nmach++;
207 		active.machs |= cpubit;
208 	}
209 	unlock(&active);
210 }
211 
212 /* disable scheduling of this cpu */
213 void
machoff(uint cpu)214 machoff(uint cpu)
215 {
216 	ulong cpubit;
217 
218 	cpubit = 1 << cpu;
219 	lock(&active);
220 	if (active.machs & cpubit) {		/* currently on? */
221 		conf.nmach--;
222 		active.machs &= ~cpubit;
223 	}
224 	unlock(&active);
225 }
226 
227 void
machinit(void)228 machinit(void)
229 {
230 	Mach *m0;
231 
232 	m->ticks = 1;
233 	m->perf.period = 1;
234 	m0 = MACHP(0);
235 	if (m->machno != 0) {
236 		/* synchronise with cpu 0 */
237 		m->ticks = m0->ticks;
238 	}
239 }
240 
241 void
mach0init(void)242 mach0init(void)
243 {
244 	conf.nmach = 0;
245 
246 	m->machno = 0;
247 	machaddr[m->machno] = m;
248 
249 	machinit();
250 	active.exiting = 0;
251 
252 	up = nil;
253 }
254 
255 void
launchinit(int ncpus)256 launchinit(int ncpus)
257 {
258 	int mach;
259 	Mach *mm;
260 	PTE *l1;
261 
262 	if(ncpus > MAXMACH)
263 		ncpus = MAXMACH;
264 	for(mach = 1; mach < ncpus; mach++){
265 		machaddr[mach] = mm = mallocalign(MACHSIZE, MACHSIZE, 0, 0);
266 		l1 = mallocalign(L1SIZE, L1SIZE, 0, 0);
267 		if(mm == nil || l1 == nil)
268 			panic("launchinit");
269 		memset(mm, 0, MACHSIZE);
270 		mm->machno = mach;
271 
272 		memmove(l1, m->mmul1, L1SIZE);  /* clone cpu0's l1 table */
273 		cachedwbse(l1, L1SIZE);
274 		mm->mmul1 = l1;
275 		cachedwbse(mm, MACHSIZE);
276 
277 	}
278 	cachedwbse(machaddr, sizeof machaddr);
279 	if((mach = startcpus(ncpus)) < ncpus)
280 			print("only %d cpu%s started\n", mach, mach == 1? "" : "s");
281 }
282 
283 static void
optionsinit(char * s)284 optionsinit(char* s)
285 {
286 	strecpy(oargb, oargb+sizeof(oargb), s);
287 
288 	oargblen = strlen(oargb);
289 	oargc = tokenize(oargb, oargv, nelem(oargv)-1);
290 	oargv[oargc] = nil;
291 }
292 
293 void
main(void)294 main(void)
295 {
296 	extern char edata[], end[];
297 	uint fw, board;
298 
299 	m = (Mach*)MACHADDR;
300 	memset(edata, 0, end - edata);	/* clear bss */
301 	mach0init();
302 	m->mmul1 = (PTE*)L1;
303 	machon(0);
304 
305 	optionsinit("/boot/boot boot");
306 	quotefmtinstall();
307 
308 	ataginit((Atag*)BOOTARGS);
309 	confinit();		/* figures out amount of memory */
310 	xinit();
311 	uartconsinit();
312 	screeninit();
313 
314 	print("\nPlan 9 from Bell Labs\n");
315 	board = getboardrev();
316 	fw = getfirmware();
317 	print("board rev: %#ux firmware rev: %d\n", board, fw);
318 	if(fw < Minfirmrev){
319 		print("Sorry, firmware (start*.elf) must be at least rev %d"
320 		      " or newer than %s\n", Minfirmrev, Minfirmdate);
321 		for(;;)
322 			;
323 	}
324 	/* set clock rate to arm_freq from config.txt (default pi1:700Mhz pi2:900MHz) */
325 	setclkrate(ClkArm, 0);
326 	trapinit();
327 	clockinit();
328 	printinit();
329 	timersinit();
330 	if(conf.monitor)
331 		swcursorinit();
332 	cpuidprint();
333 	print("clocks: CPU %lud core %lud UART %lud EMMC %lud\n",
334 		getclkrate(ClkArm), getclkrate(ClkCore), getclkrate(ClkUart), getclkrate(ClkEmmc));
335 	archreset();
336 	vgpinit();
337 
338 	procinit0();
339 	initseg();
340 	links();
341 	chandevreset();			/* most devices are discovered here */
342 	pageinit();
343 	swapinit();
344 	userinit();
345 	launchinit(getncpus());
346 	mmuinit1();
347 
348 	schedinit();
349 	assert(0);			/* shouldn't have returned */
350 }
351 
352 /*
353  *  starting place for first process
354  */
355 void
init0(void)356 init0(void)
357 {
358 	int i;
359 	Chan *c;
360 	char buf[2*KNAMELEN];
361 
362 	up->nerrlab = 0;
363 	coherence();
364 	spllo();
365 
366 	/*
367 	 * These are o.k. because rootinit is null.
368 	 * Then early kproc's will have a root and dot.
369 	 */
370 	up->slash = namec("#/", Atodir, 0, 0);
371 	pathclose(up->slash->path);
372 	up->slash->path = newpath("/");
373 	up->dot = cclone(up->slash);
374 
375 	chandevinit();
376 
377 	if(!waserror()){
378 		snprint(buf, sizeof(buf), "%s %s", "ARM", conffile);
379 		ksetenv("terminal", buf, 0);
380 		ksetenv("cputype", "arm", 0);
381 		if(cpuserver)
382 			ksetenv("service", "cpu", 0);
383 		else
384 			ksetenv("service", "terminal", 0);
385 		snprint(buf, sizeof(buf), "-a %s", getethermac());
386 		ksetenv("etherargs", buf, 0);
387 
388 		/* convert plan9.ini variables to #e and #ec */
389 		for(i = 0; i < nconf; i++) {
390 			ksetenv(confname[i], confval[i], 0);
391 			ksetenv(confname[i], confval[i], 1);
392 		}
393 		if(getconf("pitft")){
394 			c = namec("#P/pitft", Aopen, OWRITE, 0);
395 			if(!waserror()){
396 				devtab[c->type]->write(c, "init", 4, 0);
397 				poperror();
398 			}
399 			cclose(c);
400 		}
401 		poperror();
402 	}
403 	kproc("alarm", alarmkproc, 0);
404 	touser(sp);
405 	assert(0);			/* shouldn't have returned */
406 }
407 
408 static void
bootargs(uintptr base)409 bootargs(uintptr base)
410 {
411 	int i;
412 	ulong ssize;
413 	char **av, *p;
414 
415 	/*
416 	 * Push the boot args onto the stack.
417 	 * The initial value of the user stack must be such
418 	 * that the total used is larger than the maximum size
419 	 * of the argument list checked in syscall.
420 	 */
421 	i = oargblen+1;
422 	p = UINT2PTR(STACKALIGN(base + BY2PG - Ustkheadroom - i));
423 	memmove(p, oargb, i);
424 
425 	/*
426 	 * Now push the argv pointers.
427 	 * The code jumped to by touser in lproc.s expects arguments
428 	 *	main(char* argv0, ...)
429 	 * and calls
430 	 * 	startboot("/boot/boot", &argv0)
431 	 * not the usual (int argc, char* argv[])
432 	 */
433 	av = (char**)(p - (oargc+1)*sizeof(char*));
434 	ssize = base + BY2PG - PTR2UINT(av);
435 	for(i = 0; i < oargc; i++)
436 		*av++ = (oargv[i] - oargb) + (p - base) + (USTKTOP - BY2PG);
437 	*av = nil;
438 	sp = USTKTOP - ssize;
439 }
440 
441 /*
442  *  create the first process
443  */
444 void
userinit(void)445 userinit(void)
446 {
447 	Proc *p;
448 	Segment *s;
449 	KMap *k;
450 	Page *pg;
451 
452 	/* no processes yet */
453 	up = nil;
454 
455 	p = newproc();
456 	p->pgrp = newpgrp();
457 	p->egrp = smalloc(sizeof(Egrp));
458 	p->egrp->ref = 1;
459 	p->fgrp = dupfgrp(nil);
460 	p->rgrp = newrgrp();
461 	p->procmode = 0640;
462 
463 	kstrdup(&eve, "");
464 	kstrdup(&p->text, "*init*");
465 	kstrdup(&p->user, eve);
466 
467 	/*
468 	 * Kernel Stack
469 	 */
470 	p->sched.pc = PTR2UINT(init0);
471 	p->sched.sp = PTR2UINT(p->kstack+KSTACK-sizeof(up->s.args)-sizeof(uintptr));
472 	p->sched.sp = STACKALIGN(p->sched.sp);
473 
474 	/*
475 	 * User Stack
476 	 *
477 	 * Technically, newpage can't be called here because it
478 	 * should only be called when in a user context as it may
479 	 * try to sleep if there are no pages available, but that
480 	 * shouldn't be the case here.
481 	 */
482 	s = newseg(SG_STACK, USTKTOP-USTKSIZE, USTKSIZE/BY2PG);
483 	s->flushme++;
484 	p->seg[SSEG] = s;
485 	pg = newpage(1, 0, USTKTOP-BY2PG);
486 	segpage(s, pg);
487 	k = kmap(pg);
488 	bootargs(VA(k));
489 	kunmap(k);
490 
491 	/*
492 	 * Text
493 	 */
494 	s = newseg(SG_TEXT, UTZERO, 1);
495 	p->seg[TSEG] = s;
496 	pg = newpage(1, 0, UTZERO);
497 	memset(pg->cachectl, PG_TXTFLUSH, sizeof(pg->cachectl));
498 	segpage(s, pg);
499 	k = kmap(s->map[0]->pages[0]);
500 	memmove(UINT2PTR(VA(k)), initcode, sizeof initcode);
501 	kunmap(k);
502 
503 	ready(p);
504 }
505 
506 void
confinit(void)507 confinit(void)
508 {
509 	int i, userpcnt;
510 	ulong kpages;
511 	uintptr pa;
512 	char *p;
513 
514 	if(0 && (p = getconf("service")) != nil){
515 		if(strcmp(p, "cpu") == 0)
516 			cpuserver = 1;
517 		else if(strcmp(p,"terminal") == 0)
518 			cpuserver = 0;
519 	}
520 	if((p = getconf("*maxmem")) != nil){
521 		memsize = strtoul(p, 0, 0) - PHYSDRAM;
522 		if (memsize < 16*MB)		/* sanity */
523 			memsize = 16*MB;
524 	}
525 
526 	getramsize(&conf.mem[0]);
527 	if(conf.mem[0].limit == 0){
528 		conf.mem[0].base = PHYSDRAM;
529 		conf.mem[0].limit = PHYSDRAM + memsize;
530 	}
531 	/*
532 	 * pi4 extra memory (beyond video ram) indicated by board id
533 	 */
534 	switch(getboardrev()&0xF00000){
535 	case 0xA00000:
536 		break;
537 	case 0xB00000:
538 		conf.mem[1].base = 1*GiB;
539 		conf.mem[1].limit = 2*GiB;
540 		break;
541 	case 0xC00000:
542 		conf.mem[1].base = 1*GiB;
543 		conf.mem[1].limit = 0xFFF00000;
544 		break;
545 	case 0xD00000:
546 		conf.mem[1].base = 1*GiB;
547 		conf.mem[1].limit = 0xFFF00000;
548 		break;
549 	}
550 	if(conf.mem[1].limit > soc.dramsize)
551 		conf.mem[1].limit = soc.dramsize;
552 	if(p != nil){
553 		if(memsize < conf.mem[0].limit){
554 			conf.mem[0].limit = memsize;
555 			conf.mem[1].limit = 0;
556 		}else if(memsize >= conf.mem[1].base && memsize < conf.mem[1].limit)
557 			conf.mem[1].limit = memsize;
558 	}
559 
560 	if(p = getconf("*kernelpercent"))
561 		userpcnt = 100 - strtol(p, 0, 0);
562 	else
563 		userpcnt = 0;
564 
565 	conf.npage = 0;
566 	pa = PADDR(PGROUND(PTR2UINT(end)));
567 
568 	/*
569 	 *  we assume that the kernel is at the beginning of one of the
570 	 *  contiguous chunks of memory and fits therein.
571 	 */
572 	for(i=0; i<nelem(conf.mem); i++){
573 		/* take kernel out of allocatable space */
574 		if(pa > conf.mem[i].base && pa < conf.mem[i].limit)
575 			conf.mem[i].base = pa;
576 
577 		conf.mem[i].npage = (conf.mem[i].limit - conf.mem[i].base)/BY2PG;
578 		conf.npage += conf.mem[i].npage;
579 	}
580 
581 	if(userpcnt < 10 || userpcnt > 99)
582 		userpcnt = 90;
583 	conf.upages = (conf.npage*userpcnt)/100;
584 	if(conf.npage - conf.upages > 256*MiB/BY2PG)
585 		conf.upages = conf.npage - 256*MiB/BY2PG;
586 	conf.ialloc = ((conf.npage-conf.upages)/2)*BY2PG;
587 
588 	/* set up other configuration parameters */
589 	conf.nproc = 100 + ((conf.npage*BY2PG)/MB)*5;
590 	if(cpuserver)
591 		conf.nproc *= 3;
592 	if(conf.nproc > 2000)
593 		conf.nproc = 2000;
594 	conf.nswap = conf.npage*3;
595 	conf.nswppo = 4096;
596 	conf.nimage = 200;
597 
598 	conf.copymode = 1;		/* copy on reference, not copy on write */
599 
600 	/*
601 	 * Guess how much is taken by the large permanent
602 	 * datastructures. Mntcache and Mntrpc are not accounted for
603 	 * (probably ~300KB).
604 	 */
605 	kpages = conf.npage - conf.upages;
606 	kpages *= BY2PG;
607 	kpages -= conf.upages*sizeof(Page)
608 		+ conf.nproc*sizeof(Proc)
609 		+ conf.nimage*sizeof(Image)
610 		+ conf.nswap
611 		+ conf.nswppo*sizeof(Page*);
612 	mainmem->maxsize = kpages;
613 	if(!cpuserver)
614 		/*
615 		 * give terminals lots of image memory, too; the dynamic
616 		 * allocation will balance the load properly, hopefully.
617 		 * be careful with 32-bit overflow.
618 		 */
619 		imagmem->maxsize = kpages;
620 
621 }
622 
623 static void
shutdown(int ispanic)624 shutdown(int ispanic)
625 {
626 	int ms, once;
627 
628 	lock(&active);
629 	if(ispanic)
630 		active.ispanic = ispanic;
631 	else if(m->machno == 0 && (active.machs & (1<<m->machno)) == 0)
632 		active.ispanic = 0;
633 	once = active.machs & (1<<m->machno);
634 	active.machs &= ~(1<<m->machno);
635 	active.exiting = 1;
636 	unlock(&active);
637 
638 	if(once) {
639 		delay(m->machno*100);		/* stagger them */
640 		iprint("cpu%d: exiting\n", m->machno);
641 	}
642 	spllo();
643 	if (m->machno == 0)
644 		ms = 5*1000;
645 	else
646 		ms = 2*1000;
647 	for(; ms > 0; ms -= TK2MS(2)){
648 		delay(TK2MS(2));
649 		if(active.machs == 0 && consactive() == 0)
650 			break;
651 	}
652 	if(active.ispanic){
653 		if(!cpuserver)
654 			for(;;)
655 				;
656 		if(getconf("*debug"))
657 			delay(5*60*1000);
658 		else
659 			delay(10000);
660 	}
661 }
662 
663 /*
664  *  exit kernel either on a panic or user request
665  */
666 void
exit(int code)667 exit(int code)
668 {
669 	void (*f)(ulong, ulong, ulong);
670 
671 	shutdown(code);
672 	splfhi();
673 	if(m->machno == 0)
674 		archreboot();
675 	else{
676 		f = (void*)REBOOTADDR;
677 		intrcpushutdown();
678 		memmove(f, rebootcode, sizeof(rebootcode));
679 		cachedwbse(f, sizeof(rebootcode));
680 		cacheiinvse(f, sizeof(rebootcode));
681 		(*f)(0, soc.armlocal, 0);
682 		for(;;){}
683 	}
684 }
685 
686 /*
687  * stub for ../omap/devether.c
688  */
689 int
isaconfig(char * class,int ctlrno,ISAConf * isa)690 isaconfig(char *class, int ctlrno, ISAConf *isa)
691 {
692 	char cc[32], *p;
693 	int i;
694 
695 	if(strcmp(class, "ether") != 0)
696 		return 0;
697 	snprint(cc, sizeof cc, "%s%d", class, ctlrno);
698 	p = getconf(cc);
699 	if(p == nil)
700 		return (ctlrno == 0);
701 	isa->type = "";
702 	isa->nopt = tokenize(p, isa->opt, NISAOPT);
703 	for(i = 0; i < isa->nopt; i++){
704 		p = isa->opt[i];
705 		if(cistrncmp(p, "type=", 5) == 0)
706 			isa->type = p + 5;
707 	}
708 	return 1;
709 }
710 
711 /*
712  * the new kernel is already loaded at address `code'
713  * of size `size' and entry point `entry'.
714  */
715 void
reboot(void * entry,void * code,ulong size)716 reboot(void *entry, void *code, ulong size)
717 {
718 	void (*f)(ulong, ulong, ulong);
719 
720 	writeconf();
721 
722 	/*
723 	 * the boot processor is cpu0.  execute this function on it
724 	 * so that the new kernel has the same cpu0.
725 	 */
726 	if (m->machno != 0) {
727 		procwired(up, 0);
728 		sched();
729 	}
730 	if (m->machno != 0)
731 		print("on cpu%d (not 0)!\n", m->machno);
732 
733 	/* setup reboot trampoline function */
734 	f = (void*)REBOOTADDR;
735 	memmove(f, rebootcode, sizeof(rebootcode));
736 	cachedwbse(f, sizeof(rebootcode));
737 
738 	shutdown(0);
739 
740 	/*
741 	 * should be the only processor running now
742 	 */
743 
744 	delay(500);
745 	print("reboot entry %#lux code %#lux size %ld\n",
746 		PADDR(entry), PADDR(code), size);
747 	delay(100);
748 
749 	/* turn off buffered serial console */
750 	serialoq = nil;
751 	kprintoq = nil;
752 	screenputs = nil;
753 
754 	/* shutdown devices */
755 	if(!waserror()){
756 		chandevshutdown();
757 		poperror();
758 	}
759 
760 	/* stop the clock (and watchdog if any) */
761 	clockshutdown();
762 
763 	splfhi();
764 	intrshutdown();
765 
766 	/* off we go - never to return */
767 	cacheuwbinv();
768 	l2cacheuwbinv();
769 	(*f)(PADDR(entry), PADDR(code), size);
770 
771 	iprint("loaded kernel returned!\n");
772 	delay(1000);
773 	archreboot();
774 }
775