nexmon – Blame information for rev 1

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