xref: /plan9-contrib/sys/src/9/pcboot/expand.c (revision b94bb474148e9d24a82a427863d9c9eb4c20f4ae)
1 /*
2  * expand gzipped boot loader appended to this binary and execute it.
3  *
4  * due to Russ Cox, rsc@swtch.com.
5  * see http://plan9.bell-labs.com/wiki/plan9/Replacing_9load
6  */
7 #include <u.h>
8 #include <libc.h>
9 #include <a.out.h>
10 #include <flate.h>
11 #include "mem.h"
12 #include "expand.h"
13 
14 #include "inflate.guts.c"
15 
16 #define KB		1024
17 #define MB		(1024*1024)
18 
19 extern char edata[];
20 
21 /* ldecomp.s */
22 void mb586(void);
23 void splhi(void);
24 void wbinvd(void);
25 
26 /* inflate.guts.c */
27 int gunzip(uchar*, int, uchar*, int);
28 
29 int isexec(void*);
30 int isgzip(uchar*);
31 void run(void*);
32 
33 #pragma varargck type "d" ulong
34 #pragma varargck type "x" ulong
35 
36 static uchar *kernel = (uchar*)Bootkernaddr;
37 static char *dbrk = (char*)Mallocbase;
38 
39 ulong
40 swap(ulong p)
41 {
42 	return p<<24 | p>>24 | (p<<8)&0x00FF0000 | (p>>8)&0x0000FF00;
43 }
44 
45 enum {
46 	/* keyboard controller ports & cmds */
47 	Data=		0x60,		/* data port */
48 	Status=		0x64,		/* status port */
49 	 Inready=	0x01,		/*  input character ready */
50 	 Outbusy=	0x02,		/*  output busy */
51 	Cmd=		0x64,		/* command port (write only) */
52 
53 	/* system control port a */
54 	Sysctla=	0x92,
55 	 Sysctlreset=	1<<0,
56 	 Sysctla20ena=	1<<1,
57 };
58 
59 static int
60 isa20on(void)
61 {
62 	int r;
63 	ulong o;
64 	ulong *zp, *mb1p;
65 
66 	zp = 0;
67 	mb1p = (ulong *)MB;
68 	o = *zp;
69 
70 	*zp = 0x1234;
71 	*mb1p = 0x8765;
72 	mb586();
73 	wbinvd();
74 	r = *zp != *mb1p;
75 
76 	*zp = o;
77 	return r;
78 }
79 
80 static void
81 delay(uint ms)				/* approximate */
82 {
83 	int i;
84 
85 	while(ms-- > 0)
86 		for(i = 1000*1000; i > 0; i--)
87 			;
88 }
89 
90 static int
91 kbdinit(void)
92 {
93 	int c, try;
94 
95 	/* wait for a quiescent controller */
96 	try = 1000;
97 	while(try-- > 0 && (c = inb(Status)) & (Outbusy | Inready)) {
98 		if(c & Inready)
99 			inb(Data);
100 		delay(1);
101 	}
102 	return try <= 0? -1: 0;
103 }
104 
105 /*
106  *  wait for output no longer busy (but not forever,
107  *  there might not be a keyboard controller).
108  */
109 static void
110 outready(void)
111 {
112 	int i;
113 
114 	for (i = 1000; i > 0 && inb(Status) & Outbusy; i--)
115 		delay(1);
116 }
117 
118 /*
119  *  ask 8042 to enable the use of address bit 20
120  */
121 int
122 i8042a20(void)
123 {
124 	if (kbdinit() < 0)
125 		return -1;
126 	outready();
127 	outb(Cmd, 0xD1);
128 	outready();
129 	outb(Data, 0xDF);
130 	outready();
131 	return 0;
132 }
133 
134 void
135 a20init(void)
136 {
137 	int b;
138 
139 	if (isa20on())
140 		return;
141 	if (i8042a20() < 0) {		/* original method, via kbd ctlr */
142 		/* newer method, last resort */
143 		b = inb(Sysctla);
144 		if (!(b & Sysctla20ena))
145 			outb(Sysctla, (b & ~Sysctlreset) | Sysctla20ena);
146 	}
147 	if (!isa20on())
148 		print("a20 didn't come on!\n");
149 }
150 
151 void
152 _main(void)
153 {
154 	int ksize;
155 	Exec *exec;
156 
157 	splhi();
158 	a20init();		/* don't wrap addresses at 1MB boundaries */
159 	ksize = Lowmemsz - (ulong)edata;	/* payload size */
160 	memmove(kernel, edata, ksize);
161 	memset(edata, 0, end - edata);
162 
163 	cgainit();
164 	if(isgzip(kernel)) {
165 		print("gz...");
166 		memmove((uchar*)Unzipbuf, kernel, ksize);
167 
168 		/* we have to uncompress the entire kernel to get OK status */
169 		if(gunzip(kernel, Bootkernmax, (uchar*)Unzipbuf, ksize) < 0){
170 			print("gzip failed.");
171 			exits(0);
172 		}
173 	}
174 	if(isexec(kernel))
175 		run(kernel);
176 
177 	exec = (Exec *)kernel;
178 	print("unrecognized program; magic # 0x%x\n", swap(exec->magic));
179 	exits(0);
180 }
181 
182 int
183 isexec(void *v)
184 {
185 	Exec *exec;
186 
187 	exec = v;
188 	return swap(exec->magic) == I_MAGIC || swap(exec->magic) == S_MAGIC;
189 }
190 
191 void
192 run(void *v)
193 {
194 	ulong entry, text, data;
195 	uchar *base;
196 	Exec *exec;
197 
198 	base = v;
199 	exec = v;
200 	entry = swap(exec->entry) & ~KSEGM;
201 	text = swap(exec->text);
202 	data = swap(exec->data);
203 	/*
204 	 * align data segment on the expected page boundary.
205 	 * sizeof(Exec)+text is offset from base to data segment.
206 	 */
207 	memmove(base+PGROUND(sizeof(Exec)+text), base+sizeof(Exec)+text, data);
208 
209 	print("starting protected-mode loader at 0x%x\n", entry);
210 	((void(*)(void))entry)();
211 
212 	print("exec failed");
213 	exits(0);
214 }
215 
216 int
217 isgzip(uchar *p)
218 {
219 	return p[0] == 0x1F && p[1] == 0x8B;
220 }
221 
222 void*
223 malloc(ulong n)
224 {
225 	void *v;
226 
227 	v = dbrk;
228 	dbrk += ROUND(n, BY2WD);
229 	return v;
230 }
231 
232 void
233 free(void*)
234 {
235 }
236 
237 void
238 puts(char *s)
239 {
240 	for(; *s; s++)
241 		cgaputc(*s);
242 }
243 
244 int
245 print(char *fmt, ...)
246 {
247 	int sign;
248 	long d;
249 	ulong x;
250 	char *p, *s, buf[20];
251 	va_list arg;
252 	static char *hex = "0123456789abcdef";
253 
254 	va_start(arg, fmt);
255 	for(p = fmt; *p; p++){
256 		if(*p != '%') {
257 			cgaputc(*p);
258 			continue;
259 		}
260 		SET(s);
261 		switch(*++p){
262 		case 'p':
263 		case 'x':
264 			x = va_arg(arg, ulong);
265 			if(x == 0){
266 				s = "0";
267 				break;
268 			}
269 			s = buf+sizeof buf;
270 			*--s = 0;
271 			while(x > 0){
272 				*--s = hex[x&15];
273 				x /= 16;
274 			}
275 			if(s == buf+sizeof buf)
276 				*--s = '0';
277 			break;
278 		case 'd':
279 			d = va_arg(arg, ulong);
280 			if(d == 0){
281 				s = "0";
282 				break;
283 			}
284 			if(d < 0){
285 				d = -d;
286 				sign = -1;
287 			}else
288 				sign = 1;
289 			s = buf+sizeof buf;
290 			*--s = 0;
291 			while(d > 0){
292 				*--s = (d%10)+'0';
293 				d /= 10;
294 			}
295 			if(sign < 0)
296 				*--s = '-';
297 			break;
298 		case 's':
299 			s = va_arg(arg, char*);
300 			break;
301 		case 0:
302 			return 0;
303 		}
304 		puts(s);
305 	}
306 	return 0;
307 }
308 
309 void
310 exits(char*)
311 {
312 	for(;;)
313 		;
314 }
315