Contents of /trunk/linterm_tools/fw_builder/bundle-tools/fwpack.c
Parent Directory | Revision Log
Revision 658 -
(show annotations)
(download)
Mon Jan 14 16:57:24 2008 UTC (16 years, 8 months ago) by niro
File MIME type: text/plain
File size: 9591 byte(s)
Mon Jan 14 16:57:24 2008 UTC (16 years, 8 months ago) by niro
File MIME type: text/plain
File size: 9591 byte(s)
initial import
1 | /* |
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 | } |