1/*	$NetBSD: src/sys/arch/evbarm/stand/gzboot/gzboot.c,v 1.14 2009-10-26 19:16:55 cegger Exp $	*/
2
3/*
4 * Copyright (c) 2002 Wasabi Systems, Inc.
5 * All rights reserved.
6 *
7 * Written by Jason R. Thorpe for Wasabi Systems, Inc.
8 *
9 * Redistribution and use in source and binary forms, with or without
10 * modification, are permitted provided that the following conditions
11 * are met:
12 * 1. Redistributions of source code must retain the above copyright
13 *    notice, this list of conditions and the following disclaimer.
14 * 2. Redistributions in binary form must reproduce the above copyright
15 *    notice, this list of conditions and the following disclaimer in the
16 *    documentation and/or other materials provided with the distribution.
17 * 3. All advertising materials mentioning features or use of this software
18 *    must display the following acknowledgement:
19 *	This product includes software developed for the NetBSD Project by
20 *	Wasabi Systems, Inc.
21 * 4. The name of Wasabi Systems, Inc. may not be used to endorse
22 *    or promote products derived from this software without specific prior
23 *    written permission.
24 *
25 * THIS SOFTWARE IS PROVIDED BY WASABI SYSTEMS, INC. ``AS IS'' AND
26 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
27 * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
28 * PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL WASABI SYSTEMS, INC
29 * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
30 * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
31 * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
32 * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
33 * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
34 * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
35 * POSSIBILITY OF SUCH DAMAGE.
36 */
37
38/*
39 * The Gzip header parser and libz interface glue are derived from
40 * sys/lib/libsa/cread.c, which carries the following notice:
41 *
42 * Copyright (c) 1996
43 *	Matthias Drochner.  All rights reserved.
44 *
45 * Redistribution and use in source and binary forms, with or without
46 * modification, are permitted provided that the following conditions
47 * are met:
48 * 1. Redistributions of source code must retain the above copyright
49 *    notice, this list of conditions and the following disclaimer.
50 * 2. Redistributions in binary form must reproduce the above copyright
51 *    notice, this list of conditions and the following disclaimer in the
52 *    documentation and/or other materials provided with the distribution.
53 *
54 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
55 * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
56 * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
57 * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
58 * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
59 * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
60 * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
61 * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
62 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
63 * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
64 */
65
66#include <sys/param.h>
67#include <lib/libsa/stand.h>
68#include <lib/libkern/libkern.h>
69#include <lib/libz/libz.h>
70
71#include "board.h"
72#include "gzboot.h"
73
74/* zlib glue defns */
75
76#define	EOF	(-1)	/* needed by compression code */
77
78#define	Z_BUFSIZE	1024
79
80static const int gz_magic[2] = { 0x1f, 0x8b }; /* gzip magic header */
81
82/* gzip flag byte */
83#define	ASCII_FLAG	0x01	/* bit 0 set: file probably ascii text */
84#define	HEAD_CRC	0x02	/* bit 1 set: header CRC present */
85#define	EXTRA_FIELD	0x04	/* bit 2 set: extra field present */
86#define	ORIG_NAME	0x08	/* bit 3 set: original file name present */
87#define	COMMENT		0x10	/* bit 4 set: file comment present */
88#define	RESERVED	0xe0	/* bits 5..7: reserved */
89
90struct state {
91	z_stream	stream;
92	int		z_err;	/* error code for last stream operation */
93	int		z_eof;	/* set of end of input file */
94	const unsigned char *srcbuf;/* source buffer */
95	size_t		srcoff;	/* source buffer offset */
96	size_t		srcsize;/* size of source buffer */
97	unsigned char	*inbuf;	/* input buffer */
98	uint32_t	crc;	/* crc32 of uncompressed data */
99	int		spinny;	/* twiddle every N reads */
100};
101
102static uint32_t	get_u8(struct state *);
103static uint32_t	get_u32(struct state *);
104static int	check_header(struct state *);
105
106/* XXX - find a suitable header for these: */
107void	zmemcpy(unsigned char *, unsigned char *, unsigned int);
108
109/* end zlib glue defns */
110
111void	main(void);
112void	gzcopy(void *, const void *, size_t);
113
114void
115main(void)
116{
117	extern char bootprog_name[], bootprog_rev[];
118	void (*loadaddr)(void) = (void *) md_root_loadaddr;
119
120	cons_init();
121
122	printf("\n");
123	printf(">> %s, Revision %s\n", bootprog_name, bootprog_rev);
124
125	board_init();
126
127	printf(">> Load address: 0x%x\n", md_root_loadaddr);
128
129	/*
130	 * If md_root_size is 0, then it means that we are simply
131	 * decompressing from an image which was concatenated onto
132	 * the end of the gzboot binary.
133	 */
134	if (md_root_size != 0)
135		printf(">> Image size: %u\n", md_root_size);
136
137	printf("Uncompressing image...");
138	gzcopy((void *) loadaddr, md_root_image, md_root_size);
139	printf("done.\n");
140
141	printf("Jumping to image @ 0x%x...\n", md_root_loadaddr);
142
143	board_fini();
144
145	(*loadaddr)();
146
147	_rtt();
148}
149
150void abort(void);
151void
152abort(void)
153{
154
155	for (;;) ;
156}
157
158__dead void
159_rtt(void)
160{
161
162	for (;;) ;
163}
164
165/* internal utility routines */
166
167static ssize_t
168readbuf(struct state *s, void *buf, size_t len)
169{
170
171	if (s->srcsize != 0 && len > (s->srcsize - s->srcoff))
172		len = s->srcsize - s->srcoff;
173
174	if ((s->spinny++ & 7) == 0)
175		twiddle();
176	memcpy(buf, s->srcbuf + s->srcoff, len);
177	s->srcoff += len;
178
179	return (len);
180}
181
182static ssize_t
183readgz(struct state *s, void *buf, size_t len)
184{
185	unsigned char *start = buf;	/* start for CRC computation */
186
187	if (s->z_err == Z_DATA_ERROR || s->z_err == Z_ERRNO)
188		return (-1);
189	if (s->z_err == Z_STREAM_END)
190		return (0);	/* EOF */
191
192	s->stream.next_out = buf;
193	s->stream.avail_out = len;
194
195	while (s->stream.avail_out != 0) {
196		if (s->stream.avail_in == 0 && s->z_eof == 0) {
197			ssize_t got;
198			got = readbuf(s, s->inbuf, Z_BUFSIZE);
199			if (got <= 0)
200				s->z_eof = 1;
201			s->stream.avail_in = got;
202			s->stream.next_in = s->inbuf;
203		}
204
205		s->z_err = inflate(&s->stream, Z_NO_FLUSH);
206
207		if (s->z_err == Z_STREAM_END) {
208			/* Check CRC and original size */
209			s->crc = crc32(s->crc, start, (unsigned int)
210			    (s->stream.next_out - start));
211			start = s->stream.next_out;
212
213			if (get_u32(s) != s->crc) {
214				printf("FATAL: CRC checksum mismatch\n");
215				s->z_err = Z_DATA_ERROR;
216			}
217			if (get_u32(s) != s->stream.total_out) {
218				printf("FATAL: total output mismatch\n");
219				s->z_err = Z_DATA_ERROR;
220			}
221			s->z_eof = 1;
222		}
223		if (s->z_err != Z_OK && s->z_err != Z_STREAM_END) {
224			printf("FATAL: error %d from zlib\n",
225			    s->z_err);
226			return (-1);
227		}
228		if (s->z_err != Z_OK || s->z_eof)
229			break;
230	}
231
232	s->crc = crc32(s->crc, start,
233	    (unsigned int)(s->stream.next_out - start));
234
235	return ((ssize_t) (len - s->stream.avail_out));
236}
237
238/* util routines for zlib */
239
240void
241zmemcpy(unsigned char *dst, unsigned char *src, unsigned int len)
242{
243
244	memcpy(dst, src, len);
245}
246
247/* gzip utility routines */
248
249static uint32_t
250get_u8(struct state *s)
251{
252
253	if (s->z_eof)
254		return (EOF);
255
256	if (s->stream.avail_in == 0) {
257		ssize_t got;
258
259		got = readbuf(s, s->inbuf, Z_BUFSIZE);
260		if (got <= 0) {
261			s->z_eof = 1;
262			return (EOF);
263		}
264		s->stream.avail_in = got;
265		s->stream.next_in = s->inbuf;
266	}
267	s->stream.avail_in--;
268	return (*(s->stream.next_in)++);
269}
270
271static uint32_t
272get_u32(struct state *s)
273{
274	uint32_t x, c;
275
276	x  = get_u8(s);
277	x |= get_u8(s) << 8;
278	x |= get_u8(s) << 16;
279	c = get_u8(s);
280	if (c == EOF)
281		s->z_err = Z_DATA_ERROR;
282	x |= c << 24;
283	return (x);
284}
285
286static int
287check_header(struct state *s)
288{
289	int method;	/* method byte */
290	int flags;	/* flags byte */
291	unsigned int len;
292	int c;
293
294	/* Check the gzip magic header */
295	for (len = 0; len < 2; len++) {
296		c = get_u8(s);
297		if (c == gz_magic[len])
298			continue;
299		printf("FATAL: not a Gzip'd image\n");
300		return (1);
301	}
302
303	method = get_u8(s);
304	flags = get_u8(s);
305	if (method != Z_DEFLATED || (flags & RESERVED) != 0) {
306		printf("FATAL: invalid Gzip header\n");
307		return (1);
308	}
309
310	/* Discard time, xflags, and OS code: */
311	for (len = 0; len < 6; len++)
312		(void) get_u8(s);
313
314	if (flags & EXTRA_FIELD) {
315		/* skip the extra field */
316		len  = get_u8(s);
317		len |= get_u8(s) << 8;
318		/* len is garbage if EOF, but the loop below will quit anyway */
319		while (len-- && get_u8(s) != EOF)
320			/* loop */;
321	}
322	if (flags & ORIG_NAME) {
323		/* skip the original file name */
324		while ((c = get_u8(s)) != 0 && c != EOF)
325			/* loop */;
326	}
327	if (flags & COMMENT) {
328		/* skip the file comment */
329		while ((c = get_u8(s)) != 0 && c != EOF)
330			/* loop */;
331	}
332	if (flags & HEAD_CRC) {
333		/* skip header CRC */
334		for (len = 0; len < 2; len++)
335			(void) get_u8(s);
336	}
337
338	if (s->z_eof) {
339		printf("FATAL: end of image encountered parsing Gzip header\n");
340		return (1);
341	}
342
343	/* OK! */
344	return (0);
345}
346
347/* the actual gzcopy routine */
348
349void
350gzcopy(void *dst, const void *src, size_t srclen)
351{
352	struct state state;
353	unsigned char *cp = dst;
354	ssize_t len;
355
356	memset(&state, 0, sizeof(state));
357
358	state.z_err = Z_OK;
359	state.srcbuf = src;
360	state.srcsize = srclen;
361
362	if (inflateInit2(&state.stream, -15) != Z_OK) {
363		printf("FATAL: inflateInit2 failed\n");
364		_rtt();
365	}
366
367	state.stream.next_in = state.inbuf = alloc(Z_BUFSIZE);
368	if (state.inbuf == NULL) {
369		inflateEnd(&state.stream);
370		printf("FATAL: unable to allocate Z buffer\n");
371		_rtt();
372	}
373
374	/* Skip the Gzip header. */
375	if (check_header(&state)) {
376		inflateEnd(&state.stream);
377		dealloc(state.inbuf, Z_BUFSIZE);
378		_rtt();
379	}
380
381	/* Uncompress the image! */
382	while ((len = readgz(&state, cp, Z_BUFSIZE)) > 0)
383		cp += len;
384	if (len == -1)
385		_rtt();
386
387	/* All done! */
388	inflateEnd(&state.stream);
389	dealloc(state.inbuf, Z_BUFSIZE);
390}
391