Pinczakko's Guide to Award BIOS Reverse Engineering


Click here to download the off-line version of this article.

Table of Contents


Side Note

A more detailed tutorial on BIOS Reverse engineering is in my book. The book is not just a compilation of the articles I wrote in this website. It's much more than that and it has a fundamental explanation on issues regarding the BIOS security aspect and analysis. I regard it more like a "log-book" about the work that I've done through the last couple of years in BIOS subject, including undisclosed research results. It provides a thorough tutorial on Award BIOS version 6.00PG reverse engineering as well as AMI BIOS version 8 reverse engineering. Moreover, the basics and fundamental concepts is explained in detail in the first part of the book. Therefore, newbies will find it very helpful. Here it is:

my book cover

It takes quite a lot of effort to finish this book due to the excessive amount of material I wrote in it. Nonetheless, this is just the starting point. I plan to update the book some time in the future. The TIANO Core/UEFI has been gaining momentum recently and also the advances in LinuxBIOS are not minor either. These advances should take the BIOS technology one step above the ladder.


1. Foreword

I would like to welcome you to the darkside of a working example of spaghetty code, The Award BIOS. This article is not an official guide to award bios reverse engineering nor it's compiled by an Award Corp. insider. I'm just an ordinary curious person who really attracted to know how my computer BIOS works. I made this article available to the public to share my findings and looking for feedback from others since I'm sure I've made some "obscure mistakes" that I didn't realize during my reverse engineering process. There are several possibilities that make you reading this article now, perhaps you are an old-time BIOS hacker, perhaps you are a kind of person who really love "system programming" like me or you are just a curious person who like to tinker. One thing for sure, you'll get most of out of this article if you've done some BIOS hacking before and looking forward to improve your skill. However, I've made a prerequisite section below to ensure you've armed yourself with knowledge needed to get most out of this article.

You may be asking, why would anyone need this guide ? indeed, you need this guide if you found yourself cannot figure out how award BIOS code works. In my experience, unless you are disassembling a working BIOS binary, you won't be able to comprehend it. Also, you have to have the majority (if not all) of your mainboard chips datasheets. The most important one is the chipset datasheet.

The purpose of this article is to clean up the mess and positioned as a handy reference for myself and the reader as we are going through the BIOS disassembling session. I'm not held responsible about the correctness of any explanation in this article, you have to cross-check what I wrote here and what you have in your hand. Note that what I explain here based on 2Mbit(256KB) award bios version 4.51PGNM which I have. You can check it against award bios version 6.0PG or 6.0 to see if it's still valid. I'll working on that version when I have enough time. As an addition, I suggest you to read this article throughly from beginning to end to get most out of it.

2. Prerequisite

First, I would like to thank to the readers of the earlier "beta-version" of this article, from whom I consider that this part of the article should be included.

I have to admit that BIOS is somehow a state of the art code that requires lots of low level x86 knowledge that only matter to such a small audience such as operating system developer, BIOS developer, driver writer, possibly exploit and virus writer (yes exploit and virus writer! coz they are curious people). Due to this fact, there are couple of things that I won't explain here and it's your homework that you should do to comprehend this guide. They are :

  • The most important thing is you have to be able to program and understand x86 assembly language. If you don't know it, then you'd better start learning it. I'm using masm syntax throughout this article.
  • Protected mode programming. You have to learn how to switch the x86 machine from real mode to protected mode. This means you need to learn a preliminary x86 protected mode OS development. I've done it in the past, that's why I know it pretty good. You can go to www.osdever.net and other x86 operating system developer site to get some tutorials to make yourself comfortable. The most important thing to master is how the protected mode data structures work. I mean how Global Descriptor Table (GDT), Interrupt Descriptor Table (IDT), also x86 control and segment registers work. BIOS, particularly award BIOS uses them to perform its "magic" as later explained in this article.
  • What x86 "Unreal-Mode" is. Some people also call these mode of operation "Voodoo-mode" or "Flat real-mode ". It's an x86 state that's between real-mode and protected-mode. This is partially explained below.
  • x86 "direct hardware programming". You need to know how to program the hardware directly, especially the chips in your motherboard. You can practice this from within windows by developing an application that directly access the hardware. This is not a must, but it's better if you master it first. You also have to know some x86 bus protocol, such as PCI and ISA. I'll explain a bit about the bus protocols below.
  • You have to be able to comprehend part (if not all) of the datasheets of your motherboard chip. Such as the northbridge and southbridge control registers.

2.1. PCI BUS

We'll begin with the PCI bus. I've been working with this stuff for quite a while. The official standard for the PCI bus system is maintained by a board named PCISIG (PCI Special Interest Group). This board actually is some sort of cooperation between Intel and some other big corporation such as Microsoft. Anyway, in the near future PCI bus will be fully replaced by a much more faster bus system such as Arapahoe (PCI-Express a.k.a PCI-e) and Hypertransport. But PCI will still remain a standard for sometime I think. I've read some of the specification of the Hypertansport bus, it's backward compatible with PCI. This means that the addressing scheme will remains the same or at least only needs a minor modification. This also holds true for the Arapahoe. One thing I hate about this PCI stuff is that the standard is not an open standard. Thus, you gotta pay a lot to get the datasheets and whitepapers. This become my main reason providing you with this sort of tute.

First, PCI bus is a bus which is 32 bits wide. This imply that communicating using this bus should be in 32 bits addressing mode. Pretty logical isn't it? So, writing or reading to this bus will require 32 bits addresses. Note that eventhough there is a 64-bit PCI bus, it's not natively supported, since PCI uses a dual address cycle to implement it. So, we can say that PCI primarily a 32-bit bus system.

Second, this bus system is defined in the port CF8h - CFBh which acts as the configuration address port and port CFCh - CFFh which acts as the configuration data port. These ports are used to configure the corresponding PCI chip, i.e. reading/writing the PCI chip configuration register values.

Third, this bus system force us to communicate with PCI chips with the following algorithm (from host CPU point of view):

  1. Write the target bus number, device number, function number and offset/register number to the Configuration Address Port and set the Enable bit in it to one. In plain english, write the address of the register you're willing to read/write into the PCI address port.
  2. Perform a one-byte, two-byte, or four-byte I/O read from or a write to the Configuration Data Port. In plain english, write/read the data you're willing to read/write into the PCI data port.
As a note, as far as I know every bus/communication protocol implemented in chip design today uses similar algorithm to communicate with another chip which has a different bus protocol.

With the above definition, now I'll provide you with an x86 assembly code snippet that shows how to use those configuration ports.

No. Mnemonic (masm syntax) Comment
1 pushad save all the contents of General Purpose Registers
2 mov eax,80000064h put the address of the PCI chip register to be accessed in eax (offset 64h device 00:00:00 or hostbridge)
3 mov dx,0CF8h put the address port in dx. Since this is PCI, we use CF8h as the port to open an access to the device.
4 out dx,eax send the PCI address port to the I/O space of the processor
5 mov dx,0CFCh put the data port in dx. Since this is PCI, we use CFCh as the data port to communicate with the device.
6 in eax,dx put the data read from the device in eax
7 or eax, 00020202 modify the data (this is only example, don't try this in your machine, it may hang or even destroy your machine)
8 out dx,eax send it back ....
9 ............ -
10 popad pop all the saved register
11 ret return...

I think the code above clear enough. In line one the current data in the processors general purpose registers were saved. Then comes the crucial part. As I said above, PCI is 32 bits bus system hence we have to use 32 bits chunk of data to communicate with them. We do this by sending the PCI chip a 32 bits address through eax register, and using port CF8h as the port to send this data. Here's an example of the PCI register (sometimes called offset) address format. In the routine above you saw :

....
mov eax,80000064h
....
the 80000064h is the address. The meaning of these bits are:
bit position 31 30 29 28 27 26 25 24 23 22 21 20 19 18 17 16 15 14 13 12 11 10 9 8 7 6 5 4 3 2 1 0
binary value 1 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 1 1 0 0 1 0 0
hexadecimal value 8 0 0 0 0 0 6 4
  • The 31st bit is an enable bit. If this bit sets, it means that we are granted to do a write/read transaction through the PCI bus, otherwise we're prohibited to do so, that's why we need an 8 in the leftmost hexdigit.
  • Bits 30 - 24 are reserved bits.
  • Bits 23 - 16 is the PCI Bus number.
  • Bits 15 - 11 is the PCI Device number.
  • Bits 10 - 8 is the PCI Function Number.
  • Bits 7 - 0 is the offset address.

Now, we'll examine the previous value that was sent. If you're curious, you'll find out that 80000064h means we're communicating with the device in bus 0, device 0 , function 0 and at offset 64. Actually this is the memory controller configuration register of my mainboard's Northbridge. In most circumstances the PCI device that occupy bus 0, device 0, function 0 is the Hostbridge, but you'll need to consult your chipset datasheet to verify this. This stuff is pretty easy to be understood, isn't it ? The next routines are pretty easy to understand. But if you still feel confused you'd better learn assembly language a bit, since I'm not here to teach you assembly (!_!) . But, in general they do the following jobs: reading the offset data then modifying it then writing it back to the device, if not better to say tweaking it (^__^) .

2.2. ISA BUS

AFAIK, ISA bus is not a well standardized bus. Thus, any ISA device can reside virtually almost anywhere in the system's 16-bit I/O address space. My experience with ISA bus is very limited. I've only play with two chips this time around, the first is the CMOS chip and the second one is my mainboard's hardware monitoring chip, i.e. Winbond W83781D. Both chips uses the same "general algorithm" as mentioned above in the PCI BUS, i.e. :

  1. Send the address of the part of the device you're willing to read/write at first. Only after that your access to send/receive data through the data port to/from the device will be granted.
  2. Send/receive the data to be read/write through the data port.
My hardware monitoring chip defines port 295h as its address port (a.k.a index port) and port 296h as its data port. CMOS chip defines port 70h as its address port and port 71h as its data port.

3. Some Hardware Peculiarities

Due to its history, the x86 platform contains lots of hacks, especially its BIOS. This is due to the backward compatiblity that should be maintained by any x86 system. In this section I'll try to explain couple of stuff that I found during my BIOS disassembly journey that reveal these peculiarities.

3.1. BIOS Chip Addressing

The most important chips which responsible for the BIOS code handling are the southbridge and northbridge. In this respect, the northbridge is responsible for the system address space management, i.e. BIOS shadowing, handling accesses to RAM and forwarding transaction which uses BIOS ROM as its target to the southbridge which then eventually forwarded to BIOS ROM by southbridge. While the southbridge is responsible for enabling the ROM decode control, which will forward (or not) the memory addresses to be accessed to the BIOS ROM chip. The addresses shown below can reside either in the system DRAM or in BIOS ROM chip, depending on the southbridge and northbridge register setting at the time the BIOS code is executed.

Physical Address Also Known As Used by Address Aliasing Note
000F_0000h - 000F_FFFFh F_seg / F_segment 1 Mbit, 2 MBit, and 4 MBit BIOS alias to FFFF_0000h - FFFF_FFFFh in all chipset just after power-up
000E_0000h - 000E_FFFFh E_seg / E_segment 1 Mbit, 2 MBit, and 4 MBit BIOS alias to FFFE_0000h - FFFE_FFFFh in some chipset just after power-up

The address ranges shown above contain the BIOS code and pretty much system specific. So, you have to consult your chipset datasheets to understand it. Also, note that the address above which will be occupied by the BIOS code during runtime (after BIOS code executes) is only F_seg i.e. F_0000h - F_FFFFh. However, certain operating system might "trash" this address and use it for their own purposes. The addresses written above only reflect the addressing of the BIOS ROM chip to the system address space when it's set to be accessed by the BIOS code or another code that accesses the BIOS ROM chip directly. The mainboard chipsets are responsible for the mapping of certain BIOS ROM chip area to the system address space. As we will see later, this mapping can be changed by programming certain chipset registers.

BIOS chip with capacity bigger than 1 Mbit, i.e. 2 Mbit and 4 Mbit chips has a quite different addressing for their lower bios area, i.e. C_seg, D_seg and other lower segment(s). In most cases, this area is mapped to near-4GB address range. This address range is handled by the norhtbridge analogous to the PCI address range. In this scheme the chipset behaves as follows:

  • The northbridge acts as the address forwarder, meaning: it responds to this "special" memory address in different fashion compared to "normal" memory address which is forwarded directly to RAM. On the contrary, this "special" memory address is forwarded by the northbridge to the southbridge to be decoded.
  • The southbridge acts as the address decoder, meaning: it decodes this "special" memory addresses into the right chip "beneath" it, such as the BIOS chip. In this respect, the southbridge will return "void" (bus address cycle termination) if the address range is not being enabled to be decoded in the southbridge configuration registers.
Below is an example:
Physical Address Also Known As Used by Address Aliasing Note
000F_0000h - 000F_FFFFh F_seg / F_segment 1 Mbit, 2 MBit, and 4 Mbit BIOS alias to FFFF_0000h - FFFF_FFFFh in all chipset just after power-up
000E_0000h - 000E_FFFFh E_seg / E_segment 1 Mbit, 2 Mbit, and 4 Mbit BIOS alias to FFFE_0000h - FFFE_FFFFh in some chipset just after power-up
FFFD_0000h - FFFD_FFFFh D_seg / D_segment 2 Mbit, and 4 Mbit BIOS -
FFFC_0000h - FFFC_FFFFh C_seg / C_segment 2 Mbit, and 4 Mbit BIOS -
FFF8_0000h - FFFB_FFFFh - 4 Mbit BIOS -

The conclusion is: modern day chipsets performs an emulation for F_seg and E_seg handling. This is a proof that modern day x86 systems maintains backward compatibility. However, this "kludge" sometimes referred to as the thing of the past that vendors should've been removed from x86 systems.

Below is the VIA693A chipset (Northbridge) system memory map just after system power-up as written in its datasheet.

      Table 4. System Memory Map
Space Start    Size  Address Range      Comment
DOS   0        640K  00000000-0009FFFF  Cacheable
VGA   640K     128K  000A0000-000BFFFF  Used for SMM
BIOS  768K     16K   000C0000-000C3FFF  Shadow Ctrl 1
BIOS  784K     16K   000C4000-000C7FFF  Shadow Ctrl 1
BIOS  800K     16K   000C8000-000CBFFF  Shadow Ctrl 1
BIOS  816K     16K   000CC000-000CFFFF  Shadow Ctrl 1
BIOS  832K     16K   000D0000-000D3FFF  Shadow Ctrl 2
BIOS  848K     16K   000D4000-000D7FFF  Shadow Ctrl 2
BIOS  864K     16K   000D8000-000DBFFF  Shadow Ctrl 2
BIOS  880K     16K   000DC000-000DFFFF  Shadow Ctrl 2
BIOS  896K     64K   000E0000-000EFFFF  Shadow Ctrl 3
BIOS  960K     64K   000F0000-000FFFFF  Shadow Ctrl 3
Sys   1MB            00100000-DRAM Top  Can have hole
Bus   D Top          DRAM Top-FFFEFFFF
Init  4G-64K   64K   FFFEFFFF-FFFFFFFF  000Fxxxx alias
The most important thing to take into account here is the address aliasing, as you can see the FFFE_FFFFh- FFFF_FFFFh address range is an alias into 000Fxxxxh, this is where the BIOS ROM chip address mapped (at least in my mainboard, cross check with yours). But, we also have to consider that this only applies at the very beginning of boot stage (just after reset). After the chipset reprogrammed by the BIOS, this address range will be mapped into system DRAM chips. We can consider this as the Power-On default values. As a note, the majority of x86 chipsets use this address aliasing scheme, at least for the F-segment address range.

Another fact that we have to take into account: most chipset only provides default addressing scheme for F-segment just after power-up in its configuration registers, other "BIOS ROM segment(s)" remains inaccessible. The addressing scheme for these segments will be configured later by the bootblock code by altering the related chipset registers (in most cases the southbridge registers). The chipset that's being dissected here also belongs to this group.

Modern day systems connect the BIOS ROM chip to the southbridge through LPC(Low Pin Count) interface. However, the southbridge described in this article don't have such a interface. It's an old chipset and still uses ISA BUS to interface with the BIOS ROM chip.

3.2. Obscure Hardware Port

Some "obscure" hardware port which sometimes not documented in the chipset datasheets described below. Note that these info's were found from Intel ICH5, VIA 586B and VIA596B datasheet.

I/O Port address     Purpose
92h                  Fast A20 and Init Register
4D0h                 Master PIC Edge/Level Triggered (R/W)
4D1h                 Slave PIC Edge/Level Triggered (R/W)


Table 146. RTC I/O Registers (LPC I/F D31:F0)
I/O Port Locations If U128E bit = 0 Function
70h and 74h Also alias to 72h and 76h Real-Time Clock (Standard RAM) Index Register 71h and 75h Also alias to 73h and 77h Real-Time Clock (Standard RAM) Target Register 72h and 76h Extended RAM Index Register (if enabled) 73h and 77h Extended RAM Target Register (if enabled) NOTES: 1. I/O locations 70h and 71h are the standard ISA location for the real-time clock. The map for this bank is shown in Table 147. Locations 72h and 73h are for accessing the extended RAM. The extended RAM bank is also accessed using an indexed scheme. I/O address 72h is used as the address pointer and I/O address 73h is used as the data register. Index addresses above 127h are not valid. If the extended RAM is not needed, it may be disabled. 2. Software must preserve the value of bit 7 at I/O addresses 70h. When writing to this address, software must first read the value, and then write the same value for bit 7 during the sequential address write. Note that port 70h is not directly readable. The only way to read this register is through Alt Access mode. If the NMI# enable is not changed during normal operation, software can alternatively read this bit once and then retain the value for all subsequent writes to port 70h. The RTC contains two sets of indexed registers that are accessed using the two separate Index and Target registers (70/71h or 72/73h), as shown in Table 147. Table 147. RTC (Standard) RAM Bank (LPC I/F D31:F0)
Index Name
00h Seconds 01h Seconds Alarm 02h Minutes 03h Minutes Alarm 04h Hours 05h Hours Alarm 06h Day of Week 07h Day of Month 08h Month 09h Year 0Ah Register A 0Bh Register B 0Ch Register C 0Dh Register D 0Eh-EFh 114 Bytes of User RAM

3.3. "Relocatable" Hardware Port

There are several kinds of hardware port that is relocatable in the system I/O address space. In this BIOS, those ports include SMBus-related ports and Power-Management-Related ports. These ports has certain base address. This so called base address is controlled via programmable base address register. SMBus has SMBus base address register and Power-Management has Power-Management I/O base address register. Since these ports are programmable, the bootblock routine initializes the value of the base address registers in the very beginning of BIOS routine execution. Due to the programmable nature of these ports, one must start reverse engineering of BIOS in the bootblock to find out which port address(es) used by these programmable hardware ports. Otherwise one will be confused by the occurence of "weird" ports later during the reverse engineering process. An example of this case provided below.

Address    Hex                  Mnemonic
F000:F604 BE C4 F6                  mov   si, 0F6C4h          ; addr of chipset reg mask
F000:F607                         next_PCI_reg:               ; CODE XREF: Chipset_Reg_Early_Init+29
F000:F607 2E 8B 0C                  mov   cx, cs:[si]
F000:F60A BC 10 F6                  mov   sp, 0F610h
F000:F60D E9 F8 00                  jmp   Read_PCI_Byte
F000:F60D                         ; ---------------------------------------------------------------------------
F000:F610 12 F6                     dw 0F612h
F000:F612                         ; ---------------------------------------------------------------------------
F000:F612 2E 22 44 02               and   al, cs:[si+2]
F000:F616 2E 0A 44 03               or    al, cs:[si+3]
F000:F61A BC 20 F6                  mov   sp, 0F620h
F000:F61D E9 02 01                  jmp   Write_PCI_Byte
F000:F61D                         ; ---------------------------------------------------------------------------
F000:F620 22 F6                     dw 0F622h
F000:F622                         ; ---------------------------------------------------------------------------
F000:F622 83 C6 04                  add   si, 4
F000:F625 81 FE 04 F7               cmp   si, 0F704h          ; are we done yet?
.........
F000:F6F4 48 3B                     dw 3B48h                  ; B#0 D#7 F#3: PwrMngmt&SMBus - PwrMngmt IO Base Addr lo_byte
F000:F6F6 00 db 0 ; and mask
F000:F6F7 00 db 0 ; or mask
F000:F6F7 ; F000:F6F8 49 3B dw 3B49h ; B#0 D#7 F#3: PwrMngmt&SMBus - PwrMngmt IO Base Addr hi_byte F000:F6FA 40 db 40h ; and mask F000:F6FB 40 db 40h ; PwrMngmt IO Base Addr = IO Port 4000h ......... F000:F643 B9 90 3B mov cx, 3B90h ; B#0 D#7 F#3: PwrMngmt&SMBus - SMBus IO Base Addr lo_byte F000:F646 B0 00 mov al, 0 ; set SMBus IO Base lo_byte to 00h F000:F648 BC 4E F6 mov sp, 0F64Eh F000:F64B E9 D4 00 jmp Write_PCI_Byte F000:F64B ; --------------------------------------------------------------------------- F000:F64E 50 F6 dw 0F650h F000:F650 ; --------------------------------------------------------------------------- F000:F650 B9 91 3B mov cx, 3B91h ; B#0 D#7 F#3: PwrMngmt&SMBus - SMBus IO Base Addr hi_byte F000:F653 B0 50 mov al, 50h ; 'P' ; set SMBus IO Base hi_byte to 50h,
F000:F653 ; so, now SMBus IO Base is at port 5000h !!!
F000:F655 BC 5B F6 mov sp, 0F65Bh F000:F658 E9 C7 00 jmp Write_PCI_Byte F000:F658 ; --------------------------------------------------------------------------- F000:F65B 5D F6 dw 0F65Dh ......... F000:F66A BA 05 40 mov dx, 4005h ; access ACPI Reg 05h F000:F66D B0 80 mov al, 80h ; ; setting reserved bit? .........

Certainly, there are more relocatable hardware ports than those described here, but at least you've been given the hints about it. So that, once you found code in the BIOS that seems to be accessing "weird" ports, you'll know where to go.

3.4. Expansion ROM Handling

There are couples of more things to take into account, such as the Video BIOS and other expansion ROM handling. I'll try to cover that stuff later when I've done dissecting BIOS code that handle it. But here's the basic run-down of PCI Expansion ROM handling in BIOS:

  1. System BIOS detect all PCI chip in the system and initialize its BARs(Base Address Registers). Once initialization completes, the system will have a usable system-wide addressing scheme.
  2. By using the system-wide addressing scheme, system BIOS then copies the implemented PCI Expansion ROM into RAM one by one in the expansion ROM area (C000:0000h - D000:FFFFh) and execute it there until all of the PCI expansion ROM have been executed/initialized.
ISA Expansion ROM is not relevant these days. Therefore it's not discussed here.

4. Some Software Peculiarities

There are some tricky areas in the BIOS code due to the execution of some of its parts in ROM. I'll present some of my findings below.

4.1. call Instruction Peculiarity

call instruction is not available during bios code execution from within BIOS ROM chip. This is due to call instruction uses/manipulate stack while we don't have writeable area in BIOS ROM chip to be used for stack. What I mean by manipulating stack here is the implicit push instruction which is executed by the call instruction to write/save the return address in the stack. As we know clearly, address pointed to by ss:sp at this point is in ROM, meaning: we can't write into it. If you think, why don't we use the RAM altogether? the DRAM chip is not even available at this point. It hasn't been tested by the BIOS code. Thus, we haven't even know if RAM exists!

These days, to handle the lack of RAM in this stage of execution BIOS writers uses the so called "cache-as-RAM". In this approach, part of the CPU cache is used to provide stack functions. Therefore, from code-execution point of view it looks like RAM. Unfortunately, due to NDA restriction, I cannot elaborate on a sample code for cache-as-RAM implementation. If you want to find out for yourself. You can disassemble recent motherboard BIOS which is based on AMIBIOS8 code. You will find the code in its bootblock. I believe recent Phoenix-Award BIOS incorporate cache-as-RAM in its recent bootblock code as well.

4.2. retn Instruction Peculiarity

The peculiarity of retn instruction. There is macro that's called ROM_call as follows :
ROM_CALL	MACRO	RTN_NAME
LOCAL RTN_ADD
mov sp,offset DGROUP:RTN_ADD
jmp RTN_NAME
RTN_ADD: dw DGROUP:$+2
ENDM
an example of this macro "in action" as follows :
Address    Hex                  Mnemonic
F000:6000                       F000_6000_read_pci_byte proc near  
F000:6000   66 B8 00 00 00 80   mov   eax, 80000000h
F000:6006   8B C1               mov   ax, cx          ; copy offset addr to ax
F000:6008   24 FC               and   al, 0FCh        ; mask it
F000:600A   BA F8 0C            mov   dx, 0CF8h
F000:600D   66 EF               out   dx, eax
F000:600F   B2 FC               mov   dl, 0FCh
F000:6011   0A D1               or    dl, cl          ; get the byte addr
F000:6013   EC                  in    al, dx          ; read the byte
F000:6014   C3                  retn                  ; Return Near from Procedure
F000:6014                       F000_6000_read_pci_byte endp
......
F000:6043 18 00                   GDTR_F000_6043 dw 18h   ; limit of GDTR (3 valid desc entry)
F000:6045 49 60 0F 00               dd 0F6049h            ; GDT physical addr (below)
F000:6049 00 00 00 00 00 00 00 00   dq 0                  ; null descriptor
F000:6051 FF FF 00 00 0F 9F 00 00   dq 9F0F0000FFFFh      ; code descriptor:
F000:6051                                                 ; base addr = F 0000h; limit=FFFFh; DPL=0;
F000:6051                                                 ; exec/ReadOnly, conforming, accessed;
F000:6051                                                 ; granularity=byte; Present; 16-bit segment
F000:6059 FF FF 00 00 00 93 8F 00   dq 8F93000000FFFFh    ; data descriptor:
F000:6059                                                 ; base addr = 00h; seg_limit=F FFFFh; DPL=0;
F000:6059                                                 ; Present; read-write, accessed; 
F000:6059                                                 ; granularity = 4 KByte; 16-bit segment
......
F000:619B 0F 01 16 43 60        lgdt  qword ptr GDTR_F000_6043 ; Load Global Descriptor Table Register
F000:61A0 0F 20 C0              mov   eax, cr0
F000:61A3 0C 01                 or    al, 1           ; set PMode flag
F000:61A5 0F 22 C0              mov   cr0, eax
F000:61A8 EA AD 61 08 00        jmp   far ptr 8:61ADh ; jmp below in 16-bit PMode (abs addr F 61ADh)
F000:61A8                                                 ; (code segment with base addr = F 0000h)
F000:61AD                       ; ---------------------------------------------------------------------
F000:61AD B8 10 00              mov   ax, 10h         ; load ds with valid data descriptor
F000:61B0 8E D8                 mov   ds, ax          ; ds = data descriptor (GDT 3rd entry)
......
F000:61BC  B9 6B 00             mov   cx, 6Bh         ; DRAM arbitration control
F000:61BF  BC C5 61             mov   sp, 61C5h
F000:61C2 E9 3B FE jmp F000_6000_read_pci_byte ; Jump
F000:61C2 ; ------------------------------------------------------------------
F000:61C5 C7 61 dw 61C7h
F000:61C7 ; ------------------------------------------------------------------ F000:61C7 0C 02 or al, 2 ; enable VC-DRAM
as you can see, you have to take into account that the retn instruction is affected by the current value of ss:sp register pair, but ss register is not even loaded with correct 16-bit protected mode value prior to using it! How this code even works? the answer is a bit complicated. Let's look at the last time ss register value was manipulated before the code above executed :
Address    Hex                  Mnemonic
F000:E060 8C C8                 mov   ax, cs
F000:E062 8E D0                 mov   ss, ax          ; ss = cs (ss = F000h a.k.a F_segment)
F000:E064                       assume ss:F000
Note: this routine is executed in real-mode
as you can see, ss register is loaded with F000h (the current BIOS code 16-bit segment in real-mode). This code implies that the hidden descriptor cache register (that exist for every selector/segment register) is loaded with ss*16 or F_0000h physical address value. And this value is retained even when the machine is switched into 16-bit Protected Mode above since ss register is not reloaded. A snippet from Intel Software Developer Manual Vol.3 :
8.1.4. First Instruction Executed

The first instruction that is fetched and executed following a hardware reset is located at physical address FFFFFFF0H. This address is 16 bytes below the processor’s uppermost physical address. The EPROM containing the software-initialization code must be located at this address. The address FFFFFFF0H is beyond the 1-MByte addressable range of the processor while in real-address mode. The processor is initialized to this starting address as follows. The CS register has two parts: the visible segment selector part and the hidden base address part. In real address mode, the base address is normally formed by shifting the 16-bit segment selector value 4 bits to the left to produce a 20-bit base address. However, during a hardware reset, the segment selector in the CS register is loaded with F000H and the base address is loaded with FFFF0000H. The starting address is thus formed by adding the base address to the value in the EIP register (that is, FFFF0000 + FFF0H = FFFFFFF0H).
The first time the CS register is loaded with a new value after a hardware reset, the processor will follow the normal rule for address translation in real-address mode (that is, [CS base address = CS segment selector * 16]). To insure that the base address in the CS register remains unchanged until the EPROM based software-initialization code is completed, the code must not contain a far jump or far call or allow an interrupt to occur (which would cause the CS selector value to be changed).

also a snippet from DDJ (Doctor Dobbs Journal):
At power-up, the descriptor cache registers are loaded with fixed, default values, the CPU is in real mode, and all segments are marked as read/write data segments, including the code segment (CS). According to Intel, each time the CPU loads a segment register in real mode, the base address is 16 times the segment value, while the access rights and size limit attributes are given fixed, "real-mode compatible" values. This is not true. In fact, only the CS descriptor cache access rights get loaded with fixed values each time the segment register is loaded - and even then only when a far jump is encountered. Loading any other segment register in real mode does not change the access rights or the segment size limit attributes stored in the descriptor cache registers. For these segments, the access rights and segment size limit attributes are honored from any previous setting (see Figure 3). Thus it is possible to have a four giga-byte, read-only data segment in real mode on the 80386, but Intel will not acknowledge, or support this mode of operation.
If you want to know more about descriptor cache and how it works, you can search the web for articles about "descriptor cache" or "x86 unreal mode", the most comprehensive guide can be found in one of Doctor Dobbs Journal and Intel Software Developer Manual Vol.3 chapter 3 Protected Mode Memory Management in section 3.4.2 Segment Registers. Back to our ss register, now you know that the "actor" here is the descriptor cache register, especially its base address part. The visible part of ss is only a "place holder" and the "register-in-charge" for the "real" address calculation/translation is the hidden descriptor cache. Whatever you do to this descriptor cache will be in effect when any code, stack or data value addresses are translated/calculated. In our case, we have to use "stack segment" with "base address" at F_0000h physical address in 16-bit protected mode. This is not a problem, since the base address part of ss descriptor cache register already filled with F_0000h in one of the code above. This explains why the code above can be executed flawlessly. Another example:
Address    Hex                  Mnemonic
F000:61BF  BC C5 61             mov   sp, 61C5h
F000:61C2  E9 3B FE             jmp   F000_6000_read_pci_byte ; Jump
F000:61C2                       ; ------------------------------------------------------------------
F000:61C5  C7 61                dw 61C7h
in this code we have to make ss:sp points to F_61C5h for retn instruction to work. Indeed, we've done it, since ss contains F_0000h (its descriptor cache base address part) and as you can see, sp contains 61C5h, the physical address pointed to by ss:sp is F_0000h+61C5h which is F_61C5h physical address.

5. Our Tools of Trade

This section will explain the tools that are needed to carry out the reverse engineering process. There's a new sub-section that exclusively explains about IDA Pro disassembler. It's provided due to sheer amount of request from readers. Anyway, thanks for the suggestions. I just got so bored this weekend and I decided to fulfill your requests (^__^)

5.1. What do we need anyway?

You are only as good as your tools. Yeah, this also holds true here. To begin the journey, we'll need some tools as follows:

  1. IDA Pro disassembler. I'm using IDA Pro version 4.3. You can use your favourite interactive disassembler. I found IDA Pro is the most suitable for me. We need an interactive disassembler since the BIOS binary that we're going to disassemble is not a trivial code. At some points of its execution it resides in ROM, hence, no stack available. It uses some sort of stack trick to do procedure/routine calling.
  2. A good hex editor. I'm using HexWorkshop ver. 3.0b. The most beneficial feature of this hex editor is it's capability to calculate checksums for the selected range of file that we open inside of it.
  3. LHA 2.55, it's needed if you want to modify the bios binary. Or, you can use winzip or another compression/decompression program that can handle LZH/LHA file if you only want to decompress and analyze the compressed bios components.
  4. Some bios modification tools i.e. : CBROM, I'm using version 2.08, 2.07 and 1.24 and MODBIN. There are two types of modbin, modbin6 for award bios ver. 6 and modbin 4.50.xx for award bios ver. 4.5xPGNM. We need these tools to look at the bios components much more easily. You can download it at www.biosmods.com, in the download section.
  5. Some chipset datasheets. This depends on the mainboard bios binary that you're gonna dissect. Some datasheets available at www.rom.by in the PDF-s section. I'm dissecting a VIA693A-596B mainboard. I have the datasheets at my hand :-).
  6. Intel Software Developer Manual Volume 1, 2 and 3. These are needed since BIOS sometimes uses exotic instruction set. Also, there are some system data structures that are hard to remember and need to be looked up, such as GDT and IDT.

5.2. Intro to IDA Pro Techniques

This sub-section will clear up the issue on using IDA Pro. Once you grasped the concept, you'll be able to use IDA Pro efficiently.

5.2.1. Introducing IDA Pro

Reverse code engineering is carried out to comprehend the algorithm used in a software by analyzing the executable file of the corresponding software. In most cases, the software only comes with the executable, without its source code. The same is true for the BIOS, it’s only the executable binary file that’s accessible to us. Reverse code engineering carried out with the help of some tools, i.e. debugger, disassembler, hexadecimal file-editor a.k.a hex-editor, ICE (In-Circuit Emulator), etc. We will only deal with disassembler in this sub-section, i.e. IDA Pro disassembler.

IDA Pro is a very powerful disassembler. It comes with support for plugin and scripting facility and support for more than fifty processor architectures. However, every powerful tool has its downside of being hard to use and IDA Pro is not an exception. This sub-section especially designed to address the issue.

There are several editions of IDA Pro: freeware edition, standard edition and advanced edition. The latest freeware edition is IDA Pro version 4.3 (AFAIK) and it's available for download at http://www.dirfile.com/ida_pro_freeware_version.htm. Newer IDA Pro freeware version is available for download here.It's the most limited of all IDA Pro version. It only supports x86 processor and doesn't come with plugin feature, but it comes at no cost, that's why it's presented here. Fortunately, it still comes with scripting feature. The standard and advanced editions of IDA Pro 4.3 of course differ from this freeware edition, they come with support for plugin and support for much more processor architecture. We are going to learn how to use the scripting feature in the next section.

Now, let's start to use IDA Pro freeware version to open a BIOS binary file. First, IDA Pro freeware version has to be installed. After the installation finished, one special step must be carried-out to prevent unwanted bug when IDA Pro v4.3 freeware opens up a BIOS file with *.rom extension. To do so, one must edit the IDA Pro configuration file located in the root directory of the IDA Pro installation directory. The name of the file is ida.cfg. Open this file by using any text editor (such as notepad) and look for the following lines:

  DEFAULT_PROCESSOR = {		
/* Extension Processor */
"com" : "8086" // IDA will try the specified
"exe" : "" // extensions if no extension is
"dll" : "" // given.
"drv" : ""
"sys" : ""
"bin" : "" // Empty processor means the default processor
"ovl" : ""
"ovr" : ""
"ov?" : ""
"nlm" : ""
"lan" : ""
"dsk" : ""
"obj" : ""
"prc" : "68000" // PalmPilot programs
"axf" : "arm710a"
"h68" : "68000" // MC68000 for *.H68 files
"i51" : "8051" // i8051 for *.I51 files
"sav" : "pdp11" // PDP-11 for *.SAV files
"rom" : "z80" // Z80 for *.ROM files "cla*": "java" "s19": "6811" "o": "" "*": "" // Default processor }
Notice the line: "rom" : "z80" // Z80 for *.ROM files
This line must be removed or just replace the "z80" with "" in this line to disable the automatic request to load z80 processor module in IDA Pro upon opening a *.rom file. The bug occurred if the *.rom file is opened while this line is not changed since freeware IDA Pro doesn't come with z80 processor module. Thus, opening *.rom file by default will terminate IDA Pro. Some motherboard BIOS files comes with *.rom extension by default, even though it's very clear that it won't be executed in z80 processor. Fixing this bug will ensure that we will be able to open motherboard BIOS file with *.rom extension flawlessly. Note that the steps needed to remove other file-extension to processor-type "mapping" in this version of IDA Pro is similar to the z80 processor that is just described. Note that I haven't test the last version of IDA Pro freeware. Therefore I will appreciate if someone can inform me whether the bug that exists in verion 4.3 freeware still exists in the latest version.

Now let's proceed to open a sample BIOS file. This BIOS file is da8r9025.rom, BIOS file for Supermicro H8DAR-8 (OEM Only) motherboard. This motherboard used AMD-8131™ HyperTransport™ PCI-X Tunnel chip and AMD-8111™ HyperTransport™ I/O Hub chip. The dialog box below will be displayed when you start IDA Pro freeware version 4.3.


IDA Pro free, 1st screen

Just click OK to proceed. Then the next dialog box shown below will be displayed.

IDA Pro free, 2nd screen

In this dialog box, you can try one of the three options, but we will just click on the Go button. This will start IDA Pro with empty workspace as shown below

IDA Pro free main window

Then locate and drag the file to be disassembled to the IDA Pro window (as shown above). In this case, IDA Pro will show the following dialog box.

IDA Pro free, loading a binary file

In this dialog box, we will select Intel 80x86 processors: athlon as the Processor type in the drop down list box. Then click on the Set button to activate the new processor selection. Let the other option as it is. Code relocation will be carried out by using IDA Pro scripts in later section, then click OK. IDA Pro then shows the following dialog box.

IDA Pro free, processor mode selection

This dialog box asks us to choose the default operating-mode of the x86 compatible processor during the disassembling process. AMD64 Architecture Programmer's Manual Volume 2: System Programming, February 2005 in section 14.1.5 page 417 states that:

"After a RESET# or INIT, the processor is operating in 16-bit real mode."

In addition, IA-32 Intel® Architecture Software Developer's Manual Volume 3: System Programming Guide 2004 section 9.1.1 states that:

"Table 9-1 shows the state of the flags and other registers following power-up for the Pentium 4, Intel Xeon, P6 family, and Pentium processors. The state of control register CR0 is 60000010H (see Figure 9-1), which places the processor is in real-address mode with paging disabled."

Thus, we can conclude that any x86 compatible processors start their execution in 16-bit real mode just after power-up and we have to choose 16-bit mode in this dialog box. It’s accomplished by clicking No in the dialog box. Then the following dialog box pops up.

IDA Pro free, entry point information

This dialog box told us that IDA Pro can’t decide where the entry-point located. We have to locate it ourselves later. Just click OK to continue to the main window for the disassembly process.

IDA Pro free, workspace preview

Up to this point we are able to open the binary file within IDA Pro. This is not a trivial task for people new to IDA Pro. That's why it's presented in a step-by-step fashion. However, the output in the workspace is not yet usable. The next step is learning the scripting facility that IDA Pro provides to make sense about the disassembly database that IDA Pro generates.

5.2.2. IDA Pro Scripting And Key Bindings

Now we will proceed to try to decipher IDA Pro disassembly database shown in the previous sub-section with the help of the scripting facility. Before we proceed to analyze the binary, we have to learn some basic concepts about the IDA Pro scripting facility. IDA Pro scripts syntax are similar to C programming language. The syntax as follows:

  1. IDA Pro scripts only recognize one type of variable, i.e. auto. There are no other variable types such as int, char, etc. The declaration of variable in an IDA Pro script as follows:
    auto variable_name; 
  2. Every statement in an IDA Pro script ends with a semicolon (;), just like in the C programming language.
  3. Function can return a value or not, but there's no return type declaration. The syntax as follows:
    static function_name(parameter1, parameter2, parameter_n, ...)	
  4. Comment in an IDA Pro script starts with double-slash (//). The IDA Pro scripting engine ignores anything after the comment in the corresponding line.
    // comment									
    statement; // comment
  5. IDA Pro "exports" its internal functionality to the script that we build by using header files. These header files must be "included" in our script so that we are able to access that functionality. At least one header file must be included in any IDA Pro script, i.e. idc.idc. The header files are located inside a folder named idc in the IDA Pro installation directory. One must read the *.idc files inside this directory to learn about the functions that are exported by IDA Pro. The most important header file to learn is idc.idc. The syntax used to include a header file in an IDA Pro script is:
    #include < header_file_name >						
  6. The entry point of an IDA Pro script is the main function, just as in the C programming language.
Now is the time to put the theory into a simple working example, an IDA Pro sample script.
#include <idc.idc>		
// relocate one segment
static relocate_seg(src, dest)
{
auto ea_src, ea_dest, hi_limit;

hi_limit = src + 0x10000;
ea_dest = dest;

for(ea_src = src; ea_src < hi_limit ; ea_src = ea_src + 4 )
{
PatchDword( ea_dest, Dword(ea_src));
ea_dest = ea_dest + 4;
}

Message("segment relocation finished (inside relocate_seg function)..\n");
}

static main()
{
Message("creating target segment (inside entry point function main)...\n");
SegCreate([0xF000, 0], [0x10000, 0], 0xF000, 0, 0, 0);
SegRename([0xF000, 0], "_F000");

relocate_seg([0x7000,0], [0xF000, 0]);
}

The square bracket, i.e. [ ] in the script above is an operator used to form the linear address from its parameters by shifting the first parameter to left four bits and then adding the second parameter into the result, for example: [0x7000, 0] means (0x7000 << 4) + 0 , i.e. 0x7_0000 linear address. This operator is just the same as MK_FP( , ) operator in previous versions of IDA Pro. One must read idc.idc file to see the "exported" function definition to understand this script completely, such as the Message, SegCreate and SegRename function. Another "exported" function that maybe of interest can be found in numerous *.idc file in the idc directory of IDA Pro installation folder. To be able to use the function, its definition have to be looked up in the exported function definition in the corresponding *.idc header file. For example, SegCreate function is defined in idc.idc as follows:

// Create a new segment	
// startea - linear address of the start of the segment
// endea - linear address of the end of the segment
// this address will not belong to the segment
// 'endea' should be higher than 'startea'
// base - base paragraph or selector of the segment.
// a paragraph is 16byte memory chunk.
// If a selector value is specified, the selector should be
// already defined.
// use32 - 0: 16bit segment, 1: 32bit segment
// align - segment alignment. see below for alignment values
// comb - segment combination. see below for combination values.
// returns: 0-failed, 1-ok

success SegCreate( long startea,long endea,long base, long use32,
long align,long comb);

A 512KB BIOS binary file must be opened in IDA Pro with the loading address set to 0000h to be able to execute the sample script above. This loading scheme is the same as explained in the previous sub-section. In this case, we will just open the binary file of Supermicro H8DAR-8 motherboard as in the previous sub-section and then execute the script. First, we must type the script above in a plain text file. We can use notepad or another ASCII file editor for this purpose. We will name the file as function.idc. The script then executed by clicking on the File | IDC file... menu or by pressing F2, then the dialog box below will be shown.

IDA Pro free, open script file

Just select the file and click open to execute the script. If there's any mistake in the script, IDA Pro will warn you with a warning dialog box. Executing the script will display the corresponding message in the message pane of IDA Pro as shown below.

IDA Pro free, script execution result

The script above relocates the last segment (64KB) of the Supermicro H8DAR-8 BIOS code to the right place. One must be aware that IDA Pro is only an advanced tool to help the reverse code engineering task, it's not a magical tool that's going to reveal the overall structure of the BIOS binary without us being significantly involve in the process. The script relocates/copies BIOS code from physical/linear address 0x7_0000-0x7_FFFF to 0xF_0000-0xF_FFFF. The logical reason behind this algorithm is explained below.

AMD-8111 HyperTransport IO Hub Datasheet chapter 4 page 153 says that:

Note: The following ranges are always specified as BIOS address ranges. See DevB:0x80 for more information about how access to BIOS spaces may be controlled.
Size Host Address Range[31:0] Address translation for LPC bus
64K bytes FFFF_0000h - EFFFF_FFFFh FFFF_0000h - EFFFF_FFFFh
64K bytes 000F_0000h - E000F_FFFFh FFFF_0000h - EFFFF_FFFFh

In addition, AMD64 Architecture Programmer's Manual Volume 2: System Programming, February 2005 in section 14.1.5 page 417 says that:

"Normally within real mode, the code-segment base address is formed by shifting the CS-selector value left four bits. The base address is then added to the value in EIP to form the physical address into memory. As a result, the processor can only address the first 1 Mbyte of memory when in real mode. However, immediately following RESET# or INIT, the CS selector register is loaded with F000h, but the CS base-address is not formed by left-shifting the selector. Instead, the CS base address is initialized to FFFF_0000h. EIP is initialized to FFF0h. Therefore, the first instruction fetched from memory is located at physical-address FFFF_FFF0h (FFFF_0000h+0000_FFF0h). The CS base-address remains at this initial value until the CS selector register is loaded by software. This can occur as a result of executing a far jump instruction or call instruction, for example. When CS is loaded by software, the new base-address value is established as defined for real mode (by left shifting the selector value four bits)."
From the references above, we conclude that address 000F_0000h - E000F_FFFFh is an alias to address FFFF_0000h - EFFFF_FFFFh, i.e. they both points to the same physical address range. Whenever the host (CPU) accesses some value in 000F_0000h - E000F_FFFFh address range, it's actually accessing the value at FFFF_0000h - EFFFF_FFFFh range and the reverse is also true. From this fact, we know that we have to relocate 64KB of the uppermost BIOS code to address 000F_0000h - E000F_FFFFh for further investigation. This decision is made based on my previous experience with various BIOS binary files, they generally references address with F000h used as the segment value within the BIOS code. Also, note that the last 64KB of the BIOS binary file is mapped to last 64KB of the 4GB address space, i.e. 4GB-64KB to 4GB, that's why we have to relocate the last 64KB.

Simple script that is only several lines can be typed and executed directly within IDA Pro without opening a text editor. IDA Pro provides a specific dialog box for this purpose and it can be accessed by pressing Shift+F2. This is more practical for simple task, but as the number of the routine grows, one might consider coding the script as described in the previous explanation due to limitation of the number of instruction that can be entered in the dialog box. In this dialog box, enter the script to be executed and click OK to execute the script. Below is an example script.

IDA Pro free, direct script execution

Note that there is no need for #include statement in the beginning of the script, since by default all of the functions that are exported by IDA Pro in its scripts header files (*.idc) is accessible within the scripting dialog box shown above. The main function is also doesn't need to be defined. In fact, anything you write within the dialog box entry will behave as if it's written inside the main function in an IDA Pro script file.

Anyway, you might want to go to the IDA Palace for more IDA Pro script samples. It will take a while to grasp them, but IDA Palace is definitely the place to go if you're curious about IDA Pro scripting.

At present, we are able to relocate the binary within IDA Pro; the next step is to disassemble the binary within IDA Pro. Before that, we need to know how the default key binding works in IDA Pro. Key binding is the "mapping" between the keyboard button and the command carried-out when the corresponding key is pressed. The cursor must be placed in the workspace before any command is carried-out in IDA Pro. The key binding is defined in idagui.cfg file that's located in IDA Pro installation directory. An excerpt of the key binding (hot key) is provided below.

"MakeCode"              =       'C'						
"MakeData" = 'D'
"MakeAscii" = 'A'
"MakeUnicode" = 0 // create unicode string
"MakeArray" = "Numpad*"
"MakeUnknown" = 'U'

"MakeName" = 'N'
//"MakeAnyName" = "Ctrl-N"
"ManualOperand" = "Alt-F1"

"MakeFunction" = 'P'
"EditFunction" = "Alt-P"
"DelFunction" = 0
"FunctionEnd" = 'E'
One can alter idagui.cfg to change the default key binding, but we will only consider the default key binding. Now we have grasped the key binding concept, let's see how to use it in our binary. In the previous example, we are creating a new segment, i.e. 0xF000. Now, we will go to the first instruction that's executed in the BIOS within that segment, i.e. address 0xF000:0xFFF0. Press G, the dialog box below will be shown. IDA Pro free goto-address dialog

In this dialog box, enter the destination address. You must enter the address in complete form (segment:offset) as shown above, i.e. F000:FFF0. Then, click OK to go to the intended address. Note that you don't have to type the leading 0x character, since by default, the value within the input box is in hexadecimal. The result will be as shown below (inside IDA Pro workspace).

IDA Pro free goto-address result

The next step to do is to convert the value in this address into a meaningful machine instruction. To do so, press C. The result is as shown below.

IDA Pro free make-code result

Then, we can follow the jump by pressing Enter. The result is as shown below.

IDA Pro free jump result

We can return from the jump that we've just made by pressing Esc.

Up to this point, you've gained significant intuition to use IDA Pro. You just need to consult the key bindings in idagui.cfg in case want to do something and don't know what key to press. Now we're armed. What we need to do next is to understand the basic stuff by using the hex editor before proceeding through the disassembling session.

6. Award BIOS File Structure

Award BIOS file consists of several components. Some of the components are LZH level-1 compressed. We can recognize them by looking at the -lh5- signature in the beginning of that component using hex editor. Here's an example :

Address  Hex                                     ASCII

00000000 25F2 2D6C 6835 2D85 3A00 00C0 5700 0000 %.-lh5-.:...W...
00000010 0000 4120 010C 6177 6172 6465 7874 2E72 ..A ..awardext.r
00000020 6F6D DB74 2000 002C F88E FBDF DD23 49DB om.t ..,.....#I.
Beside the compressed components, there are also some "pure" 16-bit x86 binary components. Award BIOS execution begins in this "pure" binary (uncompressed) components.

We have to know the entry point to start our disassembly to this BIOS binary. We know that the execution of x86 processor begins in 16-bit real mode at address F000:FFF0 (physical address FFFF_FFF0h) following restart or power up, as per Intel Software Developer Manual Vol.3 "System Programming". Based on our intuition, this address must contain a 16-bit real mode x86 executable code. That's true. Below is the explanation of the memory map of the bios binary that's dissected in this article. It's a 2MBit/256 KB bios image for Iwill VD133 mainboard.

6.1. The Compressed Components

Memory map as seen in hex editor:
  1. 0000h - 3AACh : XGROUP ROM (awardext.rom), this is an award extension rom. It contains routine that is called from the system BIOS, i.e. original.tmp
  2. 3AADh - 97AFh : CPUCODE.BIN, this is the microcode for the BIOS.
  3. 97B0h - A5CFh : ACPITBL.BIN, the acpi table.
  4. A5D0h - A952h : Iwill.bmp, the BMP logo.
  5. A953h - B3B1h : nnoprom.bin, I haven't know yet what this component's role.
  6. B3B2h - C86Ch : Antivir.bin, the bios bootsector antivirus.
  7. C86Dh - 1BEDCh : ROSUPD.BIN, this is a custom bios component in my bios. It's used to display a customized Boot Logo and indicator
  8. 2_0000h - 3_5531h : original.tmp, this is the system BIOS of my particular BIOS. Most award bios with 2 Mbit size have original.tmp located at 2_0000h - 3_xxxxh (if you look from within hex editor). Some award bios with 4 Mbit size have the original.tmp located in the very beginning of the bios binary i.e. 0000h.
Note:
  1. Between the compressed ROSUPD.BIN and original.tmp there are padding FFh bytes. These padding bytes also found after the compressed original.tmp and the pure binary BIOS components that will be explained below. An example of these padding bytes :
    Address  Hex                                     ASCII
    
    00037D00 2A42 4253 532A 0060 0070 0060 0060 00A0 *BBSS*.`.p.`.`.. 00037D10 3377 4670 8977 ACCF C4CF 0100 00FF FFFF 3wFp.w.......... 00037D20 FFFF FFFF FFFF FFFF FFFF FFFF FFFF FFFF ................ 00037D30 FFFF FFFF FFFF FFFF FFFF FFFF FFFF FFFF ................
  2. The compressed component can be extracted easily by copying and pasting it into a new binary file in Hexworkshop. Then, decompress this new file by using LHA 2.55 or winzip. If we are into using winzip, give the new file an .lzh extension so that it'll be automatically associated with winzip. Recognizing where we should "cut" to get the new file is pretty easy, just look for the -lh5- string. Two bytes preceeding -lh5- string is the beginning of the file and the end of the file is always 00h, right before the next compressed file (with the -lh5- marker in its beginning), right before the padding bytes or right before some kind of checksum. I'll present two examples below. The highlighted bytes is the beginning or the end of the compressed file.

    compressed CPUCODE.BIN file in my BIOS :
    Address  Hex                                     ASCII
    
    00003AA0 4E61 19E6 9775 2B46 BA55 85F0 0024 382D Na...u+F.U...$8- 00003AB0 6C68 352D DC5C 0000 00A0 0000 0000 0140 lh5-.\.........@
    00003AC0 2001 0B43 5055 434F 4445 2E42 494E BCAA ..CPUCODE.BIN..
    00003AD0 2000 0038 3894 9700 52C4 A2CF F040 0000 ..88...R....@..
    00003AE0 4000 0000 0000 0000 0000 0000 0000 0000 @...............
    ........
    000097A0 0E3C 8FA7 FFF4 FFFE 9FFF D3FF FFFB FF00 .<.............. 000097B0 24D9 2D6C 6835 2DFA 0D00 00A6 2100 0000 $.-lh5-.....!...

    compressed ORIGINAL.TMP file in my BIOS :
    Address  Hex                                     ASCII
    
    0001FFF0 FFFF FFFF FFFF FFFF FFFF FFFF FFFF FFFF ................ 00020000 251A 2D6C 6835 2D09 5501 0000 0002 0000 %.-lh5-.U.......
    00020010 0000 5020 010C 6F72 6967 696E 616C 2E74 ..P ..original.t
    00020020 6D70 0CD9 2000 002D 7888 F0FD D624 A5BA mp.. ..-x....$..
    ........
    00035510 019E 6E67 BF11 8582 88D9 4E7C BEC8 C34C ..ng......N|...L
    00035520 401D 189F BDD0 A176 17F0 4383 1D73 BF99 @......v..C..s..
    00035530 00C9 FFFF FFFF FFFF FFFF FFFF FFFF FFFF ................
    00035540 FFFF FFFF FFFF FFFF FFFF FFFF FFFF FFFF ................

6.2. The Pure Binary Components

Memory map as seen in hex editor:
  1. 3_6000h - 3_6C4Ah : This routine initializes the DRAM controller (located in Host Bridge) and the DRAM clock in my BIOS
  2. 3_7000h - 3_7D1Ch : The decompression block, this routine contains the LZH decompression engine which decompresses the compressed bios components above.
  3. 3_C000h - 3_CFE4h : This area contains various routine, the lower 128KB BIOS address decode enabler, the default VGA initialization (executed if system bios is erratic), the rest of Hostbridge initialization routine, etc.
  4. 3_E000h - 3_FFFFh : This area contains the Boot Block code.
Note: Between some of the components lies padding bytes. Some are FFh bytes and some are 00h bytes.

6.3. The Memory Map In The Real System (Mainboard)

We have to note that the memory map described previously is for the BIOS binary as we see it from within a hex editor. In the mainboard BIOS chip, it's a bit different and more complex. It's mapped in my mainboard as follows (it may differ a bit with yours, consult your chipset documentation):

  1. 0_0000h - 3_FFFFh in the BIOS binary (as displayed in hex editor) is mapped into FFFC_0000h - FFFF_FFFFh in my system memory space. Due to my system's northbridge (as per its datasheet), address FFFF_0000h-FFFF_FFFFh is just an alias to F_0000h - F_FFFFh or speaking in "real-mode lingo" F000:0000h - F000:FFFFh. Note that this mapping only applies just after power-on, since it's the chipset's power-on default value. It's not guaranteed to be valid after the chipset is reprogrammed by the BIOS itself. There are some other "kludge"s though and they are really system dependent. You have to consult Intel Software Developer Manual Volume 3 (system programming) and your chipset datasheet.
  2. Due to the explanantion in 1. , the pure binary BIOS components is mapped as follows (note: just after power-on) :
    1. BootBlock : F000:E000h - F000:FFFFh
    2. Decompression Block : F000:7000h - F000:7D1Ch
    3. Early DRAM controller and DRAM Initialization : F000:6000h - F000:6C4Ah
  3. The compressed BIOS components are mapped into system memory space after they are decompressed in a different manner. They reliant on the decompression block routine, but there are few mappings that seem remain the same accross different BIOS files. These mappings are (as per my BIOS. Yours may differ, but the segment address very possibly the same):
    1. original.tmp a.k.a System BIOS : E000:0000h - F000:FFFFh
    2. awardext.rom a.k.a Award extension ROM : 4100:0000h - 4100:xxxxh. Later relocated to 6000:0000h - 6000:xxxxh by original.tmp, i.e. before it's executed.
We have to be aware of this mapping during our journey. It's very easy to get lost due to the sheer complexity of the BIOS binary address mapping into the real system. But, there's a tip that will ease our effort during our disassembly session using IDA Pro:
Begin the disassembly session with the pure binary components. Start disassembly at address F000:FFF0h (3_FFF0h if we look at the binary from within hex editor). To do so, open the binary (VD30728.BIN , i.e. Iwill VD133 BIOS binary) in IDA Pro, then disassemble this file by setting its address mapping to start at C000:0000h and remember to disable segment naming so that we can see its real-mode address in the system during its execution. Adjust the other segments address by using IDA Pro scripts and remember to adjust the addressing scheme to conform to the chipset datasheet.

7. Disassembling the BIOS

Due to Intel System Programming Guide, we'll begin the disassembly session at address F000:FFF0h (note: look at the memory mapping above and adjust IDA Pro to suit it). You may ask: How this is even possible? Intel Software Developer Manual Vol. 3 (PROCESSOR MANAGEMENT AND INITIALIZATION - First Instruction Executed) says :

The first instruction that is fetched and executed following a hardware reset is located at physical address FFFFFFF0H.
The answer is : the northbridge chipset aliases address range FFFE_FFFFh-FFFF_FFFFh to 000F_xxxxh. Also, note that the southbridge has no means to alter the translation of this address range. It just passes the addresses directly to the BIOS ROM chip. Hence, there's no difference between address FFFF_FFF0h and F_FFF0h (or F000:FFF0 in "real-mode lingo") just after power-on or reset. It's that simple heh (-_^) . This is the BootBlock area. It always contains a far jump into the bootblock routine, mostly to F000:E05Bh. From this point we can continue the disassembly to cover the majority of the pure binary part. In reality, lots of the pure binary code are never executed at all since it's very seldom your system BIOS gets corrupted and the Bootblock POST (Power On Self Test) routine takes place, unless you're messing up with it :P.

7.1. Bootblock

From this point we can disassemble the bootblock routines. Now, I'll present some of the obscure and important areas of the BIOS code in the disassembled bootblock. This is with respect to my BIOS, yours may vary but IMHO it will be similar.

7.1.1 "Virtual Shutdown" routine

Address    Hex                       Mnemonic

F000:E07F BC 0B F8 mov sp, 0F80Bh ; ret from this jmp redirected to 0E103h (F000:E103h) F000:E082 E9 7B 15 jmp Chipset_Reg_Early_Init

7.1.2 Chipset_Reg_Early_Init routine

Address    Hex                       Mnemonic

F000:F600 Chipset_Reg_Early_Init proc near ; CODE XREF: F000:E082j F000:F600 66 C1 E4 10 shl esp, 10h F000:F604 BE C4 F6 mov si, 0F6C4h ; addr of chipset reg mask F000:F607 next_PCI_reg: ; CODE XREF: Chipset_Reg_Early_Init+29j F000:F607 2E 8B 0C mov cx, cs:[si] F000:F60A BC 10 F6 mov sp, 0F610h F000:F60D E9 F8 00 jmp Read_PCI_Byte F000:F60D ; --------------------------------------------------------------------------- F000:F610 12 F6 dw 0F612h F000:F612 ; --------------------------------------------------------------------------- F000:F612 2E 22 44 02 and al, cs:[si+2] F000:F616 2E 0A 44 03 or al, cs:[si+3] F000:F61A BC 20 F6 mov sp, 0F620h F000:F61D E9 02 01 jmp Write_PCI_Byte F000:F61D ; --------------------------------------------------------------------------- F000:F620 22 F6 dw 0F622h F000:F622 ; --------------------------------------------------------------------------- F000:F622 83 C6 04 add si, 4 F000:F625 81 FE 04 F7 cmp si, 0F704h ; are we done yet? F000:F629 75 DC jnz next_PCI_reg F000:F62B BA D0 04 mov dx, 4D0h ; Master PIC Edge/Level Triggered F000:F62E 32 C0 xor al, al F000:F630 EE out dx, al F000:F631 FE C2 inc dl ; Slave PIC Edge/Level Triggered F000:F633 EE out dx, al F000:F634 B9 08 38 mov cx, 3808h ; Bus#0 Dev#7 Func#0 (PCI2ISA Bridge) reg 8h(revision id) F000:F637 BC 3D F6 mov sp, 0F63Dh F000:F63A E9 CB 00 jmp Read_PCI_Byte F000:F63A ; --------------------------------------------------------------------------- F000:F63D 3F F6 dw 0F63Fh F000:F63F ; --------------------------------------------------------------------------- F000:F63F 3C 10 cmp al, 10h ; is silicon revision > 10h F000:F641 72 53 jb silicon_rev_lt_10h ; in current mobo this jmp NOT taken (rev id = 12h) F000:F643 B9 90 3B mov cx, 3B90h ; B#0 D#7 F#3: PwrMngmt&SMBus - SMBus IO Base Addr lo_byte F000:F646 B0 00 mov al, 0 ; set SMBus IO Base lo_byte to 00h F000:F648 BC 4E F6 mov sp, 0F64Eh F000:F64B E9 D4 00 jmp Write_PCI_Byte F000:F64B ; --------------------------------------------------------------------------- F000:F64E 50 F6 dw 0F650h F000:F650 ; --------------------------------------------------------------------------- F000:F650 B9 91 3B mov cx, 3B91h ; B#0 D#7 F#3: PwrMngmt&SMBus - SMBus IO Base Addr hi_byte F000:F653 B0 50 mov al, 50h ; 'P' ; set SMBus IO Base hi_byte to 50h,
F000:F653 ; so, now SMBus IO Base is at port 5000h !!!
F000:F655 BC 5B F6 mov sp, 0F65Bh F000:F658 E9 C7 00 jmp Write_PCI_Byte F000:F658 ; --------------------------------------------------------------------------- F000:F65B 5D F6 dw 0F65Dh F000:F65D ; --------------------------------------------------------------------------- F000:F65D B9 D2 3B mov cx, 3BD2h ; B#0 D#7 F#3: PwrMngmt&SMBus - SMBus Ctrl F000:F660 B0 01 mov al, 1 ; SMBus Controller Enable F000:F662 BC 68 F6 mov sp, 0F668h F000:F665 E9 BA 00 jmp Write_PCI_Byte F000:F665 ; --------------------------------------------------------------------------- F000:F668 6A F6 dw 0F66Ah F000:F66A ; --------------------------------------------------------------------------- F000:F66A BA 05 40 mov dx, 4005h ; access ACPI Reg 05h F000:F66A ; (see chipset init above for init of port 4000h as ACPI IO) F000:F66D B0 80 mov al, 80h ; '' ; setting reserved bit? F000:F66F EE out dx, al F000:F670 B9 41 3B mov cx, 3B41h ; general config F000:F673 BC 79 F6 mov sp, 0F679h F000:F676 E9 8F 00 jmp Read_PCI_Byte F000:F676 ; --------------------------------------------------------------------------- F000:F679 7B F6 dw 0F67Bh F000:F67B ; --------------------------------------------------------------------------- F000:F67B 0C 04 or al, 4 ; RTC Enable Signal Gated with PSON (SUSC#) in F000:F67B ; Soft-Off Mode F000:F67D BC 83 F6 mov sp, 0F683h F000:F680 E9 9F 00 jmp Write_PCI_Byte F000:F680 ; --------------------------------------------------------------------------- F000:F683 85 F6 dw 0F685h F000:F685 ; --------------------------------------------------------------------------- F000:F685 reinit_SCI_interrupt: ; CODE XREF: Chipset_Reg_Early_Init+92j F000:F685 B9 42 3B mov cx, 3B42h ; B#0 D#7 F#3: SCI Interrupt Configuration F000:F688 BC 8E F6 mov sp, 0F68Eh F000:F68B EB 7B jmp short Read_PCI_Byte F000:F68B ; --------------------------------------------------------------------------- F000:F68D 90 db 90h ; E F000:F68E 90 F6 dw 0F690h F000:F690 ; --------------------------------------------------------------------------- F000:F690 A8 40 test al, 40h ; check SUSC State F000:F692 74 F1 jz reinit_SCI_interrupt F000:F694 EB 27 jmp short exit F000:F696 ; --------------------------------------------------------------------------- F000:F696 silicon_rev_lt_10h: ; CODE XREF: Chipset_Reg_Early_Init+41j F000:F696 B9 80 3B mov cx, 3B80h F000:F699 B0 00 mov al, 0 F000:F69B BC A1 F6 mov sp, 0F6A1h F000:F69E E9 81 00 jmp Write_PCI_Byte F000:F69E ; --------------------------------------------------------------------------- F000:F6A1 A3 F6 dw 0F6A3h F000:F6A3 ; --------------------------------------------------------------------------- F000:F6A3 B9 81 3B mov cx, 3B81h F000:F6A6 B0 50 mov al, 50h ; 'P' F000:F6A8 BC AE F6 mov sp, 0F6AEh F000:F6AB EB 75 jmp short Write_PCI_Byte F000:F6AB ; --------------------------------------------------------------------------- F000:F6AD 90 db 90h ; E F000:F6AE B0 F6 dw 0F6B0h F000:F6B0 ; --------------------------------------------------------------------------- F000:F6B0 B9 84 3B mov cx, 3B84h F000:F6B3 B0 07 mov al, 7 F000:F6B5 BC BB F6 mov sp, 0F6BBh F000:F6B8 EB 68 jmp short Write_PCI_Byte F000:F6B8 ; --------------------------------------------------------------------------- F000:F6BA 90 db 90h ; F000:F6BB BD F6 dw 0F6BDh F000:F6BD ; --------------------------------------------------------------------------- F000:F6BD exit: ; CODE XREF: Chipset_Reg_Early_Init+94j F000:F6BD 66 C1 EC 10 shr esp, 10h F000:F6C1 C3 retn F000:F6C1 Chipset_Reg_Early_Init endp ......... F000:F6C4 Begin_Chipset_Reg_Val F000:F6C4 47 38 dw 3847h ; bus#0 dev#7 func#0: PCI2ISA Bridge reg 47h F000:F6C6 BF db 0BFh ; disable PCI delay transaction F000:F6C7 A0 db 0A0h ; - Use INIT as CPU Reset F000:F6C7 ; - Enable (ports 4D0-1 per EISA specification) F000:F6C7 ; F000:F6C8 50 00 dw 50h ; Host Bridge reg 50h Request Phase Control F000:F6CA FF db 0FFh ; and mask F000:F6CB 10 db 10h ; enable Defer Retry When HLOCK Active F000:F6CB ; F000:F6CC 51 00 dw 51h ; Host Bridge reg 51h Response Phase Control F000:F6CE DD db 0DDh ; reset reserved bits F000:F6CF 0D db 0Dh ; enable: F000:F6CF ; - Non-Posted IOW F000:F6CF ; - Concurrent PCI Master / Host Operation F000:F6CF ; F000:F6D0 52 00 dw 52h ; Host Bridge reg 52h Dynamic Defer Timer F000:F6D2 E0 db 0E0h ; disable dynamic defer F000:F6D3 08 db 8 ; snoop stall count = 8 F000:F6D3 ; F000:F6D4 6B 00 dw 6Bh ; Host Bridge reg 6Bh DRAM Arbitration Control F000:F6D6 00 db 0 ; - Park at last bus owner F000:F6D6 ; - Disable Fast Read to Write turn-around F000:F6D6 ; - Memory Module Configuration - Normal Operation F000:F6D6 ; - MD Bus 2nd Level Strength Control = Normal Slew rate F000:F6D6 ; - CAS Bus 2nd Level Strength Control = Normal Slew rate F000:F6D6 ; - VC-DRAM disable F000:F6D6 ; - Multi-Page Open disable F000:F6D7 01 db 1 ; Enable Multi-Page Open F000:F6D7 ; F000:F6D8 71 00 dw 71h ; Host Bridge reg 71h CPU to PCI Flow Control 1 F000:F6DA 00 db 0 ; Disable: F000:F6DA ; - Dynamic Burst F000:F6DA ; - Byte Merge F000:F6DA ; - PCI I/O Cycle Post Write F000:F6DA ; - PCI Burst F000:F6DA ; - PCI Fast Back-to-Back Write F000:F6DA ; - Quick Frame Generation F000:F6DA ; - 1 Wait State PCI Cycles F000:F6DB 08 db 8 ; Enable PCI Burst F000:F6DB ; F000:F6DC 75 00 dw 75h ; Host Bridge reg 75h PCI Arbitration 1 F000:F6DE 00 db 0 ; - PCI has priority F000:F6DE ; - REQ-based (arbitrate at end of REQ#) F000:F6DE ; - Disable PCI Master Bus Time-Out F000:F6DF 80 db 80h ; Fair arbitration between PCI and CPU F000:F6DF ; F000:F6E0 76 00 dw 76h ; Host Bridge reg 76h PCI Arbitration 2 F000:F6E2 CF db 0CFh ; and mask F000:F6E3 80 db 80h ; or mask F000:F6E3 ; F000:F6E4 41 38 dw 3841h ; bus#0 dev#7 func#0: PCI2ISA Bridge reg 41h - ISA Bus control F000:F6E6 FF db 0FFh ; and mask F000:F6E7 01 db 1 ; ROM Write Enable F000:F6E7 ; F000:F6E8 43 38 dw 3843h ; bus#0 dev#7 func#0: PCI2ISA Bridge reg43h-ROM Decode Control F000:F6EA 00 db 0 ; Disable ROMCS# decode for all ranges F000:F6EB 30 db 30h ; Enable ROMCS# decode for the following ranges:
F000:F6EB ; 1. FFF00000h-FFF7FFFFh
F000:F6EB ; 2. 000E0000h-000EFFFFh
F000:F6EB ; F000:F6EC 4A 38 dw 384Ah ; bus#0 dev#7 func#0: PCI2ISA Bridge reg4Ah-IDE Interrupt Routing F000:F6EE FF db 0FFh ; and mask F000:F6EF 40 db 40h ; Access ports 00-FFh via SD bus (applies to F000:F6EF ; external devices only; internal devices such as F000:F6EF ; the mouse controller are not effected) F000:F6EF ; F000:F6F0 5A 38 dw 385Ah ; bus#0 dev#7 func#0: PCI2ISA Bridge reg5Ah-KBC / RTC Control F000:F6F2 F8 db 0F8h ; Disable: F000:F6F2 ; - Internal RTC F000:F6F2 ; - Internal PS2 Mouse F000:F6F2 ; - Internal KBC F000:F6F3 04 db 4 ; Internal RTC Enable F000:F6F3 ; F000:F6F4 48 3B dw 3B48h ; B#0 D#7 F#3: PwrMngmt&SMBus - PwrMngmt IO Base Addr lo_byte F000:F6F6 00 db 0 ; and mask F000:F6F7 00 db 0 ; or mask F000:F6F7 ; F000:F6F8 49 3B dw 3B49h ; B#0 D#7 F#3: PwrMngmt&SMBus - PwrMngmt IO Base Addr hi_byte F000:F6FA 40 db 40h ; and mask F000:F6FB 40 db 40h ; PwrMngmt IO Base Addr = IO Port 4000h F000:F6FB ; F000:F6FC 41 3B dw 3B41h ; bus#0 dev#7 func#3: PwrMngmt&SMBus - General Config F000:F6FE FF db 0FFh ; and mask F000:F6FF 80 db 80h ; enable: I/O for ACPI I/O Base F000:F6FF ; F000:F700 76 38 dw 3876h ; bus#0 dev#7 func#0: PCI2ISA Bridge reg76h-GPIO/Chip Select Control F000:F702 00 db 0 ; Disable GPO 24-26 F000:F703 02 db 2 ; Enable GPO 25 F000:F703 End_Chipset_Reg_Val

7.1.3 Init_Interrupt_n_PwrMgmt routine

Address    Hex                       Mnemonic

F000:E1A0 BF A6 E1 mov di, 0E1A6h ; ret addr below
F000:E1A3 E9 42 99 jmp Init_Interrupt_n_PwrMgmt
F000:E1A6 ; ---------------------------------------------------------------------------
F000:E1A6 B0 C1 mov al, 0C1h ; '+'
....
F000:7CDD delay: ; CODE XREF: Init_Interrupt_n_PwrMgmt:delay
F000:7CDD E2 FE loop delay
F000:7CDF FF E7 jmp di ; jmp to F000:E1A6h F000:7CDF Init_Interrupt_n_PwrMgmt endp

7.1.4 Call To "Early Silicon Support" Routine

Address    Hex                       Mnemonic

F000:E1AD E9 47 02 jmp Search_BBSS_Signature F000:E1AD ; --------------------------------------------------------------------------- F000:E1B0 B2 E1 dw 0E1B2h F000:E1B2 ; --------------------------------------------------------------------------- F000:E1B2 0B F6 or si, si F000:E1B4 74 38 jz short _BBSS_not_found F000:E1B6 8B DE mov bx, si F000:E1B8 2E 8B 04 mov ax, cs:[si] F000:E1BB 25 00 F0 and ax, 0F000h F000:E1BE C1 E8 04 shr ax, 4 F000:E1C1 05 00 F0 add ax, 0F000h F000:E1C4 8E D8 mov ds, ax ; ds=base addr for calculation of BBSS checksum F000:E1C6 assume ds:nothing F000:E1C6 32 E4 xor ah, ah F000:E1C8 33 F6 xor si, si F000:E1CA B9 FF 0F mov cx, 0FFFh ; length of BBSS F000:E1CD next_byte: ; CODE XREF: 000FE1D0j F000:E1CD AC lodsb F000:E1CE 02 E0 add ah, al ; calc 8-bit chksum for BBSS F000:E1D0 E2 FB loop next_byte F000:E1D2 3A 24 cmp ah, [si] F000:E1D4 75 18 jnz short _BBSS_not_found F000:E1D6 2E 8B 07 mov ax, cs:[bx] ; chsum ok, jump to (exec) the BBSS engine F000:E1D9 25 00 F0 and ax, 0F000h F000:E1DC 8B F0 mov si, ax F000:E1DE 81 C6 FC 0F add si, 0FFCh F000:E1E2 2E 8B 34 mov si, cs:[si] F000:E1E5 BC EC E1 mov sp, 0E1ECh F000:E1E8 FF E6 jmp si ; jmp to F000:60B4h (BBSS Engine), returns at F000:E1F8h F000:E1E8 ; the BBSS engine is silicon support routine, used to F000:E1E8 ; initialize PwrMgmt GPIO Ctlr, Host Ctlr and DRAM Ctlr F000:E1E8 ; --------------------------------------------------------------------------- F000:E1EA 87 db 87h ; E F000:E1EB DB db 0DBh ; - F000:E1EC F8 E1 dw 0E1F8h ......... F000:E1F8 BBSS_exec_done: F000:E1F8 B8 00 00 mov ax, 0 .........
The code above gets executed before the bootblock is copied to RAM. In case the RAM is faulty, the system will halt and output error code from system speaker.

7.1.5 Bootblock Is Copied And Executed In RAM

Address    Hex                       Mnemonic

F000:E2AA ;----------- Enter 16-bit Protected Mode (Flat) ------------ F000:E2AA assume ds:_F000h F000:E2AA 0F 01 16 F6 E4 lgdt qword ptr word_F000_E4F6 F000:E2AF 0F 20 C0 mov eax, cr0 F000:E2B2 0C 01 or al, 1 F000:E2B4 0F 22 C0 mov cr0, eax F000:E2B7 EB 00 jmp short $+2 ; clear prefetch, enter 16-bit PMode. We're F000:E2B7 ; using the "unchanged" hidden value of CS F000:E2B7 ; register (descriptor cache) from previous F000:E2B7 ; "PMode session" in memory_check_routine F000:E2B7 ; for code segment desc F000:E2B9 B8 08 00 mov ax, 8 F000:E2BC 8E D8 mov ds, ax ; init ds to 4GB-wide segment F000:E2BE assume ds:nothing F000:E2BE 8E C0 mov es, ax ; init es to 4GB-wide segment F000:E2C0 F000:E2C0 There are two locations to access E0000H ROM space, one is 0E0000H F000:E2C0 and another is 0FFFE0000H. Some chipsets can not access onboard ROM F000:E2C0 space at 0E0000H if any device also use the space on ISA bus. F000:E2C0 To solve this problem , we need to change address to 0FFFE0000H F000:E2C0 to read BIOS contents at 0E0000H space. F000:E2C0 assume es:nothing F000:E2C0 66 BE 00 00 0E 00 mov esi, 0E0000h F000:E2C6 67 66 81 7E 02 2D 6C 68+ cmp dword ptr [esi+2], '5hl-' F000:E2CF 74 07 jz short LHA_sign_OK F000:E2D1 66 81 CE 00 00 F0 FF or esi, 0FFF00000h F000:E2D8 F000:E2D8 move entire BIOS (i.e. original.tmp and bootblock) F000:E2D8 from ROM at E0000h-FFFFFh to RAM at 10000h-2FFFFh F000:E2D8 LHA_sign_OK: ; CODE XREF: 000FE2CF F000:E2D8 66 BF 00 00 01 00 mov edi, 10000h ; destination addr = 1000:0
F000:E2DE 66 B9 00 80 00 00 mov ecx, 8000h ; copy 128 KByte to buffer (original.tmp n Bootblock)
F000:E2E4 67 F3 66 A5 rep movs dword ptr es:[edi], dword ptr [esi]
F000:E2E8 0F 20 C0 mov eax, cr0 F000:E2EB 24 FE and al, 0FEh F000:E2ED 0F 22 C0 mov cr0, eax F000:E2F0 EB 00 jmp short $+2 ; leave 16-bit protected mode (voodoo mode) F000:E2F2 EA F7 E2 00 20 jmp far ptr _bootblock_in_RAM ......... 2000:E2F7 ; --------------------------------------------------------------------------- 2000:E2F7 Setup temporary stack at 0:1000H, at this point 2000:E2F7 Bios code (last 128 Kbyte) is still compressed 2000:E2F7 except the bootblock and decompression code 2000:E2F7 2000:E2F7 _bootblock_in_RAM: 2000:E2F7 33 C0 xor ax, ax 2000:E2F9 8E D0 mov ss, ax 2000:E2FB assume ss:nothing 2000:E2FB BC 00 10 mov sp, 1000h
The last 128KB of BIOS code (E000:0000h - F000:FFFFh) get copied to RAM as follows :
  1. Northbridge power-on default values aliases F_0000h-F_FFFFh address space with FFFE_FFFFh-FFFF_FFFFh, where the BIOS ROM chip address space mapped. That's why the following code is safely executed:
    Address    Hex                       Mnemonic
    
    F000:FFF0 EA 5B E0 00 F0 jmp far ptr entry_point ; Northbridge is responsible for decoding F000:FFF0 ; the target address of this jump into BIOS F000:FFF0 ; chip through address aliasing. So, even if F000:FFF0 ; this is a far jump (read Intel Software F000:FFF0 ; Developer Guide Vol.3 for info) F000:FFF0 ; we are still in BIOS chip d00d ;) F000:FFF0 ; vi693A: FFFE_FFFF-FFFF_FFFF is 000F_xxxx alias.
    also, northbridge power-on default values disables DRAM shadowing for this address space. Thus, read/write to this address space will not be forwarded to DRAM. At the same time, there's no control register in southbridge that controls the mapping of this address space. Hence, read operation to this address space will be "directly forwarded" to the BIOS ROM chip without being altered by the southbridge. Of course this read operation first pass through northbridge which apply the address aliasing scheme.
  2. Very close to the beginning of Bootblock execution, routine Chipset_Reg_Early_Init executed. This routine reprogram the PCI-to-ISA bridge (in southbridge) to enable decoding of address E_0000h-E_FFFFh to ROM, i.e. forwarding read operation in this address space into the BIOS ROM chip. The northbridge power-on default values disables DRAM shadowing for this address space. Thus, read/write to this address space will not be forwarded to DRAM.
  3. Then comes the routine displayed above which copied the last 128KB BIOS ROM chip content (address E_0000h - F_FFFFh) into DRAM at 1000:0000h - 2000:FFFFh and continues execution from there. This can be accomplished since this address space is mapped only to DRAM by the chipset, no special address translation.

7.1.6 Call to bios decompression routine and the jump into decompressed system bios

Address    Hex                       Mnemonic

2000:E3DC E8 33 01 call Decompress_System_BIOS 2000:E3DF EB 03 jmp short _sysbios_chksum_ok 2000:E3E1 ; --------------------------------------------------------------------------- 2000:E3E1 _sysbios_chksum_error: ; CODE XREF: 0002E347 2000:E3E1 ; 0002E350 ... 2000:E3E1 B8 00 10 mov ax, 1000h 2000:E3E4 2000:E3E4 _sysbios_chksum_ok: ; CODE XREF: 0002E3DF 2000:E3E4 8E D8 mov ds, ax ; ax = 5000h if decompression successful, otherwise ax = 1000h 2000:E3E6 assume ds:_1000h 2000:E3E6 B0 C5 mov al, 0C5h ; '+' 2000:E3E8 E6 80 out 80h, al ; manufacture's diagnostic checkpoint 2000:E3EA 2000:E3EA The source data segment is 5000H if checksum is good. 2000:E3EA the contents in this area is decompressed by routine "Decompress_System_BIOS". 2000:E3EA And segment 1000H is for shadowing original BIOS image if checksum 2000:E3EA is bad. BIOS will shadow bootblock and boot from it. 2000:E3EA E8 87 EB call Copy_Decomprssd_E_n_F_Seg ; relocate dcomprssed from decompression seg to 2000:E3EA ; E_n_F_seg in RAM (Shadow the system BIOS) 2000:E3ED B0 00 mov al, 0 2000:E3EF E8 C7 10 call Setup_Cpu_Cache 2000:E3F2 2000:E3F2 BIOS decide where to go from here. 2000:E3F2 If BIOS checksum is good, this address F80DH is shadowed by 2000:E3F2 decompressed code (i.e. original.bin and others), 2000:E3F2 And "BootBlock_POST" will be executed if checksum is bad. 2000:E3F2 EA 0D F8 00 F0 jmp far ptr loc_F000_F80D ; jump to decompressed system BIOS (F_seg) in RAM if decompression 2000:E3F2 ; successful, otherwise jump to bootblock at the same address if 2000:E3F2 ; decompression failed.
During execution of Decompress_System_BIOS routine, the compressed BIOS code (original.tmp) at 1000:0000h - 2000:FFFFh in RAM decompressed into 5000:0000h - 6000:FFFFh also in RAM. This decompressed System BIOS later relocated to E000:0000h - F000:FFFFh also in RAM. However, if decompression process failed, current compressed E_seg and F_seg located in RAM at 1000:0000h - 2000:FFFFh (of course including the copy of bootblock in RAM) will be relocated to E000:0000h-F000:0000h in RAM and then the bootblock error handling code will be executed. Note that the problem due to address aliasing and DRAM shadowing are handled during the relocation by setting the appropriate chipset registers. Below is the basic run-down of this routine:
  1. Enable FFF80000h-FFFDFFFFh decoding. Access to this address will be forwarded into the BIOS ROM chip by the PCI-to-ISA Bridge. PCI-to-ISA bridge ROM decode control register is in-charge here. This is needed, since my BIOS is 256KB and only 128KB of it has been copied into RAM, i.e. the original.tmp and bootblock which is at 1000:0000h-2000:FFFFh by now.

  2. Copy lower 128KB of BIOS code from FFFC_0000h-FFFD_FFFFh in ROM chip into 8000:0000h - 9000:FFFFh in RAM.
  3. Disable FFF8_0000h-FFFD_FFFFh decoding. Access to this address will not be forwarded into the BIOS ROM chip by the PCI-to-ISA Bridge.
  4. Verify checksum of the whole compressed BIOS image. Calculate the 8-bit checksum of copied compressed BIOS image in RAM (i.e. 8000:0000h - 9000:FFFFh + 1000:0000h - 2000:7FFDh) and compare the result against result stored in 2000:7FFEh. If 8-bit checksum doesn't match, then goto _sysbios_chksum_err, else continue to decompression routine.
  5. Look for the decompression engine by looking for *BBSS* string in segment 2000h.
  6. Decompress the compressed BIOS components by invoking the decompression engine from previous step. Note that at this stage only origininal.tmp and it's extension i.e. awardext.rom which get decompressed (probably also awardyt.rom in other Award BIOS version, I haven't verify it). The other component treated in different fashion. The Decompress routine only process their decompressed/expansion area information then put it somewhere in RAM. We need some preliminary info before delving into this step as follows:

    1. The format of the LZH level-1 compressed bios components. The address ranges where these BIOS components will be located after decompression are contained within this format. The format is as follows (it applies to all compressed components):
      Offset from 1st byte Offset in Real Header Contents
      00h N/A The header length of the component. It depends on the file/component name.
      01h N/A The header 8-bit checksum, not including the first 2 bytes (header length and header checksum byte).
      02h - 06h 00h - 04h LZH Method ID (ASCII string signature). In my BIOS it's "-lh5-" which means: 8k sliding dictionary(max 256 bytes) + static Huffman + improved encoding of position and trees.
      07h - 0Ah 05h - 08h compressed file/component size in little endian dword value, i.e. MSB at 0Ah and so forth
      0Bh - 0Eh 09h - 0Ch Uncompressed file/component size in little endian dword value, i.e. MSB at 0Eh and so forth
      0Fh - 10h 0Dh - 0Eh Decompression offset address in little endian word value, i.e. MSB at 10h and so forth. The component will be decompressed into this offset address (real mode addressing is in effect here).
      11h - 12h 0Fh - 10h Decompression segment address in little endian word value, i.e. MSB at 12h and so forth. The component will be decompressed into this segment address (real mode addressing is in effect here).
      13h 11h File attribute. My BIOS components contain 20h here, which is normally found in LZH level-1 compressed file.
      14h 12h Level. My BIOS components contain 01h here, which means it's a LZH level-1 compressed file.
      15h 13h component filename name length in byte.
      16h - [15h+filename_len] 14h - [13h+filename_len] component filename (ASCII string)
      [16h+filename_len] - [17h+filename_len] [14h+filename_len] - [15h+filename_len] file/component CRC-16 in little endian word value, i.e. MSB at [HeaderSize - 2h] and so forth.
      [18h+filename_len] [16h+filename_len] Operating System ID. In my BIOS it's always 20h (ASCII space character) which don't resemble any LZH OS ID known to me.
      [19h+filename_len] - [1Ah+filename_len] [17h+filename_len] - [18h+filename_len] Next header size. In my BIOS it's always 0000h which means no extension header.

      Note:

      1. The left-most offset and the addressing used in the contents column above are calculated from the 1st byte of the component. The "offset in Real Header" is used within the "scratch-pad RAM" explained below.
      2. Each component is terminated with EOF byte, i.e. 00h byte.
      3. In my BIOS, there is Read_Header procedure which contains routine to read and verify the content of this header. One of the key "procedure call" there is a call into Calc_LZH_hdr_CRC16, which reads the bios component header into a "scratch-pad" RAM area beginning at 3000:0000h (ds:0000h). This scratch-pad area is filled with the "real-LZH-header value" which doesn't include the first 2 bytes (header size and header 8-bit checksum), but includes the 3rd byte (offset 02h) until offset HeaderSize+02h.

    2. The location of various checksums which are checked prior and during the decompresion process.
      Location Calculation Method
      Right after compressed original.tmp original.tmp 8-bit checksum. This value is calculated after it's copied to RAM at segment 1000h and 2000h. The code as follows :
      Address    Assembly Code
      
      2000:E307 ;verify System_BIOS checksum 2000:E307 mov ax, 1000h
      2000:E30A mov ds, ax ; ds = E_seg_in_RAM
      2000:E30C assume ds:_1000h 2000:E30C mov bx, cmprssd_size_hi_word
      2000:E310 mov cx, cmprssd_size_lo_word
      2000:E314 add cl, hdr_len ; cl = LZH hdrlen 2000:E318 adc ch, 0 2000:E31B adc bx, 0 2000:E31E add cx, 3 2000:E321 adc bx, 0 2000:E324 jz short hi_word_zero 2000:E326 mov bx, cx 2000:E328 xor cx, cx 2000:E32A hi_word_zero: ; CODE XREF: 0002E324 2000:E32A xor si, si 2000:E32C xor ah, ah 2000:E32E _next_byte: ; CODE XREF: 0002E331 0002E343 2000:E32E lodsb 2000:E32F add ah, al 2000:E331 loop _next_byte 2000:E333 or bx, bx ; compressed BIOS bigger than 64kb ? 2000:E335 jz short cmp_chksum 2000:E337 mov cx, bx 2000:E339 mov bx, ds 2000:E33B add bx, 1000h ; proceed to F_seg in RAM
      2000:E33F mov ds, bx
      2000:E341 assume ds:_2000h 2000:E341 xor bx, bx 2000:E343 jmp short _next_byte 2000:E345 ; --------------------------------------------------------------------------- 2000:E345 cmp_chksum: ; CODE XREF: 0002E335 2000:E345 cmp ah, [si] ; cmp calc-ed original.tmp chksum with
      2000:E345 ; chksum in BIOS image
      2000:E347 jnz _sysbios_chksum_error
      Right after the decompression engine This is the 8-bit checksum of the decompression engine which starts at F000:7000h (2000:7000h after copied to RAM) in my BIOS. I guess this is the thing that some people call Decompression Block. The code as follows:
      Address    Assembly Code
      
      2000:E35E ; Verify checksum of decompress engine 2000:E35E mov ds, ax ; ds = 2700h (2000:7000h) 2000:E360 assume ds:nothing 2000:E360 xor ah, ah 2000:E362 xor si, si 2000:E364 mov cx, 0FFFh ; cx = size of BBSS_engine (4KB-1) 2000:E367 __next_byte: ; CODE XREF: 0002E36A 2000:E367 lodsb 2000:E368 add ah, al 2000:E36A loop __next_byte 2000:E36C cmp ah, [si] ; decompression engine chksum OK ? 2000:E36E jnz short _sysbios_chksum_error
      1 byte before decompression engine checksum (that's explained above) This is the 8-bit checksum of all compressed BIOS plus the 8-bit checksum of the decompression engine (not including its previously calculated checksum above). The code :
      Address    Assembly Code
      
      2000:E512 call Copy_C_seg_n_D_seg ; copy lower 128 KByte bios code from ROM (at FFFC_0000h -
      2000:E512 ; FFFD_0000h) to RAM (at 8000:0000h-9000:FFFFh)
      2000:E515 xor ah, ah 2000:E517 xor cx, cx 2000:E519 mov bx, 8000h 2000:E51C mov ds, bx ; ds = 8000h, contains compressed 2000:E51C ; lower 128KB bios components (awdext,etc.) 2000:E51E assume ds:nothing 2000:E51E xor si, si 2000:E520 next_Cseg_Dseg_byte: ; CODE XREF: Decompress_System_BIOS+11 2000:E520 ; Decompress_System_BIOS+1F 2000:E520 lodsb 2000:E521 add ah, al ; calc 8-bit chksum of C_seg n D_seg 2000:E523 loop next_Cseg_Dseg_byte 2000:E525 mov bx, ds 2000:E527 cmp bh, 90h ; 'ÁE ; are we in seg 9000h? 2000:E52A jnb short done 2000:E52C add bh, 10h ; move to next higher segment 2000:E52F mov ds, bx 2000:E531 assume ds:nothing 2000:E531 jmp short next_Cseg_Dseg_byte 2000:E533 ; --------------------------------------------------------------------------- 2000:E533 done: ; CODE XREF: Decompress_System_BIOS+18 2000:E533 mov bx, 1000h 2000:E536 mov ds, bx ; ds = start_addr_of E_seg n F_seg in RAM 2000:E536 ; (compressed original.tmp + bootblock) 2000:E538 assume ds:_1000h 2000:E538 xor si, si 2000:E53A cld 2000:E53B next_Eseg_Fseg_byte: ; CODE XREF: Decompress_System_BIOS+2C 2000:E53B ; Decompress_System_BIOS+3B 2000:E53B lodsb 2000:E53C add ah, al ; calc 8 bit chksum, contd from chksum above 2000:E53E loop next_Eseg_Fseg_byte 2000:E540 cmp bh, 20h ; ' ' ; are we in seg 2000h? 2000:E543 jnb short chksum_done 2000:E545 add bh, 10h ; move to next higher segment 2000:E548 mov ds, bx 2000:E54A assume ds:_2000h 2000:E54A mov cx, 7FFEh ; amount of byte in last seg (F_seg in RAM) to calc 2000:E54D jmp short next_Eseg_Fseg_byte 2000:E54F ; --------------------------------------------------------------------------- 2000:E54F chksum_done: ; CODE XREF: Decompress_System_BIOS+31 2000:E54F cmp ah, [si] ; cmp calc-ed chksum and chksum pointed to by [si].
      2000:E54F ; this is the chksum for the bios binary
      2000:E54F ; from 00000h to 37FFDh (C000:0h - F000:7FFDh)
      2000:E551 jnz _sysbios_chksum_error ; jmp back and continue to bootblock error handling routine

    3. The key parts of the decompression routine.
      Address    Assembly Code
      
      2000:E512 Decompress_System_BIOS proc near ; CODE XREF: 0002E3DC ......... 2000:E555 mov bx, 0 2000:E558 mov es, bx 2000:E55A assume es:nothing 2000:E55A mov word ptr es:7004h, 0FFFFh 2000:E561 xor al, al 2000:E563 2000:E563 ; System_BIOS Decompression started here 2000:E563 mov bx, 1000h 2000:E566 mov es, bx ; es = src_seg 2000:E568 assume es:_1000h 2000:E568 xor bx, bx ; bx = src_offset 2000:E56A call Decompress ; on ret, CF=1 if error occured 2000:E56D jb short sysbios_decomp_error 2000:E56F test ecx, 0FFFF0000h ; compressed component size more than 64KB? 2000:E576 jz short sysbios_decomp_error ; jmp if compressed component size <= 64KB 2000:E578 mov bx, 2000h 2000:E57B mov es, bx ; proceed2next cmprssd componnt (in next segment) 2000:E57D assume es:_2000h 2000:E57D mov bx, 1 ; bx = index to be added to previous src_offset in cx 2000:E580 jmp short repeat_decompress ; decompress remaining component (1st pass jmp taken) 2000:E582 ; --------------------------------------------------------------------------- 2000:E582 sysbios_decomp_error: ; CODE XREF: Decompress_System_BIOS+5B 2000:E582 ; Decompress_System_BIOS+64 2000:E582 rcl al, 1 2000:E584 mov bx, 2000h 2000:E587 mov es, bx 2000:E589 xor bx, bx 2000:E58B call Decompress 2000:E58E rcl al, 1 2000:E590 cmp al, 3 2000:E592 jnz short decompress_successfull 2000:E594 mov ax, 1000h 2000:E597 stc 2000:E598 retn 2000:E599 ; --------------------------------------------------------------------------- 2000:E599 decompress_successfull: ; CODE XREF: Decompress_System_BIOS+80 2000:E599 or al, al 2000:E59B jnz short sys_bios_dcomprss_done 2000:E59D repeat_decompress: ; CODE XREF: Decompress_System_BIOS+6E 2000:E59D ; Decompress_System_BIOS+99 2000:E59D add bx, cx ; bx = point to next compressed component 2000:E59F call Decompress 2000:E5A2 jb short sys_bios_dcomprss_done ; 1st pass jmp taken (original.tmp) 2000:E5A4 test ecx, 0FFFF0000h 2000:E5AB jz short repeat_decompress 2000:E5AD sys_bios_dcomprss_done: ; CODE XREF: Decompress_System_BIOS+89 2000:E5AD ; Decompress_System_BIOS+90 2000:E5AD call Decmprss_Sysbios_Extension 2000:E5B0 jz _sysbios_chksum_error 2000:E5B4 mov ax, 5000h 2000:E5B7 clc 2000:E5B8 retn 2000:E5B8 Decompress_System_BIOS endp
      2000:E5B9 --- Decompress --- 2000:E5B9 in: es = component_seg 2000:E5B9 bx = component_offset 2000:E5B9 out: ecx = overall_compressed_component_length 2000:E5B9 edx = original_component_size 2000:E5B9 CF, set if failed, cleared if success 2000:E5B9 ; --------------- S U B R O U T I N E --------------------------------------- 2000:E5B9 Decompress proc near ; CODE XREF: Decmprss_Sysbios_Extension:not_awdext 2000:E5B9 ; Decmprss_Sysbios_Extension:not_awdext2 ... 2000:E5B9 cmp dword ptr es:[bx+0Fh], 40000000h ; is extension component?
      2000:E5C2 jnz short not_xtension_component ; 1st pass jmp taken
      2000:E5C4 mov si, 0 2000:E5C7 mov ds, si 2000:E5C9 assume ds:nothing 2000:E5C9 mov ds:7000h, bx 2000:E5CD mov si, es 2000:E5CF mov ds:7002h, si 2000:E5D3 lea si, ds:7789h 2000:E5D7 mov ds:7004h, si 2000:E5DB movzx ecx, byte ptr es:[bx] 2000:E5E0 add ecx, es:[bx+7] 2000:E5E5 add ecx, 3 2000:E5E9 retn 2000:E5EA ; --------------------------------------------------------------------------- 2000:E5EA not_xtension_component: ; CODE XREF: Decompress+9 2000:E5EA mov dx, 3000h ; dx = scratchpad_seg for decompression 2000:E5ED push ax 2000:E5EE push es 2000:E5EF call Find_BBSS ; on ret, si contains offset right after BBSS sign 2000:E5F2 pop es 2000:E5F3 assume es:nothing 2000:E5F3 push es 2000:E5F4 mov ax, es 2000:E5F6 shr ax, 0Ch 2000:E5F9 mov es, ax 2000:E5FB assume es:nothing 2000:E5FB mov ax, cs:[si+0Eh] 2000:E5FF call ax ; call decompression engine at 2000:7789h 2000:E601 pop es 2000:E602 assume es:nothing 2000:E602 pop ax 2000:E603 retn 2000:E603 Decompress endp
      2000:7789 --- Decomprssion_Ngine --- 2000:7789 in: dx = scratch-pad_segment_for_decompression 2000:7789 es = (compressed_segment_addr>>0xC) [hi_word of src phy addr] 2000:7789 bx = compressed_offset_addr 2000:7789 2000:7789 out: ecx = overall_compressed_component_length 2000:7789 edx = original_file_size 2000:7789 ; --------------- S U B R O U T I N E --------------------------------------- 2000:7789 Decompression_Ngine proc near 2000:7789 push eax 2000:778B push bx 2000:778C push es 2000:778D mov ds, dx 2000:778F push ds 2000:7790 pop es 2000:7791 xor di, di 2000:7793 mov cx, 4000h 2000:7796 xor ax, ax 2000:7798 rep stosw ; zero-init 32KB starting at Scratchpad_Seg:0000h 2000:779A pop es 2000:779B push es 2000:779C 2000:779C ;Setup GDT to be used to get src bytes (Fetch_Byte) later 2000:779C mov word ptr ds:100h, es ; ds:100h = compressed_seg_addr>>0xC 2000:779C ; 1st pass ds:100h = 1 2000:77A0 mov ds:102h, bx ; ds:102h = compressed_offset_addr 2000:77A0 ; 1st pass bx = 0 2000:77A4 xor ecx, ecx 2000:77A7 mov ds:57A8h, ecx 2000:77AC mov ds:57ACh, ecx 2000:77B1 lea cx, ds:57A8h 2000:77B5 ror ecx, 4 2000:77B9 mov ax, ds 2000:77BB add cx, ax 2000:77BD rol ecx, 4 2000:77C1 mov word ptr ds:57A2h, 18h 2000:77C7 mov ds:57A4h, ecx 2000:77CC mov dword ptr ds:57B0h, 0FFFFh 2000:77D5 mov ax, es 2000:77D7 movzx ecx, ah ; es = hi_word addr of desc_base 2000:77DB ror ecx, 8 ; ecx = base_24_31 << 24 2000:77DF mov cl, al 2000:77E1 or ecx, 8F9300h 2000:77E8 mov ds:57B4h, ecx 2000:77ED mov dword ptr ds:57B8h, 0FFFFh 2000:77F6 mov dword ptr ds:57BCh, 8F9300h 2000:77FF push gs 2000:7801 mov di, 0 2000:7804 mov gs, di 2000:7806 assume gs:nothing 2000:7806 mov di, 6000h 2000:7809 mov word ptr gs:[di], 7789h 2000:780E 2000:780E ;check LZH header 2000:780E add bx, 12h ; LZH-header decomp_seg_addr_hi_byte index 2000:7811 call Fetch_Byte 2000:7814 sub bx, 12h ; restore bx to point to first byte 2000:7817 cmp al, 40h ; '@' ; is extension component?
      2000:7817 ; at 1st: al equ 50h (original.tmp)
      2000:7817 ; at 2nd: al equ 41h (awardext.rom)
      2000:7819 jnz short not_extension_component ; 1st-pass jmp taken
      2000:781B add bx, 11h 2000:781E call Fetch_Byte ; fetch "dest_seg_addr" lo_byte 2000:7821 sub bx, 11h ; restore bx to point to first byte 2000:7824 or al, al ; if extension component, jmp taken 2000:7826 jnz short extension_component 2000:7828 cmp dword ptr gs:[di+4], 0 2000:782E jnz short not_extension_component 2000:7830 extension_component: ; CODE XREF: Decompression_Ngine+9D 2000:7830 movzx dx, al ; dl = "dest_seg_addr" lo_byte 2000:7833 inc bx ; bx = LZH_hdr_chksum byte index 2000:7834 call Fetch_Byte 2000:7837 sub al, dl ; LZH_hdr_chksum = LZH_hdr_chksum - "dest_seg_addr"_lo_byte 2000:7839 call Patch_Byte ; store new checksum 2000:783C dec bx ; restore bx 2000:783D xor al, al ; al = 00h 2000:783F add bx, 11h ; bx = "dest_seg_addr"_lo_byte index 2000:7842 call Patch_Byte ; patch "dest_seg_addr"_lo_byte to 00h 2000:7845 sub bx, 11h 2000:7848 inc dx ; dx = "dest_seg_addr"_lo_byte + 1
      2000:7849 shl dx, 2 ; dx = 4*("dest_seg_addr"_lo_byte + 1)
      2000:784C add di, dx ; di = 6000h + dx -- look above!
      2000:784E mov gs:[di], bx ; 0000:[di] = compressed_offset_addr
      2000:7851 mov cx, es
      2000:7853 mov gs:[di+2], cx ; 0000:[di+2] = compressed_seg_addr>>0xC (hi_word of src phy addr)
      2000:7857 call Fetch_Byte ; al = LZH_hdr_len 2000:785A movzx ecx, al ; ecx = LZH_hdr_len 2000:785E add bx, 7 2000:7861 call Fetch_Dword ; eax = compressed_file_size 2000:7864 sub bx, 7 2000:7867 add ecx, eax ; ecx = LZH_header_len + compressed_file_size 2000:786A add ecx, 3 ; ecx = total_compressed_component_size 2000:786E pop gs 2000:7870 assume gs:nothing 2000:7870 jmp exit 2000:7873 ; --------------------------------------------------------------------------- 2000:7873 not_extension_component: ; CODE XREF: Decompression_Ngine+90 2000:7873 ; Decompression_Ngine+A5 2000:7873 pop gs 2000:7875 call Make_CRC16_Table
      2000:7878 call Read_Header ; fetch header component to scratchpad_seg, on error CF=1
      2000:787B jb exit ; ret with error code set 2000:787F mov ax, ds:108h ; mov ax, decomprss_seg_addr 2000:7882 mov ds:104h, ax ; mov nu_decomprss_seg_addr, ax 2000:7885 mov ax, ds:10Ah ; mov ax, decomprss_offst_addr 2000:7888 mov ds:106h, ax ; mov nu_decomprss_offst_addr, ax 2000:788B mov ecx, ds:310h ; ecx = compressed_component_size 2000:7890 xor eax, eax 2000:7893 mov al, ds:571Ch ; al = LZH_hdr_len 2000:7896 add ecx, eax ; ecx = compressed_cmpnnt_size + LZH_hdr_len 2000:7899 add ecx, 3 ; ecx = compressed_cmpnnt_size + LZH_hdr_len + 2000:7899 ; sizeof(EOF_byte) + sizeof(LZH_hdr_len_byte) + 2000:7899 ; sizeof(LZH_hdr_8bit_chk_sum) 2000:7899 ; i.e. ecx = overall_component_len 2000:789D mov edx, ds:314h ; mov edx, original_file_size 2000:78A2 push edx 2000:78A4 push ecx 2000:78A6 push bx 2000:78A7 add bx, 5 ; point to LZH ID byte 2000:78AA call Fetch_Byte 2000:78AD pop bx 2000:78AE cmp al, '0' ; is '-lh0-'? 2000:78B0 jnz short decompress_part 2000:78B2 push ds 2000:78B3 push si 2000:78B4 push bx 2000:78B5 mov di, ds:10Ah 2000:78B9 movzx ax, byte ptr ds:571Ch 2000:78BE add ax, 2 2000:78C1 add bx, ax 2000:78C3 mov cx, ds:310h 2000:78C7 mov ax, ds:108h 2000:78CA mov es, ax 2000:78CC add cx, 3 2000:78CF shr cx, 2 2000:78D2 next_dword: ; CODE XREF: Decompression_Ngine+151 2000:78D2 call Fetch_Dword 2000:78D5 add bx, 4 2000:78D8 stosd 2000:78DA loop next_dword 2000:78DC pop bx 2000:78DD pop si 2000:78DE pop ds 2000:78DF jmp short LZH_hdr_OK 2000:78E1 ; --------------------------------------------------------------------------- 2000:78E1 decompress_part: ; CODE XREF: Decompression_Ngine+127 2000:78E1 push word ptr ds:104h ; save destination seg addr 2000:78E5 push word ptr ds:106h ; save destination offset addr 2000:78E9 push large [dword ptr ds:314h] 2000:78EE call Lzh_Expand ; Lzh_Expand capable of handling compressed
      2000:78EE ; component bigger than 64KB (1 segment)
      2000:78F1 pop dword ptr ds:314h 2000:78F6 pop word ptr ds:106h 2000:78FA pop word ptr ds:104h 2000:78FE LZH_hdr_OK: ; CODE XREF: Decompression_Ngine+156 2000:78FE call Zero_Init ; zero init 32KB of scratchpad_seg 2000:7901 pop ecx 2000:7903 pop edx 2000:7905 clc 2000:7906 exit: ; CODE XREF: Decompression_Ngine+E7 2000:7906 ; Decompression_Ngine+F2 2000:7906 pop es 2000:7907 pop bx 2000:7908 pop eax 2000:790A retn 2000:790A Decompression_Ngine endp
      2000:790B --- Make_CRC16_Table --- 2000:790B 1st pass, the base address for DS is 3_0000h 2000:790B in: ds = scratch_pad_segment for CRC table 2000:790B out: ds:10Ch - ds:11Bh = CRC-16 table 2000:790B ; --------------- S U B R O U T I N E --------------------------------------- 2000:790B Make_CRC16_Table proc near ; CODE XREF: Decompression_Ngine+EC
      2000:790B 51 push cx
      2000:790C 53 push bx
      2000:790D 50 push ax
      2000:790E 56 push si
      2000:790F BE 0C 01 mov si, 10Ch
      2000:7912 B9 00 01 mov cx, 100h
      2000:7915 next_byte: ; CODE XREF: Make_CRC16_Table+2B
      2000:7915 B8 00 01 mov ax, 100h
      2000:7918 2B C1 sub ax, cx
      2000:791A 50 push ax
      2000:791B BB 00 00 mov bx, 0
      2000:791E is_bit: ; CODE XREF: Make_CRC16_Table+25
      2000:791E A9 01 00 test ax, 1
      2000:7921 74 07 jz short not_bit
      2000:7923 D1 E8 shr ax, 1
      2000:7925 35 01 A0 xor ax, 0A001h ; CRC poly 2000:7928 EB 02 jmp short point_to_next_byte 2000:792A ; --------------------------------------------------------------------------- 2000:792A not_bit: ; CODE XREF: Make_CRC16_Table+16 2000:792A D1 E8 shr ax, 1 2000:792C point_to_next_byte: ; CODE XREF: Make_CRC16_Table+1D 2000:792C 43 inc bx 2000:792D 83 FB 08 cmp bx, 8 2000:7930 72 EC jb short is_bit 2000:7932 5B pop bx 2000:7933 89 00 mov [bx+si], ax 2000:7935 46 inc si 2000:7936 E2 DD loop next_byte 2000:7938 5E pop si 2000:7939 58 pop ax 2000:793A 5B pop bx 2000:793B 59 pop cx 2000:793C C3 retn 2000:793C Make_CRC16_Table endp
      2000:79E8 --- Read_Header --- 2000:79E8 in: ds = scratchpad_segment 2000:79E8 ds:102h = LZH_hdr_byte_index 2000:79E8 2000:79E8 out: ds:102h = LZH_hdr_byte_index 2000:79E8 ds:108h = componnt_decomprrsion_seg_addr 2000:79E8 ds:10Ah = componnt_decomprrsion_offset_addr 2000:79E8 ds:310h = uncompressed_componnt_size 2000:79E8 ds:314h = component_seg:offset_decompression_addr 2000:79E8 ds:571Ch = LZH_hdr_len 2000:79E8 ds:571Dh = LZH_hdr_chksum 2000:79E8 ds:571Eh = LZH crc16 val 2000:79E8 ds:0 - ds:LZH_hdr_len = copy of current component LZH hdr 2000:79E8 ; --------------- S U B R O U T I N E --------------------------------------- 2000:79E8 Read_Header proc near ; CODE XREF: Decompression_Ngine+EF
      2000:79E8 60 pusha
      2000:79E9 06 push es
      2000:79EA 8B 1E 02 01 mov bx, ds:102h
      2000:79EE E8 DA 00 call Fetch_Byte
      2000:79F1 FF 06 02 01 inc word ptr ds:102h
      2000:79F5 A2 1C 57 mov ds:571Ch, al
      2000:79F8 07 pop es
      2000:79F9 80 3E 1C 57 00 cmp byte ptr ds:571Ch, 0
      2000:79FE 75 04 jnz short read_LZH_hdr_ok
      2000:7A00 error: ; CODE XREF: Read_Header+38
      2000:7A00 ; Read_Header+71 ...
      2000:7A00 F9 stc
      2000:7A01 E9 86 00 jmp exit
      2000:7A04 ; ---------------------------------------------------------------------------
      2000:7A04 read_LZH_hdr_ok: ; CODE XREF: Read_Header+16
      2000:7A04 06 push es
      2000:7A05 8B 1E 02 01 mov bx, ds:102h
      2000:7A09 E8 BF 00 call Fetch_Byte ; read LZH_hdr_chksum byte
      2000:7A0C FF 06 02 01 inc word ptr ds:102h
      2000:7A10 A2 1D 57 mov ds:571Dh, al ; 1st pass: 3000:571D = LZH_hdr_chksum
      2000:7A13 07 pop es
      2000:7A14 E8 26 FF call Calc_LZH_hdr_CRC16 ; fetch compressed component value to RAM,
      2000:7A14 ; then calc its CRC16 checksum
      2000:7A17 E8 88 FF call CalcHdrSum
      2000:7A1A 3A 06 1D 57 cmp al, ds:571Dh ; is the stored LZH_hdr 8-bit chksum match the one that read? 2000:7A1E 74 02 jz short LZH_hdr_8bit_chksum_ok 2000:7A20 EB DE jmp short error 2000:7A22 ; --------------------------------------------------------------------------- 2000:7A22 LZH_hdr_8bit_chksum_ok: ; CODE XREF: Read_Header+36 2000:7A22 BB 05 00 mov bx, 5 2000:7A25 B9 04 00 mov cx, 4 ; bx+cx = compressed_component_size_index (Dword) 2000:7A28 E8 99 FF call GetFromHeader 2000:7A2B 66 A3 10 03 mov ds:310h, eax 2000:7A2F BB 09 00 mov bx, 9 2000:7A32 B9 04 00 mov cx, 4 ; bx+cx = original file size (Dword) 2000:7A35 E8 8C FF call GetFromHeader 2000:7A38 66 A3 14 03 mov ds:314h, eax 2000:7A3C BB 0D 00 mov bx, 0Dh 2000:7A3F B9 02 00 mov cx, 2 ; bx+cx = decompression_component_offset addr (Word) 2000:7A42 E8 7F FF call GetFromHeader 2000:7A45 A3 0A 01 mov ds:10Ah, ax 2000:7A48 BB 0F 00 mov bx, 0Fh 2000:7A4B B9 02 00 mov cx, 2 ; bx+cx = decompression_component_segment addr (Word) 2000:7A4E E8 73 FF call GetFromHeader 2000:7A51 A3 08 01 mov ds:108h, ax 2000:7A54 80 3E 11 00 20 cmp byte ptr ds:11h, 20h ; ' ' ; is LZH level 1 file attribute? 2000:7A59 75 A5 jnz short error 2000:7A5B 80 3E 12 00 01 cmp byte ptr ds:12h, 1 ; is LZH level 1 ? 2000:7A60 75 9E jnz short error 2000:7A62 0F B6 1E 1C 57 movzx bx, byte ptr ds:571Ch ; bx = lzh_hdr_len 2000:7A67 83 EB 05 sub bx, 5 ; bx = CRC16_byte_index 2000:7A6A B9 02 00 mov cx, 2 2000:7A6D E8 54 FF call GetFromHeader ; read CRC16 value 2000:7A70 A3 1E 57 mov ds:571Eh, ax ; ds:571Eh = CRC16_val 2000:7A73 BB 13 00 mov bx, 13h ; bx = filename_len byte index 2000:7A76 8A 9F 00 00 mov bl, [bx+0] ; bl = filename_len 2000:7A7A B8 14 00 mov ax, 14h 2000:7A7D 03 D8 add bx, ax ; bx = CRC16_byte_index 2000:7A7F C6 87 00 00 24 mov byte ptr [bx+0], 24h ; '$' 2000:7A84 C6 87 01 00 00 mov byte ptr [bx+1], 0 2000:7A89 F8 clc 2000:7A8A exit: ; CODE XREF: Read_Header+19 2000:7A8A 61 popa 2000:7A8B C3 retn 2000:7A8B Read_Header endp
      2000:793D Calc_LZH_hdr_CRC16 proc near ; CODE XREF: Read_Header+2C
      2000:793D 50 push ax
      2000:793E 53 push bx
      2000:793F 51 push cx
      2000:7940 52 push dx
      2000:7941 0F B6 0E 1C 57 movzx cx, byte ptr ds:571Ch
      2000:7946 06 push es
      2000:7947 56 push si
      2000:7948 8B 1E 02 01 mov bx, ds:102h
      2000:794C BE 00 00 mov si, 0
      2000:794F next_byte: ; CODE XREF: Calc_LZH_hdr_CRC16+19
      2000:794F E8 79 01 call Fetch_Byte
      2000:7952 88 04 mov [si], al
      2000:7954 43 inc bx
      2000:7955 46 inc si
      2000:7956 E2 F7 loop next_byte
      2000:7958 8B C3 mov ax, bx
      2000:795A 2B 06 02 01 sub ax, ds:102h
      2000:795E 89 1E 02 01 mov ds:102h, bx
      2000:7962 5E pop si
      2000:7963 07 pop es
      2000:7964 A2 1C 57 mov ds:571Ch, al
      2000:7967 8B C8 mov cx, ax
      2000:7969 01 06 14 03 add ds:314h, ax
      2000:796D 41 inc cx
      2000:796E BB 00 00 mov bx, 0
      2000:7971 next_CRC_byte: ; CODE XREF: Calc_LZH_hdr_CRC16+5E
      2000:7971 0F B6 07 movzx ax, byte ptr [bx]
      2000:7974 49 dec cx
      2000:7975 E3 26 jcxz short exit
      2000:7977 50 push ax
      2000:7978 53 push bx
      2000:7979 56 push si
      2000:797A 8B F0 mov si, ax
      2000:797C A1 0C 03 mov ax, ds:30Ch
      2000:797F 33 C6 xor ax, si
      2000:7981 25 FF 00 and ax, 0FFh
      2000:7984 8B F0 mov si, ax
      2000:7986 D1 E6 shl si, 1
      2000:7988 8B 9C 0C 01 mov bx, [si+10Ch]
      2000:798C A1 0C 03 mov ax, ds:30Ch
      2000:798F C1 E8 08 shr ax, 8
      2000:7992 33 C3 xor ax, bx
      2000:7994 A3 0C 03 mov ds:30Ch, ax
      2000:7997 5E pop si
      2000:7998 5B pop bx
      2000:7999 58 pop ax
      2000:799A 43 inc bx
      2000:799B EB D4 jmp short next_CRC_byte
      2000:799D ; ---------------------------------------------------------------------------
      2000:799D exit: ; CODE XREF: Calc_LZH_hdr_CRC16+38
      2000:799D 5A pop dx
      2000:799E 59 pop cx
      2000:799F 5B pop bx
      2000:79A0 58 pop ax
      2000:79A1 C3 retn
      2000:79A1 Calc_LZH_hdr_CRC16 endp
      2000:79A2 CalcHdrSum proc near ; CODE XREF: Read_Header+2F
      2000:79A2 53 push bx
      2000:79A3 51 push cx
      2000:79A4 52 push dx
      2000:79A5 B8 00 00 mov ax, 0
      2000:79A8 0F B6 0E 1C 57 movzx cx, byte ptr ds:571Ch
      2000:79AD loc_2000_79AD: ; CODE XREF: CalcHdrSum+19.j
      2000:79AD 0F B6 1E 1C 57 movzx bx, byte ptr ds:571Ch
      2000:79B2 2B D9 sub bx, cx
      2000:79B4 0F B6 97 00 00 movzx dx, byte ptr [bx+0]
      2000:79B9 03 C2 add ax, dx
      2000:79BB E2 F0 loop loc_2000_79AD
      2000:79BD 5A pop dx
      2000:79BE 59 pop cx
      2000:79BF 5B pop bx
      2000:79C0 25 FF 00 and ax, 0FFh
      2000:79C3 C3 retn
      2000:79C3 CalcHdrSum endp
      2000:79C4 --- GetFromHeader --- 2000:79C4 in: bx = byte_index of the "component" to read 2000:79C4 cx = length of "component" to read 2000:79C4 2000:79C4 out: eax = dword_read 2000:79C4 ; --------------- S U B R O U T I N E --------------------------------------- 2000:79C4 GetFromHeader proc near ; XREF: Read_Header+40
      2000:79C4 ; Read_Header+4D ...
      2000:79C4 53 push bx
      2000:79C5 66 52 push edx
      2000:79C7 56 push si
      2000:79C8 66 33 C0 xor eax, eax
      2000:79CB 4B dec bx
      2000:79CC 41 inc cx
      2000:79CD next_byte: ; CODE XREF: GetFromHeader+1D
      2000:79CD 49 dec cx
      2000:79CE E3 13 jcxz short exit
      2000:79D0 66 C1 E0 08 shl eax, 8
      2000:79D4 8B F3 mov si, bx
      2000:79D6 03 F1 add si, cx
      2000:79D8 66 0F B6 94 00 00 movzx edx, byte ptr [si+0]
      2000:79DE 66 03 C2 add eax, edx
      2000:79E1 EB EA jmp short next_byte
      2000:79E3 ; ---------------------------------------------------------------------------
      2000:79E3 exit: ; CODE XREF: GetFromHeader+A
      2000:79E3 5E pop si
      2000:79E4 66 5A pop edx
      2000:79E6 5B pop bx
      2000:79E7 C3 retn
      2000:79E7 GetFromHeader endp
    After looking at these exhaustive list of hints, we managed to construct the mapping of the decompressed BIOS components as described below :
    Starting address of decompressed BIOS component in RAM Compressed Size Decompressed Size Decompression State (by Bootblock code) Component description
    4100:0000h 3A85h 57C0h Decompressed to RAM beginning at address in column one. awardext.rom, this is a "helper module" for original.tmp
    4001:0000h 5CDCh A000h Not decompressed yet cpucode.bin, this is the CPU microcode
    4003:0000h DFAh 21A6h Not decompressed yet acpitbl.bin, this is the ACPI table
    4002:0000h 35Ah 2D3Ch Not decompressed yet iwillbmp.bmp, this is the EPA logo
    4027:0000h A38h FECh Not decompressed yet nnoprom.bin, explanation N/A
    4007:0000h 1493h 2280h Not decompressed yet antivir.bin, this is BIOS antivirus code
    4028:0000h F63Ah 14380h Not decompressed yet ROSUPD.bin, seems to be custom Logo display procedure
    5000:0000h 15509h 20000h Decompressed to RAM beginning at address in column one. original.tmp, the system BIOS

    Note: The decompression addresses marked with green background are treated in different fashion as follows :

    1. It's not the real decompression area of the corresponding component as you can see from the explanation above. It's only some sort of "place holder" for the real decompression area that's later handled by original.tmp. The conclusion is: only original.tmp and awardext.rom get decompressed by Decompress_System_Bios routine in Bootblock. If you want to verify this, try summing up the decompressed code size, it won't fit !
    2. All of these component's decompressed segment address are changed to 4000h by Decompression_Ngine procedure as you can see in the routine at 2000:7842h above.
    3. The 40xxh shown in their "Starting Address ... (for decompression)" actually an ID that works as follows: 40 (hi-byte) is an ID that mark it as an "Extension BIOS" to be decompressed later during original.tmp execution. xx is an ID that will be used in original.tmp execution to refer to the component to be decompressed. This will be explained more thoroughly in original.tmp explanation later.
    4. All of these components are decompressed during original.tmp execution. The decompression result is placed starting at address 4000:0000h, but not at the same time. Some of it (maybe all, I'm not sure yet) also relocated from that address to retain their contents after another component also decompressed in there. More explanation on this available at original.tmp section below.

  7. Shadow the BIOS code. Assuming that the decompression routine successfully completed, the routine above then copy the decompressed system BIOS (original.tmp) from 5000:0000h - 6000:FFFFh in RAM to E_0000h - F_FFFFh also in RAM. This is accomplished as follows:
    1. Reprogram the northbridge shadow RAM control register to enable write only into E_0000h - F_FFFFh, i.e. forward write operation into this address range to DRAM (not to the BIOS ROM chip anymore).
    2. Perform a string copy operation to copy the decompressed system BIOS (original.tmp) from 5000:0000h - 6000:FFFFh to E_0000h - F_FFFFh.
    3. Reprogram the northbridge shadow RAM control register to enable read only into E_0000h - F_FFFFh, i.e. forward read operation into this address range to DRAM (not to the BIOS ROM chip anymore). This is also to write-protect the system BIOS code.
  8. Enable the microprocessor cache then jump into the decompressed system BIOS. This step is the last step in the normal Bootblock code execution path. After enabling the processor cache, the code then jump into the write-protected system BIOS (original.tmp) at F000:F80Dh in RAM as seen in the code above. This jump destination address seems to be the same across different award bioses.
Now, I'll present the "memory map" of the compressed and decompressed BIOS components just before jump into decompressed original.tmp is made. This is important since it will ease us in dissecting the decompressed original.tmp later. We have to note that by now, all code execution happens in RAM, no more code execution from within BIOS ROM chip.
Address Range in RAM Decompression State (by Bootblock code) Description
0000:6000h - 0000:6xxxh N/A This area contains the header of the extension component (component other than original.tmp and awardext.rom) fetched from the compressed BIOS at 8000:0000h - 9000:FFFFh (previously BIOS component at FFFC_0000h - FFFD_FFFFh in the BIOS chip). Note that this is fetched here by part of the bootblock in segment 2000h.
1000:0000h - 2000:5531h Compressed This area contains the compressed original.tmp. It's part of the copy of the last 128KB of the BIOS (previously BIOS component at E000:0000h - F000:FFFFh in the BIOS chip). This code is shadowed here by the bootblock in BIOS ROM chip.
2000:5532h - 2000:5FFFh Pure Binary (non-executable) This area contains only padding bytes.
2000:6000h - 2000:FFFFh Pure binary (executable) This area contains the bootblock code. It's part of the copy of the last 128KB of the BIOS (previously BIOS component at E000:0000h - F000:FFFFh in the BIOS ROM chip). This code is shadowed here by the bootblock in BIOS ROM chip. This is where our code currently executing (the "copy" of bootblock in segment 2000h).
4100:0000h - 4100:57C0h Decompressed This area contains the decompressed awardext.rom. Note that the decompression process is accomplished by part of the bootblock code in segment 2000h.
5000:0000h - 6000:FFFFh Decompressed This area contains the decompressed original.tmp. Note that the decompression process is accomplished by part of the bootblock code in segment 2000h.
8000:0000h - 9000:FFFFh Compressed This area contains the copy of the first/lower 128KB of the BIOS (previously BIOS component at FFFC_0000h - FFFD_0000h in the BIOS chip). This code is copied here by the bootblock code in segment 2000h.
E000:0000h - F000:FFFFh Decompressed This area contains copy of the decompressed original.tmp, which is copied here by the bootblock code in segment 2000h.

The last thing to note is: what explained about bootblock here only covers the normal Bootblock code execution path, which means it didn't explain about the Bootblock POST that takes place in case original.tmp corrupted. I'll try to cover it later when I have time to dissect it. This is all about the bootblock right now, from this point on we'll dissect the original.tmp.

7.2. System BIOS a.k.a Original.tmp

We'll just proceed as in bootblock above, I'll just highlight the places where the "code execution path" are obscure. So, by now, you're looking at the disassembly of the decompressed original.tmp of my bios.

7.2.1. Entry point from "Bootblock in RAM"

Address    Hex                       Mnemonic
F000:F80D                            This code is jumped into by the bootblock code 
F000:F80D                            if everything went OK
F000:F80D E9 02 F6                        jmp sysbios_entry_point ; 
This is where the bootblock jumps after relocating and write-protecting the system BIOS.

7.2.2. The awardext.rom and Extension BIOS components (lower 128KB bios-code) Relocation Routine

Address    Assembly Code

F000:EE12 sysbios_entry_point: ; CODE XREF: F000:F80D
F000:EE12 mov ax, 0
F000:EE15 mov ss, ax ; ss = 0000h
F000:EE17 mov sp, 1000h ; setup stack at 0:1000h
F000:EE1A call setup_stack ; Call Procedure
F000:EE1D call init_DRAM_shadowRW ; Call Procedure
F000:EE20 mov si, 5000h ; ds=5000h (look at copy_mem_word)
F000:EE23 mov di, 0E000h ; es=E000h (look at copy_mem_word)
F000:EE26 mov cx, 8000h ; copy 64KByte
F000:EE29 call copy_mem_word ; copy E000h segment routine, i.e.
F000:EE29 ; copy 64Kbyte from 5000:0h to E000:0h
F000:EE2C call j_init_DRAM_shadowR ; Call Procedure
F000:EE2F mov si, 4100h ; ds = XGroup segment decompressed, i.e.
F000:EE2F ; at this point 4100h
F000:EE32 mov di, 6000h ; es = new XGroup segment
F000:EE35 mov cx, 8000h ; copy 64KByte
F000:EE38 call copy_mem_word ; copy XGroup segment , i.e.
F000:EE38 ; 64Kbyte from 4100:0h to 6000:0h
F000:EE3B call Enter_UnrealMode ; jump below in UnrealMode F000:EE3E Begin_in_UnrealMode F000:EE3E mov ax, ds F000:EE40 mov es, ax ; es = ds (3rd entry in GDT) F000:EE40 ; base_addr=0000 0000h;limit 4GB F000:EE42 assume es:nothing F000:EE42 mov esi, 80000h ; mov esi,(POST_Cmprssed_Temp_Seg shl 4)
F000:EE42 ; relocate lower 128KB bios code
F000:EE48 mov edi, 160000h
F000:EE4E mov ecx, 8000h
F000:EE54 cld ; Clear Direction Flag
F000:EE55 rep movs dword ptr es:[edi], dword ptr [esi] ; move
F000:EE55 ; 128k data to 160000h (phy addr)
F000:EE59 call Leave_UnrealMode ; Call Procedure F000:EE59 End_in_UnrealMode F000:EE5C mov byte ptr [bp+214h], 0 ; mov byte ptr F000:EE5C ; POST_SPEED[bp],Normal_Boot F000:EE61 mov si, 626Bh ; offset 626Bh (E000h POST tests) F000:EE64 push 0E000h ; segment E000h F000:EE67 push si ; next instruction offset (626Bh) F000:EE68 retf ; jmp to E000:626Bh
F000:7440 Enter_UnrealMode proc near ; CODE XREF: F000:EE3B
F000:7440 mov ax, cs
F000:7442 mov ds, ax ; ds = cs
F000:7444 assume ds:F000
F000:7444 lgdt qword ptr GDTR_F000_5504 ; Load Global Descriptor Table Register F000:7449 mov eax, cr0 F000:744C or al, 1 ; Logical Inclusive OR F000:744E mov cr0, eax F000:7451 mov ax, 10h F000:7454 mov ds, ax ; ds = 10h (3rd entry in GDT) F000:7456 assume ds:nothing F000:7456 mov ss, ax ; ss = 10h (3rd entry in GDT) F000:7458 assume ss:nothing F000:7458 retn ; Return Near from Procedure F000:7458 Enter_UnrealMode endp
F000:5504 GDTR_F000_5504 dw 30h ; DATA XREF: Enter_PMode+4
F000:5504 ; GDT limit (6 valid desc)
F000:5506 dd 0F550Ah ; GDT phy addr (below)
F000:550A dq 0 ; null desc
F000:5512 dq 9F0F0000FFFFh ; code desc (08h)
F000:5512 ; base_addr=F0000h;seg_limit=64KB;code,execute/ReadOnly
F000:5512 ; conforming,accessed;granularity=1Byte;16-bit segment;
F000:5512 ; segment present,code,DPL=0
F000:551A dq 8F93000000FFFFh ; data desc (10h)
F000:551A ; base_addr=0000 0000h;seg_limit=4GB;data,R/W,accessed;
F000:551A ; granularity=4KB;16-bit segment; segment present,
F000:551A ; data,DPL=0
F000:5522 dq 0FF0093FF0000FFFFh ; data desc 18h
F000:5522 ; base_addr=FFFF0000h;seg_limit=64KB;data,R/W,accessed;
F000:5522 ; 16-bit segment,granularity = 1 byte;
F000:5522 ; segment present, data, DPL=0.
F000:552A dq 0FF0093FF8000FFFFh ; data desc 20h
F000:552A ; base_addr=FFFF8000h;seg_limit=64KB;data,R/W,accessed;
F000:552A ; 16-bit segment,granularity = 1 byte;
F000:552A ; segment present, data, DPL=0.
F000:5532 dq 930F0000FFFFh ; data desc 28h
F000:5532 ; base_addr=F0000h;seg_limit=64KB;data,R/W,accessed;
F000:5532 ; 16-bit segment,granularity = 1 byte;
F000:5532 ; segment present, data, DPL=0.
Note: after the execution of code above, the "memory map" is changed once again. But this time only for the compressed "BIOS extension" i.e. the lower 128KB of BIOS code and the decompressed awardext.rom, the "memory map" mentioned in the Bootblock explanation above partially overwritten.
New Address Range in RAM Decompression State Description
6000:0000h - 6000:57C0h Decompressed This is the relocated awardext.rom
160000h - 17FFFFh Compressed This is the relocated compressed "BIOS extension", including the compressed awardext.rom. (i.e. this is the copy of FFFC0000h - FFFDFFFF in the BIOS rom chip.

7.2.3. call to the POST routine a.k.a "POST jump table execution"

Address    Assembly Code

E000:626B The last of the these POST routines starts the EISA/ISA E000:626B section of POST and thus this call should never return. E000:626B If it does, we issue a POST code and halt. E000:626B E000:626B This routine called from F000:EE68h E000:626B E000:626B sysbios_entry_point_contd a.k.a NORMAL_POST_TESTS E000:626B mov cx, 3 ; mov cx,STD_POST_CODE E000:626E mov di, 61C2h ; mov di,offset STD_POST_TESTS
E000:6271 call RAM_POST_tests ; this won't return in normal condition
E000:6274 jmp short Halt_System ; Jump
E000:6276 ; --------------- S U B R O U T I N E --------------------------------------- E000:6276 E000:6276 RAM_POST_tests proc near ; CODE XREF: last_E000_POST+D
E000:6276 ; last_E000_POST+18 ...
E000:6276 mov al, cl ; cl = 3
E000:6278 out 80h, al ; manufacture's diagnostic checkpoint
E000:627A push 0F000h
E000:627D pop fs ; fs = F000h
E000:627F
E000:627F ;This is the beginning of the call into E000 segment
E000:627F ;POST function table
E000:627F assume fs:F000
E000:627F mov ax, cs:[di] ; in the beginning :
E000:627F ; di = 61C2h ; ax = cs:[di] = 154Eh
E000:627F ; called from E000:2489 w/ di=61FCh (dummy)
E000:6282 inc di ; Increment by 1 E000:6283 inc di ; di = di + 2 E000:6284 or ax, ax ; Logical Inclusive OR E000:6286 jz RAM_post_return ; RAM Post Error E000:6288 push di ; save di E000:6289 push cx ; save cx E000:628A call ax ; call 154Eh (relative call addr)
E000:628A ; ,one of this call
E000:628A ; won't return in normal condition
E000:628C pop cx ; restore all E000:628D pop di E000:628E jb RAM_post_return ; Jump if Below (CF=1) E000:6290 inc cx ; Increment by 1 E000:6291 jmp short RAM_POST_tests ; Jump E000:6293 ; --------------------------------------------------------------------------- E000:6293 E000:6293 RAM_post_return: ; CODE XREF: RAM_POST_tests+10 E000:6293 ; RAM_POST_tests+18 E000:6293 retn ; Return Near from Procedure E000:6293 RAM_POST_tests endp
E000:61C2 E0_POST_TESTS_TABLE: E000:61C2 dw 154Eh ; Restore boot flag
E000:61C4 dw 156Fh ; Chk_Mem_Refrsh_Toggle
E000:61C6 dw 1571h ; keyboard (and its controller) POST
E000:61C8 dw 16D2h ; chksum ROM, check EEPROM
E000:61C8 ; on error generate spkr tone
E000:61CA dw 1745h ; Check CMOS circuitry
E000:61CC dw 178Ah ; "chipset defaults" initialization
E000:61CE dw 1798h ; init CPU cache (both Cyrix and Intel)
E000:61D0 dw 17B8h ; init interrupt vector, also initialize
E000:61D0 ; "signatures" used for Ext_BIOS components
E000:61D0 ; decompression
E000:61D2 dw 194Bh ; Init_mainboard_equipment & CPU microcode
E000:61D2 ; chk ISA CMOS chksum ?
E000:61D4 dw 1ABCh ; Check checksum. Initialize keyboard controller
E000:61D4 ; and set up all of the 40: area data.
E000:61D6 dw 1B08h ; Relocate extended BIOS code
E000:61D6 ; init CPU MTRR, PCI REGs(Video BIOS ?)
E000:61D8 dw 1DC8h ; Video_Init (including EPA proc)
E000:61DA dw 2342h
E000:61DC dw 234Eh
E000:61DE dw 2353h ; dummy
E000:61E0 dw 2355h ; dummy
E000:61E2 dw 2357h ; dummy
E000:61E4 dw 2359h ; init Programmable Timer (PIT)
E000:61E6 dw 23A5h ; init PIC_1 (programmable Interrupt Ctlr)
E000:61E8 dw 23B6h ; same as above ?
E000:61EA dw 23F9h ; dummy
E000:61EC dw 23FBh ; init PIC_2
E000:61EE dw 2478h ; dummy
E000:61F0 dw 247Ah ; dummy
E000:61F2 dw 247Ah
E000:61F4 dw 247Ah
E000:61F6 dw 247Ah
E000:61F8 dw 247Ch ; this will call RAM_POST_tests again
E000:61F8 ; for values below(a.k.a ISA POST)
E000:61FA dw 0 E000:61FA END_E0_POST_TESTS_TABLE
E000:247C last_E000_POST proc near E000:247C cli ; Clear Interrupt Flag
E000:247D mov word ptr [bp+156h], 0
E000:2483 mov cx, 30h ; '0'
E000:2486 mov di, 61FCh ; this addr contains 0000h E000:2489 E000:2489 repeat_RAM_POST_tests: ; CODE XREF: last_E000_POST+10 E000:2489 call RAM_POST_tests ; this call immediately return
E000:2489 ; since cs:[di]=0000h
E000:248C jb repeat_RAM_POST_tests ; jmp if CF=1; not taken
E000:248E mov cx, 30h ; '0' E000:2491 mov di, 61FEh ; cs:[di] contains 249Ch E000:2494 E000:2494 repeat_RAM_POST_tests_2: ; CODE XREF: last_E000_POST+1B E000:2494 call RAM_POST_tests ; this call should nvr return if
E000:2494 ; everything is ok
E000:2497 jb repeat_RAM_POST_tests_2 ; Jump if Below (CF=1) E000:2499 jmp Halt_System ; E000:2499 last_E000_POST endp
E000:61FC ISA_POST_TESTS E000:61FC dw 0
E000:61FE dw 249Ch
E000:6200 dw 26AFh
E000:6202 dw 29DAh
E000:6204 dw 2A54h ; dummy
E000:6206 dw 2A54h
E000:6208 dw 2A54h
E000:620A dw 2A54h
E000:620C dw 2A54h
E000:620E dw 2A54h
E000:6210 dw 2A56h ; dummy
E000:6212 dw 2A56h
E000:6214 dw 2A56h
E000:6216 dw 2A58h
E000:6218 dw 2A64h
E000:621A dw 2B38h
E000:621C dw 2B5Eh ; dummy
E000:621E dw 2B60h ; dummy
E000:6220 dw 2B62h
E000:6222 dw 2BC8h ; HD init ?
E000:6224 dw 2BF0h ; game io port init ?
E000:6226 dw 2BF5h ; dummy
E000:6228 dw 2BF7h ; FPU error interrupt related
E000:622A dw 2C53h ; dummy
E000:622C dw 2C55h
E000:622E dw 2C61h ; dummy
E000:6230 dw 2C61h
E000:6232 dw 2C61h
E000:6234 dw 2C61h
E000:6236 dw 2C61h
E000:6238 dw 2C61h
E000:623A dw 2CA6h
E000:623C dw 6294h ; set cursor charcteristic
E000:623E dw 62EAh
E000:6240 dw 6329h
E000:6242 dw 6384h
E000:6244 dw 64D6h ; dummy
E000:6246 dw 64D6h
E000:6248 dw 64D6h
E000:624A dw 64D6h
E000:624C dw 64D6h
E000:624E dw 64D6h
E000:6250 dw 64D6h
E000:6252 dw 64D6h
E000:6254 dw 64D6h
E000:6256 dw 64D6h
E000:6258 dw 64D6h
E000:625A dw 64D6h
E000:625C dw 64D6h
E000:625E dw 64D8h ; bootstrap
E000:6260 dw 66A1h
E000:6262 dw 673Ch
E000:6264 dw 6841h ; issues int 19h (bootstrap) E000:6266 dw 0 E000:6266 END_ISA_POST_TESTS
Note:
The "POST jump table" procedures will set the Carry Flag (CF=1) if they encounter something wrong during their execution. Upon returning of the POST procedure, the Carry Flag will be tested, if it's set, then the "RAM_POST_TESTS" will immediately returns which will Halt the machine and output sound from system speaker.

7.2.4. The "segment vector" Routines

Below is only an example of its usage. There are lot of places where it's implemented. There are couple of variation of this "segment vector". Some will jump from segment E000h to F000h, some will jump from segment F000h to E000h, some jump from E000h to 6000h(relocated decompressed awardext.rom) and some jump from F000h to 6000h(relocated decompressed awardext.rom).
  1. First variant: jump from segment E000h to F000h
Address    Assembly Code

E000:1553 Restore_Warm_Boot_Flag proc near ; CODE XREF: POST_1S
.........
E000:155A call F_Vect_Read_CMOS_Byte ......... E000:156E Restore_Warm_Boot_Flag endp
Address Machine Code Assembly Code E000:6CA2 F_Vect_Read_CMOS_Byte proc near ; CODE XREF: Restore_Warm_Boot_Flag+7p
E000:6CA2 ; 000E1747p ...
E000:6CA2 push 0E000h
E000:6CA5 push 6CB3h
E000:6CA8 push 0EC31h E000:6CAB push 0E4FDh ; Read_CMOS_Byte
E000:6CAE jmp far ptr F000_Vector E000:6CB3 ; --------------------------------------------------------------------------- E000:6CB3 retn E000:6CB3 F_Vect_Read_CMOS_Byte endp
F000:EC30 F000_Vector: ; CODE XREF: 000E1781J 000E17AAJ ...
F000:EC30 retn
F000:EC31 ; ---------------------------------------------------------------------------
F000:EC31 retf
F000:E4FD Read_CMOS_Byte proc near F000:E4FD xchg bx, bx F000:E4FF nop F000:E500 out 70h, al ; CMOS Memory: F000:E500 ; used by real-time clock F000:E502 jcxz short $+2 F000:E504 jcxz short $+2 F000:E506 xchg bx, bx F000:E508 in al, 71h ; CMOS Memory F000:E50A jcxz short $+2 F000:E50C jcxz short $+2 F000:E50E retn F000:E50E Read_CMOS_Byte endp
  • Second variant: jump from segment E000h to 6000h
    Address    Machine Code          Assembly Code
    
    ......... E000:1737 push cs E000:1738 push 1743h ; ret addr below
    E000:173B push 1829h ; func addr in XGROUP ROM
    E000:173E jmp far ptr loc_6000_2 E000:1743 ; --------------------------------------------------------------------------- E000:1743 clc
    E000:1744 retn
    .........

    6000:0000 locret_6000_0: ; CODE XREF: 00060017j 6000:0000 retn ; jump to target procedure 6000:0001 ; --------------------------------------------------------------------------- 6000:0001 retf ; back to caller
    6000:0002 loc_6000_2: ; push return addr for retn
    6000:0002 push 1 ; (addr_of retf above)
    6000:0005 push ax 6000:0006 pushf 6000:0007 cli 6000:0008 xchg bp, sp 6000:000A mov ax, [bp+4] ; mov ax,1 ; look at 1st inst above 6000:000D xchg ax, [bp+6] ; xchg ax,word_pushed_by_org_tmp 6000:0010 mov [bp+4], ax ; [sp+4] = word_pushed_by_org_tmp 6000:0013 xchg bp, sp ; modify sp 6000:0015 popf 6000:0016 pop ax 6000:0017 jmp short locret_6000_0 ; jump into word_pushed_by_original.tmp
    6000:1829 cli ; Clear Interrupt Flag
    .........
    6000:18B3 retn ; Return Near from Procedure
  • Third variant: jump from segment 6000h to F000h
    Address    Assembly Code
    
    6000:4F60 reinit_chipset proc far 6000:4F60 push ds 6000:4F61 mov ax, 0F000h 6000:4F64 mov ds, ax ; ds = F000h 6000:4F66 assume ds:nothing 6000:4F66 mov bx, 0E38h ; ptr to PCI reg vals (ds:bx = F000:E38h) 6000:4F69 6000:4F69 next_PCI_reg: ; CODE XREF: reinit_chipset+3D 6000:4F69 cmp bx, 0EF5h ; are we finished ? 6000:4F6D jz exit_PCI_init ; if yes, then exit 6000:4F6F mov cx, [bx+1] ; cx = PCI addr to read 6000:4F72 call setup_read_write_PCI ; on ret, ax = F70Bh, di = F725h
    6000:4F75 push cs
    6000:4F76 push 4F7Fh
    6000:4F79 push ax ; goto F000:F70B (Read_PCI_Byte) 6000:4F7A jmp far ptr 0E000h:6188h ; goto_seg_F000 6000:4F7F ; --------------------------------------------------------------------------- 6000:4F7F mov dx, [bx+3] ; reverse-and mask
    .........

    E000:6188 goto_F000_seg: ; CODE XREF: HD_init_?+3BD
    E000:6188 ; HD_init_?+578 ...
    E000:6188 68 31 EC push 0EC31h
    E000:618B 50 push ax
    E000:618C 9C pushf ; Push Flags Register onto the Stack
    E000:618D FA cli ; Clear Interrupt Flag
    E000:618E 87 EC xchg bp, sp ; Exchange Register/Memory with Register
    E000:6190 8B 46 04 mov ax, [bp+4] ; mov ax, EC31h
    E000:6193 87 46 06 xchg ax, [bp+6] ; xchg ret addr and EC31h
    E000:6196 89 46 04 mov [bp+4], ax ; mov [sp+4],[sp+6]
    E000:6199 87 EC xchg bp, sp ; Exchange Register/Memory with Register
    E000:619B 9D popf ; Pop Stack into Flags Register
    E000:619C 58 pop ax
    E000:619D EA 30 EC 00 F0 jmp far ptr F000_func_vector ; Jump

    F000:EC30 F000_func_vector: ; CODE XREF: chk_cmos_circuit+3C
    F000:EC30 C3 retn ; jump to target function
    F000:EC31 ; -------------------------------------------------------------------
    F000:EC31 CB retf ; return to calling segment:offset (6000:4F7F)
    F000:F70B read_PCI_byte proc near ; CODE XREF: enable_ROM_write?+4
    .........
    F000:F724 retn ; Return Near to F000:EC31h F000:F724 read_PCI_byte endp
  • 7.2.5. "chksum_ROM" Procedure

    This procedure is part of the "E0_POST_TESTS", which is the POST routine invoked using the "POST jump table". There's no immediate return from within this procedure. But, a call into "Check_F_Next" will accomplish the "near return" needed to proceed into the next "POST procedure" execution.
    E000:16D2                   chksum_ROM proc near
    .........
    E000:16FF 74 1E                  jz Check_F_Next    ; yes. This jump will return this routine 
    E000:16FF                                           ; to where it's called
    .........
    E000:171D EB E6                  jmp short spkr_endless_loop ; Jump
    E000:171D                   chksum_ROM endp
    
    E000:171F Check_F_Next proc near ; CODE XREF: chksum_ROM+2D
    .........
    E000:1743 F8 clc ; signal successful execution
    E000:1744 C3 retn ; retn to RAM_POST_TESTS, proceed to next POST proc E000:1744 Check_F_Next endp ; sp = -6

    7.2.6. Original.tmp Decompression routine for The "Extension_BIOS Components"

    This is one of the most confusing thing to comprehend at first. But, by understanding it, we "virtually" have no more thing to worry about the "BIOS code execution path". I suspect that the same technique as what I'm going to explain here is used accross the majority of award bios. The basic run-down of this routine is explained below.
    1. Decompress_System_BIOS procedure called from the "main bootblock execution path" (during execution of bootblock code in RAM at segment 2000h) saved the needed signatures to the predefined area in RAM as shown below:
      2000:E512 Decompress_System_BIOS proc near ; CODE XREF: 0002E3DC
      2000:E512 E8 EB DA                  call  Copy_C_seg_n_D_seg  ; copy lower 128 KByte bios code from ROM (at FFFC_0000h -
      2000:E512                                                     ; FFFD_0000h) to RAM (at 8000:0000h-9000:FFFFh)
      2000:E515 32 E4                     xor   ah, ah
      2000:E517 33 C9                     xor   cx, cx
      2000:E519 BB 00 80                  mov   bx, 8000h
      2000:E51C 8E DB                     mov   ds, bx              ; ds = 8000h, contains compressed
      2000:E51C                                                     ; lower 128KB bios components (awdext,etc.)
      2000:E51E                           assume es:nothing, ds:nothing
      2000:E51E 33 F6                     xor   si, si
      2000:E520                         next_Cseg_Dseg_byte:        ; CODE XREF: Decompress_System_BIOS+11
      2000:E520                                                     ; Decompress_System_BIOS+1F
      2000:E520 AC                        lodsb
      2000:E521 02 E0                     add   ah, al              ; calc 8-bit chksum of C_seg n D_seg
      2000:E523 E2 FB                     loop  next_Cseg_Dseg_byte
      2000:E525 8C DB                     mov   bx, ds
      2000:E527 80 FF 90                  cmp   bh, 90h ; '?'       ; are we in seg 9000h?
      2000:E52A 73 07                     jnb   short done
      2000:E52C 80 C7 10                  add   bh, 10h             ; move to next higher segment
      2000:E52F 8E DB                     mov   ds, bx
      2000:E531                           assume ds:nothing
      2000:E531 EB ED                     jmp   short next_Cseg_Dseg_byte
      2000:E533                         ; ---------------------------------------------------------------------------
      2000:E533                         done:                       ; CODE XREF: Decompress_System_BIOS+18
      2000:E533 BB 00 10                  mov   bx, 1000h
      2000:E536 8E DB                     mov   ds, bx              ; ds = start_addr_of E_seg n F_seg in RAM
      2000:E536                                                     ; (compressed original.tmp + bootblock)
      2000:E538                           assume ds:_1000h
      2000:E538 33 F6                     xor   si, si
      2000:E53A FC                        cld
      2000:E53B                         next_Eseg_Fseg_byte:        ; CODE XREF: Decompress_System_BIOS+2C
      2000:E53B                                                     ; Decompress_System_BIOS+3B
      2000:E53B AC                        lodsb
      2000:E53C 02 E0                     add   ah, al              ; calc 8 bit chksum, cont'd from chksum above
      2000:E53E E2 FB                     loop  next_Eseg_Fseg_byte
      2000:E540 80 FF 20                  cmp   bh, 20h ; ' '       ; are we in seg 2000h?
      2000:E543 73 0A                     jnb   short chksum_done
      2000:E545 80 C7 10                  add   bh, 10h             ; move to next higher segment
      2000:E548 8E DB                     mov   ds, bx
      2000:E54A                           assume ds:_2000h
      2000:E54A B9 FE 7F                  mov   cx, 7FFEh           ; amount of byte in last seg (F_seg in RAM) to calc
      2000:E54D EB EC                     jmp   short next_Eseg_Fseg_byte
      2000:E54F                         ; ---------------------------------------------------------------------------
      2000:E54F                         chksum_done:                ; CODE XREF: Decompress_System_BIOS+31j
      2000:E54F 3A 24                     cmp   ah, [si]            ; cmp calc-ed chksum and chksum pointed to by [si].
      2000:E54F                                                     ; this is the chksum for the bios binary
      2000:E54F                                                     ; from 00000h to 37FFDh (C000:0h - F000:7FFDh)
      2000:E551 0F 85 8C FE               jnz   _sysbios_chksum_error ; jmp back and continue to bootblock error handling routine
      2000:E555   mov   bx, 0
      2000:E558   mov   es, bx
      2000:E55A   assume es:nothing
      2000:E55A   mov   es:word_0_7004, 0FFFFh ; save signature to be used by original.tmp
      2000:E55A ; (POST_8S) for decompression
      2000:E561 xor al, al 2000:E563 2000:E563 ; System_BIOS Decompression started here 2000:E563 mov bx, 1000h 2000:E566 mov es, bx ; es = src_seg 2000:E568 assume es:_1000h 2000:E568 xor bx, bx ; bx = src_offset 2000:E56A call Decompress ; on ret, CF=1 if error occured 2000:E56D 72 13 jb short sysbios_decomp_error 2000:E56F 66 F7 C1 00 00 FF FF test ecx, 0FFFF0000h ; compressed component size more than 64KB? 2000:E576 74 0A jz short sysbios_decomp_error ; jmp if compressed component size <= 64KB 2000:E578 BB 00 20 mov bx, 2000h 2000:E57B 8E C3 mov es, bx ; proceed2next cmprssd componnt (in next segment) 2000:E57D assume es:_2000h 2000:E57D BB 01 00 mov bx, 1 ; bx = index to be added to previous src_offset in cx 2000:E580 EB 1B jmp short repeat_decompress ; decompress remaining component (1st pass jmp taken) 2000:E582 ; --------------------------------------------------------------------------- 2000:E582 sysbios_decomp_error: ; CODE XREF: Decompress_System_BIOS+5B 2000:E582 ; Decompress_System_BIOS+64 2000:E582 D0 D0 rcl al, 1 2000:E584 BB 00 20 mov bx, 2000h 2000:E587 8E C3 mov es, bx 2000:E589 33 DB xor bx, bx 2000:E58B E8 2B 00 call Decompress 2000:E58E D0 D0 rcl al, 1 2000:E590 3C 03 cmp al, 3 2000:E592 75 05 jnz short decompress_successfull 2000:E594 B8 00 10 mov ax, 1000h 2000:E597 F9 stc 2000:E598 C3 retn 2000:E599 ; --------------------------------------------------------------------------- 2000:E599 decompress_successfull: ; CODE XREF: Decompress_System_BIOS+80 2000:E599 0A C0 or al, al 2000:E59B 75 10 jnz short sys_bios_dcomprss_done 2000:E59D repeat_decompress: ; CODE XREF: Decompress_System_BIOS+6E 2000:E59D ; Decompress_System_BIOS+99 2000:E59D 03 D9 add bx, cx ; bx = point to next compressed component 2000:E59F E8 17 00 call Decompress 2000:E5A2 72 09 jb short sys_bios_dcomprss_done ; 1st pass jmp taken 2000:E5A4 66 F7 C1 00 00 FF FF test ecx, 0FFFF0000h 2000:E5AB 74 F0 jz short repeat_decompress 2000:E5AD sys_bios_dcomprss_done: ; CODE XREF: Decompress_System_BIOS+89 2000:E5AD ; Decompress_System_BIOS+90 2000:E5AD E8 AB DA call Decmprss_Sysbios_Extension 2000:E5B0 0F 84 2D FE jz _sysbios_chksum_error 2000:E5B4 B8 00 50 mov ax, 5000h ; set success flag 2000:E5B7 F8 clc 2000:E5B8 C3 retn 2000:E5B8 Decompress_System_BIOS endp
    2. Decmprss_Sysbios_Extension will save segment 8000h or 9000h as the source segment for the "extension BIOS component" for decompression. This is accomplished during execution of bootblock in RAM (at seg 2000h) as follows:
      2000:C05B                         -- Decmprss_Sysbios_Extension --
      2000:C05B in: es = src_seg
      2000:C05B bx = src_offset
      2000:C05B
      2000:C05B out: ZF = 1 --> error occured
      2000:C05B ZF = 0 --> execution succeeded
      2000:C05B ; --------------- S U B R O U T I N E ---------------------------------------
      2000:C05B Decmprss_Sysbios_Extension proc near 2000:C05B ; CODE XREF: Decompress_System_BIOS:sys_bios_dcomprss_donep 2000:C05B BB 00 80 mov bx, 8000h ; es = seg_addr of C_seg n D_seg copy in RAM
      2000:C05B ; this seg_addr will be saved to RAM (0000:60XXh)
      2000:C05B ; for extension component other than awdext.rom/awdeyt.rom
      2000:C05E 8E C3 mov es, bx 2000:C060 assume es:nothing 2000:C060 33 DB xor bx, bx ; reset src_offset_addr 2000:C062 66 33 C9 xor ecx, ecx ; reset compressed src size 2000:C065 51 push cx 2000:C066 repeat_decomprss: ; CODE XREF: Decmprss_Sysbios_Extension+30j 2000:C066 03 D9 add bx, cx 2000:C068 72 09 jb short move_to_next_seg 2000:C06A 66 F7 C1 00 00 FF FF test ecx, 0FFFF0000h ; is size more than 64KB? 2000:C071 74 0A jz short size_less_than_64KB 2000:C073 move_to_next_seg: ; CODE XREF: Decmprss_Sysbios_Extension+Dj 2000:C073 8C C1 mov cx, es 2000:C075 81 C1 00 10 add cx, 1000h 2000:C079 8E C1 mov es, cx 2000:C07B assume es:nothing 2000:C07B EB 1A jmp short reset_byte_counter 2000:C07D ; --------------------------------------------------------------------------- 2000:C07D size_less_than_64KB: ; CODE XREF: Decmprss_Sysbios_Extension+16j 2000:C07D 26 80 7F 12 41 cmp byte ptr es:[bx+12h], 41h ; 'A' ; is awardext.rom? 2000:C082 75 04 jnz short not_awdext 2000:C084 58 pop ax 2000:C085 0C 01 or al, 1 2000:C087 50 push ax 2000:C088 not_awdext: ; CODE XREF: Decmprss_Sysbios_Extension+27j 2000:C088 E8 2E 25 call Decompress 2000:C08B 73 D9 jnb short repeat_decomprss 2000:C08D 8C C3 mov bx, es 2000:C08F 81 C3 00 10 add bx, 1000h 2000:C093 8E C3 mov es, bx 2000:C095 assume es:nothing 2000:C095 33 DB xor bx, bx 2000:C097 reset_byte_counter: ; CODE XREF: Decmprss_Sysbios_Extension+20j 2000:C097 33 C9 xor cx, cx 2000:C099 repeat_dcomprss2: ; CODE XREF: Decmprss_Sysbios_Extension+4Ej 2000:C099 03 D9 add bx, cx 2000:C09B 26 80 7F 12 41 cmp byte ptr es:[bx+12h], 41h ; 'A' 2000:C0A0 75 04 jnz short not_awdext2 2000:C0A2 58 pop ax 2000:C0A3 0C 01 or al, 1 ; set successful flag 2000:C0A5 50 push ax 2000:C0A6 not_awdext2: ; CODE XREF: Decmprss_Sysbios_Extension+45j 2000:C0A6 E8 10 25 call Decompress 2000:C0A9 73 EE jnb short repeat_dcomprss2 2000:C0AB 58 pop ax 2000:C0AC 0A C0 or al, al ; al=0 indicate an error occured 2000:C0AC ; (awdext_rom doesn't exist) 2000:C0AE C3 retn 2000:C0AE Decmprss_Sysbios_Extension endp
    3. Decompression_Ngine procedure called from Decompress procedure during bootblock execution in RAM (at segment 2000h) modify the header as needed and save the result in predefined area in RAM, as follows:
      2000:7789 --- Decomprssion_Ngine ---
      2000:7789 in: dx = scratch-pad_segment_for_decompression
      2000:7789 es = (compressed_segment_addr>>0xC) [hi_word of src phy addr]
      2000:7789 bx = compressed_offset_addr
      2000:7789
      2000:7789 out: ecx = overall_compressed_component_length
      2000:7789 edx = original_file_size
      2000:7789 ; --------------- S U B R O U T I N E ---------------------------------------
      2000:7789 Decompression_Ngine proc near ......... 2000:77FF push gs 2000:7801 mov di, 0 2000:7804 mov gs, di 2000:7806 assume gs:nothing 2000:7806 mov di, 6000h
      2000:7809 mov word ptr gs:[di], 7789h ; save Decompression_Ngine offset
      2000:780E 2000:780E ;check LZH header 2000:780E add bx, 12h ; LZH-header decomp_seg_addr_hi_byte index 2000:7811 call Fetch_Byte 2000:7814 sub bx, 12h ; restore bx to point to first byte 2000:7817 cmp al, 40h ; '@' ; is extension component?
      2000:7817 ; at 1st: al equ 50h (original.tmp)
      2000:7817 ; at 2nd: al equ 41h (awardext.rom)
      2000:7819 jnz short not_extension_component ; 1st-pass jmp taken 2000:781B add bx, 11h 2000:781E call Fetch_Byte ; fetch "dest_seg_addr" lo_byte 2000:7821 sub bx, 11h ; restore bx to point to first byte 2000:7824 or al, al ; if extension component, jmp taken
      2000:7826 jnz short extension_component
      2000:7828 cmp dword ptr gs:[di+4], 0 ; cmp dword [0000:6004]:0
      2000:7828 ; 1st pass from original.tmp, [0000:6004]=FFFFh
      2000:7828 ; (programmed by POST_8S in original.tmp prior to calling this engine)
      2000:782E jnz short not_extension_component
      2000:7830 extension_component: ; CODE XREF: Decompression_Ngine+9D
      2000:7830 movzx dx, al ; dl = "dest_seg_addr" lo_byte
      2000:7833 inc bx ; bx = LZH_hdr_chksum byte index
      2000:7834 call Fetch_Byte
      2000:7837 sub al, dl ; LZH_hdr_chksum = LZH_hdr_chksum - "dest_seg_addr"_lo_byte
      2000:7839 call Patch_Byte ; store new checksum
      2000:783C dec bx ; restore bx
      2000:783D xor al, al ; al = 00h
      2000:783F add bx, 11h ; bx = "dest_seg_addr"_lo_byte index
      2000:7842 call Patch_Byte ; patch "dest_seg_addr"_lo_byte to 00h 2000:7845 sub bx, 11h 2000:7848 inc dx ; dx = "dest_seg_addr"_lo_byte + 1
      2000:7849 shl dx, 2 ; dx = 4*("dest_seg_addr"_lo_byte + 1)
      2000:784C add di, dx ; di = 6000h + dx -- look above!
      2000:784E mov gs:[di], bx ; 0000:[di] = compressed_offset_addr
      2000:7851 mov cx, es
      2000:7853 mov gs:[di+2], cx ; 0000:[di+2] = compressed_seg_addr>>0xC (hi_word of src phy addr)
      2000:7857 call Fetch_Byte ; al = LZH_hdr_len 2000:785A movzx ecx, al ; ecx = LZH_hdr_len 2000:785E add bx, 7 2000:7861 call Fetch_Dword ; eax = compressed_file_size 2000:7864 sub bx, 7 2000:7867 add ecx, eax ; ecx = LZH_header_len + compressed_file_size 2000:786A add ecx, 3 ; ecx = total_compressed_component_size 2000:786E pop gs 2000:7870 assume gs:nothing 2000:7870 jmp exit 2000:7873 ; --------------------------------------------------------------------------- 2000:7873 not_extension_component: ; CODE XREF: Decompression_Ngine+90 2000:7873 ; Decompression_Ngine+A5 2000:7873 pop gs 2000:7875 call Make_CRC16_Table 2000:7878 call Read_Header ; fetch header component to scratchpad_seg, on error CF=1 2000:787B jb exit ; ret with error code set 2000:787F mov ax, ds:108h ; mov ax, decomprss_seg_addr 2000:7882 mov ds:104h, ax ; mov nu_decomprss_seg_addr, ax 2000:7885 mov ax, ds:10Ah ; mov ax, decomprss_offst_addr 2000:7888 mov ds:106h, ax ; mov nu_decomprss_offst_addr, ax 2000:788B mov ecx, ds:310h ; ecx = compressed_component_size 2000:7890 xor eax, eax 2000:7893 mov al, ds:571Ch ; al = LZH_hdr_len 2000:7896 add ecx, eax ; ecx = compressed_cmpnnt_size + LZH_hdr_len 2000:7899 add ecx, 3 ; ecx = compressed_cmpnnt_size + LZH_hdr_len + 2000:7899 ; sizeof(EOF_byte) + sizeof(LZH_hdr_len_byte) + 2000:7899 ; sizeof(LZH_hdr_8bit_chk_sum) 2000:7899 ; i.e. ecx = overall_component_len 2000:789D mov edx, ds:314h ; mov edx, original_file_size 2000:78A2 push edx 2000:78A4 push ecx 2000:78A6 push bx 2000:78A7 add bx, 5 ; point to LZH ID byte 2000:78AA call Fetch_Byte 2000:78AD pop bx 2000:78AE cmp al, '0' ; is '-lh0-'? 2000:78B0 jnz short decompress_part 2000:78B2 push ds 2000:78B3 push si 2000:78B4 push bx 2000:78B5 mov di, ds:10Ah 2000:78B9 movzx ax, byte ptr ds:571Ch 2000:78BE add ax, 2 2000:78C1 add bx, ax 2000:78C3 mov cx, ds:310h 2000:78C7 mov ax, ds:108h 2000:78CA mov es, ax 2000:78CC add cx, 3 2000:78CF shr cx, 2 2000:78D2 next_dword: ; CODE XREF: Decompression_Ngine+151j 2000:78D2 call Fetch_Dword 2000:78D5 add bx, 4 2000:78D8 stosd 2000:78DA loop next_dword 2000:78DC pop bx 2000:78DD pop si 2000:78DE pop ds 2000:78DF jmp short LZH_hdr_OK 2000:78E1 ; --------------------------------------------------------------------------- 2000:78E1 decompress_part: ; CODE XREF: Decompression_Ngine+127 2000:78E1 push word ptr ds:104h ; save destination seg addr 2000:78E5 push word ptr ds:106h ; save destination offset addr 2000:78E9 push large [dword ptr ds:314h] 2000:78EE call Lzh_Expand ; Lzh_Expand capable of handling compressed 2000:78EE ; component bigger than 64KB (1 segment) 2000:78F1 pop dword ptr ds:314h 2000:78F6 pop word ptr ds:106h 2000:78FA pop word ptr ds:104h 2000:78FE LZH_hdr_OK: ; CODE XREF: Decompression_Ngine+156 2000:78FE call Zero_Init ; zero init 32KB of scratchpad_seg 2000:7901 pop ecx 2000:7903 pop edx 2000:7905 clc 2000:7906 exit: ; CODE XREF: Decompression_Ngine+E7
      2000:7906 ; Decompression_Ngine+F2
      2000:7906 pop es
      2000:7907 pop bx
      2000:7908 pop eax
      2000:790A retn
      2000:790A Decompression_Ngine endp
      The lines marked with red color write the "signatures" into memory. For example, nnoprom.bin component is defined with ID: 4027h. In this routine nnoprom.bin's "index" will be saved. The index is calculated as follows (also look at the code above):
      index = 4*(lo_byte(ID) + 1) 
      This index is used to calculate the address to save the informations/signatures. In nnoprom.bin's case the index is A0h ( from [4 * (27h + 1)] ). Thus, the address to save the information starts at 60A0h. As you can see above, the first information that's saved is the component's offset address within the compressed "Extension_BIOS components". It's saved to address 60A0h (for nnoprom.bin). Then, the temporary "expansion/decompression segment address" saved to 60A2h (for nnoprom.bin). This temporary "expansion/decompression segment address" always 4000h for all "extension BIOS components" as you can see in the code above (the temporary decompression segment value is patched to 4000h before being saved to RAM). The same process is carried out for all other "extension BIOS components".
    4. Next, the POST_8S routine in original.tmp (executed during POST) responsible for preparing the needed signature for the decompression as you can see below :
      E000:17B8                         POST_8S proc near
      .........
      E000:183F BE 00 00                  mov   si, 0
      E000:1842 8E DE                     mov   ds, si
      E000:1844                           assume ds:nothing
      E000:1844 BE 00 70                  mov   si, 7000h
      E000:1847 8B 44 04 mov ax, [si+4] ; ax = FFFFh (0000:7004h filled before by Decompress_System_BIOS
      E000:1847 ; during bootblock_in_RAM execution)
      E000:184A BF 00 00 mov di, 0
      E000:184D 8E C7 mov es, di
      E000:184F assume es:nothing
      E000:184F BF 00 60 mov di, 6000h
      E000:1852 26 89 45 04 mov es:[di+4], ax ; [0000:6004] = FFFFh --> signature to do LZH decompression
      E000:1852 ; for extension components
      E000:1856 3D FF FF cmp ax, 0FFFFh
      E000:1859 74 10 jz short signature_ok
      E000:185B 8B 04 mov ax, [si] E000:185D 26 89 45 04 mov es:[di+4], ax E000:1861 8B 44 02 mov ax, [si+2] E000:1864 C1 E8 0C shr ax, 0Ch E000:1867 26 89 45 06 mov es:[di+6], ax E000:186B signature_ok: ; CODE XREF: POST_8S+A1
      E000:186B E8 A2 6C call _init_Pwr_Mgmt_ctlr
      E000:186E F8 clc
      E000:186F C3 retn
      E000:186F POST_8S endp
    5. Next, init_nnoprom_rosupd routine (this is just an example, other component will differ slightly) decompresses the nnoprom.bin and rosupd.bin in the following code :
      E000:71C1                         init_nnoprom_rosupd proc near ; CODE XREF: POST_11S
      .........
      E000:71CF BF A0 00                  mov   di, 0A0h ; 'a'      ; nnoprom.bin index
      E000:71CF ; nnoprom.bin-->4027h; A0h = 4h*(lo_byte(4027h)+1h)
      E000:71D2 E8 74 FC call near ptr decompress_BIOS_component ; decompress NNOPROM.BIN
      E000:71D5 0F 82 ED 00 jb decompression_error E000:71D9 68 00 40 push 4000h E000:71DC 1F pop ds ; ds = 4000h; decompression result seg E000:71DD assume ds:nothing E000:71DD 33 F6 xor si, si E000:71DF 68 00 70 push 7000h E000:71E2 07 pop es ; es = 7000h E000:71E3 assume es:nothing E000:71E3 33 FF xor di, di E000:71E5 B9 00 40 mov cx, 4000h E000:71E8 FC cld E000:71E9 F3 66 A5 rep movsd ; copy nnoprom decompression result from E000:71E9 ; seg 4000h to seg 7000h E000:71EC BF 03 00 mov di, 3 E000:71EF 66 26 81 3D 24 4E 4E 4F cmp dword ptr es:[di], 'ONN$' ; match (decompressed) nnoprom.bin signature E000:71F7 0F 85 CB 00 jnz decompression_error E000:71FB 68 F8 9F push 9FF8h E000:71FE 07 pop es ; es = 9FF8h E000:71FF assume es:nothing E000:71FF 33 FF xor di, di E000:7201 B9 68 00 mov cx, 68h ; 'h' E000:7204 32 C0 xor al, al E000:7206 F3 AA rep stosb E000:7208 BF A4 00 mov di, 0A4h ; 'a' ; ROSUPD.bin index
      E000:720B E8 3B FC call near ptr decompress_BIOS_component ; decompress ROSUPD.bin
      E000:720E 0F 82 B4 00 jb decompression_error E000:7212 1E push ds E000:7213 06 push es E000:7214 0F A0 push fs E000:7216 0F A8 push gs E000:7218 9A DD 5F 00 F0 call ds_es_fs_gs_flat_4GB E000:721D 66 33 F6 xor esi, esi E000:7220 8E DE mov ds, si E000:7222 assume ds:nothing E000:7222 8E C6 mov es, si E000:7224 assume es:nothing E000:7224 68 00 40 push 4000h E000:7227 5E pop si E000:7228 66 C1 E6 04 shl esi, 4 ; esi = 4_0000h (decompressed ROSUPD.bin) E000:722C 66 BF 00 00 10 00 mov edi, 100000h E000:7232 66 8B CB mov ecx, ebx E000:7235 66 C1 E9 02 shr ecx, 2 E000:7239 FC cld E000:723A F3 67 66 26 A5 rep movs dword ptr es:[edi], dword ptr es:[esi] ; copy decmprssd ROSUPD.BIN to 1MB E000:723F 0F A9 pop gs E000:7241 0F A1 pop fs E000:7243 07 pop es E000:7244 assume es:nothing E000:7244 1F pop ds E000:7245 assume ds:nothing E000:7245 68 F8 9F push 9FF8h E000:7248 07 pop es E000:7249 assume es:nothing E000:7249 66 26 C7 06 00 00 00 00+ mov dword ptr es:0, 100000h E000:7253 66 26 C7 06 04 00 00 00+ mov dword ptr es:4, 40000h E000:725D 66 33 C0 xor eax, eax E000:7260 B8 00 E0 mov ax, 0E000h E000:7263 66 C1 E0 04 shl eax, 4 E000:7267 66 05 56 71 00 00 add eax, 7156h E000:726D 66 26 A3 08 00 mov es:8, eax E000:7272 B8 07 00 mov ax, 7 E000:7275 26 A3 0C 00 mov es:0Ch, ax E000:7279 B8 00 70 mov ax, 7000h E000:727C 26 A3 0E 00 mov es:0Eh, ax E000:7280 66 33 C0 xor eax, eax E000:7283 B8 00 E0 mov ax, 0E000h E000:7286 66 C1 E0 04 shl eax, 4 E000:728A 66 05 AA 71 00 00 add eax, 71AAh E000:7290 66 26 A3 10 00 mov es:10h, eax E000:7295 66 BE 80 FF 09 00 mov esi, 9FF80h E000:729B 66 81 C6 00 00 00 00 add esi, 0 E000:72A2 B0 36 mov al, 36h ; '6' E000:72A4 0E push cs E000:72A5 68 B0 72 push 72B0h E000:72A8 68 FD E4 push 0E4FDh ; read CMOS byte E000:72AB EA 88 61 00 E0 jmp far ptr Fseg_vector E000:72B0 ; --------------------------------------------------------------------------- E000:72B0 8A D8 mov bl, al E000:72B2 B8 00 00 mov ax, 0 E000:72B5 E8 41 FE call near ptr call_nnoprom_at_7000h ; execute decompressed nnoprom.bin E000:72B8 9C pushf E000:72B9 9D popf E000:72BA 72 0A jb short decompression_error E000:72BC B8 00 00 mov ax, 0 E000:72BF 8E D8 mov ds, ax E000:72C1 assume ds:nothing E000:72C1 80 0E B7 04 03 or ds:byte_0_4B7, 3 E000:72C6 decompression_error: ; CODE XREF: init_nnoprom_rosupd+14 E000:72C6 ; init_nnoprom_rosupd+36 ... E000:72C6 66 61 popad E000:72C8 07 pop es E000:72C9 assume es:nothing E000:72C9 1F pop ds E000:72CA assume ds:nothing E000:72CA C3 retn E000:72CA init_nnoprom_rosupd endp ; sp = 2
      E000:6E49 -- decompress_BIOS_component -- E000:6E49 in: di = index to compressed component (as written at 0000:6XXXh) E000:6E49 ; --------------- S U B R O U T I N E --------------------------------------- E000:6E49 decompress_BIOS_component proc far E000:6E49 ; CODE XREF: EPA_Procedure+43 E000:6E49 ; EPA_Procedure+5E ... E000:6E49 1E push ds E000:6E4A 06 push es E000:6E4B 55 push bp E000:6E4C 57 push di E000:6E4D 56 push si E000:6E4E 81 E7 FF 3F and di, 3FFFh ; clear di's MSB E000:6E52 FA cli E000:6E53 B0 FF mov al, 0FFh ; enable cache E000:6E55 E8 14 FE call F0_init_cpu_cache E000:6E58 68 00 E0 push 0E000h E000:6E5B 68 69 6E push 6E69h E000:6E5E 68 31 EC push 0EC31h E000:6E61 68 D4 E3 push 0E3D4h ; mod_A20_access_mode E000:6E64 EA 30 EC 00 F0 jmp far ptr F000_Vector E000:6E69 ; --------------------------------------------------------------------------- E000:6E69 E8 2F 7D call ds_ss_Enter_Voodoo_Mode E000:6E6C 8C D8 mov ax, ds E000:6E6E 8E C0 mov es, ax ; es = voodoo mode, base at 0000_0000h E000:6E70 assume es:nothing E000:6E70 E8 45 7D call ss_Leave_Voodoo_mode E000:6E73 5A pop dx ; dx = si E000:6E74 58 pop ax ; ax = di --> compressed component index E000:6E75 26 66 8B 9D 00 60 mov ebx, es:[di+6000h] ; ebx = compressed component physical addr ((seg>>0xC)+offset) E000:6E7B 66 0B DB or ebx, ebx ; is compressd component exist? E000:6E7E 0F 84 2E 01 jz no_cmprssd_component E000:6E82 83 FB FF cmp bx, 0FFFFh E000:6E85 0F 84 27 01 jz no_cmprssd_component E000:6E89 F6 C4 40 test ah, 40h E000:6E8C 74 04 jz short exec_decompress ; for nnoprom.bin, jmp taken E000:6E8E F8 clc E000:6E8F E9 1F 01 jmp decomprss_BIOS_componnt_ret E000:6E92 ; --------------------------------------------------------------------------- E000:6E92 exec_decompress: ; CODE XREF: decompress_BIOS_component+43
      E000:6E92 26 8B 3E 00 60 mov di, es:6000h ; di = Decompression_Ngine offset addr
      E000:6E97 67 66 8B 35 00 00 16 00 mov esi, ds:160000h ; mov esi,[awardext.rom 4Byte hdr]
      E000:6E9F 66 F7 D6 not esi
      E000:6EA2 67 66 89 35 00 00 08 00 mov ds:80000h, esi ; modify ds:80000h value; restored below
      E000:6EAA 66 81 FB 00 00 10 00 cmp ebx, 100000h ; compressed_component_phy_addr > 1MB ?
      E000:6EB1 72 57 jb short above_1MB ; for nnoprom.bin, jmp _not_ taken
      E000:6EB3 57 push di ; save Decompression_Ngine offset
      E000:6EB4 66 BE 00 00 09 00 mov esi, 90000h
      E000:6EBA 66 BF 00 00 14 00 mov edi, 140000h
      E000:6EC0 66 B9 00 40 00 00 mov ecx, 4000h ; copy one segment
      E000:6EC6 FC cld
      E000:6EC7 67 F3 66 A5 rep movs dword ptr es:[edi], dword ptr [esi]
      E000:6ECB 66 BE 00 00 16 00 mov esi, 160000h
      E000:6ED1 66 BF 00 00 08 00 mov edi, 80000h
      E000:6ED7 66 B9 00 80 00 00 mov ecx, 8000h ; 2 segment (128KB)
      E000:6EDD FC cld
      E000:6EDE 67 F3 66 A5 rep movs dword ptr es:[edi], dword ptr [esi] ; copy 2 segment (128KB) from 16_0000h to 8_0000h
      E000:6EDE ; for decompression purposes
      E000:6EE2 5F pop di
      E000:6EE3 66 C1 CB 10 ror ebx, 10h
      E000:6EE7 8E C3 mov es, bx ; es = HI_WORD(phy addr of compressed component)
      E000:6EE9 66 C1 CB 10 ror ebx, 10h ; restore ebx
      E000:6EED 26 8B 4F 11 mov cx, es:[bx+11h] ; cx = decompression segment address.
      E000:6EED ; The component will be decompressed into this segment address
      E000:6EF1 51 push cx ; push decompression segment addr
      E000:6EF2 26 FF 37 push word ptr es:[bx] ; push (LZH hdr len and LZH hdr chksum)
      E000:6EF5 F6 C4 80 test ah, 80h
      E000:6EF8 74 36 jz short decompress ; all extension BIOS component-->jmp taken E000:6EFA 26 89 57 11 mov es:[bx+11h], dx E000:6EFE 02 CD add cl, ch E000:6F00 02 D6 add dl, dh E000:6F02 2A CE sub cl, dh E000:6F04 26 28 4F 01 sub es:[bx+1], cl E000:6F08 EB 26 jmp short decompress E000:6F0A ; --------------------------------------------------------------------------- E000:6F0A above_1MB: ; CODE XREF: decompress_BIOS_component+68 E000:6F0A 66 81 C3 00 00 0E 00 add ebx, 0E0000h E000:6F11 26 67 8B 4B 11 mov cx, es:[ebx+11h] ; cx = target segment addr (the component will be E000:6F11 ; decompressed into this segmnt) E000:6F16 51 push cx E000:6F17 26 67 FF 33 push word ptr es:[ebx] E000:6F1B F6 C4 80 test ah, 80h E000:6F1E 74 10 jz short decompress E000:6F20 26 67 89 53 11 mov es:[ebx+11h], dx E000:6F25 02 CD add cl, ch E000:6F27 02 D6 add dl, dh E000:6F29 2A CE sub cl, dh E000:6F2B 26 67 28 4B 01 sub es:[ebx+1], cl E000:6F30 decompress: ; CODE XREF: decompress_BIOS_component+AF
      E000:6F30 ; decompress_BIOS_component+BF ...
      E000:6F30 66 C1 CB 10 ror ebx, 10h
      E000:6F34 8E C3 mov es, bx ; es = phy_addr>>0xC (hi_word of phy addr)
      E000:6F36 66 C1 CB 10 ror ebx, 10h ; bx = compressed component offset
      E000:6F3A 0E push cs
      E000:6F3B 68 49 6F push 6F49h ; ret addr below
      E000:6F3E 68 FF DF push 0DFFFh
      E000:6F41 BA 00 30 mov dx, 3000h ; scratch_pad_seg
      E000:6F44 68 00 20 push 2000h
      E000:6F47 57 push di ; call Decompression_Ngine
      E000:6F48 CB retf
      E000:6F49 ; --------------------------------------------------------------------------- E000:6F49 68 00 E0 push 0E000h E000:6F4C 68 5A 6F push 6F5Ah E000:6F4F 68 31 EC push 0EC31h E000:6F52 68 D4 E3 push 0E3D4h ; disable_A20... E000:6F55 EA 30 EC 00 F0 jmp far ptr F000_Vector E000:6F5A ; --------------------------------------------------------------------------- E000:6F5A E8 3E 7C call ds_ss_Enter_Voodoo_Mode E000:6F5D 8C D8 mov ax, ds E000:6F5F 8E C0 mov es, ax ; es --> voodoo mode, base at 0000_0000h E000:6F61 E8 54 7C call ss_Leave_Voodoo_mode E000:6F64 67 66 A1 00 00 08 00 mov eax, ds:80000h E000:6F6B 67 66 3B 05 00 00 16 00 cmp eax, ds:160000h E000:6F73 75 18 jnz short restore_header E000:6F75 66 C1 CB 10 ror ebx, 10h E000:6F79 8E C3 mov es, bx E000:6F7B 66 C1 CB 10 ror ebx, 10h E000:6F7F 26 8F 07 pop word ptr es:[bx] E000:6F82 26 8F 47 11 pop word ptr es:[bx+11h] E000:6F86 26 66 8B 5F 0B mov ebx, es:[bx+0Bh] E000:6F8B EB 0F jmp short disable_A20 E000:6F8D ; --------------------------------------------------------------------------- E000:6F8D restore_header: ; CODE XREF: decompress_BIOS_component+12A E000:6F8D 26 67 8F 03 pop word ptr es:[ebx] E000:6F91 26 67 8F 43 11 pop word ptr es:[ebx+11h] E000:6F96 26 67 66 8B 5B 0B mov ebx, es:[ebx+0Bh] E000:6F9C disable_A20: ; CODE XREF: decompress_BIOS_component+142 E000:6F9C 68 00 E0 push 0E000h E000:6F9F 68 AD 6F push 6FADh E000:6FA2 68 31 EC push 0EC31h E000:6FA5 68 24 E4 push 0E424h ; disable gate A20 E000:6FA8 EA 30 EC 00 F0 jmp far ptr F000_Vector E000:6FAD ; --------------------------------------------------------------------------- E000:6FAD F8 clc E000:6FAE EB 01 jmp short decomprss_BIOS_componnt_ret E000:6FB0 ; --------------------------------------------------------------------------- E000:6FB0 no_cmprssd_component: ; CODE XREF: decompress_BIOS_component+35 E000:6FB0 ; decompress_BIOS_component+3C E000:6FB0 F9 stc E000:6FB1 decomprss_BIOS_componnt_ret: E000:6FB1 ; CODE XREF: decompress_BIOS_component+46 E000:6FB1 ; decompress_BIOS_component+165 E000:6FB1 9C pushf E000:6FB2 66 53 push ebx E000:6FB4 68 00 E0 push 0E000h E000:6FB7 68 C5 6F push 6FC5h E000:6FBA 68 31 EC push 0EC31h E000:6FBD 68 D4 E3 push 0E3D4h ; enable gate A20 E000:6FC0 EA 30 EC 00 F0 jmp far ptr F000_Vector E000:6FC5 ; --------------------------------------------------------------------------- E000:6FC5 E8 D3 7B call ds_ss_Enter_Voodoo_Mode E000:6FC8 8C D8 mov ax, ds E000:6FCA 8E C0 mov es, ax E000:6FCC E8 E9 7B call ss_Leave_Voodoo_mode E000:6FCF 67 66 A1 00 00 08 00 mov eax, ds:80000h E000:6FD6 67 66 3B 05 00 00 16 00 cmp eax, ds:160000h E000:6FDE 75 2B jnz short exit E000:6FE0 66 BF 00 00 08 00 mov edi, 80000h E000:6FE6 66 B9 00 40 00 00 mov ecx, 4000h E000:6FEC 66 33 C0 xor eax, eax E000:6FEF FC cld E000:6FF0 67 F3 66 AB rep stos dword ptr es:[edi] E000:6FF4 66 BE 00 00 14 00 mov esi, 140000h E000:6FFA 66 BF 00 00 09 00 mov edi, 90000h E000:7000 66 B9 00 40 00 00 mov ecx, 4000h E000:7006 FC cld E000:7007 67 F3 66 A5 rep movs dword ptr es:[edi], dword ptr [esi] E000:700B exit: ; CODE XREF: decompress_BIOS_component+195 E000:700B 68 00 E0 push 0E000h E000:700E 68 1C 70 push 701Ch E000:7011 68 31 EC push 0EC31h E000:7014 68 24 E4 push 0E424h ; turn off Gate A20 E000:7017 EA 30 EC 00 F0 jmp far ptr F000_Vector E000:701C ; --------------------------------------------------------------------------- E000:701C 66 5B pop ebx E000:701E B0 00 mov al, 0 ; disable CPU cache E000:7020 E8 49 FC call F0_init_cpu_cache E000:7023 9D popf E000:7024 5D pop bp E000:7025 07 pop es E000:7026 1F pop ds E000:7027 C3 retn E000:7027 decompress_BIOS_component endp ; sp = -18h
      The decompression and handling of other components are similar nnoprom.bin and rosupd.bin above.
    After the explanation above, we only need to follow the "POST jump table execution" to be able to know which "execution path" is taken by the BIOS in which circumstances. Having doing this approach we'll be able to do what we please to our "to be hacked" award bios >:). However, I ain't going to stop here, in the following sections I'll present the handling of some extension components that are interesting to me (^_^).

    7.2.7. Microcode Update Routine

    The microcode update routine is called from within POST_9S. The routine as follows:
    E000:3BBE                         init_microcode proc near    ; CODE XREF: POST_9S+52
    E000:3BBE E8 4E 38 call is_intel_CPU
    E000:3BC1 75 5F jnz short exit
    E000:3BC3 66 B8 01 00 00 00 mov eax, 1
    E000:3BC9 0F A2 cpuid
    E000:3BCB BB 00 20 mov bx, 2000h
    E000:3BCE 8E DB mov ds, bx
    E000:3BD0 assume ds:_2000h
    E000:3BD0 BB 00 90 mov bx, 9000h
    E000:3BD3 3B 47 0C cmp ax, [bx+0Ch]
    E000:3BD6 75 05 jnz short decompress_microcode
    E000:3BD8 E8 48 00 call get_cpu_microcode_ver
    E000:3BDB 74 2C jz short update_microcode
    E000:3BDD decompress_microcode: ; CODE XREF: init_microcode+18
    E000:3BDD 66 50 push eax
    E000:3BDF BF 08 00 mov di, 8 ; compressed microcode index (4*(lo_byte(4001h)+1))
    E000:3BE2 E8 64 32 call near ptr decompress_BIOS_component
    E000:3BE5 66 58 pop eax E000:3BE7 72 39 jb short exit E000:3BE9 66 C1 EB 0B shr ebx, 0Bh E000:3BED 66 8B CB mov ecx, ebx E000:3BF0 BB 00 40 mov bx, 4000h E000:3BF3 8E DB mov ds, bx ; point to seg 4000h (microcode decompression rsult) E000:3BF5 assume ds:nothing E000:3BF5 33 DB xor bx, bx ; init bx E000:3BF7 next_microcode: ; CODE XREF: init_microcode+47 E000:3BF7 3B 47 0C cmp ax, [bx+0Ch] E000:3BFA 75 05 jnz short microcode_not_match E000:3BFC E8 24 00 call get_cpu_microcode_ver E000:3BFF 74 08 jz short update_microcode E000:3C01 microcode_not_match: ; CODE XREF: init_microcode+3C E000:3C01 81 C3 00 08 add bx, 800h ; length of one microcode E000:3C05 E2 F0 loop next_microcode E000:3C07 EB 19 jmp short exit E000:3C09 ; --------------------------------------------------------------------------- E000:3C09 update_microcode: ; CODE XREF: init_microcode+1D
    E000:3C09 ; init_microcode+41
    E000:3C09 66 B9 79 00 00 00 mov ecx, 79h ; 'y'
    E000:3C0F 66 33 C0 xor eax, eax
    E000:3C12 66 33 D2 xor edx, edx ; microcode update sign
    E000:3C15 8C D8 mov ax, ds
    E000:3C17 66 C1 E0 04 shl eax, 4
    E000:3C1B 83 C3 30 add bx, 30h ; '0'
    E000:3C1E 8B C3 mov ax, bx ; eax = linear addr of the microcode
    E000:3C20 0F 30 wrmsr ; start microcode update
    E000:3C22 exit: ; CODE XREF: init_microcode+3 E000:3C22 ; init_microcode+29 ... E000:3C22 C3 retn E000:3C22 init_microcode endp

    8. Rants and Raves

    Hey, if you've read this article this far, you must be curious (^__^). Let's take a break a bit. I want to say some facts that are ridiculous. Well, at least to me. BIOS code is meant to be "twisted" so that code digger like us will find a hard time to figure it out. But, yeah here we are, understanding the big picture. Code digger rules my man! Nothing more dangerous than curiousity.

    It's funny to see that the core LZH decompression routine that is used by award bioses (at least v4.51 that's dissected here) is just a complete "copy and paste" from Haruhiko Okumura's LZH code that anyone can find in the web. It's just the language that's different, Okumura's is in C while award's is in x86 assembly, the subroutines were exactly the same!

    Another fact is, Phoenix code is very similar to award's code. I don't know who is "stealing" who, or perhaps there' s another source where both of them steal from (^__^). Oh no... I guess they are just doing reverse engineering like me with their tons of money. Unfortunately I don't have those tons of money he..he..he..

    Well, let me stop ranting and continue my work on AMI BIOS. I guess we all waiting for it, right? It'll take sometime coz I'm very busy. If anyone of you who read this article have done it and had something to say or want to share your work with the world, I really keen to know. Why don't we join forces, right? my mail address is in the end of this article. I know some of us have done it.. we just haven't been in contact or perhaps it's better if we find our own way. Time will tell (-__-)

    Greetz go to: Kris Kaspersky, Petr Soucek, Polaris, Havok, Zero, Mike Tedder a.k.a bpoint, apple_rom, Ilfak Guilfanov and many others who share their knowledge with the world.

    9. Closing

    What I've explained above possibly far too premature to be ended here. But, I consider this article finished here as the Beta 6 version. If you follow this article from beginning to end, you'll be able to understand the "BIG Picture" of how the Award BIOS works. I think all of the issue dissected here is enough to do any type of modification you wish to do with award bios. If you find any mistake(s) within this article or have any suggestion, please contact me. Goodluck with your BIOS reverse engineering journey, I hope you enjoy it as much as I do (^__^) .


    Copyright © Darmawan M S a.k.a Pinczakko

    Comments