Read AP fw ident better from hfwget
[agere_fw_utils.git] / hfwget.c
blobb00e7478278addc9ab5a541a9cad002c1cacb901
1 /*
2 * Hermes AP firmware extractor for Windows drivers (c) 2003 by Mark Smith
3 * This may be distributed freely under the GPL v2 so long as this copyright
4 * notice is included.
6 * Following modifications (c) 2008 David Kilroy
7 * primary plug data
8 * compatibility info
9 * firmware identification
10 * carry on without filename (wldel48b, and old wlluc48)
11 * binary output format for linux kernel driver
12 * big endian translations
13 * refactorring
15 * These modifications may be distributed freely under the GPL v2 so
16 * long as this copyright notice is included.
18 #include <stddef.h>
19 #include <stdio.h>
20 #include <stdint.h>
21 #include <stdlib.h>
22 #include <memory.h>
23 #include <string.h>
25 #if __STDC_VERSION__>=199901L
26 # include <stdbool.h>
27 #else
28 typedef int bool;
30 # define false 0
31 # define true (!0)
32 #endif
34 /* Typedefs for little and big endian values */
35 typedef uint32_t __le32;
36 typedef uint16_t __le16;
37 typedef uint32_t __be32;
38 typedef uint16_t __be16;
40 /* Driver endianness */
41 typedef uint32_t __de32;
42 typedef uint16_t __de16;
45 /* Typedefs for sized integers */
46 typedef uint32_t u32;
47 typedef uint16_t u16;
48 typedef uint8_t u8;
50 #define ARRAY_SIZE(x) (sizeof(x)/sizeof((x)[0]))
52 /*** Macros to deal with different endianess ***/
54 /* 0xAABB to 0xBBAA */
55 #define swap_bytes_16(value) \
56 ((((value) >> 8) & 0xFF) | \
57 (((value) & 0xFF) << 8))
59 /* 0xAABBCCDD to 0xDDCCBBAA */
60 #define reverse_bytes_32(value) \
61 ((((value) >> 24) & 0x0000FF) | \
62 (((value) >> 8) & 0x00FF00) | \
63 (((value) << 8) & 0xFF0000) | \
64 (((value) & 0xFF) << 24))
66 /* 0xAABBCCDD to 0xBBAADDCC */
67 #define swap_bytes_32(value) \
68 ((((value) >> 8) & 0x00FF00FF) | \
69 (((value) << 8) & 0xFF00FF00))
71 /* 0xAABBCCDD to 0xCCDDAABB */
72 #define swap_words_32(value) \
73 ((((value) >> 16) & 0x0000FFFF) | \
74 (((value) << 16) & 0xFFFF0000))
76 /* address -> 0 1 2 3
77 * Pure LE stores 0x12345678 as 0x78 0x56 0x34 0x12
78 * Pure BE stores 0x12345678 as 0x12 0x34 0x56 0x78
79 * BEW+LEB stores 0x12345678 as 0x34 0x12 0x78 0x56
80 * LEW+BEB stores 0x12345678 as 0x56 0x78 0x12 0x34
82 static bool host_bytes_in_word_be = false;
83 static bool host_words_in_dword_be = false;
84 static bool driver_is_be = false;
86 #define host_to_le16(value) (__le16) \
87 (host_bytes_in_word_be ? swap_bytes_16(value) : (value))
88 #define host_to_le32(value) (__le32) \
89 (host_words_in_dword_be ? \
90 (host_bytes_in_word_be ? reverse_bytes_32(value) \
91 : swap_bytes_32(value)) : \
92 (host_bytes_in_word_be ? swap_words_32(value) : (value)))
94 #define le16_to_host(value) (u16) \
95 (host_bytes_in_word_be ? swap_bytes_16(value) : (value))
96 #define le32_to_host(value) (u32) \
97 (host_words_in_dword_be ? \
98 (host_bytes_in_word_be ? reverse_bytes_32(value) \
99 : swap_bytes_32(value)) : \
100 (host_bytes_in_word_be ? swap_words_32(value) : (value)))
102 #define host_to_be16(value) (__be16) \
103 (host_bytes_in_word_be ? (value) : swap_bytes_16(value))
104 #define host_to_be32(value) (__be32) \
105 (host_words_in_dword_be ? \
106 (host_bytes_in_word_be ? (value) : swap_bytes_32(value)) : \
107 (host_bytes_in_word_be ? swap_words_32(value) \
108 : reverse_bytes_32(value)))
110 #define be16_to_host(value) (u16) \
111 (host_bytes_in_word_be ? (value) : swap_bytes_16(value))
112 #define be32_to_host(value) (u32) \
113 (host_words_in_dword_be ? \
114 (host_bytes_in_word_be ? (value) : swap_bytes_32(value)) : \
115 (host_bytes_in_word_be ? swap_words_32(value) \
116 : reverse_bytes_32(value)))
118 #define driver_to_host_16(value) (u16) \
119 (driver_is_be ? be16_to_host(value) : le16_to_host(value))
120 #define driver_to_host_32(value) (u32) \
121 (driver_is_be ? be32_to_host(value) : le32_to_host(value))
122 #define host_to_driver_16(value) (__de16) \
123 (driver_is_be ? host_to_be16(value) : host_to_le16(value))
124 #define host_to_driver_32(value) (__de32) \
125 (driver_is_be ? host_to_be32(value) : host_to_le32(value))
127 /**** Structures to read image data from driver ****/
129 /* Decode Windows firmware blocks */
130 struct fwblock_wdrv {
131 __le32 offset;
132 __le16 size;
133 __le16 flags;
134 __le32 data_p;
137 /* Decode Mac firmware blocks */
138 struct fwblock_mdrv {
139 __be16 len;
140 __be16 code;
141 __be16 prg_mode;
142 __be16 size;
143 __be32 offset;
144 __be32 flags;
145 __be32 data_p;
148 union fwblock_drv {
149 struct fwblock_wdrv *w;
150 struct fwblock_mdrv *m;
153 struct plugarray_drv {
154 __de32 code;
155 __de32 targ_off;
156 __de32 length;
159 struct ident_info_drv {
160 __de16 size;
161 __de16 code;
162 __de16 comp_id;
163 __de16 variant;
164 __de16 version_major;
165 __de16 version_minor;
168 struct compat_info_drv {
169 __de16 size;
170 __de16 code;
171 __de16 role;
172 __de16 id;
173 struct {
174 __de16 variant;
175 __de16 bottom;
176 __de16 top;
177 } range[20];
180 struct fwtable_drv {
181 __de32 segarray_p;
182 __de32 halfentry;
183 __de32 plugarray_p;
184 __de32 pri_plugarray_p;
185 __de32 compat_p;
186 __de32 ident_p;
189 /*** Structures to use on host. ***/
190 struct fwblock {
191 uint32_t offset;
192 uint16_t size;
193 uint16_t flags;
194 uint8_t *data;
197 struct plugarray {
198 u32 code;
199 u32 targ_off;
200 u32 length;
203 struct ident_info {
204 u16 size;
205 u16 code;
206 u16 comp_id;
207 u16 variant;
208 u16 version_major;
209 u16 version_minor;
212 struct compat_info {
213 u16 size;
214 u16 code;
215 u16 role;
216 u16 id;
217 struct {
218 u16 variant;
219 u16 bottom;
220 u16 top;
221 } range[20];
224 struct fwtable {
225 struct fwblock *segarray;
226 u32 halfentry;
227 struct plugarray *plugarray;
228 struct plugarray *pri_plugarray;
229 struct compat_info *compat;
230 struct ident_info *ident;
233 /* Structure detailing firmware differences between Windows and Mac */
234 struct fw_layout {
235 const int block_prefix; /* Bytes before the start of a firmware block */
236 const size_t lead_block_bytes; /* Bytes used by lead blocks */
237 const size_t datap_offset; /* Offset of data_p in fw_block struct */
238 size_t max_offset; /* No firmware after this offset */
239 ptrdiff_t addr_delta; /* Difference between addresses encoded in the
240 * driver and the file offset of the associated
241 * data */
242 bool mac; /* Use mac structures */
245 static struct fw_layout firmware_layout[] =
247 { /* Windows */
248 4, 0 * sizeof(struct fwblock_wdrv),
249 offsetof(struct fwblock_wdrv, data_p)
251 { /* Mac */
252 0, 1 * sizeof(struct fwblock_mdrv),
253 offsetof(struct fwblock_mdrv, data_p)
257 /* Structure to map firmware identifiers to description strings */
258 static const struct {
259 u16 id;
260 char *comp_string;
261 } compat_table[] =
263 /* Firmware type */
264 { 21, "Primary firmware" },
265 { 22, "Intermediate firmware" },
266 { 31, "Station firmware" },
267 { 32, "AP firmware" },
268 { 0x14B, "AP firmware" },
270 /* Driver type */
271 { 41, "Windows 9x/NT Miniport NDIS 3.1" },
272 { 42, "Packet" },
273 { 43, "DOS ODI" },
274 { 44, "32 bit ODI" },
275 { 45, "Mac OS" },
276 { 46, "Windows CE Miniport" },
277 { 47, "Linux HCF-light based (public domain)" },
278 { 48, "Windows 9x/NT Miniport NDIS 5.0" },
279 { 49, "Linux HCF-library based" },
280 { 50, "QNX" },
281 { 51, "Windows 9x/NT Miniport NDIS 5.0 USB" },
282 { 52, "Windows 9x/NT Miniport NDIS 4.0" },
283 { 53, "VxWorks END Station driver" },
284 { 54, "VxWorks END Access Point driver" },
285 { 55, "Mac OS?" },
286 { 56, "VxWorks END Station/AP driver" },
288 /* Others */
289 { 63, "WaveLAN Station FW update utility" },
290 { 81, "WaveLAN/IEEE AP" },
291 { 83, "WaveLAN/IEEE Ethernet Converter" },
292 { 87, "USB Boot Loader" },
293 { 0xFF, "Unknown" }
296 /* Checking endianess at runtime because performance isn't an issue,
297 * and I'd rather not add a configure step */
298 static void check_endianess(void)
300 union {
301 u32 dword;
302 u16 word[2];
303 u8 byte[4];
304 } data;
306 data.dword = 0x12345678;
307 if (data.word[0] == 0x1234)
309 host_words_in_dword_be = true;
311 else if (data.word[0] == 0x5678)
313 host_words_in_dword_be = false;
315 else
317 fprintf(stderr, "Can't determine endianess of host!\n");
318 exit(EXIT_FAILURE);
321 data.word[0] = 0x1234;
322 if (data.byte[0] == 0x12)
324 host_bytes_in_word_be = true;
326 else if (data.byte[0] == 0x34)
328 host_bytes_in_word_be = false;
330 else
332 fprintf(stderr, "Can't determine endianess of host!\n");
333 exit(EXIT_FAILURE);
336 if (host_bytes_in_word_be == host_words_in_dword_be)
338 fprintf(stdout, "Detected %s host\n",
339 host_bytes_in_word_be ? "big endian" : "little endian");
341 else
343 fprintf(stdout, "Detected host with mixed endianess\n");
345 return;
348 /* Locate firmware by looking for a T???????.HEX filename */
349 static char* find_fw_filename(const u8 *hostdriver, size_t flen, char hexchar)
351 const u8 *p, *end;
352 bool found;
354 /* Find the ?1XXYYZZ.HEX string */
355 p = hostdriver;
356 end = hostdriver + flen;
358 for (found = false; (found == false) && (p != NULL); )
360 p = memchr(p, hexchar, (end - p));
362 if (p != NULL)
364 if (memcmp(".HEX", p + 8, 4) == 0)
366 found = true;
368 else
370 p++;
375 if (p != NULL)
377 printf("Found firmware %s at file offset 0x%08x\n",
378 p, (p - hostdriver));
380 else
382 printf("%c-firmware not found!\n", hexchar);
385 return (char*) p;
388 /* Find the start of the firmware based on a hint as to where the end
389 * of the firmware image is. The start of the firmware image is
390 * defined by a signature. */
391 static u8* find_fw(const u8 *hostdriver, size_t flen,
392 const u8 *signature, size_t slen,
393 const u8 *hint)
395 const u8 *p = hint - slen;
396 bool found = false;
397 size_t i;
399 printf("Searching for firmware from offset 0x%08x, start signature",
400 hint - hostdriver);
401 for (i = 0; i < slen; i++)
403 printf(" %02x", signature[i]);
405 printf("...\n");
407 /* Really should use a mask here, but its not necessary for the moment. */
408 for (found = false; (p > hostdriver) && (found == false); p--)
409 if (memcmp(p, signature, slen) == 0)
410 found = true;
412 if (!found)
414 printf("Signature not found!\n");
415 return NULL;
418 p++;
419 printf("Found signature at file offset 0x%08x\n", p - hostdriver);
421 return (u8*) p;
424 /* Returns a pointer to the PE header */
425 static void* pe_header(const void *data)
427 __le32 *e_lfanew = (__le32 *) (data + 0x3c);
429 /* data + *e_lfanew gives us the NT SIGNATURE
430 * The NT signature is 4 bytes long.
431 * The PE header follows immediately.
433 return (void *)(data + (le32_to_host(*e_lfanew) + 4));
436 /* returns the expected imagebase */
437 static u32 pe_imagebase(const void *data)
439 void *pe_hdr = pe_header(data);
441 return le32_to_host(*((u32 *) (pe_hdr + 0x30)));
444 typedef __be32 mac_cpu_type_t;
445 typedef __be32 mac_cpu_subtype_t;
446 typedef __be32 mac_vmprot_t;
448 struct mach_header {
449 __be32 magic;
450 mac_cpu_type_t cputype;
451 mac_cpu_subtype_t cpusubtype;
452 __be32 filetype;
453 __be32 ncmds;
454 __be32 sizeofcmds;
455 __be32 flags;
458 struct mach_load_command {
459 __be32 cmd;
460 __be32 cmdsize;
463 struct mach_segment_command {
464 __be32 cmd;
465 __be32 cmdsize;
466 char segname[16];
467 __be32 vmaddr;
468 __be32 vmsize;
469 __be32 fileoff;
470 __be32 filesize;
471 mac_vmprot_t maxprot;
472 mac_vmprot_t initprot;
473 __be32 nsects;
474 __be32 flags;
477 struct mach_section {
478 char sectname[16];
479 char segname[16];
480 __be32 addr;
481 __be32 size;
482 __be32 offset;
483 __be32 align;
484 __be32 reloff;
485 __be32 nreloc;
486 __be32 flags;
487 __be32 reserved1;
488 __be32 reserved2;
491 /* Returns the start of the segment that contains the firmware. Also
492 * identifies the end of the section. It appears that the firmware
493 * contains another second copy of our pointers later on (at about
494 * offset 0x12330) which interfere with our search.
496 static const struct mach_section* macho_fw_section(const void *data)
498 const struct mach_header *hdr = data;
499 u32 i, j;
500 const void *p = data + sizeof(struct mach_header);
502 for (i = 0; i < be32_to_host(hdr->ncmds); i++)
504 const struct mach_load_command *load_cmd = p;
506 if (be32_to_host(load_cmd->cmd) == 0x0001)
508 /* LC_SEGMENT */
510 const struct mach_segment_command *seg_cmd = p;
511 p += sizeof(struct mach_segment_command);
512 for (j = 0; j < be32_to_host(seg_cmd->nsects); j++)
514 const struct mach_section *sect = p;
516 if ((strcmp(sect->sectname, "__data") == 0) &&
517 (strcmp(sect->segname, "__DATA") == 0))
519 return sect;
522 p += sizeof(struct mach_section);
526 /* advance to past the load command */
527 p += sizeof(struct mach_load_command);
528 p += be32_to_host(load_cmd->cmdsize);
531 printf("Couldn't find Mach-O __data/__DATA section\n");
532 return NULL;
535 #define MH_MAGIC 0xfeedface /* BE Mach-O magic number */
536 #define MH_CIGAM 0xcefaedfe /* LE Mach-O magic number */
537 #define MH_MAGIC_64 0xfeedfacf /* BE Mach-O 64-bit magic number */
538 #define MH_CIGAM_64 0xcffaedfe /* LE Mach-O 64-bit magic number */
540 /* Validates the Mach-O object file; only accepts 32-bit BE
541 * PPC Mach-O object files because classic AirPort was only
542 * ever used on 32-bit PPC machines.
544 * Returns:
545 * 0 = success
546 * -1 = Not a 32-bit PPC Mach-O object file
547 * -2 = Not a Mach-O object file
549 static int macho_validate(const void *data)
551 const struct mach_header *hdr = data;
553 switch (be32_to_host(hdr->magic))
555 case MH_MAGIC:
556 /* Yay, what we need */
557 break;
558 case MH_MAGIC_64:
559 case MH_CIGAM_64:
560 case MH_CIGAM:
561 /* 64-bit or LE 32-bit, can't use it */
562 return -1;
563 default:
564 /* Not a Mach-O file at all */
565 return -2;
568 /* PPC */
569 if (be32_to_host(hdr->cputype) != 0x12)
570 return -1;
572 /* MH_OBJECT */
573 if (be32_to_host(hdr->filetype) != 0x0001)
574 return -1;
576 return 0;
579 /* Returns a pointer within data, to the fwblock pointing containing a
580 * pointer to addr (in driver address space)*/
581 static u8* find_fwblock_entry(const u8 *data, const struct fw_layout *layout,
582 u32 addr)
584 u32 *p = (u32*) ((unsigned int)(data + layout->max_offset) & 0xFFFFFFFCu);
585 bool found = false;
587 printf("Now searching for driver's firmware block entry (0x%08x)...\n",
588 addr);
590 /* Convert to driver endianness to compare against file data */
591 addr = host_to_driver_32(addr);
593 /* Note that we're not searching each byte position for a match.
594 * This should be fine because the data should have been placed on
595 * a 4-byte boundary */
597 for (found = false; ((u8*)p >= data); p--)
599 if (*p == addr)
601 found = true;
602 break;
606 /* Compensate for the fields before the data_p pointer */
607 p -= layout->datap_offset / sizeof(*p);
609 return (u8*) p;
612 static struct fwtable_drv* find_fwtable_entry(const u8 *data,
613 const struct fw_layout *layout,
614 u32 fwblock)
616 u32 *p = (u32*) ((unsigned int)(data + layout->max_offset) & 0xFFFFFFFCu);
617 struct fwtable_drv *firmware;
618 bool found = false;
620 printf("Looking for main firmware table....\n");
622 /* Convert to driver endianess to compare against file data */
623 fwblock = host_to_driver_32(fwblock);
625 for (found = false; ((u8*)p >= data); p--)
627 if (*p == fwblock)
629 found = true;
630 break;
634 firmware = (struct fwtable_drv *)p;
635 if (found == false)
637 printf("Main table not found - contact Mark!\n");
639 else
641 printf("Found at file offset 0x%08x\n", (u8*)p - data);
643 return firmware;
646 /* Copy all data in firmware block from virtual address space to
647 * mapped file address space.
649 * Also convert from little endian to host endian while we're at it.
650 * Some data will need to be converted back to LE when written out,
651 * but it will be easier than trying to kepp track of the endianness.
653 static void copy_fw_data(struct fwtable *firmware,
654 const struct fwtable_drv *driver_fw,
655 const void *data,
656 const struct fw_layout *layout)
658 union fwblock_drv block;
659 const struct plugarray_drv *pdr;
660 const struct plugarray_drv *pri;
661 const struct ident_info_drv *ident;
662 const struct compat_info_drv *compat;
664 /* Static data structures to write host endian data to */
665 static struct fwblock block_data[4] = {{ 0, 0, 0, 0 }};
666 static struct plugarray plug_data[64] = {{ 0, 0, 0 }};
667 static struct plugarray pri_plug_data[64] = {{ 0, 0, 0 }};
668 static struct ident_info ident_data = { 0, 0, 0, 0, 0, 0 };
669 static struct compat_info compat_data[16] = {{ 0 }};
670 u32 delta = (u32)data - layout->addr_delta;
671 size_t i;
673 /* Calculate valid pointers to driver data */
674 block.w = (struct fwblock_wdrv *)
675 (driver_to_host_32(driver_fw->segarray_p) + delta);
676 pdr = (struct plugarray_drv *)
677 (driver_to_host_32(driver_fw->plugarray_p) + delta);
678 pri = (struct plugarray_drv *)
679 (driver_to_host_32(driver_fw->pri_plugarray_p) + delta);
680 ident = (struct ident_info_drv *)
681 (driver_to_host_32(driver_fw->ident_p) + delta);
682 compat = (struct compat_info_drv *)
683 (driver_to_host_32(driver_fw->compat_p) + delta);
685 /* Setup pointers to host data */
686 firmware->segarray = &block_data[0];
687 firmware->plugarray = &plug_data[0];
688 firmware->pri_plugarray = &pri_plug_data[0];
689 firmware->compat = &compat_data[0];
690 firmware->ident = &ident_data;
692 firmware->halfentry = driver_to_host_32(driver_fw->halfentry);
694 for (i = 0; i < ARRAY_SIZE(block_data); i++)
696 u32 offset = layout->mac ? driver_to_host_32(block.m[i].offset) :
697 driver_to_host_32(block.w[i].offset);
698 u32 data_p = ((layout->mac ? driver_to_host_32(block.m[i].data_p) :
699 driver_to_host_32(block.w[i].data_p)) +
700 layout->block_prefix);
701 u16 size = layout->mac ? driver_to_host_16(block.m[i].size) :
702 driver_to_host_16(block.w[i].size);
703 u16 flags = layout->mac ? (u16) driver_to_host_32(block.m[i].flags) :
704 driver_to_host_16(block.w[i].flags);
706 if (offset != 0)
708 firmware->segarray[i].data = (uint8_t *)(data_p + delta);
709 firmware->segarray[i].offset = offset;
710 firmware->segarray[i].size = size;
711 firmware->segarray[i].flags = flags;
712 printf("Segment: %d File offs: 0x%08x Target mem: 0x%08x "
713 "Length 0x%04x%s\n",
715 (void *)(&block.w[i]) - data,
716 firmware->segarray[i].offset,
717 firmware->segarray[i].size,
718 (firmware->segarray[i].size == 0) ? " (ignored)" : "");
720 else
722 firmware->segarray[i].data = NULL;
723 firmware->segarray[i].offset = 0;
724 firmware->segarray[i].size = 0;
725 firmware->segarray[i].flags = 0;
726 break;
730 printf("Production Data plugrecords at file offset 0x%08x\n",
731 (void *)pdr - data);
732 printf("Primary plugrecords at file offset 0x%08x\n",
733 (void *)pri - data);
734 printf("Compatibility info at file offset 0x%08x\n",
735 (void *)compat - data);
736 printf("Identity info at file offset 0x%08x\n",
737 (void *)ident - data);
739 /* Copy plugarray */
740 for (i = 0; (pdr[i].code != 0) && (i < ARRAY_SIZE(plug_data)); i++)
742 firmware->plugarray[i].code = driver_to_host_32(pdr[i].code);
743 firmware->plugarray[i].targ_off = driver_to_host_32(pdr[i].targ_off);
744 firmware->plugarray[i].length = driver_to_host_32(pdr[i].length);
747 /* Copy pri_array */
748 for (i = 0; (pri[i].code != 0) && (i < ARRAY_SIZE(pri_plug_data)); i++)
750 firmware->pri_plugarray[i].code = driver_to_host_32(pri[i].code);
751 firmware->pri_plugarray[i].targ_off =
752 driver_to_host_32(pri[i].targ_off);
753 firmware->pri_plugarray[i].length = driver_to_host_32(pri[i].length);
756 /* Copy identifiers */
757 firmware->ident->size = driver_to_host_16(ident->size);
758 firmware->ident->code = driver_to_host_16(ident->code);
759 firmware->ident->comp_id = driver_to_host_16(ident->comp_id);
760 firmware->ident->variant = driver_to_host_16(ident->variant);
761 firmware->ident->version_major = driver_to_host_16(ident->version_major);
762 firmware->ident->version_minor = driver_to_host_16(ident->version_minor);
764 /* Copy compat_info */
765 for (i = 0; (compat[i].size != 0) && (i < ARRAY_SIZE(compat_data)); i++)
767 size_t j;
769 firmware->compat[i].size = driver_to_host_16(compat[i].size);
770 firmware->compat[i].code = driver_to_host_16(compat[i].code);
771 firmware->compat[i].role = driver_to_host_16(compat[i].role);
772 firmware->compat[i].id = driver_to_host_16(compat[i].id);
773 for (j = 0; j < ARRAY_SIZE(compat[i].range); j++)
775 firmware->compat[i].range[j].variant =
776 driver_to_host_16(compat[i].range[j].variant);
777 firmware->compat[i].range[j].bottom =
778 driver_to_host_16(compat[i].range[j].bottom);
779 firmware->compat[i].range[j].top =
780 driver_to_host_16(compat[i].range[j].top);
785 static void print_fw_ident(const struct fwtable *firmware)
787 size_t i;
789 if ((firmware->ident->code == 0xFD20u) || /* FW_IDENTITY */
790 (firmware->ident->code == 0x014Bu)) /* AP_IDENTITY */
792 for (i = 0; i < ARRAY_SIZE(compat_table); i++)
794 if (compat_table[i].id == firmware->ident->comp_id)
795 break;
797 if (i == ARRAY_SIZE(compat_table))
798 i--;
800 printf("Firmware identity: %s, Variant %d Version %d.%2d\n",
801 compat_table[i].comp_string,
802 firmware->ident->variant,
803 firmware->ident->version_major,
804 firmware->ident->version_minor);
808 static int write_hermesap_fw(FILE *output, const struct fwtable *firmware)
810 unsigned int i;
812 fprintf(output, "HFW1\nENTRY %08X\n", firmware->halfentry * 2);
814 for (i = 0; firmware->plugarray[i].code != 0; i++)
816 fprintf(output, "PLUG %08X %08X %08X\n",
817 firmware->plugarray[i].code,
818 firmware->plugarray[i].targ_off,
819 firmware->plugarray[i].length);
822 for (i = 0; firmware->segarray[i].offset != 0; i++)
824 u16 j;
826 if (i != 0)
827 fprintf(output, "\n");
828 fprintf(output, "SEG %08X %08X %08X",
829 firmware->segarray[i].offset,
830 firmware->segarray[i].size,
833 for (j = 0; j < firmware->segarray[i].size; j += 2)
835 if ((j % 16) == 0)
836 fprintf(output, "\nDATA");
838 fprintf(output, " %02X%02X",
839 firmware->segarray[i].data[j],
840 firmware->segarray[i].data[j + 1]);
844 fputc('\n', output);
846 return 0;
849 static size_t count_blocks(const struct fwblock *first_block)
851 const struct fwblock *block = first_block;
852 size_t count = 0;
854 while (block->offset != 0)
856 if (block->size > 0)
857 count++;
858 block++;
860 return count;
863 static size_t acc_block_size(const struct fwblock *first_block)
865 const struct fwblock *block = first_block;
866 size_t len = 0;
868 while (block->offset != 0)
870 len += block->size;
871 block++;
873 return len;
876 static size_t count_pdr(const struct plugarray *first_pdr)
878 const struct plugarray *pdr = first_pdr;
879 size_t count = 0;
881 while (pdr->code)
883 count++;
884 pdr++;
886 return count;
889 static void dump_blocks(FILE *output, const struct fwblock *first_block)
891 const struct fwblock *block = first_block;
892 u8 block_hdr[sizeof(block->offset) + sizeof(block->size)];
893 __le32 *addr = (__le32 *) &block_hdr[0];
894 __le16 *size = (__le16 *) &block_hdr[sizeof(block->offset)];
896 while (block->offset != 0)
898 if (block->size > 0)
900 *addr = host_to_le32(block->offset);
901 *size = host_to_le16(block->size);
903 (void)fwrite(&block_hdr, 1, sizeof(block_hdr), output);
904 (void)fwrite(block->data, 1, block->size, output); /* data */
906 block++;
908 *addr = host_to_le32(0xFFFFFFFFu); /* set block end */
909 *size = host_to_le16(0);
910 (void)fwrite(&block_hdr, 1, sizeof(block_hdr), output);
913 static void dump_pdr(FILE *output, const struct plugarray *first_pdr)
915 const struct plugarray *r = first_pdr;
916 u8 pdr[sizeof(r->code) + sizeof(r->targ_off) + sizeof(r->length)];
917 __le32 *code = (__le32*) &pdr[0];
918 __le32 *addr = (__le32*) &pdr[sizeof(r->code)];
919 __le32 *len = (__le32*) &pdr[sizeof(r->code) + sizeof(r->targ_off)];
921 if (r)
923 while (r->code != 0)
925 *code = host_to_le32(r->code);
926 *addr = host_to_le32(r->targ_off);
927 *len = host_to_le32(r->length);
928 (void)fwrite(&pdr, 1, sizeof(pdr), output);
929 r++;
932 *code = *addr = *len = host_to_le32(0); /* pdr end */
933 (void)fwrite(&pdr, 1, sizeof(pdr), output);
936 static void dump_compat(FILE *output, const struct compat_info *compat)
938 __le16 buf[sizeof(*compat) / sizeof(__le16)];
940 /* Dump non-zero length blocks.
941 * No need to reformat. */
942 while (compat->size != 0)
944 size_t i;
946 for (i = 0; i < ARRAY_SIZE(buf); i++)
948 buf[i] = host_to_le16(((u16 *) compat)[i]);
950 (void)fwrite(buf, 1, sizeof(buf), output);
951 compat++;
953 /* sentinel */
954 memset(&buf, 0, sizeof(buf));
955 (void)fwrite(&buf, 1, sizeof(buf), output);
958 #define VERSION "HFW000"
959 /* Returns zero, or a negative number to indicate an error */
960 static int write_kernel_fw(FILE *output, const struct fwtable *firmware)
962 /* Note: does not deal with BE/LE issues */
963 u32 image_header[6];
964 u32 *ptr;
965 u16 headersize = ((sizeof(VERSION) - 1) + sizeof(u16) + (sizeof(u32) * 6));
966 u32 blocks = count_blocks(&firmware->segarray[0]);
967 u32 blk_offset = 0; /* Immediately after header */
968 u32 pdr_offset = (acc_block_size(&firmware->segarray[0]) +
969 ((blocks + 1) * (sizeof(u32) + sizeof(u16))));
970 u32 pri_offset = pdr_offset +
971 ((count_pdr(&firmware->plugarray[0]) + 1) * sizeof(u32) * 3);
972 u32 cpt_offset = pri_offset +
973 ((count_pdr(&firmware->pri_plugarray[0]) + 1) * sizeof(u32) * 3);
976 (void)fwrite(VERSION, 1, sizeof(VERSION) - 1, output);
978 headersize = host_to_le16(headersize);
979 (void)fwrite(&headersize, 1, sizeof(headersize), output);
981 ptr = &image_header[0];
982 *ptr = host_to_le32(firmware->halfentry); /* entrypoint */
983 ptr++;
984 *ptr = host_to_le32(blocks);
985 ptr++;
986 *ptr = host_to_le32(blk_offset);
987 ptr++;
988 *ptr = host_to_le32(pdr_offset);
989 ptr++;
990 *ptr = host_to_le32(pri_offset);
991 ptr++;
992 *ptr = host_to_le32(cpt_offset);
993 ptr++;
994 (void)fwrite(&image_header, 1, sizeof(image_header), output);
996 dump_blocks(output, firmware->segarray);
997 dump_pdr(output, firmware->plugarray);
998 dump_pdr(output, firmware->pri_plugarray);
999 dump_compat(output, firmware->compat);
1000 return 0;
1003 static int dump_fw(const char *basename, char hexchar,
1004 const void *data,
1005 const struct fw_layout *layout,
1006 const u8 *signature, size_t slen)
1008 struct fwtable_drv *fw_image; /* structure all elements of a given firmware */
1009 struct fwtable firmware;
1010 char *fwname;
1011 char *filename;
1012 const void *hint;
1013 void *fw; /* pointer to the actual blocks for programming */
1014 void *fwblock; /* location of structure listing blocks to program for a given firmware */
1015 u32 vaddr;
1016 FILE *output;
1018 printf("\nAttempting to dump %c firmware:\n", hexchar);
1019 fwname = find_fw_filename(data, layout->max_offset, hexchar);
1020 if (fwname)
1022 /* The filename is towards the end of the FW block,
1023 * so use it as a hint to locate the start of the block.
1025 hint = (u8*) fwname;
1027 else
1029 hint = data + layout->max_offset;
1032 /* Now find the first firmware blob using the signature */
1033 fw = find_fw(data, layout->max_offset, signature, slen, hint);
1034 if (!fw)
1035 return -1;
1037 vaddr = (fw - data) + layout->addr_delta;
1039 printf("Driver address of first firmware blob is 0x%08x\n", vaddr);
1041 /* Some drivers fwtables point before the actual block start. */
1042 vaddr -= layout->block_prefix;
1044 fwblock = find_fwblock_entry(data, layout, vaddr);
1046 vaddr = (fwblock - data) + layout->addr_delta;
1048 if (!fwblock)
1050 printf("Firmware block entry not found - contact Mark!\n");
1051 return -2;
1053 else
1055 printf("Found firmware block entry at virtual location 0x%08x, "
1056 "file offset 0x%08x\n", vaddr, fwblock - data);
1059 /* Got to the first fwblock. Static offset per arch */
1060 fwblock -= layout->lead_block_bytes;
1061 vaddr = (fwblock - data) + layout->addr_delta;
1063 fw_image = find_fwtable_entry(data, layout, vaddr);
1064 if (!fw_image)
1065 return -3;
1067 copy_fw_data(&firmware, fw_image, data, layout);
1069 /* Print FW ident information */
1070 printf("Entry point at 0x%08x\n", firmware.halfentry * 2);
1071 print_fw_ident(&firmware);
1073 filename = malloc(strlen(basename) + 12);
1074 strcpy(filename, basename);
1075 strcat(filename,
1076 (firmware.ident->comp_id == 31) ? "_sta_fw.bin" : "_ap_fw.bin");
1078 printf("Dumping to %s...\n", filename);
1079 if ((output = fopen(filename, "wb")) == NULL)
1081 printf("Unable to open %s, aborting.\n", filename);
1082 free(filename);
1083 return -4;
1086 #if 0
1087 write_hermesap_fw(output, &firmware);
1088 #else
1089 write_kernel_fw(output, &firmware);
1090 #endif
1092 fclose(output);
1093 printf("Dump of %s complete.\n", filename);
1094 free(filename);
1096 return 0;
1099 struct fw_layout* detect_fw_layout(const void *data, size_t flen)
1101 int macho = macho_validate(data);
1102 struct fw_layout *layout;
1103 bool mac = false;
1105 if (macho == 0)
1107 const struct mach_section *fw_section;
1109 printf("Driver looks like Apple Mach-O format\n");
1111 mac = true;
1112 driver_is_be = true;
1113 layout = &firmware_layout[mac];
1115 fw_section = macho_fw_section(data);
1117 layout->addr_delta = (be32_to_host(fw_section->addr) -
1118 be32_to_host(fw_section->offset));
1120 /* restrict search area */
1121 layout->max_offset = (size_t) (be32_to_host(fw_section->offset) +
1122 be32_to_host(fw_section->size));
1123 layout->mac = mac;
1125 else if (macho == -1)
1127 printf("Driver looks like Apple Mach-O format\n"
1128 "But only a 32-bit PPC Mach-O format driver is supported.\n");
1129 return NULL;
1131 else if (memcmp(data, "MZ", 2) == 0)
1133 printf("Driver looks like Microsoft PE format\n");
1134 driver_is_be = false;
1135 mac = false;
1137 layout = &firmware_layout[mac];
1139 layout->addr_delta = (ptrdiff_t) pe_imagebase(data);
1140 layout->max_offset = flen;
1141 layout->mac = mac;
1143 else if (memcmp(data, "Joy!", 4) == 0)
1145 printf("Driver looks like Apple PEF format\n");
1146 printf("I don't know how to extract for this format.\n");
1148 /* Need to look at each of the section headers.
1149 * Number of section headers if given at offset 0x20 (32) (__be16?)
1150 * First section header starts at offset 0x28 (40)
1151 * Each section header is 0x1C (28) bytes long
1152 * Subsequent section headers follow immediately after.
1153 * Each section header specifies its imagebase at offset 0x4 (__be32?)
1154 * We need to use the imagebase of the section in which the firmware is found.
1155 * The offset to the section data is at offset 0x14 (20) (__be32?).
1156 * This offset is relative to the beginning of the file.
1157 * The length of each sections data is at offset 0x10 (16) (__be32?)
1159 /* layout->addr_delta = pef_imagebase(data); */
1160 return NULL;
1162 else
1164 printf("Unknown object file format\n");
1165 return NULL;
1168 return layout;
1172 * Main
1174 int main(int argc, char *argv[])
1176 FILE *input;
1177 void *data;
1178 size_t read_bytes;
1179 size_t flen;
1181 printf("Lucent Firmware Extractor v1.1\n"
1182 "(c) 2003 Mark Smith (username 'Mark' on HermesAP board)\n"
1183 "(c) 2007,2008 Dave Kilroy\n");
1185 check_endianess();
1187 /* Attempt to load file */
1188 if (argc != 3)
1190 printf("Usage: %s <driver> <output_base>\n", argv[0]);
1191 return -1;
1194 /* TODO: parse options better
1195 * Want to be able to specify:
1196 * input file (WLAGS49B.SYS)
1197 * output file base name (wlags_)
1198 * desired output format --hermesap or --kernel
1199 * Others?
1202 if ((input = fopen(argv[1], "rb")) == NULL)
1204 printf("Unable to open %s, aborting.\n", argv[1]);
1205 return -1;
1208 /* Get file length */
1209 fseek(input, 0L, SEEK_END);
1210 flen = ftell(input);
1211 printf("File %s length %u (0x%08x)\n", argv[1], flen, flen);
1213 /* Rewind file pointer */
1214 fseek(input, 0L, SEEK_SET);
1216 /* Allocate memory and load the file */
1217 data = malloc(flen);
1218 read_bytes = fread(data, 1, flen, input);
1219 fclose(input);
1221 if (read_bytes == flen)
1223 u8 t_sig[4] = { 0x61, 0x44, 0xfe, 0xfb };
1224 u8 r_sig[4] = { 0x0f, 0x60, 0xfc, 0x63 };
1225 struct fw_layout *layout;
1227 printf("Memory allocated and file read OK\n");
1229 layout = detect_fw_layout(data, flen);
1230 if (layout)
1232 dump_fw(argv[2], 'T', data, layout, t_sig, sizeof(t_sig));
1233 dump_fw(argv[2], 'R', data, layout, r_sig, sizeof(r_sig));
1236 else
1238 printf("Only read %d out of %d bytes\n", read_bytes, flen);
1241 free(data);
1242 printf("\nAll dumps complete.\n\n");
1243 return 0;