Initial commit.
[b43-tools.git] / fwcutter / fwcutter.c
1 /*
2  * firmware cutter for broadcom 43xx wireless driver files
3  * 
4  * Copyright (c) 2005 Martin Langer <martin-langer@gmx.de>,
5  *               2005-2007 Michael Buesch <mb@bu3sch.de>
6  *               2005 Alex Beregszaszi
7  *               2007 Johannes Berg <johannes@sipsolutions.net>
8  *
9  *   Redistribution and use in source and binary forms, with or without
10  *   modification, are permitted provided that the following conditions
11  *   are met:
12  *
13  *     1. Redistributions of source code must retain the above copyright
14  *        notice, this list of conditions and the following disclaimer.
15  *     2. Redistributions in binary form must reproduce the above
16  *        copyright notice, this list of conditions and the following
17  *        disclaimer in the documentation and/or other materials provided
18  *        with the distribution.
19  *
20  *   THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESS OR IMPLIED
21  *   WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
22  *   OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
23  *   DISCLAIMED. IN NO EVENT SHALL THE CONTRIBUTORS BE LIABLE FOR ANY
24  *   DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
25  *   DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE
26  *   GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
27  *   INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
28  *   WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE
29  *   OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE,
30  *   EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
31  */
32
33 #include <stdlib.h>
34 #include <ctype.h>
35 #include <string.h>
36 #include <stdio.h>
37 #include <errno.h>
38 #include <sys/stat.h>
39 #include <sys/types.h>
40
41 #ifdef __DragonFly__
42 #include <sys/endian.h>
43 #else
44 #include <byteswap.h>
45 #endif
46
47 #include "md5.h"
48 #include "fwcutter.h"
49 #include "fwcutter_list.h"
50
51 #ifdef __DragonFly__
52 #define V3_FW_DIRNAME   "v3"
53 #define V4_FW_DIRNAME   "v4"
54 #else
55 #define V3_FW_DIRNAME   "b43legacy"
56 #define V4_FW_DIRNAME   "b43"
57 #endif
58
59 static struct cmdline_args cmdargs;
60
61
62 /* Convert a CPU-endian 16bit integer to Big-Endian */
63 static be16_t to_be16(uint16_t v)
64 {
65         uint8_t ret[2];
66
67         ret[0] = (v & 0xFF00) >> 8;
68         ret[1] = (v & 0x00FF);
69
70         return *((be16_t *)ret);
71 }
72
73 #if 0
74 /* Convert a Big-Endian 16bit integer to CPU-endian */
75 static uint16_t from_be16(be16_t v)
76 {
77         uint16_t ret = 0;
78
79         ret |= (uint16_t)(((uint8_t *)&v)[0]) << 8;
80         ret |= (uint16_t)(((uint8_t *)&v)[1]);
81
82         return ret;
83 }
84 #endif
85
86 /* Convert a CPU-endian 32bit integer to Big-Endian */
87 static be32_t to_be32(uint32_t v)
88 {
89         uint8_t ret[4];
90
91         ret[0] = (v & 0xFF000000) >> 24;
92         ret[1] = (v & 0x00FF0000) >> 16;
93         ret[2] = (v & 0x0000FF00) >> 8;
94         ret[3] = (v & 0x000000FF);
95
96         return *((be32_t *)ret);
97 }
98
99 /* Convert a Big-Endian 32bit integer to CPU-endian */
100 static uint32_t from_be32(be32_t v)
101 {
102         uint32_t ret = 0;
103
104         ret |= (uint32_t)(((uint8_t *)&v)[0]) << 24;
105         ret |= (uint32_t)(((uint8_t *)&v)[1]) << 16;
106         ret |= (uint32_t)(((uint8_t *)&v)[2]) << 8;
107         ret |= (uint32_t)(((uint8_t *)&v)[3]);
108
109         return ret;
110 }
111
112 /* tiny disassembler */
113 static void print_ucode_version(struct insn *insn)
114 {
115         int val;
116
117         /*
118          * The instruction we're looking for is a store to memory
119          * offset insn->op3 of the constant formed like `val' below.
120          * 0x2de00 is the opcode for type 1, 0x378 is the opcode
121          * for type 2 and 3.
122          */
123         if (insn->opcode != 0x378 && insn->opcode != 0x2de00)
124                 return;
125
126         val = ((0xFF & insn->op1) << 8) | (0xFF & insn->op2);
127
128         /*
129          * Memory offsets are word-offsets, for the meaning
130          * see http://bcm-v4.sipsolutions.net/802.11/ObjectMemory
131          */
132         switch (insn->op3) {
133         case 0:
134                 printf("  ucode version:  %d\n", val);
135                 break;
136         case 1:
137                 printf("  ucode revision: %d\n", val);
138                 break;
139         case 2:
140                 printf("  ucode date:     %.4d-%.2d-%.2d\n",
141                        2000 + (val >> 12), (val >> 8) & 0xF, val & 0xFF);
142                 break;
143         case 3:
144                 printf("  ucode time:     %.2d:%.2d:%.2d\n",
145                        val >> 11, (val >> 5) & 0x3f, val & 0x1f);
146                 break;
147         }
148 }
149
150 static void disasm_ucode_1(uint64_t in, struct insn *out)
151 {
152         /* xxyyyzzz00oooooX -> ooooo Xxx yyy zzz
153          * if we swap the upper and lower 32-bits first it becomes easier:
154          * 00oooooxxxyyyzzz -> ooooo xxx yyy zzz
155          */
156         in = (in >> 32) | (in << 32);
157
158         out->op3        = in & 0xFFF;
159         out->op2        = (in >> 12) & 0xFFF;
160         out->op1        = (in >> 24) & 0xFFF;
161         out->opcode     = (in >> 36) & 0xFFFFF;
162         /* the rest of the in word should be zero */
163 }
164
165 static void disasm_ucode_2(uint64_t in, struct insn *out)
166 {
167         /* xxyyyzzz0000oooX -> ooo Xxx yyy zzz
168          * if we swap the upper and lower 32-bits first it becomes easier:
169          * 0000oooxxxyyyzzz -> ooo xxx yyy zzz
170          */
171         in = (in >> 32) | (in << 32);
172
173         out->op3        = in & 0xFFF;
174         out->op2        = (in >> 12) & 0xFFF;
175         out->op1        = (in >> 24) & 0xFFF;
176         out->opcode     = (in >> 36) & 0xFFF;
177         /* the rest of the in word should be zero */
178 }
179
180 static void disasm_ucode_3(uint64_t in, struct insn *out)
181 {
182         /*
183          * like 2, but each operand has one bit more; appears
184          * to use the same instruction set slightly extended
185          */
186         in = (in >> 32) | (in << 32);
187
188         out->op3        = in & 0x1FFF;
189         out->op2        = (in >> 13) & 0x1FFF;
190         out->op1        = (in >> 26) & 0x1FFF;
191         out->opcode     = (in >> 39) & 0xFFF;
192         /* the rest of the in word should be zero */
193 }
194
195 static void analyse_ucode(int ucode_rev, uint8_t *data, uint32_t len)
196 {
197         uint64_t *insns = (uint64_t*)data;
198         struct insn insn;
199         uint32_t i;
200
201         for (i=0; i<len/sizeof(*insns); i++) {
202                 switch (ucode_rev) {
203                 case 1:
204                         disasm_ucode_1(insns[i], &insn);
205                         print_ucode_version(&insn);
206                         break;
207                 case 2:
208                         disasm_ucode_2(insns[i], &insn);
209                         print_ucode_version(&insn);
210                         break;
211                 case 3:
212                         disasm_ucode_3(insns[i], &insn);
213                         print_ucode_version(&insn);
214                         break;
215                 }
216         }
217 }
218
219 static void swap_endianness_ucode(uint8_t *buf, uint32_t len)
220 {
221         uint32_t *buf32 = (uint32_t*)buf;
222         uint32_t i;
223
224         for (i=0; i<len/4; i++)
225                 buf32[i] = bswap_32(buf32[i]);
226 }
227
228 #define swap_endianness_pcm swap_endianness_ucode
229
230 static void swap_endianness_iv(struct iv *iv)
231 {
232         iv->reg = bswap_16(iv->reg);
233         iv->size = bswap_16(iv->size);
234         iv->val = bswap_32(iv->val);
235 }
236
237 static void build_ivs(struct b43_iv **_out, size_t *_out_size,
238                       struct iv *in, size_t in_size,
239                       struct fw_header *hdr,
240                       uint32_t flags)
241 {
242         struct iv *iv;
243         struct b43_iv *out;
244         uint32_t i;
245         size_t out_size = 0;
246
247         if (sizeof(struct b43_iv) != 6) {
248                 printf("sizeof(struct b43_iv) != 6\n");
249                 exit(255);
250         }
251
252         out = malloc(in_size);
253         if (!out) {
254                 perror("failed to allocate buffer");
255                 exit(1);
256         }
257         *_out = out;
258         for (i = 0; i < in_size / sizeof(*iv); i++, in++) {
259                 if (flags & FW_FLAG_LE)
260                         swap_endianness_iv(in);
261                 /* input-IV is BigEndian */
262                 if (in->reg & to_be16(~FW_IV_OFFSET_MASK)) {
263                         printf("Input file IV offset > 0x%X\n", FW_IV_OFFSET_MASK);
264                         exit(1);
265                 }
266                 out->offset_size = in->reg;
267                 if (in->size == to_be16(4)) {
268                         out->offset_size |= to_be16(FW_IV_32BIT);
269                         out->data.d32 = in->val;
270
271                         out_size += sizeof(be16_t) + sizeof(be32_t);
272                         out = (struct b43_iv *)((uint8_t *)out + sizeof(be16_t) + sizeof(be32_t));
273                 } else if (in->size == to_be16(2)) {
274                         if (in->val & to_be32(~0xFFFF)) {
275                                 printf("Input file 16bit IV value overflow\n");
276                                 exit(1);
277                         }
278                         out->data.d16 = to_be16(from_be32(in->val));
279
280                         out_size += sizeof(be16_t) + sizeof(be16_t);
281                         out = (struct b43_iv *)((uint8_t *)out + sizeof(be16_t) + sizeof(be16_t));
282                 } else {
283                         printf("Input file IV size != 2|4\n");
284                         exit(1);
285                 }
286         }
287         hdr->size = to_be32(i);
288         *_out_size = out_size;
289 }
290
291 static void write_file(const char *name, uint8_t *buf, uint32_t len,
292                        const struct fw_header *hdr, uint32_t flags)
293 {
294         FILE *f;
295         char nbuf[4096];
296         const char *dir;
297         int r;
298
299         if (flags & FW_FLAG_V4)
300                 dir = V4_FW_DIRNAME;
301         else
302                 dir = V3_FW_DIRNAME;
303
304         r = snprintf(nbuf, sizeof(nbuf),
305                      "%s/%s", cmdargs.target_dir, dir);
306         if (r >= sizeof(nbuf)) {
307                 fprintf(stderr, "name too long");
308                 exit(2);
309         }
310
311         r = mkdir(nbuf, 0770);
312         if (r && errno != EEXIST) {
313                 perror("failed to create output directory");
314                 exit(2);
315         }
316
317         r = snprintf(nbuf, sizeof(nbuf),
318                      "%s/%s/%s.fw", cmdargs.target_dir, dir, name);
319         if (r >= sizeof(nbuf)) {
320                 fprintf(stderr, "name too long");
321                 exit(2);
322         }
323         f = fopen(nbuf, "w");
324         if (!f) {
325                 perror("failed to open file");
326                 exit(2);
327         }
328         if (fwrite(hdr, sizeof(*hdr), 1, f) != 1) {
329                 perror("failed to write file");
330                 exit(2);
331         }
332         if (fwrite(buf, 1, len, f) != len) {
333                 perror("failed to write file");
334                 exit(2);
335         }
336         fclose(f);
337 }
338
339 static void extract_or_identify(FILE *f, const struct extract *extract,
340                                 uint32_t flags)
341 {
342         uint8_t *buf;
343         size_t data_length;
344         int ucode_rev = 0;
345         struct fw_header hdr;
346
347         memset(&hdr, 0, sizeof(hdr));
348         hdr.ver = FW_HDR_VER;
349
350         if (fseek(f, extract->offset, SEEK_SET)) {
351                 perror("failed to seek on file");
352                 exit(2);
353         }
354
355         buf = malloc(extract->length);
356         if (!buf) {
357                 perror("failed to allocate buffer");
358                 exit(3);
359         }
360         if (fread(buf, 1, extract->length, f) != extract->length) {
361                 perror("failed to read complete data");
362                 exit(3);
363         }
364
365         switch (extract->type) {
366         case EXT_UCODE_3:
367                 ucode_rev += 1;
368         case EXT_UCODE_2:
369                 ucode_rev += 1;
370         case EXT_UCODE_1:
371                 ucode_rev += 1;
372                 data_length = extract->length;
373                 if (flags & FW_FLAG_LE)
374                         swap_endianness_ucode(buf, data_length);
375                 analyse_ucode(ucode_rev, buf, data_length);
376                 hdr.type = FW_TYPE_UCODE;
377                 hdr.size = to_be32(data_length);
378                 break;
379         case EXT_PCM:
380                 data_length = extract->length;
381                 if (flags & FW_FLAG_LE)
382                         swap_endianness_pcm(buf, data_length);
383                 hdr.type = FW_TYPE_PCM;
384                 hdr.size = to_be32(data_length);
385                 break;
386         case EXT_IV: {
387                 struct b43_iv *ivs;
388
389                 hdr.type = FW_TYPE_IV;
390                 build_ivs(&ivs, &data_length,
391                           (struct iv *)buf, extract->length,
392                           &hdr, flags);
393                 free(buf);
394                 buf = (uint8_t *)ivs;
395                 break;
396         }
397         default:
398                 exit(255);
399         }
400
401         if (!cmdargs.identify_only)
402                 write_file(extract->name, buf, data_length, &hdr, flags);
403
404         free(buf);
405 }
406
407 static void print_banner(void)
408 {
409         printf("b43-fwcutter version " FWCUTTER_VERSION "\n");
410 }
411
412 static void print_file(const struct file *file)
413 {
414         char filename[30];
415         char shortname[30];
416
417         if (file->flags & FW_FLAG_V4)
418                 printf(V4_FW_DIRNAME "\t\t");
419         else
420                 printf(V3_FW_DIRNAME "\t");
421
422         if (strlen(file->name) > 20) {
423                 strncpy(shortname, file->name, 20);
424                 shortname[20] = '\0';
425                 snprintf(filename, sizeof(filename), "%s..", shortname);
426         } else
427                 strcpy (filename, file->name);
428
429         printf("%s\t", filename);
430         if (strlen(filename) < 8) printf("\t");
431         if (strlen(filename) < 16) printf("\t");
432
433         printf("%s\t", file->ucode_version);
434         if (strlen(file->ucode_version) < 8) printf("\t");
435
436         printf("%s\n", file->md5);
437 }
438
439 static void print_supported_files(void)
440 {
441         int i;
442
443         print_banner();
444         printf("\nExtracting firmware is possible "
445                "from these binary driver files:\n\n");
446         printf("<driver>\t"
447                "<filename>\t\t"
448                "<microcode>\t"
449                "<MD5 checksum>\n\n");
450         /* print for legacy driver first */
451         for (i = 0; i < FILES; i++)
452                 if (!(files[i].flags & FW_FLAG_V4))
453                         print_file(&files[i]);
454         for (i = 0; i < FILES; i++)
455                 if (files[i].flags & FW_FLAG_V4)
456                         print_file(&files[i]);
457         printf("\n");
458 }
459
460 static const struct file *find_file(FILE *fd)
461 {
462         unsigned char buffer[16384], signature[16];
463         struct MD5Context md5c;
464         char md5sig[33];
465         int i;
466
467         MD5Init(&md5c);
468         while ((i = (int) fread(buffer, 1, sizeof(buffer), fd)) > 0)
469                 MD5Update(&md5c, buffer, (unsigned) i);
470         MD5Final(signature, &md5c);
471
472         snprintf(md5sig, sizeof(md5sig),
473                  "%.2x%.2x%.2x%.2x%.2x%.2x%.2x%.2x"
474                  "%.2x%.2x%.2x%.2x%.2x%.2x%.2x%.2x",
475                  signature[0], signature[1], signature[2], signature[3],
476                  signature[4], signature[5], signature[6], signature[7],
477                  signature[8], signature[9], signature[10], signature[11],
478                  signature[12], signature[13], signature[14], signature[15]);
479
480         for (i = 0; i < FILES; ++i) {
481                 if (strcasecmp(md5sig, files[i].md5) == 0) {
482                         printf("This file is recognised as:\n");
483                         printf("  filename   :  %s\n", files[i].name);
484                         printf("  version    :  %s\n", files[i].ucode_version);
485                         printf("  MD5        :  %s\n", files[i].md5);
486                         return &files[i];
487                 }
488         }
489         printf("Sorry, the input file is either wrong or "
490                "not supported by b43-fwcutter.\n");
491         printf("This file has an unknown MD5sum %s.\n", md5sig);
492
493         return NULL;
494 }
495
496 static void print_usage(int argc, char *argv[])
497 {
498         print_banner();
499         printf("\nUsage: %s [OPTION] [driver.sys]\n", argv[0]);
500         printf("  -l|--list             "
501                "List supported driver versions\n");
502         printf("  -i|--identify         "
503                "Only identify the driver file (don't extract)\n");
504         printf("  -w|--target-dir DIR   "
505                "Extract and write firmware to DIR\n");
506         printf("  -v|--version          "
507                "Print b43-fwcutter version\n");
508         printf("  -h|--help             "
509                "Print this help\n");
510         printf("\nExample: %s bcmwl5.sys\n"
511                "         to extract the firmware blobs from bcmwl5.sys\n", 
512                argv[0]);
513 }
514
515 static int do_cmp_arg(char **argv, int *pos,
516                       const char *template,
517                       int allow_merged,
518                       char **param)
519 {
520         char *arg;
521         char *next_arg;
522         size_t arg_len, template_len;
523
524         arg = argv[*pos];
525         next_arg = argv[*pos + 1];
526         arg_len = strlen(arg);
527         template_len = strlen(template);
528
529         if (param) {
530                 /* Maybe we have a merged parameter here.
531                  * A merged parameter is "-pfoobar" for example.
532                  */
533                 if (allow_merged && arg_len > template_len) {
534                         if (memcmp(arg, template, template_len) == 0) {
535                                 *param = arg + template_len;
536                                 return ARG_MATCH;
537                         }
538                         return ARG_NOMATCH;
539                 } else if (arg_len != template_len)
540                         return ARG_NOMATCH;
541                 *param = next_arg;
542         }
543         if (strcmp(arg, template) == 0) {
544                 if (param) {
545                         /* Skip the parameter on the next iteration. */
546                         (*pos)++;
547                         if (*param == 0) {
548                                 printf("%s needs a parameter\n", arg);
549                                 return ARG_ERROR;
550                         }
551                 }
552                 return ARG_MATCH;
553         }
554
555         return ARG_NOMATCH;
556 }
557
558 /* Simple and lean command line argument parsing. */
559 static int cmp_arg(char **argv, int *pos,
560                    const char *long_template,
561                    const char *short_template,
562                    char **param)
563 {
564         int err;
565
566         if (long_template) {
567                 err = do_cmp_arg(argv, pos, long_template, 0, param);
568                 if (err == ARG_MATCH || err == ARG_ERROR)
569                         return err;
570         }
571         err = ARG_NOMATCH;
572         if (short_template)
573                 err = do_cmp_arg(argv, pos, short_template, 1, param);
574         return err;
575 }
576
577 static int parse_args(int argc, char *argv[])
578 {
579         int i, res;
580         char *param;
581
582         if (argc < 2)
583                 goto out_usage;
584         for (i = 1; i < argc; i++) {
585                 res = cmp_arg(argv, &i, "--list", "-l", 0);
586                 if (res == ARG_MATCH) {
587                         print_supported_files();
588                         return 1;
589                 } else if (res == ARG_ERROR)
590                         goto out;
591
592                 res = cmp_arg(argv, &i, "--version", "-v", 0);
593                 if (res == ARG_MATCH) {
594                         print_banner();
595                         return 1;
596                 } else if (res == ARG_ERROR)
597                         goto out;
598
599                 res = cmp_arg(argv, &i, "--help", "-h", 0);
600                 if (res == ARG_MATCH)
601                         goto out_usage;
602                 else if (res == ARG_ERROR)
603                         goto out;
604
605                 res = cmp_arg(argv, &i, "--identify", "-i", 0);
606                 if (res == ARG_MATCH) {
607                         cmdargs.identify_only = 1;
608                         continue;
609                 } else if (res == ARG_ERROR)
610                         goto out;
611
612                 res = cmp_arg(argv, &i, "--target-dir", "-w", &param);
613                 if (res == ARG_MATCH) {
614                         cmdargs.target_dir = param;
615                         continue;
616                 } else if (res == ARG_ERROR)
617                         goto out;
618
619                 cmdargs.infile = argv[i];
620                 break;
621         }
622
623         if (!cmdargs.infile)
624                 goto out_usage;
625         return 0;
626
627 out_usage:
628         print_usage(argc, argv);
629 out:
630         return -1;      
631 }
632
633 int main(int argc, char *argv[])
634 {
635         FILE *fd;
636         const struct file *file;
637         const struct extract *extract;
638         int err;
639         const char *dir;
640
641         cmdargs.target_dir = ".";
642         err = parse_args(argc, argv);
643         if (err == 1)
644                 return 0;
645         else if (err != 0)
646                 return err;
647
648         fd = fopen(cmdargs.infile, "rb");
649         if (!fd) {
650                 fprintf(stderr, "Cannot open input file %s\n", cmdargs.infile);
651                 return 2;
652         }
653
654         err = -1;
655         file = find_file(fd);
656         if (!file)
657                 goto out_close;
658
659         if (file->flags & FW_FLAG_V4)
660                 dir = V4_FW_DIRNAME;
661         else
662                 dir = V3_FW_DIRNAME;
663
664         extract = file->extract;
665         while (extract->name) {
666                 printf("%s %s/%s.fw\n",
667                        cmdargs.identify_only ? "Contains" : "Extracting",
668                        dir, extract->name);
669                 extract_or_identify(fd, extract, file->flags);
670                 extract++;
671         }
672
673         err = 0;
674 out_close:
675         fclose(fd);
676
677         return err;
678 }