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
6 * Following modifications (c) 2008 David Kilroy
9 * firmware identification
10 * carry on without filename (wldel48b, and old wlluc48)
11 * binary output format for linux kernel driver
12 * big endian translations
15 * These modifications may be distributed freely under the GPL v2 so
16 * long as this copyright notice is included.
25 #if __STDC_VERSION__>=199901L
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 */
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))
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
{
137 /* Decode Mac firmware blocks */
138 struct fwblock_mdrv
{
149 struct fwblock_wdrv
*w
;
150 struct fwblock_mdrv
*m
;
153 struct plugarray_drv
{
159 struct ident_info_drv
{
164 __de16 version_major
;
165 __de16 version_minor
;
168 struct compat_info_drv
{
184 __de32 pri_plugarray_p
;
189 /*** Structures to use on host. ***/
225 struct fwblock
*segarray
;
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 */
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
242 bool mac
; /* Use mac structures */
245 static struct fw_layout firmware_layout
[] =
248 4, 0 * sizeof(struct fwblock_wdrv
),
249 offsetof(struct fwblock_wdrv
, data_p
)
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 {
264 { 21, "Primary firmware" },
265 { 22, "Intermediate firmware" },
266 { 31, "Station firmware" },
267 { 32, "AP firmware" },
268 { 0x14B, "AP firmware" },
271 { 41, "Windows 9x/NT Miniport NDIS 3.1" },
274 { 44, "32 bit ODI" },
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" },
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" },
286 { 56, "VxWorks END Station/AP driver" },
289 { 63, "WaveLAN Station FW update utility" },
290 { 81, "WaveLAN/IEEE AP" },
291 { 83, "WaveLAN/IEEE Ethernet Converter" },
292 { 87, "USB Boot Loader" },
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)
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;
317 fprintf(stderr
, "Can't determine endianess of host!\n");
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;
332 fprintf(stderr
, "Can't determine endianess of host!\n");
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");
343 fprintf(stdout
, "Detected host with mixed endianess\n");
348 /* Locate firmware by looking for a T???????.HEX filename */
349 static char* find_fw_filename(const u8
*hostdriver
, size_t flen
, char hexchar
)
354 /* Find the ?1XXYYZZ.HEX string */
356 end
= hostdriver
+ flen
;
358 for (found
= false; (found
== false) && (p
!= NULL
); )
360 p
= memchr(p
, hexchar
, (end
- p
));
364 if (memcmp(".HEX", p
+ 8, 4) == 0)
377 printf("Found firmware %s at file offset 0x%08tx\n",
378 p
, (p
- hostdriver
));
382 printf("%c-firmware not found!\n", hexchar
);
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
,
395 const u8
*p
= hint
- slen
;
399 printf("Searching for firmware from offset 0x%08tx, start signature",
401 for (i
= 0; i
< slen
; i
++)
403 printf(" %02x", signature
[i
]);
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)
414 printf("Signature not found!\n");
419 printf("Found signature at file offset 0x%08tx\n", p
- hostdriver
);
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
;
450 mac_cpu_type_t cputype
;
451 mac_cpu_subtype_t cpusubtype
;
458 struct mach_load_command
{
463 struct mach_segment_command
{
471 mac_vmprot_t maxprot
;
472 mac_vmprot_t initprot
;
477 struct mach_section
{
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
;
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)
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))
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");
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.
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
))
556 /* Yay, what we need */
561 /* 64-bit or LE 32-bit, can't use it */
564 /* Not a Mach-O file at all */
569 if (be32_to_host(hdr
->cputype
) != 0x12)
573 if (be32_to_host(hdr
->filetype
) != 0x0001)
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
,
584 u32
*p
= (u32
*) (data
+ ((layout
->max_offset
- 4u) & 0xFFFFFFFCu
));
587 printf("Now searching for driver's firmware block entry (0x%08x)...\n",
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
--)
606 /* Compensate for the fields before the data_p pointer */
607 p
-= layout
->datap_offset
/ sizeof(*p
);
612 static struct fwtable_drv
* find_fwtable_entry(const u8
*data
,
613 const struct fw_layout
*layout
,
616 u32
*p
= (u32
*) (data
+ ((layout
->max_offset
- 4u) & 0xFFFFFFFCu
));
617 struct fwtable_drv
*firmware
;
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
--)
634 firmware
= (struct fwtable_drv
*)p
;
637 printf("Main table not found - contact Mark!\n");
641 printf("Found at file offset 0x%08tx\n", (u8
*)p
- data
);
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
,
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 size_t delta
= (size_t)data
- layout
->addr_delta
;
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
);
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: %zd File offs: 0x%08tx Target mem: 0x%08x "
715 (void *)(&block
.w
[i
]) - data
,
716 firmware
->segarray
[i
].offset
,
717 firmware
->segarray
[i
].size
,
718 (firmware
->segarray
[i
].size
== 0) ? " (ignored)" : "");
722 firmware
->segarray
[i
].data
= NULL
;
723 firmware
->segarray
[i
].offset
= 0;
724 firmware
->segarray
[i
].size
= 0;
725 firmware
->segarray
[i
].flags
= 0;
730 printf("Production Data plugrecords at file offset 0x%08tx\n",
732 printf("Primary plugrecords at file offset 0x%08tx\n",
734 printf("Compatibility info at file offset 0x%08tx\n",
735 (void *)compat
- data
);
736 printf("Identity info at file offset 0x%08tx\n",
737 (void *)ident
- data
);
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
);
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
++)
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
)
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
)
797 if (i
== ARRAY_SIZE(compat_table
))
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
);
809 static int write_hermesap_fw(FILE *output
, const struct fwtable
*firmware
)
813 fprintf(output
, "HFW1\nENTRY %08X\n", firmware
->halfentry
* 2);
815 for (i
= 0; firmware
->plugarray
[i
].code
!= 0; i
++)
817 fprintf(output
, "PLUG %08X %08X %08X\n",
818 firmware
->plugarray
[i
].code
,
819 firmware
->plugarray
[i
].targ_off
,
820 firmware
->plugarray
[i
].length
);
823 for (i
= 0; firmware
->segarray
[i
].offset
!= 0; i
++)
828 fprintf(output
, "\n");
829 fprintf(output
, "SEG %08X %08X %08X",
830 firmware
->segarray
[i
].offset
,
831 firmware
->segarray
[i
].size
,
834 for (j
= 0; j
< firmware
->segarray
[i
].size
; j
+= 2)
837 fprintf(output
, "\nDATA");
839 fprintf(output
, " %02X%02X",
840 firmware
->segarray
[i
].data
[j
],
841 firmware
->segarray
[i
].data
[j
+ 1]);
851 static size_t count_blocks(const struct fwblock
*first_block
)
853 const struct fwblock
*block
= first_block
;
856 while (block
->offset
!= 0)
865 static size_t acc_block_size(const struct fwblock
*first_block
)
867 const struct fwblock
*block
= first_block
;
870 while (block
->offset
!= 0)
878 static size_t count_pdr(const struct plugarray
*first_pdr
)
880 const struct plugarray
*pdr
= first_pdr
;
891 static void dump_blocks(FILE *output
, const struct fwblock
*first_block
)
893 const struct fwblock
*block
= first_block
;
894 u8 block_hdr
[sizeof(block
->offset
) + sizeof(block
->size
)];
895 __le32
*addr
= (__le32
*) &block_hdr
[0];
896 __le16
*size
= (__le16
*) &block_hdr
[sizeof(block
->offset
)];
898 while (block
->offset
!= 0)
902 *addr
= host_to_le32(block
->offset
);
903 *size
= host_to_le16(block
->size
);
905 (void)fwrite(&block_hdr
, 1, sizeof(block_hdr
), output
);
906 (void)fwrite(block
->data
, 1, block
->size
, output
); /* data */
910 *addr
= host_to_le32(0xFFFFFFFFu
); /* set block end */
911 *size
= host_to_le16(0);
912 (void)fwrite(&block_hdr
, 1, sizeof(block_hdr
), output
);
915 static void dump_pdr(FILE *output
, const struct plugarray
*first_pdr
)
917 const struct plugarray
*r
= first_pdr
;
918 u8 pdr
[sizeof(r
->code
) + sizeof(r
->targ_off
) + sizeof(r
->length
)];
919 __le32
*code
= (__le32
*) &pdr
[0];
920 __le32
*addr
= (__le32
*) &pdr
[sizeof(r
->code
)];
921 __le32
*len
= (__le32
*) &pdr
[sizeof(r
->code
) + sizeof(r
->targ_off
)];
927 *code
= host_to_le32(r
->code
);
928 *addr
= host_to_le32(r
->targ_off
);
929 *len
= host_to_le32(r
->length
);
930 (void)fwrite(&pdr
, 1, sizeof(pdr
), output
);
934 *code
= *addr
= *len
= host_to_le32(0); /* pdr end */
935 (void)fwrite(&pdr
, 1, sizeof(pdr
), output
);
938 static void dump_compat(FILE *output
, const struct compat_info
*compat
)
940 __le16 buf
[sizeof(*compat
) / sizeof(__le16
)];
942 /* Dump non-zero length blocks.
943 * No need to reformat. */
944 while (compat
->size
!= 0)
948 for (i
= 0; i
< ARRAY_SIZE(buf
); i
++)
950 buf
[i
] = host_to_le16(((u16
*) compat
)[i
]);
952 (void)fwrite(buf
, 1, sizeof(buf
), output
);
956 memset(&buf
, 0, sizeof(buf
));
957 (void)fwrite(&buf
, 1, sizeof(buf
), output
);
960 #define VERSION "HFW000"
961 /* Returns zero, or a negative number to indicate an error */
962 static int write_kernel_fw(FILE *output
, const struct fwtable
*firmware
)
964 /* Note: does not deal with BE/LE issues */
967 u16 headersize
= ((sizeof(VERSION
) - 1) + sizeof(u16
) + (sizeof(u32
) * 6));
968 u32 blocks
= count_blocks(&firmware
->segarray
[0]);
969 u32 blk_offset
= 0; /* Immediately after header */
970 u32 pdr_offset
= (acc_block_size(&firmware
->segarray
[0]) +
971 ((blocks
+ 1) * (sizeof(u32
) + sizeof(u16
))));
972 u32 pri_offset
= pdr_offset
+
973 ((count_pdr(&firmware
->plugarray
[0]) + 1) * sizeof(u32
) * 3);
974 u32 cpt_offset
= pri_offset
+
975 ((count_pdr(&firmware
->pri_plugarray
[0]) + 1) * sizeof(u32
) * 3);
978 (void)fwrite(VERSION
, 1, sizeof(VERSION
) - 1, output
);
980 headersize
= host_to_le16(headersize
);
981 (void)fwrite(&headersize
, 1, sizeof(headersize
), output
);
983 ptr
= &image_header
[0];
984 *ptr
= host_to_le32(firmware
->halfentry
); /* entrypoint */
986 *ptr
= host_to_le32(blocks
);
988 *ptr
= host_to_le32(blk_offset
);
990 *ptr
= host_to_le32(pdr_offset
);
992 *ptr
= host_to_le32(pri_offset
);
994 *ptr
= host_to_le32(cpt_offset
);
996 (void)fwrite(&image_header
, 1, sizeof(image_header
), output
);
998 dump_blocks(output
, firmware
->segarray
);
999 dump_pdr(output
, firmware
->plugarray
);
1000 dump_pdr(output
, firmware
->pri_plugarray
);
1001 dump_compat(output
, firmware
->compat
);
1005 static int dump_fw(const char *basename
, char hexchar
,
1007 const struct fw_layout
*layout
,
1008 const u8
*signature
, size_t slen
)
1010 struct fwtable_drv
*fw_image
; /* structure all elements of a given firmware */
1011 struct fwtable firmware
;
1015 void *fw
; /* pointer to the actual blocks for programming */
1016 void *fwblock
; /* location of structure listing blocks to program for a given firmware */
1020 printf("\nAttempting to dump %c firmware:\n", hexchar
);
1021 fwname
= find_fw_filename(data
, layout
->max_offset
, hexchar
);
1024 /* The filename is towards the end of the FW block,
1025 * so use it as a hint to locate the start of the block.
1027 hint
= (u8
*) fwname
;
1031 hint
= data
+ layout
->max_offset
;
1034 /* Now find the first firmware blob using the signature */
1035 fw
= find_fw(data
, layout
->max_offset
, signature
, slen
, hint
);
1039 vaddr
= (fw
- data
) + layout
->addr_delta
;
1041 printf("Driver address of first firmware blob is 0x%08x\n", vaddr
);
1043 /* Some drivers fwtables point before the actual block start. */
1044 vaddr
-= layout
->block_prefix
;
1046 fwblock
= find_fwblock_entry(data
, layout
, vaddr
);
1048 vaddr
= (fwblock
- data
) + layout
->addr_delta
;
1052 printf("Firmware block entry not found - contact Mark!\n");
1057 printf("Found firmware block entry at virtual location 0x%08x, "
1058 "file offset 0x%08tx\n", vaddr
, fwblock
- data
);
1061 /* Got to the first fwblock. Static offset per arch */
1062 fwblock
-= layout
->lead_block_bytes
;
1063 vaddr
= (fwblock
- data
) + layout
->addr_delta
;
1065 fw_image
= find_fwtable_entry(data
, layout
, vaddr
);
1069 copy_fw_data(&firmware
, fw_image
, data
, layout
);
1071 /* Print FW ident information */
1072 printf("Entry point at 0x%08x\n", firmware
.halfentry
* 2);
1073 print_fw_ident(&firmware
);
1075 filename
= malloc(strlen(basename
) + 12);
1076 strcpy(filename
, basename
);
1078 (firmware
.ident
->comp_id
== 31) ? "_sta_fw.bin" : "_ap_fw.bin");
1080 printf("Dumping to %s...\n", filename
);
1081 if ((output
= fopen(filename
, "wb")) == NULL
)
1083 printf("Unable to open %s, aborting.\n", filename
);
1089 write_hermesap_fw(output
, &firmware
);
1091 write_kernel_fw(output
, &firmware
);
1095 printf("Dump of %s complete.\n", filename
);
1101 struct fw_layout
* detect_fw_layout(const void *data
, size_t flen
)
1103 int macho
= macho_validate(data
);
1104 struct fw_layout
*layout
;
1109 const struct mach_section
*fw_section
;
1111 printf("Driver looks like Apple Mach-O format\n");
1114 driver_is_be
= true;
1115 layout
= &firmware_layout
[mac
];
1117 fw_section
= macho_fw_section(data
);
1119 layout
->addr_delta
= (be32_to_host(fw_section
->addr
) -
1120 be32_to_host(fw_section
->offset
));
1122 /* restrict search area */
1123 layout
->max_offset
= (size_t) (be32_to_host(fw_section
->offset
) +
1124 be32_to_host(fw_section
->size
));
1127 else if (macho
== -1)
1129 printf("Driver looks like Apple Mach-O format\n"
1130 "But only a 32-bit PPC Mach-O format driver is supported.\n");
1133 else if (memcmp(data
, "MZ", 2) == 0)
1135 printf("Driver looks like Microsoft PE format\n");
1136 driver_is_be
= false;
1139 layout
= &firmware_layout
[mac
];
1141 layout
->addr_delta
= (ptrdiff_t) pe_imagebase(data
);
1142 layout
->max_offset
= flen
;
1145 else if (memcmp(data
, "Joy!", 4) == 0)
1147 printf("Driver looks like Apple PEF format\n");
1148 printf("I don't know how to extract for this format.\n");
1150 /* Need to look at each of the section headers.
1151 * Number of section headers if given at offset 0x20 (32) (__be16?)
1152 * First section header starts at offset 0x28 (40)
1153 * Each section header is 0x1C (28) bytes long
1154 * Subsequent section headers follow immediately after.
1155 * Each section header specifies its imagebase at offset 0x4 (__be32?)
1156 * We need to use the imagebase of the section in which the firmware is found.
1157 * The offset to the section data is at offset 0x14 (20) (__be32?).
1158 * This offset is relative to the beginning of the file.
1159 * The length of each sections data is at offset 0x10 (16) (__be32?)
1161 /* layout->addr_delta = pef_imagebase(data); */
1166 printf("Unknown object file format\n");
1176 int main(int argc
, char *argv
[])
1183 printf("Lucent Firmware Extractor v1.1\n"
1184 "(c) 2003 Mark Smith (username 'Mark' on HermesAP board)\n"
1185 "(c) 2007,2008 Dave Kilroy\n");
1189 /* Attempt to load file */
1192 printf("Usage: %s <driver> <output_base>\n", argv
[0]);
1196 /* TODO: parse options better
1197 * Want to be able to specify:
1198 * input file (WLAGS49B.SYS)
1199 * output file base name (wlags_)
1200 * desired output format --hermesap or --kernel
1204 if ((input
= fopen(argv
[1], "rb")) == NULL
)
1206 printf("Unable to open %s, aborting.\n", argv
[1]);
1210 /* Get file length */
1211 fseek(input
, 0L, SEEK_END
);
1212 flen
= ftell(input
);
1213 printf("File %s length %zu (0x%08zx)\n", argv
[1], flen
, flen
);
1215 /* Rewind file pointer */
1216 fseek(input
, 0L, SEEK_SET
);
1218 /* Allocate memory and load the file */
1219 data
= malloc(flen
);
1220 read_bytes
= fread(data
, 1, flen
, input
);
1223 if (read_bytes
== flen
)
1225 u8 t_sig
[4] = { 0x61, 0x44, 0xfe, 0xfb };
1226 u8 r_sig
[4] = { 0x0f, 0x60, 0xfc, 0x63 };
1227 struct fw_layout
*layout
;
1229 printf("Memory allocated and file read OK\n");
1231 layout
= detect_fw_layout(data
, flen
);
1234 dump_fw(argv
[2], 'T', data
, layout
, t_sig
, sizeof(t_sig
));
1235 dump_fw(argv
[2], 'R', data
, layout
, r_sig
, sizeof(r_sig
));
1240 printf("Only read %zd out of %zd bytes\n", read_bytes
, flen
);
1244 printf("\nAll dumps complete.\n\n");