Fix dump_h* utilities on x86_64
[agere_fw_utils.git] / dump_fw.c
blobcc75662fce0c5f781e9fc86fbb053de1e062e588
1 /*
2 * Program to link against Agere FW images, and dump contents to
3 * binary files for loading directly by linux drivers.
5 * Copyright (C) 2008 David Kilroy
7 * This program is free software; you can redistribute it and/or modify
8 * it under the terms of the GNU General Public License as published by
9 * the Free Software Foundation; either version 2 of the License, or
10 * (at your option) any later version.
12 * This program is distributed in the hope that it will be useful,
13 * but WITHOUT ANY WARRANTY; without even the implied warranty of
14 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
15 * GNU General Public License for more details.
17 * You should have received a copy of the GNU General Public License along
18 * with this program; if not, write to the Free Software Foundation, Inc.,
19 * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
22 /* Output format (LE numbers)
24 * vers [6] The text HFW and 3 ASCII digits indicating version
25 * headersize [2] Size of header inc vers, headersize and length
26 * entry point [4] NIC address of entry point
27 * blocks [4] Number of blocks (n)
28 * blk_offset [4] Offset to block data
29 * pdr_offset [4] Offset to PDR data
30 * pri_offset [4] Offset to primary plug data
31 * cpt_offset [4] Offset to compatibility data
32 * signature [arb] firmware signature
33 * Block_1
34 * ..
35 * Block_n
36 * Block_term
37 * pda_1
38 * ..
39 * pda_n
40 * pda_term
41 * pri_1
42 * ..
43 * pri_n
44 * pri_term
45 * compat_1
46 * ..
47 * compat_n
48 * compat_term
50 * Where Block_n is:
51 * addr [4] NIC address to program data
52 * length [2] Number of bytes of data to program
53 * data [arbitrary] Data to program
55 * block term is:
56 * 0xFFFFFFFF [4] BLOCK_END identifier
57 * 0x0000 [2] zero length
59 * pda_n and pri_n are:
60 * id [4] (Primary) PDA identifier
61 * addr [4] Address to program (Primary) PDA
62 * len [4] Number of bytes to program
64 * pda_term and pri_n are:
65 * 0x00000000 [4] PDI_END
66 * 0x00000000 [4]
67 * 0x00000000 [4]
69 * compat_n is:
70 * size [2] Length of LTV - ((n_bytes/2) - 1)
71 * code [2] LTV code - 0xFD21 (FW compatibility range)
72 * 0xFD22 (Modem I/F compatibility range)
73 * 0xFD23 (Controller I/F compatibility range)
74 * role [2] Who this restriction applies to?
75 * 0x00 - 'Supplier'
76 * 0x01 - 'Actor'
77 * id [2]
78 * spec_1 Specifications
79 * ...
80 * spec_20
82 * spec_n is:
83 * variant [2]
84 * bottom [2]
85 * top [2]
87 * There is more information available in the driver. In particular
88 * whether the block is supposed to be programmed to NV or volatile,
89 * and various flags.
91 * Apart from the header, the output format is compatible with the
92 * spectrum_cs image. The header is arbitrary.
96 #include <stdio.h>
97 #include <stdint.h>
98 #include <stdlib.h>
99 #include <string.h>
100 #include "dhf.h"
102 #define AP_SUFFIX "_ap_fw.bin"
103 #define STA_SUFFIX "_sta_fw.bin"
104 #define VERSION "HFW000"
106 /* 0xAABB to 0xBBAA */
107 #define swap_bytes_16(value) \
108 ((((value) >> 8) & 0xFF) | \
109 (((value) & 0xFF) << 8))
110 /* 0xAABBCCDD to 0xDDCCBBAA */
111 #define reverse_bytes_32(value) \
112 ((((value) >> 24) & 0x0000FF) | \
113 (((value) >> 8) & 0x00FF00) | \
114 (((value) << 8) & 0xFF0000) | \
115 (((value) & 0xFF) << 24))
116 /* 0xAABBCCDD to 0xBBAADDCC */
117 #define swap_bytes_32(value) \
118 ((((value) >> 8) & 0x00FF00FF) | \
119 (((value) << 8) & 0xFF00FF00))
120 /* 0xAABBCCDD to 0xCCDDAABB */
121 #define swap_words_32(value) \
122 ((((value) >> 16) & 0x0000FFFF) | \
123 (((value) << 16) & 0xFFFF0000))
125 /* address 0 1 2 3 */
126 /* Pure LE stores 0x12345678 as 0x78 0x56 0x34 0x12 */
127 /* Pure BE stores 0x12345678 as 0x12 0x34 0x56 0x78 */
128 /* BEW+LEB stores 0x12345678 as 0x34 0x12 0x78 0x56 */
129 /* LEW+BEB stores 0x12345678 as 0x56 0x78 0x12 0x34 */
130 int host_bytes_in_word_be = 0;
131 int host_words_in_dword_be = 0;
133 #define host_to_le16(value) \
134 (host_bytes_in_word_be ? swap_bytes_16(value) : (value))
135 #define host_to_le32(value) \
136 (host_words_in_dword_be ? \
137 (host_bytes_in_word_be ? reverse_bytes_32(value) \
138 : swap_bytes_32(value)) : \
139 (host_bytes_in_word_be ? swap_words_32(value) : (value)))
141 #define le16_to_host(value) \
142 (host_bytes_in_word_be ? swap_bytes_16(value) : (value))
143 #define le32_to_host(value) \
144 (host_words_in_dword_be ? \
145 (host_bytes_in_word_be ? reverse_bytes_32(value) \
146 : swap_bytes_32(value)) : \
147 (host_bytes_in_word_be ? swap_words_32(value) : (value)))
149 /* Use C99 exact width types */
150 typedef uint32_t u32;
151 typedef uint16_t u16;
152 typedef uint8_t u8;
154 /* Checking endianess at runtime because performance isn't an issue,
155 * and I'd rather not add a configure step since this is slotting into the
156 * Agere source code. */
157 void check_endianess(void) {
158 union {
159 u32 dword;
160 u16 word[2];
161 u8 byte[4];
162 } data;
164 data.dword = 0x12345678;
165 if (data.word[0] == 0x1234) {
166 host_words_in_dword_be = 1;
168 else if (data.word[0] == 0x5678) {
169 host_words_in_dword_be = 0;
170 } else {
171 fprintf(stderr, "Can't determine endianess of host!\n");
172 exit(1);
175 data.word[0] = 0x1234;
176 if (data.byte[0] == 0x12) {
177 host_bytes_in_word_be = 1;
178 } else if (data.byte[0] == 0x34) {
179 host_bytes_in_word_be = 0;
180 } else {
181 fprintf(stderr, "Can't determine endianess of host!\n");
184 if (host_bytes_in_word_be == host_words_in_dword_be) {
185 fprintf (stdout, "Detected %s host\n",
186 host_bytes_in_word_be ? "big endian" : "little endian");
187 } else {
188 fprintf (stdout, "Detected host with mixed endianess\n");
190 return;
193 size_t count_blocks(memimage *image)
195 #if __wl_lkm < 718
196 # define MEMBLOCK memblock
197 # define BLKSIZE p->size
198 # define IF_HAVE_SEGMENT
199 #else
200 # define MEMBLOCK CFG_PROG_STRCT
201 # define BLKSIZE p->len
202 # define IF_HAVE_SEGMENT if (p->segment_size)
203 #endif
205 MEMBLOCK *p = image->codep;
206 size_t count = 0;
207 while (BLKSIZE)
209 /* Ignore zero data segments which will not be written */
210 IF_HAVE_SEGMENT
212 count++;
214 p++;
216 return count;
220 size_t count_pdr(plugrecord *r)
222 size_t count = 0;
223 if (r)
225 while (r->code)
227 count++;
228 r++;
231 return count;
234 size_t acc_block_size(memimage *image)
236 #if __wl_lkm < 718
237 # define MEMBLOCK memblock
238 # define BLKSIZE p->size
239 # define SEGSIZE p->size
240 # define IF_HAVE_SEGMENT
241 #else
242 # define MEMBLOCK CFG_PROG_STRCT
243 # define BLKSIZE p->len
244 # define SEGSIZE p->segment_size
245 # define IF_HAVE_SEGMENT if (p->segment_size)
246 #endif
248 MEMBLOCK *p = image->codep;
249 size_t len = 0;
250 while (BLKSIZE)
252 /* Ignore zero data segments which will not be written */
253 IF_HAVE_SEGMENT
255 len += SEGSIZE;
257 p++;
259 return len;
262 void dump_blocks(FILE* f, memimage *image)
264 #if __wl_lkm < 718
265 # define MEMBLOCK memblock
266 # define BLKSIZE p->size
267 # define SEGSIZE p->size
268 # define NICADDR p->addr
269 # define SEGDATA &(p->data[4]) /* Avoid initial CRC */
270 # define IF_HAVE_SEGMENT
271 #else
272 # define MEMBLOCK CFG_PROG_STRCT
273 # define BLKSIZE p->len
274 # define SEGSIZE p->segment_size
275 # define NICADDR p->nic_addr
276 # define SEGDATA p->host_addr
277 # define IF_HAVE_SEGMENT if (p->segment_size)
278 #endif
280 MEMBLOCK *p = image->codep;
281 u8 block_hdr[sizeof(NICADDR) + sizeof(SEGSIZE)];
282 u32 *addr = (u32 *) &block_hdr[0];
283 u16 *size = (u16 *) &block_hdr[sizeof(NICADDR)];
285 while (BLKSIZE)
287 IF_HAVE_SEGMENT
289 /* There is data to program in this block */
290 *addr = host_to_le32(NICADDR);
291 *size = host_to_le16(SEGSIZE);
292 fwrite (&block_hdr, 1, sizeof(block_hdr), f);
293 fwrite (SEGDATA, 1, SEGSIZE, f);
295 p++;
297 *addr = host_to_le32(0xFFFFFFFFu); /* Agree with spectrum BLOCK_END */
298 *size = host_to_le16(0u);
300 fwrite (&block_hdr, 1, sizeof(block_hdr), f);
302 return;
305 void dump_pdr(FILE *f, plugrecord *r)
307 u8 pdr[sizeof(r->code) + sizeof(r->addr) + sizeof(r->len)];
308 u32 *code = (u32*) &pdr[0];
309 u32 *addr = (u32*) &pdr[sizeof(r->code)];
310 u32 *len = (u32*) &pdr[sizeof(r->code) + sizeof(r->addr)];
312 if (!r)
313 goto terminate;
315 while (r->code)
317 *code = host_to_le32(r->code);
318 *addr = host_to_le32(r->addr);
319 *len = host_to_le32(r->len);
320 fwrite(&pdr, 1, sizeof(pdr), f);
321 r++;
323 terminate:
324 /* Terminate the PDR list */
325 *code = 0;
326 *addr = 0;
327 *len = 0;
328 fwrite(&pdr, 1, sizeof(pdr), f);
329 return;
332 void dump_compat(FILE *f, CFG_RANGE20_STRCT *c)
334 #if __wl_lkm < 718
335 #define VARIANT_STRUCT variant
336 #define VARIANT_NO number
337 #else
338 #define VARIANT_STRUCT var_rec
339 #define VARIANT_NO variant
340 #endif
341 u8 hdr[sizeof(c->id) + sizeof(c->typ) + sizeof(c->role) + sizeof(c->id)];
342 u8 spec[sizeof(c->VARIANT_STRUCT[0])];
343 u16 *len = (u16*) &hdr[0];
344 u16 *typ = (u16*) &hdr[sizeof(c->len)];
345 u16 *role = (u16*) &hdr[sizeof(c->len) + sizeof(c->typ)];
346 u16 *id = (u16*) &hdr[sizeof(c->len) + sizeof(c->typ) + sizeof(c->role)];
347 u16 *variant = (u16*) &spec[0];
348 u16 *bottom = (u16*) &spec[sizeof(c->VARIANT_STRUCT[0].VARIANT_NO)];
349 u16 *top = (u16*) &spec[sizeof(c->VARIANT_STRUCT[0].VARIANT_NO) + sizeof(c->VARIANT_STRUCT[0].bottom)];
350 int i;
352 while(c->len)
354 *len = host_to_le16(c->len);
355 *typ = host_to_le16(c->typ);
356 *role = host_to_le16(c->role);
357 *id = host_to_le16(c->id);
358 fwrite(&hdr, 1, sizeof(hdr), f);
360 for (i = 0; i < sizeof(c->VARIANT_STRUCT)/sizeof(c->VARIANT_STRUCT[0]); i++)
362 *variant = host_to_le16(c->VARIANT_STRUCT[i].VARIANT_NO);
363 *bottom = host_to_le16(c->VARIANT_STRUCT[i].bottom);
364 *top = host_to_le16(c->VARIANT_STRUCT[i].top);
365 fwrite(&spec, 1, sizeof(spec), f);
368 c++;
371 /* sentinel */
372 memset(&hdr[0], 0, sizeof(hdr));
373 memset(&spec[0], 0, sizeof(spec));
374 fwrite(&hdr, 1, sizeof(hdr), f);
375 for (i = 0; i < sizeof(c->VARIANT_STRUCT)/sizeof(c->VARIANT_STRUCT[0]); i++)
377 fwrite(&spec, 1, sizeof(spec), f);
380 return;
383 void dump_image(FILE* f, memimage *image)
385 #if __wl_lkm < 722
386 #define PDA pdaplug
387 #define PRI priplug
388 #else
389 #define PDA place_holder_1
390 #define PRI place_holder_2
391 #endif
392 u32 image_header[6];
393 u32 blocks = count_blocks(image);
394 u32 blk_offset = 0; /* Immediately after header */
395 u32 pdr_offset = (acc_block_size(image) +
396 ((blocks + 1) * (sizeof(u32) + sizeof(u16))));
397 u32 pri_offset = pdr_offset +
398 ((count_pdr(image->PDA) + 1) * sizeof(u32) * 3);
399 u32 cpt_offset = pri_offset +
400 ((count_pdr(image->PRI) + 1) * sizeof(u32) * 3);
401 u16 headersize = ((sizeof(VERSION)-1) +
402 sizeof(u16) +
403 (sizeof(u32)*6)
404 #if __wl_lkm >= 718
405 + sizeof(image->signature)
406 #endif
408 u32 *ptr = &image_header[0];
409 fwrite (VERSION, 1, sizeof(VERSION)-1, f);
410 headersize = host_to_le16(headersize);
411 fwrite (&headersize, 1, sizeof(headersize), f);
413 *ptr = host_to_le32(image->execution);
414 ptr++;
415 *ptr = host_to_le32(blocks);
416 ptr++;
417 *ptr = host_to_le32(blk_offset);
418 ptr++;
419 *ptr = host_to_le32(pdr_offset);
420 ptr++;
421 *ptr = host_to_le32(pri_offset);
422 ptr++;
423 *ptr = host_to_le32(cpt_offset);
424 ptr++;
425 fwrite (&image_header, 1, sizeof(image_header), f);
426 #if __wl_lkm >= 718
427 fwrite (&image->signature, 1, sizeof(image->signature), f);
428 #endif
430 dump_blocks(f, image);
431 dump_pdr(f, image->PDA);
432 dump_pdr(f, image->PRI);
433 dump_compat(f, image->compat);
435 return;
438 #if __wl_lkm < 722
439 extern memimage ap;
440 extern memimage station;
441 #else
442 extern memimage fw_image;
443 #define ap fw_image
444 #define station fw_image
445 #endif
447 int main (int argc, char** argv)
449 char *ap_filename;
450 char *sta_filename;
451 FILE *ap_file;
452 FILE *sta_file;
453 size_t len;
454 int rc = 0;
456 if (argc < 2)
458 printf("Please specify a root filename.\n"
459 "%s will be appended for primary firmware\n"
460 "%s will be appended for secondary firmaware\n",
461 AP_SUFFIX, STA_SUFFIX);
462 return 1;
465 check_endianess();
467 len = strlen(argv[1]);
469 if (ap.identity->comp_id != COMP_ID_FW_AP)
470 goto sta;
472 ap_filename = malloc(len + sizeof(AP_SUFFIX) + 1);
473 if (!ap_filename)
475 fprintf(stderr, "Out of memory\n");
476 return 1;
478 strncpy(ap_filename, argv[1], len);
479 ap_filename[len] = 0;
480 strcat(ap_filename, AP_SUFFIX);
482 ap_file = fopen(ap_filename, "w");
483 if (!ap_file)
485 fprintf(stderr, "Can't open %s for writing\n", ap_filename);
486 rc = 1;
488 else
490 dump_image(ap_file, &ap);
491 fclose(ap_file);
492 fprintf (stdout, "Written %s\n", ap_filename);
494 free(ap_filename);
496 sta:
497 if (station.identity->comp_id != COMP_ID_FW_STA)
498 goto out;
500 sta_filename = malloc(len + sizeof(STA_SUFFIX) + 1);
501 if (!sta_filename)
503 fprintf(stderr, "Out of memory\n");
504 return 1;
506 strncpy(sta_filename, argv[1], len);
507 sta_filename[len] = 0;
508 strcat(sta_filename,STA_SUFFIX);
510 sta_file = fopen(sta_filename,"w");
511 if (!sta_file)
513 fprintf(stderr, "Can't open %s for writing\n", sta_filename);
514 rc = 1;
516 else
518 dump_image(sta_file, &station);
519 fclose(sta_file);
520 fprintf (stdout, "Written %s\n", sta_filename);
522 free(sta_filename);
524 out:
525 return rc;