IBM non-authorised Mini-PCI wireless card, Sir?

IBM make really good Laptops. Sometimes they just need hacking...

They're called ThinkPad's and IBM have got the sense that when they have a nearly-perfect design they shouldn't change it. These machines also tend to run GNU/Linux better than most laptops and have three mouse buttons. I have a ThinkPad R31 and Matthew Garrett decided to buy an IBM ThinkPad X40 which came with a built-in 2.4Ghz aeriel and empty Mini-PCI slot. It was just asking to have a wireless network card installed. Now there are many wifi cards out there, so there's no point buying an IBM ultra-expensive one for £150.00 when any old Mini-PCI card will do (PCI is a standard after all...).

Well, IBM—in their American FCC-paranoidian wisdom—check if there is a network card inserted on bootup and see whether they have them in their list. That list of secret PCI-ID's looks like what follows—and what follows is taken from a `bios.img' dumped from the flash of an ThinkPad X40 laptop.

...And if you have an HP laptop

HP also restrict the IDs that are allowed; This guys did some similar funky stuff to modify the BIOS of an HP ZE2000 to accept his Atheros Mini-PCI card.

MiniPCI cards that are ``Supported'' in the TP X40

It's not so much which cards are supported, as which cards don't bring up an annoying message saying:

1802: Unauthorized network card is plugged in - Power off and remove the miniPCI network card.
OffsetPCI ID & Sub-systemManufacturerCard
0x6acc0168c:1014/17ab:8331AtherosIBM 11a/b/g Wireless LAN Mini PCI Adapter (P/N:31P9701)
0x6acc98086:1043/8086:2551IntelIBM 802.11b. Intel Pro/Wireless 2100B
0x6acd2168c:0013/1468:0408Atheros802.11a/b/g
0x6acdb8086:4220/8086:2711IntelIBM 802.11b/g. Intel Pro/Wireless 2200BG (Centrino?)
0x6ace48086:4220/8086:2712IntelIBM 802.11b/g. Intel Pro/Wireless 2200BG (Centrino?)
0x6aced0000:0000/0000:0000(null)End-of-array marker (or possibley a spare?)
0x780111260:3873/8086:1380Harris SemiconductorPrism 2.5 Wavelan chipset (Intel)
0x7801b1260:3873/1668:0406Harris SemiconductorPrism 2.5 Wavelan chipset (Actiontec)
aka/ IBM High Rate Wireless LAN Card (Orinoco/Prism 2.5/HostAP Linux driver)

Now say you had a card with PCI-ID 8086:1043/8086:2527 that was only one byte different from the second one in the list, you'd be annoyed wouldn't you...

ThinkPad X40 Bios Version 1.14 (1UET63WW)

Phoenix FirstBIOS(tm) Notebook Pro Version 2.0 for IBM ThinkPad (NoteBIOS Pro):

0006aca0  00 08 cd 10 59 5b 58 c3  14 10 57 05 42 43 50 55  |....Y[X...W.BCPU|
0006acb0  53 42 01 01 10 00 06 14  10 2e 05 58 c3 e9 77 1a  |SB.........X..w.|
0006acc0  8c 16 14 10 ab 17 31 83  00 86 80 43 10 86 80 51  |......1....C...Q|
          ^^^^^^^^^^^-^^^^^^^^^^^     ^^^^^^^^^^^-^^^^^^^^                    
0006acd0  25 00 8c 16 13 00 68 14  08 04 00 86 80 20 42 86  |%.....h...... B.|
          ^^    ^^^^^^^^^^^-^^^^^^^^^^^^    ^^^^^^^^^^^-^^                    
0006ace0  80 11 27 00 86 80 20 42  86 80 12 27 00 00 00 00  |..'... B...'....|
          ^^^^^^^^    ^^^^^^^^^^^--^^^^^^^^^^^    ^^^^^^^^                    
0006acf0  00 00 00 00 00 00 01 05  03 07 00 20 80 00 00 10  |........... ....|
          ^^-^^^^^^^^^^^                                                      

Grep for the unrelated string `BCPUSB' to locate the first set of PCI ID's. Later on, in a completely different place, are the two Prism 2.5 cards:

00078010  c3 60 12 73 38 86 80 13  25 1a 05 60 12 73 38 68  |.`.s8...%..`.s8h|
             ^^^^^^^^^^^-^^^^^^^^^^^^       ^^^^^^^^^^^-^^                    
00078020  16 06 04 27 02 bb f0 00  ba 19 00 9a 11 54 00 f0  |...'.........T..|
          ^^^^^^^^                                                            

This BIOS Image in question was approximately 1029kB bytes in size. The image suppiled by IBM on their ``Flash Upgrade Disk'' is about 650kB long and is a compressed version of the finally image. I haven't worked out the compression system yet (it seems quite simple and appears to drop markers with the top bit set, eg. 0xdf and 0xff.

Phoenix FirstBIOS Modular BIOS's

I've just learnt alot about BIOS's. A company producing a machine doesn't write their own code from scratch, they buy a VGA BIOS from one company, a PXE booter from another, a main BIOS from another, SCSI, RAID, TCPA and the like. There are then linked together in a BIOS image which is flashed as a whole onto the computer. Think of the modules as files and the image as a small read-only filesystem.

[generalisation] There are three types of x86 BIOS around, AMI, Award and Phoenix. Near the start of the new century when there were calls to ditch classical BIOS's and remove legacy fluff, Award and Phoenix (who in turn seems to own Award now) came out with two new streams of BIOS. The Award Medallion BIOS and the Phoenix FirstBIOS. Both of these have new flash layouts so think of it as there being five common variants.

All the types are very similar, yet slightly different. They all contain modules, some of which are compressed. If compression is used, it is a variant of LHA; such as LZINT (FirstBIOS), LZSS (Phoenix) or LZARI (Award?). All the flash images have a BCP ``BIOS Configuration Parameter'' section and support for Bootup Logos. Finally, the BIOS images are throughly undocumented formats unless you have licensed the source.

Tools

IBM has chosen the Phoenix FirstBIOS for it's recent ThinkPad laptops, more specifically the Notebook Pro variant. Now when you turn on the machine it all looks very IBM-ish, but in reality these are just LOGO files (.BMP Microsoft bitmaps) that were added as modules to the flash image. Lets look at some tools, which are annoyingly all DOS based and/or written in crazy languages like Pascal or Visual Basic.

phlash16.exe
This is the offical Phoenix flasher program that will write the contents of a BIOS image to flash, or backup the system flash to a file under DOS.
winphlash.exe
A windows-based flasher also produced by Phoenix.
phnxdeco
A program written by Anthony Borisow for taking apart Phoenix images. Runs on Linux and DOS, and some versions come with GPL'ed source. The most recent version (without source) is available as a binary download and is the only one capable of extracting the FirstBIOS images. This program can be used with varied success to list, extract and produce a reverse-engineered script file. http://biosgfx.euro.ru/phoenixdeco/
prepare.exe
Phoneix produced utility that takes a script (.scr) file along with binary files and produces Module (.mod
catenate.exe
Allegedly an offical program, this is supposed to take a set of .mod compiled/compressed module files and produce an image.
biosedit.exe/BEDemo.zip
BIOS Editor is Phoenix's offical solution for messing about with flash images; you can get a Windows demo (non-saving) of it from: ftp://dcsdemo:phoenix@dcsdemo.phoenix.com/firstbios/BEDemo.zip
uniflash
GPL'ed flasher program that works with many chipsets. Pascal under DOS, but didn't work in this case.
awardmod
GPL'ed VB/C++ program for modifying recent Award Medallion images. No Use in this particular case.

BIOS Structure

Flash chips come in 128kB, 256kB, 512kB and 1MB sizes which are referred to as 1 Megabit, 2 Megabit... chips. The T40 BIOS is designed to fit on a 8Mb flash chip and so is just a little shorter than a megabyte (1029kB); this means that you should be careful when adding any additional modules. What? You can add modules? Yes, it's effectively a small filesystem with extra metadata about whether modules are compressed and where they should be loaded into memory.

Using ``./phnxdeco bios.img -ls'' it spews the following before crashing:

C  I   LEVEL      START       END     LENGTH  RATIO   LINK TO   FILEOFFSET
----   -----    ---------  ---------  ------  -----  ---------  ----------
X  0    NONE    FFFE 85DD  FFFE FFFD   7A06    100%  FFFE 7372    E85DDh
D  0    NONE    FFFE 7372  FFFE 85DC   1250    100%  FFFE 5347    E7372h
C  0    NONE    FFFE 5347  FFFE 7371   2010    100%  FFFE 4EFC    E5347h
G  0    NONE    FFFE 4EFC  FFFE 5346    430    100%  FFFE 4DED    E4EFCh
A  0    NONE    FFFE 4DED  FFFE 4EFB     F4    100%  FFFE 4D9F    E4DEDh
A  1    NONE    FFFE 4D9F  FFFE 4DEC     33    100%  FFFE 4D51    E4D9Fh
A  2    NONE    FFFE 4D51  FFFE 4D9E     33    100%  FFFE 4CE4    E4D51h
L  1    NONE    FFFE 49F5  FFFE 4C21    212    100%  FFFE 4708    E49F5h
L  2    NONE    FFFE 4708  FFFE 49F4    2D2    100%  FFFE 46D3    E4708h
R  0   LZINT    FFFD 0720  FFFD 850A   7DD0     49%  FFFC A705    D0720h
R  1    NONE    FFFC A705  FFFD 071F   6000    100%  FFFC 2EEA    CA705h
R  2    NONE    FFFC 2EEA  FFFC A704   7800    100%  FFFB E9F8    C2EEAh
E  0   LZINT    FFFB E9F8  FFFC 2EE9   44D7     40%  FFFB B16D    BE9F8h
T  0   LZINT    FFFB B16D  FFFB E9F7   3870     48%  FFFA BCF2    BB16Dh
M  0    NONE    FFFA BCF2  FFFB B16C   F460    100%  FFF9 8D77    ABCF2h
Q  0    NONE    FFF9 8D77  FFFA BCF1  12F60    100%  FFF9 42B6    98D77h
A  3   LZINT    FFF9 42B6  FFF9 8D76   4AA6     37%  FFF9 1A9B    942B6h

You'll notice that the files don't have names, but like a Zip file they have start and end locations, lengths and some are compressed. The left-hand two columns are Class and Instance. The single letter can be thought of as the file name and the second column makes sure there are classes. L1 and L2 are the first and second bitmaps Logos in the image. The names translate as follows: (list from phnxdeco source).

case 'A': return("ACPI");		// ACPI AML/Table
case 'B': return("BIOSCODE");           // BIOSCode Group
case 'C': return("UPDATE");             // CPU P6Update
case 'D': return("DISPLAY");            // Display
case 'E': return("SETUP");              // Editor
case 'F': return("FONT");               // 
case 'G': return("DECOMPCODE");		// 
case 'I': return("BOOTBLOCK");          // 
case 'L': return("LOGO");               // Logo Bitmap
case 'M': return("MISER");              // Power Management
case 'N': return("ROMPILOTLOAD");       //
case 'O': return("NETWORK");            // Networking
case 'P': return("ROMPILOTINIT");       //
case 'R': return("OPROM");              // PCI Option ROM
case 'S': return("STRINGS");            // User Interface Strings
case 'T': return("TEMPLATE");           // 
case 'U': return("USER");               //
case 'X': return("ROMEXEC");            // Bootstraping?
case 'W': return("WAV");                // Audio
default: return("User-Defined");

This ThinkPad BIOS also contains additional type/files called '*' 'H' 'K' '<' and 'Q' which all seem to be related to the TCPA (Trust Computing) components in the ThinkPad. The default: statement above means that phnxdeco screws up extracting these weird files. Wish I had the source to the latest version to fix it :-(.

Get to the point!

This long excursion with trying to explode and then recombine the BIOS images was to investigate checksuming. What would happen if we just hex-edited the bytes in an individual module and recombined them? Well, it turns out not much happens, there are no module-specific checksums, but there is a global checksum for the whole BIOS.

000e86c0  45 58 54 44 15 c5 0a b6  00 00 00 00 43 4b 53 4d  |EXTD........CKSM|
                      ^^^^^^^^^^^                                             
                32-bit additive checksum                    Extended  Checksum

The checksum is such that when the entire file is added up, four bytes at a time, the result is zero. To make this happen, the value in-between the Extended Checksum brackets is changed. In short, if you change a value upwards (add), then you need to subtract the same difference from the correctly aligned byte of the checksum.

Putting theory into practice

This information is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. When you fry your laptop and have to send it back to IBM, do not blame me!

Remember that wifi card with ID 8086:1043/8086:2527 ? After hexediting the file, the differences in the 1st, 2nd and 3rd columns are zero; but in the 4th column the difference is (0x51+0x27 = -0x2a) meaning that +0x2a must be added onto the checksum in the correct column—the net effect must be ``zero''. (0xb6+0x2a=0xe0):

           1--2--3--4   1--2--3--4  1--2--3--4   1--2--3--4
 0006acb0  53 42 01 01 10 00 06 14  10 2e 05 58 c3 e9 77 1a  |SB.........X..w.|
-0006acc0  8c 16 14 10 ab 17 31 83  00 86 80 43 10 86 80 51  |......1....C...Q|
+0006acc0  8c 16 14 10 ab 17 31 83  00 86 80 43 10 86 80 27  |......1....C...Q|
 0006acd0  25 00 8c 16 13 00 68 14  08 04 00 86 80 20 42 86  |%.....h...... B.|

 000e86b0  44 2a 5c 2a 71 2a 79 2a  8c 2a 94 2a 8e 6c 63 00  |D*\*q*y*.*.*.lc.|
-000e86c0  45 58 54 44 15 c5 0a b6  00 00 00 00 43 4b 53 4d  |EXTD........CKSM|
+000e86c0  45 58 54 44 15 c5 0a e0  00 00 00 00 43 4b 53 4d  |EXTD........CKSM|
 000e86d0  58 09 b4 1b 6a 24 23 27  55 00 e5 00 02 50 d8 80  |X...j$#'U....P..|

Hey Presto, save, burn, flash, insert, boot and congratulate yourself for having beaten Big Blue at their own game—and saved £100.00!

Further Investigation

Nota Bene: Need to confirm the exact ordering of the bytes in all cases (little-endian?). XOR produces a similar result but with an inverted top-bit, proving that the checksum is additive and has carry issues. Following diff was generated using prepare and catenate programs and is known to be correct. The phflash also has a flag for validating that the checksum is correct:

-0008ab60  51 25 00 8c 16 13 00 68  14 08 04 00 86 80 20 42  |Q%.....h...... B|
+0008ab60  68 24 00 8c 16 13 00 68  14 08 04 00 86 80 20 42  |h$.....h...... B|

-000a00e0  8c 2a 94 2a 8e 6c 63 00  45 58 54 44 f4 6e 3e 7b  |.*.*.lc.EXTD.n>{|
+000a00e0  8c 2a 94 2a 8e 6c 63 00  45 58 54 44 dd 6f 3e 7b  |.*.*.lc.EXTD.o>{|

>>> hex(0x5125-0x6824+0xf46e) + ' Correct'
'0xdd6f Correct'
>>> hex(0x5125^0x6824^0xf46e) + ' Wrong'
'0xcd6f Wrong'
>>> hex(0x2551+0x2468+0x6ef4) + ' Wrong'
'0xb8ad Wrong'

In Addition, some of the following could be lines of investigate:

Taking it further, Sravan Ravinutala reports that he had modified his BIOS and accidently flashed it with an incorrect checksum. Thankfully his machine booted fine again and so it would appear that the checksum is only there for the benefit of the flash utility to check integrity of the file and not for the BIOS itself to check. If you flash the image using the `devbios' support under Linux then you should just be able to hex-edit the BIOS image.

Nota Bene: Do not rely on this.

BIOS Flashing under Linux

In 1998, Stefan Reinauer announced a `/dev/bios' device and then announced an update a couple of months later; then again in 2000. It seems to have lived in OpenBIOS CVS since. I got this driver to work on my ThinkPad R31 (it intelligently knows about a whole series of Flash types) and using it was as easily as typing:

# make
# insmod ./bios.o
# cat /proc/bios
# dd if=/dev/bios bs=4k of=./my-bios.img

[no reboot required!] The reason for doing it as a kernel module rather than a userspace program seems to be to guarantee time-critical requirements when writing to a chip, if you were doing the equivalent in userspace, you'd have to do lots of ugly hacks to stop your program with being task-switched to another process in the middle:

In 2003 Jeremy Jackson ported the code to the Linux MTD (Memory Technology Device) framework which means means [in theory] you can use the flash socket on a network card to reflash your system BIOS. Think of MTD as covering all the types of Flash that aren't Compact Flash/SD Cards. Specifically, MTD devices have to be [silently] erased first and things—yes, I know the name sucks and as always confused me:

FreeBIOS (aka LinuxBIOS) also appear to have some tools. I have so-far failed to get these to work, even out of CVS:

Paul Sladen and Matthew Garrett. 2004-04-27 → 2004-04-29 by the end of which, Matthew had a working laptop with MiniPCI wireless