Forum RSS Feed Follow @ Twitter Follow @ Twitter

Thread Rating:
  • 0 Vote(s) - 0 Average
  • 1
  • 2
  • 3
  • 4
  • 5
Need help with BIOS reverse-engineering
Hello guys,

I'm trying to repair an old ETX-fromfactor module "SECO M671". It is based on Intel Celeron M + 855GM chipset + ICH4 hub. Google can't help with the docs for this module, but I've found out that Kontron pm-15c module is very similar to the M671 I have, i.e. all the chips (from processor/chipset and to small stuff like PCI-ISA bridges etc) are exactly the same, just arranged in a different way. More detailed specs and datasheets are in the Drive folder I share.

The PC (old industrial PC with an AT power supply and LVDS screen, just in case) based on this module suddenly stopped booting at all (black screen). The "baseboard" that the ETX module plugs into does not expose PCI, but does expose ISA in PC/104 from-factor, so I had to assemble (FPGA + some CD4050) an ISA POSTcard to read POSTcodes. The POST sequence turned out to be D0h, 00h, D1h, D2h. It resembles AMIBIOS8 codes (if you ignore 00h), for which D2h means bad bootblock.
I have a backup of the BIOS that OEM has supplied with the PC (and some DOS utilities for flashing BIOS and also CMOS, but they are useless as long as the PC is unable to POST). So I desoldered the BIOS ROM (Intel FWH-compatible chip SST49LF004B) and read it with a parallel programmer (TL866). But I found that the contents were perfectly fine: they matched the backup binary (except some offset difference and an additional VGA BIOS module I've found inside the flash chip, which was not present in any of the backups).
Also I tried to compare the contents to some AMI BIOS binaries and finally concluded, that the BIOS is custom [or is it? Does anyone recognize this code?].

I had no previous x86 assembler reversing experience (nevertheless I've used AVR inline asm, so I'm familiar with basic concepts), but I figured that I had no choice but to understand what happens after D2h.
I loaded the dump of the flash chip into IDA Pro 6.8 and created an appropriate segmentation (gotta say, segmented addressing still drives me crazy sometimes): copied 0x60000-0x7FFFF to E000:0000-F000:FFFF (chipset datasheet says 128K are mapped, not just 64K) and considering that x86 starts up in 16-bit real mode I set segment bitness to 16. All jump addresses lined up nicely, code started to make sense. I've found the far jump to the main code from the top 16-bytes (x86 reset vector is 0xFFFF_FFF0, and I checked chipset datasheet to ensure that it's mapped to 0xFFFF0 and to the top of the FWH flash).

Further investigation required me to create subroutines (to be able to view function graphs and to enable proximity view). And the thing worked out not so nice this time. First round of questions:
  1. First, how do I control function chunking in IDA? A lot of times it automatically includes into subroutine some fragments ("chunks") that should be marked as functions on their own. Then I've to undefine the function code, make it a code again, define the "chunk" as a separate function and then define the initial function again. Frustrating.
  2. The code does not setup stack "properly", but it does use SP as the return address pointer. IDA fails to pick up things like "mov SP, F123h" and "bswap ESP" (the code uses "two-level stack" sometimes by just swapping the ESP bytes and swapping them back when needed). It's pretty tedious task to recover the call (jump) tree by hand. How can I automate it?
Despite those difficulties, I've started to reverse-engineer the code. And a second round of question arises:
  1. I've found the subroutine that switches to protected mode and parsed the GDT (thanks to IDA's autocomments). It says that the 0x0-0x0000_FFFF (GDT uses linear addressing, right?) segment is 16-bit, 1-byte granular, and the 0x0-0x000F_FFFF segment is 32-bit, 4-KiB granular. Therefore, the BIOS ROM-mapped addresses, i.e. F000:XXXXh, that are executed after loading the GDT have to be disassembled as 32-bit segments? I have tried to load the blob as a 32-bit segment (make the offsets, etc) and start the analysis at the addresses called after loading GDT (and enabling A20, forgot about that). But it produces garbage: IDA eventually reaches some bytes that are not a valid instruction, and usually it happens at the same addresses where 16-bit analysis produced jumps. Therefore the jumps must've been assembled as 16-bit... But the GDT... !? Do I get something about the segmentation wrong again? 0x0000_FFFF entry can't be in the table for nothing.
  2. There is a piece of code that uses CPUID to determine that it's running on a Pentium 4, and if so it reads 10th bit of 2Ch MSR. I've found that bits 0:15 are reserved on all P4s. Does anyone have any idea on what happens inside reserved MSR bits?
  3. There are LOTS of conditional jumps into an endless loop. Is this a common technique or something? My idea is that it's either a way to reset the CPU using WDT timer in ICH4 (but it has to be configured in advance, right?) or a way to wait for an interrupt, but I can't understand how interrupts can be used if the code does not load IDT. Or is it an error-handling strategy? Just send a POSTcode and go loop forever?
Hope someone can help me sort it all out. Here is a google drive folder (no google account should be required) with the binaries and the datasheets (and current IDA database), I'll attach the binary to the post too, just in case:

BTW, I've found chipset/ICH datasheets not too detailed. I know there has to exist a BWG - Intel BIOS writer's guide for this family of processors. I've even seen one for Pentium Pro, but that system architecture is considerably older than mine. So, did any modern BWGs leak to the public? I suppose reading one of these would help me a lot.

Attached Files
.bin   tl_image.BIN (Size: 512 KB / Downloads: 2)
Quote:understand what happens after D2h.
Well, find this starting address (code bytes: B0,D2,E6,80) and begin.

F000:E918 mov     al, 0D2h ; out     80h, al       ; out Post code 
F000:E91C mov     si, 0E49Ch                       ; address of config table
F000:E91F: ... code which configure Northbridge registers from table
F000:E93B: ... code which set word 0105h to pci register 0FCh of Bus#0, Device #0, Function#0.
F000:E947 mov     edi, 3                    ; 0x00000011b , set bit mask checking memory SPD modules, mean 50h,51h i2c addresses.
F000:E94F mov     ah, 2                            ; cell address
F000:E951: ...                                     ; code which read 0x02 memory cell from SPD
F000:E959  cmp     al, 7                           ; checking it's 0x07 - DDR type
F000:E95B  jnz     loc_FEA62                       ; if no jmp to loop itself ;) . Panic
F000:E95F  inc cx; cmp cl, 2; jb short loc_FE94F   ; only two memory slots checking
F000:E967 mov     ah, 15h                          ; cell address
F000:E969: ...                                     ; code which read 0x15 memory cell from SPD
F000:E96F jb      short loc_FE977                  ; check what is reading well done
F000:E971 test    al, 1Bh;jnz     loc_FEA62        ; testing bits and if "not good" do Panic with loop
... check others data from SPD (0x12,0x12,0x19,0x1A,0x1E,0x1B,0x1D,0x0C,0x05,0x0D), to set bits in EDI cpu register which is used later to configure chipset
F000:EBEF  mov     al, 0D3h ; out     80h, al      ; next 0D3h Post code output
Remove and insert again memory modules on motherboard.
В общем, потереть ластиком контакты планок модулей памяти, удаляя окислы.
Quote:BTW, I've found chipset/ICH datasheets not too detailed.
ICH4 . Ask Intel Wink why absent description of not zeroed Reserved  registers.
Some log i2c bus with mobo(i945p chipset) with installed DDR2 memory modules.

Forum Jump:

Users browsing this thread: 1 Guest(s)
Expand chat
Expand chat
Expand chat

To join us in the community live chat, please register or log-in