Read AP fw ident better from hfwget
[agere_fw_utils.git] / dump_fw.c
blob67e389bd6d66734760aacef24a0f77b6620db6f0
1 /*
2 * Program to link against Agere FW images, and dump contents to
3 * binary files for loading directly by linux drivers.
5 *
6 */
8 /* Output format (LE numbers)
10 * vers [6] The text HFW and 3 ASCII digits indicating version
11 * headersize [2] Size of header inc vers, headersize and length
12 * entry point [4] NIC address of entry point
13 * blocks [4] Number of blocks (n)
14 * blk_offset [4] Offset to block data
15 * pdr_offset [4] Offset to PDR data
16 * pri_offset [4] Offset to primary plug data
17 * cpt_offset [4] Offset to compatibility data
18 * signature [arb] firmware signature
19 * Block_1
20 * ..
21 * Block_n
22 * Block_term
23 * pda_1
24 * ..
25 * pda_n
26 * pda_term
27 * pri_1
28 * ..
29 * pri_n
30 * pri_term
31 * compat_1
32 * ..
33 * compat_n
34 * compat_term
36 * Where Block_n is:
37 * addr [4] NIC address to program data
38 * length [2] Number of bytes of data to program
39 * data [arbitrary] Data to program
41 * block term is:
42 * 0xFFFFFFFF [4] BLOCK_END identifier
43 * 0x0000 [2] zero length
45 * pda_n and pri_n are:
46 * id [4] (Primary) PDA identifier
47 * addr [4] Address to program (Primary) PDA
48 * len [4] Number of bytes to program
50 * pda_term and pri_n are:
51 * 0x00000000 [4] PDI_END
52 * 0x00000000 [4]
53 * 0x00000000 [4]
55 * compat_n is:
56 * size [2] Length of LTV - ((n_bytes/2) - 1)
57 * code [2] LTV code - 0xFD21 (FW compatibility range)
58 * 0xFD22 (Modem I/F compatibility range)
59 * 0xFD23 (Controller I/F compatibility range)
60 * role [2] Who this restriction applies to?
61 * 0x00 - 'Supplier'
62 * 0x01 - 'Actor'
63 * id [2]
64 * spec_1 Specifications
65 * ...
66 * spec_20
68 * spec_n is:
69 * variant [2]
70 * bottom [2]
71 * top [2]
73 * There is more information available in the driver. In particular
74 * whether the block is supposed to be programmed to NV or volatile,
75 * and various flags.
77 * Apart from the header, the output format is compatible with the
78 * spectrum_cs image. The header is arbitrary.
82 #include <stdio.h>
83 #include <stdint.h>
84 #include <stdlib.h>
85 #include <string.h>
86 #include "dhf.h"
88 #define AP_SUFFIX "_ap_fw.bin"
89 #define STA_SUFFIX "_sta_fw.bin"
90 #define VERSION "HFW000"
92 /* 0xAABB to 0xBBAA */
93 #define swap_bytes_16(value) \
94 ((((value) >> 8) & 0xFF) | \
95 (((value) & 0xFF) << 8))
96 /* 0xAABBCCDD to 0xDDCCBBAA */
97 #define reverse_bytes_32(value) \
98 ((((value) >> 24) & 0x0000FF) | \
99 (((value) >> 8) & 0x00FF00) | \
100 (((value) << 8) & 0xFF0000) | \
101 (((value) & 0xFF) << 24))
102 /* 0xAABBCCDD to 0xBBAADDCC */
103 #define swap_bytes_32(value) \
104 ((((value) >> 8) & 0x00FF00FF) | \
105 (((value) << 8) & 0xFF00FF00))
106 /* 0xAABBCCDD to 0xCCDDAABB */
107 #define swap_words_32(value) \
108 ((((value) >> 16) & 0x0000FFFF) | \
109 (((value) << 16) & 0xFFFF0000))
111 /* address 0 1 2 3 */
112 /* Pure LE stores 0x12345678 as 0x78 0x56 0x34 0x12 */
113 /* Pure BE stores 0x12345678 as 0x12 0x34 0x56 0x78 */
114 /* BEW+LEB stores 0x12345678 as 0x34 0x12 0x78 0x56 */
115 /* LEW+BEB stores 0x12345678 as 0x56 0x78 0x12 0x34 */
116 int host_bytes_in_word_be = 0;
117 int host_words_in_dword_be = 0;
119 #define host_to_le16(value) \
120 (host_bytes_in_word_be ? swap_bytes_16(value) : (value))
121 #define host_to_le32(value) \
122 (host_words_in_dword_be ? \
123 (host_bytes_in_word_be ? reverse_bytes_32(value) \
124 : swap_bytes_32(value)) : \
125 (host_bytes_in_word_be ? swap_words_32(value) : (value)))
127 #define le16_to_host(value) \
128 (host_bytes_in_word_be ? swap_bytes_16(value) : (value))
129 #define le32_to_host(value) \
130 (host_words_in_dword_be ? \
131 (host_bytes_in_word_be ? reverse_bytes_32(value) \
132 : swap_bytes_32(value)) : \
133 (host_bytes_in_word_be ? swap_words_32(value) : (value)))
135 /* Use C99 exact width types */
136 typedef uint32_t u32;
137 typedef uint16_t u16;
138 typedef uint8_t u8;
140 /* Checking endianess at runtime because performance isn't an issue,
141 * and I'd rather not add a configure step since this is slotting into the
142 * Agere source code. */
143 void check_endianess(void) {
144 union {
145 u32 dword;
146 u16 word[2];
147 u8 byte[4];
148 } data;
150 data.dword = 0x12345678;
151 if (data.word[0] == 0x1234) {
152 host_words_in_dword_be = 1;
154 else if (data.word[0] == 0x5678) {
155 host_words_in_dword_be = 0;
156 } else {
157 fprintf(stderr, "Can't determine endianess of host!\n");
158 exit(1);
161 data.word[0] = 0x1234;
162 if (data.byte[0] == 0x12) {
163 host_bytes_in_word_be = 1;
164 } else if (data.byte[0] == 0x34) {
165 host_bytes_in_word_be = 0;
166 } else {
167 fprintf(stderr, "Can't determine endianess of host!\n");
170 if (host_bytes_in_word_be == host_words_in_dword_be) {
171 fprintf (stdout, "Detected %s host\n",
172 host_bytes_in_word_be ? "big endian" : "little endian");
173 } else {
174 fprintf (stdout, "Detected host with mixed endianess\n");
176 return;
179 size_t count_blocks(memimage *image)
181 #if __wl_lkm < 718
182 # define MEMBLOCK memblock
183 # define BLKSIZE p->size
184 # define IF_HAVE_SEGMENT
185 #else
186 # define MEMBLOCK CFG_PROG_STRCT
187 # define BLKSIZE p->len
188 # define IF_HAVE_SEGMENT if (p->segment_size)
189 #endif
191 MEMBLOCK *p = image->codep;
192 size_t count = 0;
193 while (BLKSIZE)
195 /* Ignore zero data segments which will not be written */
196 IF_HAVE_SEGMENT
198 count++;
200 p++;
202 return count;
206 size_t count_pdr(plugrecord *r)
208 size_t count = 0;
209 if (r)
211 while (r->code)
213 count++;
214 r++;
217 return count;
220 size_t acc_block_size(memimage *image)
222 #if __wl_lkm < 718
223 # define MEMBLOCK memblock
224 # define BLKSIZE p->size
225 # define SEGSIZE p->size
226 # define IF_HAVE_SEGMENT
227 #else
228 # define MEMBLOCK CFG_PROG_STRCT
229 # define BLKSIZE p->len
230 # define SEGSIZE p->segment_size
231 # define IF_HAVE_SEGMENT if (p->segment_size)
232 #endif
234 MEMBLOCK *p = image->codep;
235 size_t len = 0;
236 while (BLKSIZE)
238 /* Ignore zero data segments which will not be written */
239 IF_HAVE_SEGMENT
241 len += SEGSIZE;
243 p++;
245 return len;
248 void dump_blocks(FILE* f, memimage *image)
250 #if __wl_lkm < 718
251 # define MEMBLOCK memblock
252 # define BLKSIZE p->size
253 # define SEGSIZE p->size
254 # define NICADDR p->addr
255 # define SEGDATA &(p->data[4]) /* Avoid initial CRC */
256 # define IF_HAVE_SEGMENT
257 #else
258 # define MEMBLOCK CFG_PROG_STRCT
259 # define BLKSIZE p->len
260 # define SEGSIZE p->segment_size
261 # define NICADDR p->nic_addr
262 # define SEGDATA p->host_addr
263 # define IF_HAVE_SEGMENT if (p->segment_size)
264 #endif
266 MEMBLOCK *p = image->codep;
267 u8 block_hdr[sizeof(NICADDR) + sizeof(SEGSIZE)];
268 u32 *addr = (u32 *) &block_hdr[0];
269 u16 *size = (u16 *) &block_hdr[sizeof(NICADDR)];
271 while (BLKSIZE)
273 IF_HAVE_SEGMENT
275 /* There is data to program in this block */
276 *addr = host_to_le32(NICADDR);
277 *size = host_to_le16(SEGSIZE);
278 fwrite (&block_hdr, 1, sizeof(block_hdr), f);
279 fwrite (SEGDATA, 1, SEGSIZE, f);
281 p++;
283 *addr = host_to_le32(0xFFFFFFFFu); /* Agree with spectrum BLOCK_END */
284 *size = host_to_le16(0u);
286 fwrite (&block_hdr, 1, sizeof(block_hdr), f);
288 return;
291 void dump_pdr(FILE *f, plugrecord *r)
293 u8 pdr[sizeof(r->code) + sizeof(r->addr) + sizeof(r->len)];
294 u32 *code = (u32*) &pdr[0];
295 u32 *addr = (u32*) &pdr[sizeof(r->code)];
296 u32 *len = (u32*) &pdr[sizeof(r->code) + sizeof(r->addr)];
298 if (!r)
299 goto terminate;
301 while (r->code)
303 *code = host_to_le32(r->code);
304 *addr = host_to_le32(r->addr);
305 *len = host_to_le32(r->len);
306 fwrite(&pdr, 1, sizeof(pdr), f);
307 r++;
309 terminate:
310 /* Terminate the PDR list */
311 *code = 0;
312 *addr = 0;
313 *len = 0;
314 fwrite(&pdr, 1, sizeof(pdr), f);
315 return;
318 void dump_compat(FILE *f, CFG_RANGE20_STRCT *c)
320 #if __wl_lkm < 718
321 #define VARIANT_STRUCT variant
322 #define VARIANT_NO number
323 #else
324 #define VARIANT_STRUCT var_rec
325 #define VARIANT_NO variant
326 #endif
327 u8 hdr[sizeof(c->id) + sizeof(c->typ) + sizeof(c->role) + sizeof(c->id)];
328 u8 spec[sizeof(c->VARIANT_STRUCT[0])];
329 u16 *len = (u16*) &hdr[0];
330 u16 *typ = (u16*) &hdr[sizeof(c->len)];
331 u16 *role = (u16*) &hdr[sizeof(c->len) + sizeof(c->typ)];
332 u16 *id = (u16*) &hdr[sizeof(c->len) + sizeof(c->typ) + sizeof(c->role)];
333 u16 *variant = (u16*) &spec[0];
334 u16 *bottom = (u16*) &spec[sizeof(c->VARIANT_STRUCT[0].VARIANT_NO)];
335 u16 *top = (u16*) &spec[sizeof(c->VARIANT_STRUCT[0].VARIANT_NO) + sizeof(c->VARIANT_STRUCT[0].bottom)];
336 int i;
338 while(c->len)
340 *len = host_to_le16(c->len);
341 *typ = host_to_le16(c->typ);
342 *role = host_to_le16(c->role);
343 *id = host_to_le16(c->id);
344 fwrite(&hdr, 1, sizeof(hdr), f);
346 for (i = 0; i < sizeof(c->VARIANT_STRUCT)/sizeof(c->VARIANT_STRUCT[0]); i++)
348 *variant = host_to_le16(c->VARIANT_STRUCT[i].VARIANT_NO);
349 *bottom = host_to_le16(c->VARIANT_STRUCT[i].bottom);
350 *top = host_to_le16(c->VARIANT_STRUCT[i].top);
351 fwrite(&spec, 1, sizeof(spec), f);
354 c++;
357 /* sentinel */
358 memset(&hdr[0], 0, sizeof(hdr));
359 memset(&spec[0], 0, sizeof(spec));
360 fwrite(&hdr, 1, sizeof(hdr), f);
361 for (i = 0; i < sizeof(c->VARIANT_STRUCT)/sizeof(c->VARIANT_STRUCT[0]); i++)
363 fwrite(&spec, 1, sizeof(spec), f);
366 return;
369 void dump_image(FILE* f, memimage *image)
371 #if __wl_lkm < 722
372 #define PDA pdaplug
373 #define PRI priplug
374 #else
375 #define PDA place_holder_1
376 #define PRI place_holder_2
377 #endif
378 u32 image_header[6];
379 u32 blocks = count_blocks(image);
380 u32 blk_offset = 0; /* Immediately after header */
381 u32 pdr_offset = (acc_block_size(image) +
382 ((blocks + 1) * (sizeof(u32) + sizeof(u16))));
383 u32 pri_offset = pdr_offset +
384 ((count_pdr(image->PDA) + 1) * sizeof(u32) * 3);
385 u32 cpt_offset = pri_offset +
386 ((count_pdr(image->PRI) + 1) * sizeof(u32) * 3);
387 u16 headersize = ((sizeof(VERSION)-1) +
388 sizeof(u16) +
389 (sizeof(u32)*6)
390 #if __wl_lkm >= 718
391 + sizeof(image->signature)
392 #endif
394 u32 *ptr = &image_header[0];
395 fwrite (VERSION, 1, sizeof(VERSION)-1, f);
396 headersize = host_to_le16(headersize);
397 fwrite (&headersize, 1, sizeof(headersize), f);
399 *ptr = host_to_le32(image->execution);
400 ptr++;
401 *ptr = host_to_le32(blocks);
402 ptr++;
403 *ptr = host_to_le32(blk_offset);
404 ptr++;
405 *ptr = host_to_le32(pdr_offset);
406 ptr++;
407 *ptr = host_to_le32(pri_offset);
408 ptr++;
409 *ptr = host_to_le32(cpt_offset);
410 ptr++;
411 fwrite (&image_header, 1, sizeof(image_header), f);
412 #if __wl_lkm >= 718
413 fwrite (&image->signature, 1, sizeof(image->signature), f);
414 #endif
416 dump_blocks(f, image);
417 dump_pdr(f, image->PDA);
418 dump_pdr(f, image->PRI);
419 dump_compat(f, image->compat);
421 return;
424 #if __wl_lkm < 722
425 extern memimage ap;
426 extern memimage station;
427 #else
428 extern memimage fw_image;
429 #define ap fw_image
430 #define station fw_image
431 #endif
433 int main (int argc, char** argv)
435 char *ap_filename;
436 char *sta_filename;
437 FILE *ap_file;
438 FILE *sta_file;
439 size_t len;
440 int rc = 0;
442 if (argc < 2)
444 printf("Please specify a root filename.\n"
445 "%s will be appended for primary firmware\n"
446 "%s will be appended for secondary firmaware\n",
447 AP_SUFFIX, STA_SUFFIX);
448 return 1;
451 check_endianess();
453 len = strlen(argv[1]);
455 if (ap.identity->comp_id != COMP_ID_FW_AP)
456 goto sta;
458 ap_filename = malloc(len + sizeof(AP_SUFFIX) + 1);
459 if (!ap_filename)
461 fprintf(stderr, "Out of memory\n");
462 return 1;
464 strncpy(ap_filename, argv[1], len);
465 ap_filename[len] = 0;
466 strcat(ap_filename, AP_SUFFIX);
468 ap_file = fopen(ap_filename, "w");
469 if (!ap_file)
471 fprintf(stderr, "Can't open %s for writing\n", ap_filename);
472 rc = 1;
474 else
476 dump_image(ap_file, &ap);
477 fclose(ap_file);
478 fprintf (stdout, "Written %s\n", ap_filename);
480 free(ap_filename);
482 sta:
483 if (station.identity->comp_id != COMP_ID_FW_STA)
484 goto out;
486 sta_filename = malloc(len + sizeof(STA_SUFFIX) + 1);
487 if (!sta_filename)
489 fprintf(stderr, "Out of memory\n");
490 return 1;
492 strncpy(sta_filename, argv[1], len);
493 sta_filename[len] = 0;
494 strcat(sta_filename,STA_SUFFIX);
496 sta_file = fopen(sta_filename,"w");
497 if (!sta_file)
499 fprintf(stderr, "Can't open %s for writing\n", sta_filename);
500 rc = 1;
502 else
504 dump_image(sta_file, &station);
505 fclose(sta_file);
506 fprintf (stdout, "Written %s\n", sta_filename);
508 free(sta_filename);
510 out:
511 return rc;