I mentioned this bug previously.  Here's the basic explanation of what's
wrong with the current code in fwcutter.c:

     1. Consider a little-endian binary containing ucode firmware.  Each
        8-byte instruction is stored with byte order 01234567 (with 0
        being the least significant byte.)
     2. In extract_or_identify(), the byte order is
        reversed /32-bit-wise/ since the b43 driver expects big-endian
        32-bit integers.  After this transformation, the instructions
        are stored with byte order 32107654.
     3. In disasm_ucode_1(),  disasm_ucode_2(), disasm_ucode_3(), the
        upper and lower halves of the instruction are swapped.  Now the
        instruction's byte order is 76543210.
     4. These routines treat the instruction as a *NATIVE* 64-bit
        integer, and break it down into bitfields.  The byte order is
        correct on big-endian hosts, but completely reversed for
        little-endians hosts.

The source is quite confusing because it manipulates the ucode as 4-byte
quantities for purposes of output endianness, but then tries to analyze
them as 8-byte instructions.  This is presumably the source of the bug.

My patch avoids reordering the ucode endianness until after running
analyse_ucode() to extract version information.  It passes the firmware
flags to analyse_ucode() so that it can correctly convert each
instruction to a native-endian 64-bit integer.

TO TEST THIS PATCH: try extracting firmware FW13 on a little-endian
host.  The unpatched b43-fwcutter will not identify ucode
version/revision/date/time, but with this patch it will.

Dan
--- fwcutter.c.orig	2009-06-02 11:52:55.000000000 -0400
+++ fwcutter.c	2009-09-15 14:26:49.000000000 -0400
@@ -42,6 +42,7 @@
 #include <sys/endian.h>
 #else
 #include <byteswap.h>
+#include <endian.h>
 #endif
 
 #include "md5.h"
@@ -143,24 +144,19 @@
 		printf("  ucode revision: %d\n", val);
 		break;
 	case 2:
-		printf("  ucode date:     %.4d-%.2d-%.2d\n",
+		printf("  ucode date:	  %.4d-%.2d-%.2d\n",
 		       2000 + (val >> 12), (val >> 8) & 0xF, val & 0xFF);
 		break;
 	case 3:
-		printf("  ucode time:     %.2d:%.2d:%.2d\n",
-		       val >> 11, (val >> 5) & 0x3f, val & 0x1f);
+		printf("  ucode time:	  %.2d:%.2d:%.2d\n",
+		       val >> 11, (val >> 5) & 0x3f, (val & 0x1f) << 1);
 		break;
 	}
 }
 
 static void disasm_ucode_1(uint64_t in, struct insn *out)
 {
-	/* xxyyyzzz00oooooX -> ooooo Xxx yyy zzz
-	 * if we swap the upper and lower 32-bits first it becomes easier:
-	 * 00oooooxxxyyyzzz -> ooooo xxx yyy zzz
-	 */
-	in = (in >> 32) | (in << 32);
-
+	/* 00oooooxxxyyyzzz -> ooooo xxx yyy zzz */
 	out->op3	= in & 0xFFF;
 	out->op2	= (in >> 12) & 0xFFF;
 	out->op1	= (in >> 24) & 0xFFF;
@@ -170,12 +166,7 @@
 
 static void disasm_ucode_2(uint64_t in, struct insn *out)
 {
-	/* xxyyyzzz0000oooX -> ooo Xxx yyy zzz
-	 * if we swap the upper and lower 32-bits first it becomes easier:
-	 * 0000oooxxxyyyzzz -> ooo xxx yyy zzz
-	 */
-	in = (in >> 32) | (in << 32);
-
+	/* 0000oooxxxyyyzzz -> ooo xxx yyy zzz */
 	out->op3	= in & 0xFFF;
 	out->op2	= (in >> 12) & 0xFFF;
 	out->op1	= (in >> 24) & 0xFFF;
@@ -189,8 +180,6 @@
 	 * like 2, but each operand has one bit more; appears
 	 * to use the same instruction set slightly extended
 	 */
-	in = (in >> 32) | (in << 32);
-
 	out->op3	= in & 0x1FFF;
 	out->op2	= (in >> 13) & 0x1FFF;
 	out->op1	= (in >> 26) & 0x1FFF;
@@ -198,24 +187,28 @@
 	/* the rest of the in word should be zero */
 }
 
-static void analyse_ucode(int ucode_rev, uint8_t *data, uint32_t len)
+static void analyse_ucode(int ucode_rev, uint8_t *data, uint32_t len, uint32_t flags)
 {
 	uint64_t *insns = (uint64_t*)data;
+	uint64_t inscode;
 	struct insn insn;
 	uint32_t i;
 
 	for (i=0; i<len/sizeof(*insns); i++) {
+		if (flags & FW_FLAG_LE) inscode = le64toh(insns[i]);
+		else inscode = be64toh(insns[i]);
+
 		switch (ucode_rev) {
 		case 1:
-			disasm_ucode_1(insns[i], &insn);
+			disasm_ucode_1(inscode, &insn);
 			print_ucode_version(&insn);
 			break;
 		case 2:
-			disasm_ucode_2(insns[i], &insn);
+			disasm_ucode_2(inscode, &insn);
 			print_ucode_version(&insn);
 			break;
 		case 3:
-			disasm_ucode_3(insns[i], &insn);
+			disasm_ucode_3(inscode, &insn);
 			print_ucode_version(&insn);
 			break;
 		}
@@ -376,9 +369,18 @@
 	case EXT_UCODE_1:
 		ucode_rev += 1;
 		data_length = extract->length;
+		/* AFAICT, the instructions are stored as uint64 in
+		 * the standard endianness for the target.  So
+		 * analyse_ucode() needs to know their endianness.
+		 */
+		analyse_ucode(ucode_rev, buf, data_length, flags);
+
+		/* ucode firmware files require buf to be stored as a
+		 * series of big-endian 32-bit integers, so we convert
+		 * them if needed before outputting them.
+		 */
 		if (flags & FW_FLAG_LE)
 			swap_endianness_ucode(buf, data_length);
-		analyse_ucode(ucode_rev, buf, data_length);
 		hdr.type = FW_TYPE_UCODE;
 		hdr.size = to_be32(data_length);
 		break;
_______________________________________________
Bcm43xx-dev mailing list
[email protected]
https://lists.berlios.de/mailman/listinfo/bcm43xx-dev

Reply via email to