Welcome to the dark corner of BIOS reverse engineering, code injection and various modification techniques only deemed by those immensely curious about BIOS

Monday, June 8, 2009

Pinczakko Guide to Award BIOS Reverse Engineering

0. Side Note

It's very time consuming to relocate this article from the old Geocities website to this blog. Therefore, I opted to relocate it to Google sites. You can read it at: http://sites.google.com/site/pinczakko/pinczakko-s-guide-to-award-bios-reverse-engineering

This blog post is a work in progress. This reminder will be removed once this lengthy detailed post is completed.

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 blog and the old Geocities 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 (at the moment, the book is out of print):

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.

[+/-] details

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.

[+/-] details

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 (^__^) .

[+/-] details

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:

  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.

[+/-] details

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.

[+/-] details

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.

[+/-] details

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)


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

[+/-] details

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.

[+/-] details

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.

[+/-] details

Post a Comment


control valves said...

Thanks for the helpful information. Hope to hear more from you.

Darmawan Salihun said...

I have relocated this article to Google sites because it's time consuming to reformat it for Blog posting over here

agus santoso said...

very time consuming to read it all :D

BTW, can you help me reviving my old intel D845PEBT2 ?
Recovery BIOS didn't work.
Try to hotflash (desolder the chip, and put PLCC socket onboard), didn't work either.
Dumping, editing BIO file,
and today i found about "attacking intel bios" from Rafal Wojtczuk and Alexander Tereshkin.
suddenly i realize, am lost.
I can dump and edit all that binnaries, but i don't know how to repack them, and finally flash it to the PLCC32 chip.