Annotation of /trunk/linterm_tools/fw_builder/bundle-tools/fwpack.c
Parent Directory | Revision Log
Revision 658 -
(hide annotations)
(download)
Mon Jan 14 16:57:24 2008 UTC (16 years, 4 months ago) by niro
File MIME type: text/plain
File size: 9591 byte(s)
Mon Jan 14 16:57:24 2008 UTC (16 years, 4 months ago) by niro
File MIME type: text/plain
File size: 9591 byte(s)
initial import
1 | niro | 658 | /* |
2 | A small tool to generate Wyse WinTerm firmware upgrade/add-on images | ||
3 | Copyright (C) 2005 Wilmer van der Gaast <wilmer@gaast.net> | ||
4 | |||
5 | This program is free software; you can redistribute it and/or | ||
6 | modify it under the terms of the second version of the GNU | ||
7 | General Public License as published by the Free Software | ||
8 | Foundation. | ||
9 | |||
10 | !!! GPL EXCEPTION: Since this program includes bits of data from original | ||
11 | WYSE images during the compilation progress, you're NOT allowed to | ||
12 | distribute binaries! Distribute this program ONLY IN SOURCE FORM !!! | ||
13 | |||
14 | This program is distributed in the hope that it will be useful, | ||
15 | but WITHOUT ANY WARRANTY; without even the implied warranty of | ||
16 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | ||
17 | GNU General Public License for more details. | ||
18 | |||
19 | You should have received a copy of the GNU General Public License | ||
20 | along with this program; if not, write to the Free Software | ||
21 | Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. | ||
22 | */ | ||
23 | |||
24 | #include <stdio.h> | ||
25 | #include <stdlib.h> | ||
26 | #include <sys/types.h> | ||
27 | #include <sys/stat.h> | ||
28 | #include <fcntl.h> | ||
29 | #include <unistd.h> | ||
30 | #include <string.h> | ||
31 | #include <errno.h> | ||
32 | |||
33 | #include "fwstruct.h" | ||
34 | #include "fwhead.h" | ||
35 | |||
36 | #ifndef BYTE_ORDER | ||
37 | #error "Couldn't determine byte order" | ||
38 | #endif | ||
39 | |||
40 | #define BUFSIZE 512 | ||
41 | |||
42 | struct fwfile | ||
43 | { | ||
44 | int fd; | ||
45 | u_int32_t cs; | ||
46 | u_int8_t cslen; | ||
47 | union | ||
48 | { | ||
49 | u_int8_t byte[4]; | ||
50 | u_int32_t val; | ||
51 | } csbuf; | ||
52 | }; | ||
53 | |||
54 | struct fwfile *fwstart( char *fn ); | ||
55 | int fwwrite( struct fwfile *fw, void *data, int size ); | ||
56 | int fwpad( struct fwfile *fw, int align ); | ||
57 | u_int32_t fwcs( struct fwfile *fw ); | ||
58 | void fwclose( struct fwfile *fw ); | ||
59 | |||
60 | int pad( off_t offset, int align ); | ||
61 | char *basename( char *fn ); | ||
62 | void verify_offset( int fd, off_t expect ); | ||
63 | |||
64 | int main( int argc, char *argv[] ) | ||
65 | { | ||
66 | struct index *idx = NULL, *idxstart = NULL; | ||
67 | struct fwfile *fw = NULL; | ||
68 | u_int32_t int32; | ||
69 | off_t cur; | ||
70 | int fd, i; | ||
71 | |||
72 | printf( "fwpack - Generate WYSE Winterm firmware bundles\n\n" ); | ||
73 | |||
74 | structcheck(); | ||
75 | |||
76 | if( argc < 3 ) | ||
77 | { | ||
78 | fprintf( stderr, "Syntax: %s <image> <bios> [<other files> <...>]\n" | ||
79 | "\n" | ||
80 | "First argument is the destination file. After that, specify the files to put\n" | ||
81 | "into the image. The first file will be used as the BIOS file, after that you\n" | ||
82 | "can put other files you want to be in the image. The original firmware files\n" | ||
83 | "have some flags that this tool won't create, so this tool is probably not\n" | ||
84 | "suitable to create original WinCE firmware (or add-on) images. It's mainly\n" | ||
85 | "meant to create Linux images.\n" | ||
86 | "\n" | ||
87 | "Also, a file named k will automatically be used for the hardware magic section\n" | ||
88 | "in the headers.\n" | ||
89 | "\n" | ||
90 | "Don't forget to read the documentation before using this tool!\n", argv[0] ); | ||
91 | return( 1 ); | ||
92 | } | ||
93 | |||
94 | if( strlen( basename( argv[2] ) ) > 8 ) | ||
95 | { | ||
96 | fprintf( stderr, "BIOS filename too long!\n" ); | ||
97 | return( 1 ); | ||
98 | } | ||
99 | |||
100 | printf( "Checking all files and building index" ); | ||
101 | fflush( stdout ); | ||
102 | cur = 0x59; | ||
103 | for( i = 2; i < argc; i ++ ) | ||
104 | { | ||
105 | struct fwfile *cscalc; | ||
106 | char rdbuf[BUFSIZE]; | ||
107 | struct stat fs; | ||
108 | int n; | ||
109 | |||
110 | fd = open( argv[i], O_RDONLY ); | ||
111 | if( fd < 0 ) | ||
112 | { | ||
113 | perror( "open" ); | ||
114 | return( 1 ); | ||
115 | } | ||
116 | if( fstat( fd, &fs ) != 0 ) | ||
117 | { | ||
118 | perror( "fstat" ); | ||
119 | return( 1 ); | ||
120 | } | ||
121 | |||
122 | if( idx == NULL ) | ||
123 | { | ||
124 | idx = idxstart = calloc( sizeof( struct index ), 1 ); | ||
125 | } | ||
126 | else | ||
127 | { | ||
128 | idx->next = calloc( sizeof( struct index ), 1 ); | ||
129 | idx = idx->next; | ||
130 | } | ||
131 | idx->idx_offset = cur; | ||
132 | idx->realfile = argv[i]; | ||
133 | |||
134 | /* Add one byte so the filename will be ASCIIZ. */ | ||
135 | idx->h = calloc( 1, 1 + sizeof( struct header ) + strlen( basename( argv[i] ) ) ); | ||
136 | idx->h->fnlen = strlen( basename( argv[i] ) ); | ||
137 | idx->h->hsize = idx->h->fnlen + sizeof( struct header ); | ||
138 | memcpy( idx->h->fn, basename( argv[i] ), idx->h->fnlen ); | ||
139 | |||
140 | /* BIOS gets a special flag. */ | ||
141 | if( i == 2 ) | ||
142 | idx->h->bios_flag = 0x10000; | ||
143 | else | ||
144 | idx->h->bios_flag = 0x20000; | ||
145 | |||
146 | /* This flag is always 1 for system files, 5 for addon files. We have only system files. */ | ||
147 | idx->h->sys_flag[0] = 1; | ||
148 | |||
149 | /* No idea what this is for, just typing something. :-) */ | ||
150 | idx->h->time = fs.st_mtime; | ||
151 | |||
152 | /* This is a bit of a hack, but isn't that exactly what we're doing anyway? :-) */ | ||
153 | cscalc = fwstart( "/dev/null" ); | ||
154 | while( ( n = read( fd, rdbuf, BUFSIZE ) ) > 0 ) | ||
155 | { | ||
156 | fwwrite( cscalc, rdbuf, n ); | ||
157 | idx->h->fsize += n; | ||
158 | } | ||
159 | idx->h->checksum = fwcs( cscalc ); | ||
160 | fwclose( cscalc ); | ||
161 | |||
162 | if( i == 2 && idx->h->fsize != 0x40000 ) | ||
163 | printf( "\nWARNING: BIOS file isn't %d bytes long!\n", 0x40000 ); | ||
164 | |||
165 | /* See if this is a hardware magic code? */ | ||
166 | if( ( strcmp( idx->h->fn, "k" ) == 0 ) && idx->h->fsize == 20 ) | ||
167 | { | ||
168 | lseek( fd, 0, SEEK_SET ); | ||
169 | if( read( fd, fhead + 0x3f, 20 ) != 20 ) | ||
170 | { | ||
171 | perror( "read" ); | ||
172 | return( 1 ); | ||
173 | } | ||
174 | } | ||
175 | |||
176 | cur += idx->h->hsize; | ||
177 | close( fd ); | ||
178 | |||
179 | printf( "." ); | ||
180 | fflush( stdout ); | ||
181 | } | ||
182 | printf( " Done!\n" ); | ||
183 | |||
184 | printf( "Building for machine type: %02x %02x %02x %02x\n", | ||
185 | fhead[0x3f], fhead[0x40], fhead[0x41], fhead[0x42] ); | ||
186 | |||
187 | /* Make sure there are at least two 0-bytes to end the index */ | ||
188 | if( pad( cur, 4 ) < 2 ) | ||
189 | { | ||
190 | cur += 2; | ||
191 | } | ||
192 | cur += pad( cur, 4 ); | ||
193 | |||
194 | printf( "Calculating file offsets...\n" ); | ||
195 | for( idx = idxstart; idx; idx = idx->next ) | ||
196 | { | ||
197 | idx->h->offset = cur; | ||
198 | cur += idx->h->fsize; | ||
199 | cur += pad( cur, 4 ); | ||
200 | } | ||
201 | |||
202 | /* Add four bytes for the checksum. */ | ||
203 | cur += 4; | ||
204 | printf( "Expected image size: %d\n", (int) cur ); | ||
205 | |||
206 | printf( "Now creating image headers and index... " ); | ||
207 | fflush( stdout ); | ||
208 | fw = fwstart( argv[1] ); | ||
209 | if( fw == NULL ) | ||
210 | { | ||
211 | perror( "fwstart" ); | ||
212 | return( 1 ); | ||
213 | } | ||
214 | |||
215 | /* Put the name of the BIOS image in the header. */ | ||
216 | strncpy( fhead + 0x37, basename( argv[2] ), 8 ); | ||
217 | |||
218 | /* The image size. */ | ||
219 | int32 = cvt32( cur ); | ||
220 | memcpy( fhead + 0x13, &int32, 4 ); | ||
221 | |||
222 | /* Write the image header. */ | ||
223 | fwwrite( fw, fhead, sizeof( fhead ) ); | ||
224 | |||
225 | for( idx = idxstart; idx; idx = idx->next ) | ||
226 | { | ||
227 | /* Save this one now, we don't want to use it in swapped byte order. */ | ||
228 | int size = idx->h->hsize; | ||
229 | |||
230 | verify_offset( fw->fd, idx->idx_offset ); | ||
231 | |||
232 | cvtheader( idx->h ); | ||
233 | fwwrite( fw, idx->h, size ); | ||
234 | cvtheader( idx->h ); | ||
235 | } | ||
236 | |||
237 | /* There should be at least two 0-bytes at the end of the index. */ | ||
238 | if( pad( lseek( fw->fd, 0, SEEK_CUR ), 4 ) < 2 ) | ||
239 | { | ||
240 | u_int16_t zero = 0; | ||
241 | fwwrite( fw, &zero, 2 ); | ||
242 | } | ||
243 | |||
244 | printf( "Done!\n" ); | ||
245 | |||
246 | printf( "Writing the files...\n" ); | ||
247 | for( idx = idxstart; idx; idx = idx->next ) | ||
248 | { | ||
249 | char rdbuf[BUFSIZE]; | ||
250 | int n; | ||
251 | |||
252 | printf( " %s\n", idx->h->fn ); | ||
253 | |||
254 | /* Since we opened this same file a few moments ago, let's assume it can't go wrong. */ | ||
255 | fd = open( idx->realfile, O_RDONLY ); | ||
256 | |||
257 | /* Try to start every file at a 4-byte boundary. */ | ||
258 | fwpad( fw, 4 ); | ||
259 | |||
260 | verify_offset( fw->fd, idx->h->offset ); | ||
261 | |||
262 | while( ( n = read( fd, rdbuf, BUFSIZE ) ) > 0 ) | ||
263 | { | ||
264 | if( fwwrite( fw, rdbuf, n ) <= 0 ) | ||
265 | { | ||
266 | perror( "fwwrite" ); | ||
267 | return( 1 ); | ||
268 | } | ||
269 | } | ||
270 | |||
271 | close( fd ); | ||
272 | } | ||
273 | |||
274 | fwpad( fw, 4 ); | ||
275 | |||
276 | int32 = fwcs( fw ); | ||
277 | int32 = cvt32( int32 ); | ||
278 | fwwrite( fw, &int32, 4 ); | ||
279 | |||
280 | verify_offset( fw->fd, cur ); | ||
281 | |||
282 | /* This is actually not very likely to go wrong... */ | ||
283 | if( fwcs( fw ) == 0 ) | ||
284 | { | ||
285 | printf( "End checksum of file is 0, good!\n" ); | ||
286 | } | ||
287 | else | ||
288 | { | ||
289 | fprintf( stderr, "WARNING: The file checksum is not right!\n" ); | ||
290 | } | ||
291 | |||
292 | fwclose( fw ); | ||
293 | |||
294 | return( 0 ); | ||
295 | } | ||
296 | |||
297 | struct fwfile *fwstart( char *fn ) | ||
298 | { | ||
299 | struct fwfile *fw; | ||
300 | |||
301 | /* Using calloc because it gives zeroed mem. */ | ||
302 | fw = calloc( 1, sizeof( struct fwfile ) ); | ||
303 | fw->fd = open( fn, O_RDWR | O_CREAT | O_TRUNC, 0644 ); | ||
304 | fw->cs = 0xfffe1000; | ||
305 | |||
306 | if( fw->fd < 0 ) | ||
307 | { | ||
308 | free( fw ); | ||
309 | return( NULL ); | ||
310 | } | ||
311 | |||
312 | return( fw ); | ||
313 | } | ||
314 | |||
315 | int fwwrite( struct fwfile *fw, void *data, int size ) | ||
316 | { | ||
317 | if( write( fw->fd, data, size ) != size ) | ||
318 | return( -1 ); | ||
319 | |||
320 | /* If the checksum buffer isn't empty, try to empty it first. */ | ||
321 | if( fw->cslen != 0 ) | ||
322 | { | ||
323 | if( size < ( 4 - fw->cslen ) ) | ||
324 | { | ||
325 | memcpy( fw->csbuf.byte + fw->cslen, data, size ); | ||
326 | fw->cslen += size; | ||
327 | return( size ); | ||
328 | } | ||
329 | memcpy( fw->csbuf.byte + fw->cslen, data, ( 4 - fw->cslen ) ); | ||
330 | fw->cs -= cvt32( fw->csbuf.val ); | ||
331 | data += ( 4 - fw->cslen ); | ||
332 | size -= ( 4 - fw->cslen ); | ||
333 | fw->cslen = 0; | ||
334 | } | ||
335 | |||
336 | while( size >= 4 ) | ||
337 | { | ||
338 | memcpy( fw->csbuf.byte, data, 4 ); | ||
339 | fw->cs -= cvt32( fw->csbuf.val ); | ||
340 | data += 4; | ||
341 | size -= 4; | ||
342 | } | ||
343 | |||
344 | if( size > 0 ) | ||
345 | { | ||
346 | fw->csbuf.val = 0; | ||
347 | memcpy( fw->csbuf.byte, data, size ); | ||
348 | fw->cslen = size; | ||
349 | } | ||
350 | |||
351 | return( 1 ); | ||
352 | } | ||
353 | |||
354 | int fwpad( struct fwfile *fw, int align ) | ||
355 | { | ||
356 | int padlen; | ||
357 | char *dat; | ||
358 | |||
359 | padlen = pad( lseek( fw->fd, 0, SEEK_CUR ), align ); | ||
360 | if( padlen == 0 ) | ||
361 | return( 0 ); | ||
362 | |||
363 | dat = calloc( padlen, 1 ); | ||
364 | if( fwwrite( fw, dat, padlen ) < 0 ) | ||
365 | return( -1 ); | ||
366 | else | ||
367 | return( padlen ); | ||
368 | } | ||
369 | |||
370 | u_int32_t fwcs( struct fwfile *fw ) | ||
371 | { | ||
372 | if( fw->cslen != 0 ) | ||
373 | return( fw->cs - fw->csbuf.val ); | ||
374 | else | ||
375 | return( fw->cs ); | ||
376 | } | ||
377 | |||
378 | void fwclose( struct fwfile *fw ) | ||
379 | { | ||
380 | close( fw->fd ); | ||
381 | free( fw ); | ||
382 | } | ||
383 | |||
384 | int pad( off_t offset, int align ) | ||
385 | { | ||
386 | return( ( align - ( offset % align ) ) % align ); | ||
387 | } | ||
388 | |||
389 | char *basename( char *fn ) | ||
390 | { | ||
391 | char *base = fn; | ||
392 | |||
393 | while( *fn ) | ||
394 | { | ||
395 | if( fn[0] == '/' && fn[1] && fn[1] != '/' ) | ||
396 | base = fn + 1; | ||
397 | |||
398 | fn ++; | ||
399 | } | ||
400 | |||
401 | return( base ); | ||
402 | } | ||
403 | |||
404 | void verify_offset( int fd, off_t expect ) | ||
405 | { | ||
406 | off_t cur; | ||
407 | |||
408 | if( ( cur = lseek( fd, 0, SEEK_CUR ) ) != expect ) | ||
409 | { | ||
410 | fprintf( stderr, "BUG: Something went wrong! Current offset: 0x%x, expected offset: 0x%x\n", | ||
411 | (int) cur, (int) expect ); | ||
412 | exit( 1 ); | ||
413 | } | ||
414 | } |