xref: /plan9/sys/src/9/pcboot/expand.c (revision 084126e8a4f119eb0ee0e36d1d73ebf0b5608c3b)
125210b06SDavid du Colombier /*
225210b06SDavid du Colombier  * expand gzipped boot loader appended to this binary and execute it.
325210b06SDavid du Colombier  *
425210b06SDavid du Colombier  * due to Russ Cox, rsc@swtch.com.
525210b06SDavid du Colombier  * see http://plan9.bell-labs.com/wiki/plan9/Replacing_9load
625210b06SDavid du Colombier  */
725210b06SDavid du Colombier #include <u.h>
825210b06SDavid du Colombier #include <libc.h>
925210b06SDavid du Colombier #include <a.out.h>
1025210b06SDavid du Colombier #include <flate.h>
1125210b06SDavid du Colombier #include "mem.h"
1225210b06SDavid du Colombier #include "expand.h"
1325210b06SDavid du Colombier 
1425210b06SDavid du Colombier #include "inflate.guts.c"
1525210b06SDavid du Colombier 
1625210b06SDavid du Colombier #define KB		1024
1725210b06SDavid du Colombier #define MB		(1024*1024)
1825210b06SDavid du Colombier 
1925210b06SDavid du Colombier extern char edata[];
2025210b06SDavid du Colombier 
2125210b06SDavid du Colombier /* ldecomp.s */
2225210b06SDavid du Colombier void mb586(void);
2325210b06SDavid du Colombier void splhi(void);
2425210b06SDavid du Colombier void wbinvd(void);
2525210b06SDavid du Colombier 
2625210b06SDavid du Colombier /* inflate.guts.c */
2725210b06SDavid du Colombier int gunzip(uchar*, int, uchar*, int);
2825210b06SDavid du Colombier 
2925210b06SDavid du Colombier int isexec(void*);
3025210b06SDavid du Colombier int isgzip(uchar*);
3125210b06SDavid du Colombier void run(void*);
3225210b06SDavid du Colombier 
3325210b06SDavid du Colombier #pragma varargck type "d" ulong
3425210b06SDavid du Colombier #pragma varargck type "x" ulong
3525210b06SDavid du Colombier 
3625210b06SDavid du Colombier static uchar *kernel = (uchar*)Bootkernaddr;
3725210b06SDavid du Colombier static char *dbrk = (char*)Mallocbase;
3825210b06SDavid du Colombier 
3925210b06SDavid du Colombier ulong
swap(ulong p)4025210b06SDavid du Colombier swap(ulong p)
4125210b06SDavid du Colombier {
4225210b06SDavid du Colombier 	return p<<24 | p>>24 | (p<<8)&0x00FF0000 | (p>>8)&0x0000FF00;
4325210b06SDavid du Colombier }
4425210b06SDavid du Colombier 
4525210b06SDavid du Colombier enum {
4625210b06SDavid du Colombier 	/* keyboard controller ports & cmds */
4725210b06SDavid du Colombier 	Data=		0x60,		/* data port */
4825210b06SDavid du Colombier 	Status=		0x64,		/* status port */
4925210b06SDavid du Colombier 	 Inready=	0x01,		/*  input character ready */
5025210b06SDavid du Colombier 	 Outbusy=	0x02,		/*  output busy */
5125210b06SDavid du Colombier 	Cmd=		0x64,		/* command port (write only) */
5225210b06SDavid du Colombier 
5325210b06SDavid du Colombier 	/* system control port a */
5425210b06SDavid du Colombier 	Sysctla=	0x92,
5525210b06SDavid du Colombier 	 Sysctlreset=	1<<0,
5625210b06SDavid du Colombier 	 Sysctla20ena=	1<<1,
5725210b06SDavid du Colombier };
5825210b06SDavid du Colombier 
5925210b06SDavid du Colombier static int
isa20on(void)6025210b06SDavid du Colombier isa20on(void)
6125210b06SDavid du Colombier {
6225210b06SDavid du Colombier 	int r;
6325210b06SDavid du Colombier 	ulong o;
6425210b06SDavid du Colombier 	ulong *zp, *mb1p;
6525210b06SDavid du Colombier 
6625210b06SDavid du Colombier 	zp = 0;
6725210b06SDavid du Colombier 	mb1p = (ulong *)MB;
6825210b06SDavid du Colombier 	o = *zp;
6925210b06SDavid du Colombier 
7025210b06SDavid du Colombier 	*zp = 0x1234;
7125210b06SDavid du Colombier 	*mb1p = 0x8765;
7225210b06SDavid du Colombier 	mb586();
7325210b06SDavid du Colombier 	wbinvd();
7425210b06SDavid du Colombier 	r = *zp != *mb1p;
7525210b06SDavid du Colombier 
7625210b06SDavid du Colombier 	*zp = o;
7725210b06SDavid du Colombier 	return r;
7825210b06SDavid du Colombier }
7925210b06SDavid du Colombier 
8025210b06SDavid du Colombier static void
delay(uint ms)8125210b06SDavid du Colombier delay(uint ms)				/* approximate */
8225210b06SDavid du Colombier {
8325210b06SDavid du Colombier 	int i;
8425210b06SDavid du Colombier 
8525210b06SDavid du Colombier 	while(ms-- > 0)
8625210b06SDavid du Colombier 		for(i = 1000*1000; i > 0; i--)
8725210b06SDavid du Colombier 			;
8825210b06SDavid du Colombier }
8925210b06SDavid du Colombier 
9025210b06SDavid du Colombier static int
kbdinit(void)9125210b06SDavid du Colombier kbdinit(void)
9225210b06SDavid du Colombier {
9325210b06SDavid du Colombier 	int c, try;
9425210b06SDavid du Colombier 
9525210b06SDavid du Colombier 	/* wait for a quiescent controller */
967d80c4cdSDavid du Colombier 	try = 500;			/* was 1000 */
9725210b06SDavid du Colombier 	while(try-- > 0 && (c = inb(Status)) & (Outbusy | Inready)) {
9825210b06SDavid du Colombier 		if(c & Inready)
9925210b06SDavid du Colombier 			inb(Data);
10025210b06SDavid du Colombier 		delay(1);
10125210b06SDavid du Colombier 	}
10225210b06SDavid du Colombier 	return try <= 0? -1: 0;
10325210b06SDavid du Colombier }
10425210b06SDavid du Colombier 
10525210b06SDavid du Colombier /*
10625210b06SDavid du Colombier  *  wait for output no longer busy (but not forever,
10725210b06SDavid du Colombier  *  there might not be a keyboard controller).
10825210b06SDavid du Colombier  */
10925210b06SDavid du Colombier static void
outready(void)11025210b06SDavid du Colombier outready(void)
11125210b06SDavid du Colombier {
11225210b06SDavid du Colombier 	int i;
11325210b06SDavid du Colombier 
11425210b06SDavid du Colombier 	for (i = 1000; i > 0 && inb(Status) & Outbusy; i--)
11525210b06SDavid du Colombier 		delay(1);
11625210b06SDavid du Colombier }
11725210b06SDavid du Colombier 
11825210b06SDavid du Colombier /*
11925210b06SDavid du Colombier  *  ask 8042 to enable the use of address bit 20
12025210b06SDavid du Colombier  */
12125210b06SDavid du Colombier int
i8042a20(void)12225210b06SDavid du Colombier i8042a20(void)
12325210b06SDavid du Colombier {
12425210b06SDavid du Colombier 	if (kbdinit() < 0)
12525210b06SDavid du Colombier 		return -1;
12625210b06SDavid du Colombier 	outready();
12725210b06SDavid du Colombier 	outb(Cmd, 0xD1);
12825210b06SDavid du Colombier 	outready();
12925210b06SDavid du Colombier 	outb(Data, 0xDF);
13025210b06SDavid du Colombier 	outready();
13125210b06SDavid du Colombier 	return 0;
13225210b06SDavid du Colombier }
13325210b06SDavid du Colombier 
13425210b06SDavid du Colombier void
a20init(void)13525210b06SDavid du Colombier a20init(void)
13625210b06SDavid du Colombier {
13725210b06SDavid du Colombier 	int b;
13825210b06SDavid du Colombier 
13925210b06SDavid du Colombier 	if (isa20on())
14025210b06SDavid du Colombier 		return;
14125210b06SDavid du Colombier 	if (i8042a20() < 0) {		/* original method, via kbd ctlr */
14225210b06SDavid du Colombier 		/* newer method, last resort */
14325210b06SDavid du Colombier 		b = inb(Sysctla);
14425210b06SDavid du Colombier 		if (!(b & Sysctla20ena))
14525210b06SDavid du Colombier 			outb(Sysctla, (b & ~Sysctlreset) | Sysctla20ena);
14625210b06SDavid du Colombier 	}
147*084126e8SDavid du Colombier 	if (!isa20on()){
14825210b06SDavid du Colombier 		print("a20 didn't come on!\n");
149*084126e8SDavid du Colombier 		for(;;)
150*084126e8SDavid du Colombier 			;
151*084126e8SDavid du Colombier 	}
15225210b06SDavid du Colombier }
15325210b06SDavid du Colombier 
15425210b06SDavid du Colombier void
_main(void)15525210b06SDavid du Colombier _main(void)
15625210b06SDavid du Colombier {
15725210b06SDavid du Colombier 	int ksize;
15825210b06SDavid du Colombier 	Exec *exec;
15925210b06SDavid du Colombier 
16025210b06SDavid du Colombier 	splhi();
16125210b06SDavid du Colombier 	a20init();		/* don't wrap addresses at 1MB boundaries */
16225210b06SDavid du Colombier 	ksize = Lowmemsz - (ulong)edata;	/* payload size */
16325210b06SDavid du Colombier 	memmove(kernel, edata, ksize);
16425210b06SDavid du Colombier 	memset(edata, 0, end - edata);
16525210b06SDavid du Colombier 
16625210b06SDavid du Colombier 	cgainit();
16725210b06SDavid du Colombier 	if(isgzip(kernel)) {
16825210b06SDavid du Colombier 		print("gz...");
16925210b06SDavid du Colombier 		memmove((uchar*)Unzipbuf, kernel, ksize);
17025210b06SDavid du Colombier 
17125210b06SDavid du Colombier 		/* we have to uncompress the entire kernel to get OK status */
17225210b06SDavid du Colombier 		if(gunzip(kernel, Bootkernmax, (uchar*)Unzipbuf, ksize) < 0){
17325210b06SDavid du Colombier 			print("gzip failed.");
17425210b06SDavid du Colombier 			exits(0);
17525210b06SDavid du Colombier 		}
17625210b06SDavid du Colombier 	}
17725210b06SDavid du Colombier 	if(isexec(kernel))
17825210b06SDavid du Colombier 		run(kernel);
17925210b06SDavid du Colombier 
18025210b06SDavid du Colombier 	exec = (Exec *)kernel;
18125210b06SDavid du Colombier 	print("unrecognized program; magic # 0x%x\n", swap(exec->magic));
18225210b06SDavid du Colombier 	exits(0);
18325210b06SDavid du Colombier }
18425210b06SDavid du Colombier 
18525210b06SDavid du Colombier int
isexec(void * v)18625210b06SDavid du Colombier isexec(void *v)
18725210b06SDavid du Colombier {
18825210b06SDavid du Colombier 	Exec *exec;
18925210b06SDavid du Colombier 
19025210b06SDavid du Colombier 	exec = v;
19125210b06SDavid du Colombier 	return swap(exec->magic) == I_MAGIC || swap(exec->magic) == S_MAGIC;
19225210b06SDavid du Colombier }
19325210b06SDavid du Colombier 
19425210b06SDavid du Colombier void
run(void * v)19525210b06SDavid du Colombier run(void *v)
19625210b06SDavid du Colombier {
19725210b06SDavid du Colombier 	ulong entry, text, data;
19825210b06SDavid du Colombier 	uchar *base;
19925210b06SDavid du Colombier 	Exec *exec;
20025210b06SDavid du Colombier 
20125210b06SDavid du Colombier 	base = v;
20225210b06SDavid du Colombier 	exec = v;
20325210b06SDavid du Colombier 	entry = swap(exec->entry) & ~KSEGM;
20425210b06SDavid du Colombier 	text = swap(exec->text);
20525210b06SDavid du Colombier 	data = swap(exec->data);
20625210b06SDavid du Colombier 	/*
20725210b06SDavid du Colombier 	 * align data segment on the expected page boundary.
20825210b06SDavid du Colombier 	 * sizeof(Exec)+text is offset from base to data segment.
20925210b06SDavid du Colombier 	 */
21025210b06SDavid du Colombier 	memmove(base+PGROUND(sizeof(Exec)+text), base+sizeof(Exec)+text, data);
21125210b06SDavid du Colombier 
21225210b06SDavid du Colombier 	print("starting protected-mode loader at 0x%x\n", entry);
21325210b06SDavid du Colombier 	((void(*)(void))entry)();
21425210b06SDavid du Colombier 
21525210b06SDavid du Colombier 	print("exec failed");
21625210b06SDavid du Colombier 	exits(0);
21725210b06SDavid du Colombier }
21825210b06SDavid du Colombier 
21925210b06SDavid du Colombier int
isgzip(uchar * p)22025210b06SDavid du Colombier isgzip(uchar *p)
22125210b06SDavid du Colombier {
22225210b06SDavid du Colombier 	return p[0] == 0x1F && p[1] == 0x8B;
22325210b06SDavid du Colombier }
22425210b06SDavid du Colombier 
22525210b06SDavid du Colombier void*
malloc(ulong n)22625210b06SDavid du Colombier malloc(ulong n)
22725210b06SDavid du Colombier {
22825210b06SDavid du Colombier 	void *v;
22925210b06SDavid du Colombier 
23025210b06SDavid du Colombier 	v = dbrk;
23125210b06SDavid du Colombier 	dbrk += ROUND(n, BY2WD);
23225210b06SDavid du Colombier 	return v;
23325210b06SDavid du Colombier }
23425210b06SDavid du Colombier 
23525210b06SDavid du Colombier void
free(void *)23625210b06SDavid du Colombier free(void*)
23725210b06SDavid du Colombier {
23825210b06SDavid du Colombier }
23925210b06SDavid du Colombier 
24025210b06SDavid du Colombier void
puts(char * s)24125210b06SDavid du Colombier puts(char *s)
24225210b06SDavid du Colombier {
24325210b06SDavid du Colombier 	for(; *s; s++)
24425210b06SDavid du Colombier 		cgaputc(*s);
24525210b06SDavid du Colombier }
24625210b06SDavid du Colombier 
24725210b06SDavid du Colombier int
print(char * fmt,...)24825210b06SDavid du Colombier print(char *fmt, ...)
24925210b06SDavid du Colombier {
25025210b06SDavid du Colombier 	int sign;
25125210b06SDavid du Colombier 	long d;
25225210b06SDavid du Colombier 	ulong x;
25325210b06SDavid du Colombier 	char *p, *s, buf[20];
25425210b06SDavid du Colombier 	va_list arg;
25525210b06SDavid du Colombier 	static char *hex = "0123456789abcdef";
25625210b06SDavid du Colombier 
25725210b06SDavid du Colombier 	va_start(arg, fmt);
25825210b06SDavid du Colombier 	for(p = fmt; *p; p++){
25925210b06SDavid du Colombier 		if(*p != '%') {
26025210b06SDavid du Colombier 			cgaputc(*p);
26125210b06SDavid du Colombier 			continue;
26225210b06SDavid du Colombier 		}
26325210b06SDavid du Colombier 		SET(s);
26425210b06SDavid du Colombier 		switch(*++p){
26525210b06SDavid du Colombier 		case 'p':
26625210b06SDavid du Colombier 		case 'x':
26725210b06SDavid du Colombier 			x = va_arg(arg, ulong);
26825210b06SDavid du Colombier 			if(x == 0){
26925210b06SDavid du Colombier 				s = "0";
27025210b06SDavid du Colombier 				break;
27125210b06SDavid du Colombier 			}
27225210b06SDavid du Colombier 			s = buf+sizeof buf;
27325210b06SDavid du Colombier 			*--s = 0;
27425210b06SDavid du Colombier 			while(x > 0){
27525210b06SDavid du Colombier 				*--s = hex[x&15];
27625210b06SDavid du Colombier 				x /= 16;
27725210b06SDavid du Colombier 			}
27825210b06SDavid du Colombier 			if(s == buf+sizeof buf)
27925210b06SDavid du Colombier 				*--s = '0';
28025210b06SDavid du Colombier 			break;
28125210b06SDavid du Colombier 		case 'd':
28225210b06SDavid du Colombier 			d = va_arg(arg, ulong);
28325210b06SDavid du Colombier 			if(d == 0){
28425210b06SDavid du Colombier 				s = "0";
28525210b06SDavid du Colombier 				break;
28625210b06SDavid du Colombier 			}
28725210b06SDavid du Colombier 			if(d < 0){
28825210b06SDavid du Colombier 				d = -d;
28925210b06SDavid du Colombier 				sign = -1;
29025210b06SDavid du Colombier 			}else
29125210b06SDavid du Colombier 				sign = 1;
29225210b06SDavid du Colombier 			s = buf+sizeof buf;
29325210b06SDavid du Colombier 			*--s = 0;
29425210b06SDavid du Colombier 			while(d > 0){
29525210b06SDavid du Colombier 				*--s = (d%10)+'0';
29625210b06SDavid du Colombier 				d /= 10;
29725210b06SDavid du Colombier 			}
29825210b06SDavid du Colombier 			if(sign < 0)
29925210b06SDavid du Colombier 				*--s = '-';
30025210b06SDavid du Colombier 			break;
30125210b06SDavid du Colombier 		case 's':
30225210b06SDavid du Colombier 			s = va_arg(arg, char*);
30325210b06SDavid du Colombier 			break;
30425210b06SDavid du Colombier 		case 0:
30525210b06SDavid du Colombier 			return 0;
30625210b06SDavid du Colombier 		}
30725210b06SDavid du Colombier 		puts(s);
30825210b06SDavid du Colombier 	}
30925210b06SDavid du Colombier 	return 0;
31025210b06SDavid du Colombier }
31125210b06SDavid du Colombier 
31225210b06SDavid du Colombier void
exits(char *)31325210b06SDavid du Colombier exits(char*)
31425210b06SDavid du Colombier {
31525210b06SDavid du Colombier 	for(;;)
31625210b06SDavid du Colombier 		;
31725210b06SDavid du Colombier }
318