Hello,

I would like to answer your question about how I found out about the BIOS structure. Actually I didn't know anything about a two weeks ago, I just have general knowledge how computers work. First obstacle was the compression of whole BIOS file, which IBM/Lenovo uses, but search for first bytes of the file (BCPVPD) revealed some decompression utility (and that it can be called COMPIBM) and later even the existence of phcomp.exe (which was very hard to find though).
So now I have the BIOS file... but I know nothing about it and windows binaries of phnxdeco completely fail at splitting it to modules. Yet there seem to be only old binaries for windows and some linux distribution repositories contain a newer one, so I finally decide to download it on some linux computer and it mostly works. The BIOS from Lenovo contains modules which result in forbidden characters in filename, so I do a quick fix to the C code of phnxdeco (luckily it's available from the linux repositories) and eventually have it split.
Later I used cygwin to create a "windows" binary - cygwin is actually a linux-like environment - and have to replace more characters, because windows also disallow asterisk, question mark and maybe something more, not sure now. This version is the one you can download from ender.in/bios.tools/.
Now I can see the card IDs in BiosCode5.
Searching forums also allows me to learn about existence of Phoenix BIOS editor, manage to find 2.0 and also 2.1 from Intel's site. Yet the first fails completely on the BIOS from Lenovo and the second complains it's too large. Eventually I manage to get 2.2 which can mostly open the BIOS, it extracts it all and the ends in a neverending loop.
Trying to use prepare/catenate, as learned from forums, on the files extracted by BIOS editor, it goes with one warning, the file is completely different from the previous one, I opt not to go this way.
I use prepare on the BiosCode5 with changed card ID. The resulting file is larger and I have to find out what to do. I use prepare/catenate to generate the whole BIOS and then use it again with the changed file. I open them both and compare, learn what changes in the headers of the modules and mimic that in the original BIOS file from Lenovo.

At this point, I do a first post here in this forum and have a modified BIOS ready, with some changed card ID. From what I know about the BIOS structure now, the modification was done correctly.

Bill Morrow points out it would be nice to skip the check completely. Now I haven't actually given it a thought, after a few tries with ndisasm, I go to find something better and get IDA Pro. A good one, really. It manages to auto-disassemble some parts of the BiosCode5 and by some sort of luck, I almost immediately notice the checking routine. I had almost no knowledge of assembler, but instructions as add, mov, jmp were pretty self-explaining and I remember I had some assembler guide on my harddrive, so I find it and learn how conditional jumps work and what are clc/stc. It seems apparent that when the function returns, the only difference is the carry flag (CF) set to zero or one, so that must be a return value.

Nice, modify stc->clc, use prepare.exe and yay, the compressed size does not change against original. Seems safer than needing to modify the structure of BIOS file.

Someone who says he has a T4x with bad video chip volunteers to test it and I agree. I do the modification for T40-T42 BIOS, it's pretty quick the second time. Yet the person is pretty unresponsive and I eventually end up testing it on someone's T30. Now there I found some similar check, which seems to return result different way, but by now I'm pretty sure I under some more asm instructions and can modify them, so I change to return always the same result.

The person flashes T30 with it and replies something which I can't really understand, it's confused, but says that it actually boots when the harddrive is not present. I look at the BIOS and notice I've done a mistake. I notice there's one more similar checking routine, which IDA didn't reveal by itself and having found out about PCI class IDs (0x200 network ethernet, 0x280 network other = wireless) I realize that what I'd modified was ethernet-devices check. Well, at least it powers up. Done the correct modification, owner of T30 flashed, and it works. Not-approved wireless card accepted. This was easy, because BiosCode1, where the whitelist resides, is not compressed.

Doing the same for R32, where no-1802 oes not work, and first success. After third machine I realize, that the checking routine always asks some far function for 0x100, 0x102, 0x12C and 0x12E addresses, searching for these numbers (last two) makes it easy to find the whitelist check even when IDA does not auto-disassemble the part.

Recently I've learned something more. At some forum I've found T60 BIOS where SLIC table is added - I think it's the thing that allows OEM Vista bypass activation. Comparing it with original, I realize that each module's offset actually says where to find next module. There's added module in the BIOS and it's done simply by rewriting some (not know if chosen randomly, but looks so) module's (A) offset to a pointer to a free space, and putting a new module in the free space with offset set to the old offset of (A). Nothing more is changed and people say it works. And what more, people say that BIOS updates _don't break it_. It must mean that BIOS is flashed module by module and when not present, it's not overwritten. So there should be no risk

And right now I have realized what the "magic bytes" mentioned in my first post are. They're addresses at which the modules should be loaded (and perhaps also flashed?). IDA sometimes shows you call/jmp far ptr XXXXh:XXXXh. The "magic bytes" are the first part, finally I won't have to try to tell which module is at which address by trying to check if at called addresses is any meaningful code.
For example in T60 2.20 BIOS, BiosCode5 is at 5082h, BiosCode1 at 4190h and BiosCode4 at 3B2Fh.

Well that's my story. I think I'll make a tool which will be able to replace a module in existing BIOS file, with the ability to make more room for it (when shrinking nothing is needed, just write correct length) and a tool to recalculate checksum (it took me three hours before I finally tried to use catenate with -Debug and learned that only upper 1MB gets checksummed).