fwcutter.c 22 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937938939940941942943944
  1. /*
  2. * firmware cutter for broadcom 43xx wireless driver files
  3. *
  4. * Copyright (c) 2005 Martin Langer <martin-langer@gmx.de>,
  5. * 2005-2014 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. #include <stdlib.h>
  33. #include <ctype.h>
  34. #include <string.h>
  35. #include <stdio.h>
  36. #include <errno.h>
  37. #include <sys/stat.h>
  38. #include <sys/types.h>
  39. #if defined(__DragonFly__) || defined(__FreeBSD__)
  40. #include <sys/endian.h>
  41. #elif defined(__APPLE__)
  42. #include <libkern/OSByteOrder.h>
  43. #else
  44. #include <byteswap.h>
  45. #endif
  46. #include "md5.h"
  47. #include "fwcutter.h"
  48. #include "fwcutter_list.h"
  49. #if defined(__DragonFly__) || defined(__FreeBSD__)
  50. #define V3_FW_DIRNAME "v3"
  51. #define V4_FW_DIRNAME "v4"
  52. #else
  53. #define V3_FW_DIRNAME "b43legacy"
  54. #define V4_FW_DIRNAME "b43"
  55. #endif
  56. static struct cmdline_args cmdargs;
  57. /* check whether file will be listed/extracted from */
  58. static int file_ok(const struct file *f)
  59. {
  60. return !(f->flags & FW_FLAG_UNSUPPORTED) || cmdargs.unsupported;
  61. }
  62. /* Convert a Big-Endian 16bit integer to CPU-endian */
  63. static uint16_t from_be16(be16_t v)
  64. {
  65. uint16_t ret = 0;
  66. ret |= (uint16_t)(((uint8_t *)&v)[0]) << 8;
  67. ret |= (uint16_t)(((uint8_t *)&v)[1]);
  68. return ret;
  69. }
  70. /* Convert a CPU-endian 16bit integer to Big-Endian */
  71. static be16_t to_be16(uint16_t v)
  72. {
  73. return (be16_t)from_be16((be16_t)v);
  74. }
  75. /* Convert a Big-Endian 32bit integer to CPU-endian */
  76. static uint32_t from_be32(be32_t v)
  77. {
  78. uint32_t ret = 0;
  79. ret |= (uint32_t)(((uint8_t *)&v)[0]) << 24;
  80. ret |= (uint32_t)(((uint8_t *)&v)[1]) << 16;
  81. ret |= (uint32_t)(((uint8_t *)&v)[2]) << 8;
  82. ret |= (uint32_t)(((uint8_t *)&v)[3]);
  83. return ret;
  84. }
  85. /* Convert a CPU-endian 32bit integer to Big-Endian */
  86. static be32_t to_be32(uint32_t v)
  87. {
  88. return (be32_t)from_be32((be32_t)v);
  89. }
  90. /* Convert a Little-Endian 32bit integer to CPU-endian */
  91. static uint32_t from_le32(le32_t v)
  92. {
  93. uint32_t ret = 0;
  94. ret |= (uint32_t)(((uint8_t *)&v)[0]);
  95. ret |= (uint32_t)(((uint8_t *)&v)[1]) << 8;
  96. ret |= (uint32_t)(((uint8_t *)&v)[2]) << 16;
  97. ret |= (uint32_t)(((uint8_t *)&v)[3]) << 24;
  98. return ret;
  99. }
  100. /* Convert a CPU-endian 32bit integer to Little-Endian */
  101. static le32_t to_le32(uint32_t v)
  102. {
  103. return (le32_t)from_le32((le32_t)v);
  104. }
  105. /* tiny disassembler */
  106. static void print_ucode_version(struct insn *insn)
  107. {
  108. int val;
  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. val = ((0xFF & insn->op1) << 8) | (0xFF & insn->op2);
  118. /*
  119. * Memory offsets are word-offsets, for the meaning
  120. * see http://bcm-v4.sipsolutions.net/802.11/ObjectMemory
  121. */
  122. switch (insn->op3) {
  123. case 0:
  124. printf(" ucode version: %d\n", val);
  125. break;
  126. case 1:
  127. printf(" ucode revision: %d\n", val);
  128. break;
  129. case 2:
  130. printf(" ucode date: %.4d-%.2d-%.2d\n",
  131. 2000 + (val >> 12), (val >> 8) & 0xF, val & 0xFF);
  132. break;
  133. case 3:
  134. printf(" ucode time: %.2d:%.2d:%.2d\n",
  135. val >> 11, (val >> 5) & 0x3f, val & 0x1f);
  136. break;
  137. }
  138. }
  139. static void disasm_ucode_1(uint64_t in, struct insn *out)
  140. {
  141. /* xxyyyzzz00oooooX -> ooooo Xxx yyy zzz
  142. * if we swap the upper and lower 32-bits first it becomes easier:
  143. * 00oooooxxxyyyzzz -> ooooo xxx yyy zzz
  144. */
  145. in = (in >> 32) | (in << 32);
  146. out->op3 = in & 0xFFF;
  147. out->op2 = (in >> 12) & 0xFFF;
  148. out->op1 = (in >> 24) & 0xFFF;
  149. out->opcode = (in >> 36) & 0xFFFFF;
  150. /* the rest of the in word should be zero */
  151. }
  152. static void disasm_ucode_2(uint64_t in, struct insn *out)
  153. {
  154. /* xxyyyzzz0000oooX -> ooo Xxx yyy zzz
  155. * if we swap the upper and lower 32-bits first it becomes easier:
  156. * 0000oooxxxyyyzzz -> ooo xxx yyy zzz
  157. */
  158. in = (in >> 32) | (in << 32);
  159. out->op3 = in & 0xFFF;
  160. out->op2 = (in >> 12) & 0xFFF;
  161. out->op1 = (in >> 24) & 0xFFF;
  162. out->opcode = (in >> 36) & 0xFFF;
  163. /* the rest of the in word should be zero */
  164. }
  165. static void disasm_ucode_3(uint64_t in, struct insn *out)
  166. {
  167. /*
  168. * like 2, but each operand has one bit more; appears
  169. * to use the same instruction set slightly extended
  170. */
  171. in = (in >> 32) | (in << 32);
  172. out->op3 = in & 0x1FFF;
  173. out->op2 = (in >> 13) & 0x1FFF;
  174. out->op1 = (in >> 26) & 0x1FFF;
  175. out->opcode = (in >> 39) & 0xFFF;
  176. /* the rest of the in word should be zero */
  177. }
  178. static void analyse_ucode(int ucode_rev, uint8_t *data, uint32_t len)
  179. {
  180. uint64_t *insns = (uint64_t*)data;
  181. struct insn insn;
  182. uint32_t i;
  183. for (i=0; i<len/sizeof(*insns); i++) {
  184. switch (ucode_rev) {
  185. case 1:
  186. disasm_ucode_1(insns[i], &insn);
  187. print_ucode_version(&insn);
  188. break;
  189. case 2:
  190. disasm_ucode_2(insns[i], &insn);
  191. print_ucode_version(&insn);
  192. break;
  193. case 3:
  194. disasm_ucode_3(insns[i], &insn);
  195. print_ucode_version(&insn);
  196. break;
  197. }
  198. }
  199. }
  200. static void swap_endianness_ucode(uint8_t *buf, uint32_t len)
  201. {
  202. uint32_t *buf32 = (uint32_t*)buf;
  203. uint32_t i;
  204. for (i=0; i<len/4; i++)
  205. buf32[i] = bswap_32(buf32[i]);
  206. }
  207. #define swap_endianness_pcm swap_endianness_ucode
  208. static void swap_endianness_iv(struct iv *iv)
  209. {
  210. iv->reg = bswap_16(iv->reg);
  211. iv->size = bswap_16(iv->size);
  212. iv->val = bswap_32(iv->val);
  213. }
  214. static uint8_t *read_object(FILE *f, const struct extract *extract)
  215. {
  216. uint8_t *buf;
  217. if (fseek(f, extract->offset, SEEK_SET)) {
  218. perror("failed to seek on file");
  219. exit(2);
  220. }
  221. buf = malloc(extract->length);
  222. if (!buf) {
  223. perror("failed to allocate buffer");
  224. exit(3);
  225. }
  226. if (fread(buf, 1, extract->length, f) != extract->length) {
  227. perror("failed to read complete data");
  228. exit(3);
  229. }
  230. return buf;
  231. }
  232. static void build_ivs(struct b43_iv **_out, size_t *_out_size,
  233. struct iv *in, size_t in_size,
  234. struct fw_header *hdr,
  235. uint32_t flags)
  236. {
  237. struct iv *iv;
  238. struct b43_iv *out;
  239. uint32_t i;
  240. size_t out_size = 0;
  241. if (sizeof(struct b43_iv) != 6) {
  242. printf("sizeof(struct b43_iv) != 6\n");
  243. exit(255);
  244. }
  245. out = malloc(in_size);
  246. if (!out) {
  247. perror("failed to allocate buffer");
  248. exit(1);
  249. }
  250. *_out = out;
  251. for (i = 0; i < in_size / sizeof(*iv); i++, in++) {
  252. if (flags & FW_FLAG_LE)
  253. swap_endianness_iv(in);
  254. /* input-IV is BigEndian */
  255. if (in->reg & to_be16(~FW_IV_OFFSET_MASK)) {
  256. printf("Input file IV offset > 0x%X\n", FW_IV_OFFSET_MASK);
  257. exit(1);
  258. }
  259. out->offset_size = in->reg;
  260. if (in->size == to_be16(4)) {
  261. out->offset_size |= to_be16(FW_IV_32BIT);
  262. out->data.d32 = in->val;
  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. out_size += sizeof(be16_t) + sizeof(be16_t);
  272. out = (struct b43_iv *)((uint8_t *)out + sizeof(be16_t) + sizeof(be16_t));
  273. } else {
  274. printf("Input file IV size != 2|4\n");
  275. exit(1);
  276. }
  277. }
  278. hdr->size = to_be32(i);
  279. *_out_size = out_size;
  280. }
  281. static void write_file(const char *name, uint8_t *buf, uint32_t len,
  282. const struct fw_header *hdr, uint32_t flags)
  283. {
  284. FILE *f;
  285. char nbuf[4096];
  286. const char *dir;
  287. int r;
  288. if (flags & FW_FLAG_V4)
  289. dir = V4_FW_DIRNAME;
  290. else
  291. dir = V3_FW_DIRNAME;
  292. r = snprintf(nbuf, sizeof(nbuf),
  293. "%s/%s", cmdargs.target_dir, dir);
  294. if (r >= sizeof(nbuf)) {
  295. fprintf(stderr, "name too long");
  296. exit(2);
  297. }
  298. r = mkdir(nbuf, 0770);
  299. if (r && errno != EEXIST) {
  300. perror("failed to create output directory");
  301. exit(2);
  302. }
  303. r = snprintf(nbuf, sizeof(nbuf),
  304. "%s/%s/%s.fw", cmdargs.target_dir, dir, name);
  305. if (r >= sizeof(nbuf)) {
  306. fprintf(stderr, "name too long");
  307. exit(2);
  308. }
  309. f = fopen(nbuf, "w");
  310. if (!f) {
  311. perror("failed to open file");
  312. exit(2);
  313. }
  314. if (fwrite(hdr, sizeof(*hdr), 1, f) != 1) {
  315. perror("failed to write file");
  316. exit(2);
  317. }
  318. if (fwrite(buf, 1, len, f) != len) {
  319. perror("failed to write file");
  320. exit(2);
  321. }
  322. fclose(f);
  323. }
  324. static void extract_or_identify(FILE *f, const char *dir,
  325. const struct extract *extract, uint32_t flags)
  326. {
  327. uint8_t *buf;
  328. size_t data_length;
  329. int ucode_rev = 0;
  330. struct fw_header hdr;
  331. memset(&hdr, 0, sizeof(hdr));
  332. hdr.ver = FW_HDR_VER;
  333. printf("%s %s/%s.fw\n",
  334. cmdargs.mode == FWCM_IDENTIFY ? "Contains" : "Extracting",
  335. dir, extract->name);
  336. buf = read_object(f, extract);
  337. switch (extract->type) {
  338. case EXT_UCODE_3:
  339. ucode_rev += 1;
  340. case EXT_UCODE_2:
  341. ucode_rev += 1;
  342. case EXT_UCODE_1:
  343. ucode_rev += 1;
  344. data_length = extract->length;
  345. if (flags & FW_FLAG_LE)
  346. swap_endianness_ucode(buf, data_length);
  347. analyse_ucode(ucode_rev, buf, data_length);
  348. hdr.type = FW_TYPE_UCODE;
  349. hdr.size = to_be32(data_length);
  350. break;
  351. case EXT_PCM:
  352. data_length = extract->length;
  353. if (flags & FW_FLAG_LE)
  354. swap_endianness_pcm(buf, data_length);
  355. hdr.type = FW_TYPE_PCM;
  356. hdr.size = to_be32(data_length);
  357. break;
  358. case EXT_IV: {
  359. struct b43_iv *ivs;
  360. hdr.type = FW_TYPE_IV;
  361. build_ivs(&ivs, &data_length,
  362. (struct iv *)buf, extract->length,
  363. &hdr, flags);
  364. free(buf);
  365. buf = (uint8_t *)ivs;
  366. break;
  367. }
  368. default:
  369. exit(255);
  370. }
  371. if (cmdargs.mode == FWCM_EXTRACT_B43)
  372. write_file(extract->name, buf, data_length, &hdr, flags);
  373. free(buf);
  374. }
  375. static int brcmsmac_name_to_idx(const char *name)
  376. {
  377. if (strcmp("lcn0bsinitvals24", name) == 0) {
  378. return D11LCN0BSINITVALS24;
  379. } else if (strcmp("lcn0initvals24", name) == 0) {
  380. return D11LCN0INITVALS24;
  381. } else if (strcmp("lcn1bsinitvals24", name) == 0) {
  382. return D11LCN1BSINITVALS24;
  383. } else if (strcmp("lcn1initvals24", name) == 0) {
  384. return D11LCN1INITVALS24;
  385. } else if (strcmp("lcn2bsinitvals24", name) == 0) {
  386. return D11LCN2BSINITVALS24;
  387. } else if (strcmp("lcn2initvals24", name) == 0) {
  388. return D11LCN2INITVALS24;
  389. } else if (strcmp("n0absinitvals16", name) == 0) {
  390. return D11N0ABSINITVALS16;
  391. } else if (strcmp("n0bsinitvals16", name) == 0) {
  392. return D11N0BSINITVALS16;
  393. } else if (strcmp("n0initvals16", name) == 0) {
  394. return D11N0INITVALS16;
  395. } else if (strcmp("ucode16_mimo", name) == 0) {
  396. return D11UCODE_OVERSIGHT16_MIMO;
  397. } else if (strcmp("ucode24_lcn", name) == 0) {
  398. return D11UCODE_OVERSIGHT24_LCN;
  399. }
  400. return 0;
  401. }
  402. static int brcmsmac_name_to_size_idx(const char *name)
  403. {
  404. if (strcmp("ucode16_mimo", name) == 0) {
  405. return D11UCODE_OVERSIGHT16_MIMOSZ;
  406. } else if (strcmp("ucode24_lcn", name) == 0) {
  407. return D11UCODE_OVERSIGHT24_LCNSZ;
  408. }
  409. return 0;
  410. }
  411. static void brcmsmac_clear_file(void)
  412. {
  413. FILE *f;
  414. char nbuf[4096];
  415. int r;
  416. r = snprintf(nbuf, sizeof(nbuf),
  417. "%s/brcm", cmdargs.target_dir);
  418. if (r >= sizeof(nbuf)) {
  419. fprintf(stderr, "name too long");
  420. exit(2);
  421. }
  422. r = mkdir(nbuf, 0770);
  423. if (r && errno != EEXIST) {
  424. perror("failed to create output directory");
  425. exit(2);
  426. }
  427. r = snprintf(nbuf, sizeof(nbuf),
  428. "%s/brcm/bcm43xx-0.fw", cmdargs.target_dir);
  429. if (r >= sizeof(nbuf)) {
  430. fprintf(stderr, "name too long");
  431. exit(2);
  432. }
  433. f = fopen(nbuf, "w");
  434. if (!f) {
  435. perror("failed to open data file");
  436. exit(2);
  437. }
  438. fclose(f);
  439. r = snprintf(nbuf, sizeof(nbuf),
  440. "%s/brcm/bcm43xx_hdr-0.fw", cmdargs.target_dir);
  441. if (r >= sizeof(nbuf)) {
  442. fprintf(stderr, "name too long");
  443. exit(2);
  444. }
  445. f = fopen(nbuf, "w");
  446. fclose(f);
  447. }
  448. static void brcmsmac_write_file(int idx, uint8_t *buf, uint32_t len)
  449. {
  450. FILE *f;
  451. FILE *h;
  452. char nbuf[4096];
  453. int r;
  454. int offset;
  455. struct firmware_hdr fw_hdr;
  456. r = snprintf(nbuf, sizeof(nbuf),
  457. "%s/brcm/bcm43xx-0.fw", cmdargs.target_dir);
  458. if (r >= sizeof(nbuf)) {
  459. fprintf(stderr, "name too long");
  460. exit(2);
  461. }
  462. f = fopen(nbuf, "a");
  463. if (!f) {
  464. perror("failed to open data file");
  465. exit(2);
  466. }
  467. fseek(f, 0L, SEEK_END);
  468. r = snprintf(nbuf, sizeof(nbuf),
  469. "%s/brcm/bcm43xx_hdr-0.fw", cmdargs.target_dir);
  470. if (r >= sizeof(nbuf)) {
  471. fprintf(stderr, "name too long");
  472. exit(2);
  473. }
  474. h = fopen(nbuf, "a");
  475. if (!h) {
  476. perror("failed to open header file");
  477. exit(2);
  478. }
  479. fseek(h, 0L, SEEK_END);
  480. offset = ftell(f);
  481. fw_hdr.offset = to_le32(offset);
  482. fw_hdr.len = to_le32(len);
  483. fw_hdr.idx = to_le32(idx);
  484. if (fwrite(&fw_hdr, sizeof(fw_hdr), 1, h) != 1) {
  485. perror("failed to write file");
  486. exit(2);
  487. }
  488. fclose(h);
  489. if (fwrite(buf, 1, len, f) != len) {
  490. perror("failed to write file");
  491. exit(2);
  492. }
  493. fclose(f);
  494. }
  495. static void brcmsmac_add_dummy_entries(void)
  496. {
  497. uint8_t buf[4] = {0};
  498. brcmsmac_write_file(D11N0ABSINITVALS16, buf, 4);
  499. brcmsmac_write_file(D11UCODE_OVERSIGHT_BOMMAJOR, buf, 4);
  500. brcmsmac_write_file(D11UCODE_OVERSIGHT_BOMMINOR, buf, 4);
  501. }
  502. static void brcmsmac_extract(FILE *f, const struct extract *extract,
  503. uint32_t flags)
  504. {
  505. uint8_t *buf;
  506. size_t data_length;
  507. int ucode_rev = 0;
  508. int brcmsmac_idx = 0;
  509. be32_t size;
  510. brcmsmac_idx = brcmsmac_name_to_idx(extract->name);
  511. if (!brcmsmac_idx)
  512. return;
  513. printf("%s %s\n",
  514. cmdargs.mode == FWCM_IDENTIFY ? "Contains" : "Extracting",
  515. extract->name);
  516. buf = read_object(f, extract);
  517. switch (extract->type) {
  518. case EXT_UCODE_3:
  519. ucode_rev += 1;
  520. case EXT_UCODE_2:
  521. ucode_rev += 1;
  522. case EXT_UCODE_1:
  523. ucode_rev += 1;
  524. data_length = extract->length;
  525. if (flags & FW_FLAG_LE)
  526. swap_endianness_ucode(buf, data_length);
  527. analyse_ucode(ucode_rev, buf, data_length);
  528. swap_endianness_ucode(buf, data_length);
  529. size = to_le32(data_length);
  530. break;
  531. case EXT_PCM:
  532. data_length = extract->length;
  533. if (!(flags & FW_FLAG_LE))
  534. swap_endianness_pcm(buf, data_length);
  535. size = to_le32(data_length);
  536. break;
  537. case EXT_IV: {
  538. data_length = extract->length;
  539. if (!(flags & FW_FLAG_LE)) {
  540. struct iv *in = (struct iv *)buf;
  541. int i;
  542. for (i = 0; i < data_length / sizeof(struct iv); i++, in++)
  543. swap_endianness_iv(in);
  544. }
  545. size = to_le32(data_length);
  546. break;
  547. }
  548. default:
  549. exit(255);
  550. }
  551. brcmsmac_write_file(brcmsmac_idx, buf, data_length);
  552. int size_idx = brcmsmac_name_to_size_idx(extract->name);
  553. if (size_idx)
  554. brcmsmac_write_file(size_idx, (uint8_t *)&size, 4);
  555. free(buf);
  556. }
  557. static void print_banner(void)
  558. {
  559. printf("b43-fwcutter version " FWCUTTER_VERSION "\n");
  560. }
  561. static void print_file(const struct file *file)
  562. {
  563. char filename[30];
  564. char shortname[30];
  565. if (file->flags & FW_FLAG_V4)
  566. printf(V4_FW_DIRNAME "\t\t");
  567. else
  568. printf(V3_FW_DIRNAME "\t");
  569. if (strlen(file->name) > 20) {
  570. strncpy(shortname, file->name, 20);
  571. shortname[20] = '\0';
  572. snprintf(filename, sizeof(filename), "%s..", shortname);
  573. } else
  574. strcpy (filename, file->name);
  575. printf("%s\t", filename);
  576. if (strlen(filename) < 8) printf("\t");
  577. if (strlen(filename) < 16) printf("\t");
  578. printf("%s\t", file->ucode_version);
  579. if (strlen(file->ucode_version) < 8) printf("\t");
  580. printf("%s\n", file->md5);
  581. }
  582. static void print_supported_files(void)
  583. {
  584. int i;
  585. print_banner();
  586. printf("\nExtracting firmware is possible "
  587. "from these binary driver files.\n"
  588. "Please read http://linuxwireless.org/en/users/Drivers/b43#devicefirmware\n\n");
  589. printf("<driver>\t"
  590. "<filename>\t\t"
  591. "<microcode>\t"
  592. "<MD5 checksum>\n\n");
  593. /* print for legacy driver first */
  594. for (i = 0; i < ARRAY_SIZE(files); i++)
  595. if (file_ok(&files[i]) && !(files[i].flags & FW_FLAG_V4))
  596. print_file(&files[i]);
  597. for (i = 0; i < ARRAY_SIZE(files); i++)
  598. if (file_ok(&files[i]) && files[i].flags & FW_FLAG_V4)
  599. print_file(&files[i]);
  600. printf("\n");
  601. }
  602. static const struct file *find_file(FILE *fd)
  603. {
  604. unsigned char buffer[16384], signature[16];
  605. struct MD5Context md5c;
  606. char md5sig[33];
  607. int i;
  608. MD5Init(&md5c);
  609. while ((i = (int) fread(buffer, 1, sizeof(buffer), fd)) > 0)
  610. MD5Update(&md5c, buffer, (unsigned) i);
  611. MD5Final(signature, &md5c);
  612. snprintf(md5sig, sizeof(md5sig),
  613. "%.2x%.2x%.2x%.2x%.2x%.2x%.2x%.2x"
  614. "%.2x%.2x%.2x%.2x%.2x%.2x%.2x%.2x",
  615. signature[0], signature[1], signature[2], signature[3],
  616. signature[4], signature[5], signature[6], signature[7],
  617. signature[8], signature[9], signature[10], signature[11],
  618. signature[12], signature[13], signature[14], signature[15]);
  619. for (i = 0; i < ARRAY_SIZE(files); i++) {
  620. if (file_ok(&files[i]) &&
  621. strcasecmp(md5sig, files[i].md5) == 0) {
  622. printf("This file is recognised as:\n");
  623. printf(" filename : %s\n", files[i].name);
  624. printf(" version : %s\n", files[i].ucode_version);
  625. printf(" MD5 : %s\n", files[i].md5);
  626. return &files[i];
  627. }
  628. }
  629. printf("Sorry, the input file is either wrong or "
  630. "not supported by b43-fwcutter.\n");
  631. printf("This file has an unknown MD5sum %s.\n", md5sig);
  632. return NULL;
  633. }
  634. static void print_usage(int argc, char *argv[])
  635. {
  636. print_banner();
  637. printf("\nA tool to extract firmware for a Broadcom 43xx device\n");
  638. printf("from a proprietary Broadcom 43xx device driver file.\n");
  639. printf("\nUsage: %s [OPTION] [proprietary-driver-file]\n", argv[0]);
  640. printf(" --unsupported "
  641. "Allow working on extractable but unsupported drivers\n");
  642. printf(" -l|--list "
  643. "List supported driver versions\n");
  644. printf(" -b|--brcmsmac "
  645. "create firmware for brcmsmac\n");
  646. printf(" -i|--identify "
  647. "Only identify the driver file (don't extract)\n");
  648. printf(" -w|--target-dir DIR "
  649. "Extract and write firmware to DIR\n");
  650. printf(" -v|--version "
  651. "Print b43-fwcutter version\n");
  652. printf(" -h|--help "
  653. "Print this help\n");
  654. printf("\nExample: %s -w /lib/firmware wl_apsta.o\n"
  655. " to extract the firmware blobs from wl_apsta.o and store\n"
  656. " the resulting firmware in /lib/firmware\n",
  657. argv[0]);
  658. }
  659. static int do_cmp_arg(char **argv, int *pos,
  660. const char *template,
  661. int allow_merged,
  662. char **param)
  663. {
  664. char *arg;
  665. char *next_arg;
  666. size_t arg_len, template_len;
  667. arg = argv[*pos];
  668. next_arg = argv[*pos + 1];
  669. arg_len = strlen(arg);
  670. template_len = strlen(template);
  671. if (param) {
  672. /* Maybe we have a merged parameter here.
  673. * A merged parameter is "-pfoobar" for example.
  674. */
  675. if (allow_merged && arg_len > template_len) {
  676. if (memcmp(arg, template, template_len) == 0) {
  677. *param = arg + template_len;
  678. return ARG_MATCH;
  679. }
  680. return ARG_NOMATCH;
  681. } else if (arg_len != template_len)
  682. return ARG_NOMATCH;
  683. *param = next_arg;
  684. }
  685. if (strcmp(arg, template) == 0) {
  686. if (param) {
  687. /* Skip the parameter on the next iteration. */
  688. (*pos)++;
  689. if (!*param) {
  690. printf("%s needs a parameter\n", arg);
  691. return ARG_ERROR;
  692. }
  693. }
  694. return ARG_MATCH;
  695. }
  696. return ARG_NOMATCH;
  697. }
  698. /* Simple and lean command line argument parsing. */
  699. static int cmp_arg(char **argv, int *pos,
  700. const char *long_template,
  701. const char *short_template,
  702. char **param)
  703. {
  704. int err;
  705. if (long_template) {
  706. err = do_cmp_arg(argv, pos, long_template, 0, param);
  707. if (err == ARG_MATCH || err == ARG_ERROR)
  708. return err;
  709. }
  710. err = ARG_NOMATCH;
  711. if (short_template)
  712. err = do_cmp_arg(argv, pos, short_template, 1, param);
  713. return err;
  714. }
  715. static int parse_args(int argc, char *argv[])
  716. {
  717. int i, res;
  718. char *param;
  719. if (argc < 2)
  720. goto out_usage;
  721. for (i = 1; i < argc; i++) {
  722. res = cmp_arg(argv, &i, "--list", "-l", NULL);
  723. if (res == ARG_MATCH) {
  724. cmdargs.mode = FWCM_LIST;
  725. continue;
  726. } else if (res == ARG_ERROR)
  727. goto out;
  728. res = cmp_arg(argv, &i, "--version", "-v", NULL);
  729. if (res == ARG_MATCH) {
  730. print_banner();
  731. return 1;
  732. } else if (res == ARG_ERROR)
  733. goto out;
  734. res = cmp_arg(argv, &i, "--help", "-h", NULL);
  735. if (res == ARG_MATCH)
  736. goto out_usage;
  737. else if (res == ARG_ERROR)
  738. goto out;
  739. res = cmp_arg(argv, &i, "--identify", "-i", NULL);
  740. if (res == ARG_MATCH) {
  741. cmdargs.mode = FWCM_IDENTIFY;
  742. continue;
  743. } else if (res == ARG_ERROR)
  744. goto out;
  745. res = cmp_arg(argv, &i, "--brcmsmac", "-b", NULL);
  746. if (res == ARG_MATCH) {
  747. cmdargs.mode = FWCM_EXTRACT_BRCMSMAC;
  748. continue;
  749. } else if (res == ARG_ERROR)
  750. goto out;
  751. res = cmp_arg(argv, &i, "--unsupported", NULL, NULL);
  752. if (res == ARG_MATCH) {
  753. cmdargs.unsupported = 1;
  754. continue;
  755. } else if (res == ARG_ERROR)
  756. goto out;
  757. res = cmp_arg(argv, &i, "--target-dir", "-w", &param);
  758. if (res == ARG_MATCH) {
  759. cmdargs.target_dir = param;
  760. continue;
  761. } else if (res == ARG_ERROR)
  762. goto out;
  763. cmdargs.infile = argv[i];
  764. break;
  765. }
  766. if (!cmdargs.infile && cmdargs.mode != FWCM_LIST)
  767. goto out_usage;
  768. return 0;
  769. out_usage:
  770. print_usage(argc, argv);
  771. out:
  772. return -1;
  773. }
  774. int main(int argc, char *argv[])
  775. {
  776. FILE *fd;
  777. const struct file *file;
  778. const struct extract *extract;
  779. int err;
  780. const char *dir;
  781. cmdargs.target_dir = ".";
  782. err = parse_args(argc, argv);
  783. if (err == 1)
  784. return 0;
  785. else if (err != 0)
  786. return err;
  787. if (cmdargs.mode == FWCM_LIST) {
  788. print_supported_files();
  789. return 0;
  790. }
  791. fd = fopen(cmdargs.infile, "rb");
  792. if (!fd) {
  793. fprintf(stderr, "Cannot open input file %s\n", cmdargs.infile);
  794. return 2;
  795. }
  796. err = -1;
  797. file = find_file(fd);
  798. if (!file)
  799. goto out_close;
  800. if (file->flags & FW_FLAG_V4)
  801. dir = V4_FW_DIRNAME;
  802. else
  803. dir = V3_FW_DIRNAME;
  804. if (cmdargs.mode == FWCM_EXTRACT_BRCMSMAC) {
  805. brcmsmac_clear_file();
  806. extract = file->extract;
  807. while (extract->name) {
  808. brcmsmac_extract(fd, extract, file->flags);
  809. extract++;
  810. }
  811. brcmsmac_add_dummy_entries();
  812. } else {
  813. extract = file->extract;
  814. while (extract->name) {
  815. extract_or_identify(fd, dir, extract, file->flags);
  816. extract++;
  817. }
  818. }
  819. err = 0;
  820. out_close:
  821. fclose(fd);
  822. return err;
  823. }