Award BIOS "POST Jump Table" Hacking
a.k.a
Award BIOS Code Injection

Table of Contents


Foreword

This article is only for bios hackers who already done some bios hacking before, especially Award BIOS and its variant. If you haven't done any bios hacking before or is not knowledgeable enough in bios, this article maybe not useful at all. To put simply, this article basically describes an advanced and elegant way to do bios code injection.

First, let me explain that this article is not an official article, I write it merely as a documentation for myself. But, I present it to the public, since I think it might be of some use to somebody who does some bios hacking on his/her own. Any damages that may happen due to applying the technique I explain here is not my responsiblity. If you try it, then you are on your own. I suggest you to stop reading this article right now if you don't agree on my terms. It doesn't do any good to you unless you are really curious to know this advanced bios hacking technique.

Based on my previous two article i.e. Advanced Award BIOS v4.51PG Hacking Tutorial and Pinczakko's Guide to Award BIOS Reverse Engineering, it's very clear that there's a much more safe and elegant BIOS hacking technique waiting to be exploited i.e., patching the so called "POST jump table" to include a "jump" into our own custom procedure in Award Bioses. There are several reasons why I choose this approach to Award BIOS hacking :

The following is the detail of the testbed used for this radical modification :

Processor : - Intel Celeron 300A, overclocked to 518 MHz by using ABIT Slotket II adapter
- Intel Pentium II 450, overclocked to 512 MHz
Mainboard : Iwill VD133 (slot 1) with VIA693A northbridge and VIA596B southbridge
Videocard : PowerColor Nvidia Riva TNT2 M64 32MB
RAM : 256MB SDRAM with unknown chip
Soundcard : Addonics Yamaha YMF724
Network Card : Realtek RTL8139C
Harddrive : Maxtor 20GB 5400RPM
CDROM : Teac 40X
Monitor : Samsung SyncMaster 551v (15')
Operating System : - Windows 2000, used to run the modification tools
- Real-mode DOS, used to flash the BIOS

Perhaps it sounds crazy, but the fact is: the testbed is my computer that I use for working everyday, he..he..he.. >:). OK, enough with the intro, the next section will explain the tools needed to accomplish this task. Goodluck.

Tools Of The Trade

You are only as good as your tools. Yeah, this also holds true here. We'll need some tools as follows :

  1. IDA Pro disassembler. I'm using IDA Pro version 4.50. 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.
  2. A good hex editor. I'm using HexWorkshop ver. 3.02. 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. We use this tool to edit the bios binary.
  3. Nasm, the netwide assembler. Just download it at http://nasm.sourceforge.net. We use this to assemble the code that will be injected to the BIOS. You won't need nasm if you want to use other assembler such as fasm as mentioned below.
  4. Fasmw, the Flat Assembler for Windows. Google for it and you'll find it on the net. I've been switching to fasm for a while coz it's much more suitable for BIOS hacking, i.e. direct binary manipulation.
  5. A text editor, we use this to edit and write the injected x86 assembly language code. Anyway, notepad is enough. Note that this is not needed if you use fasm (*_^).
  6. Some bios modification tools i.e. : Note: Actually, among these tools, modbin is the only one that we need. I'm using modbin 4.50.80C. Read my latest article (Pinczakko's Guide to Award BIOS Patching) in this website to find out why.
  7. Some chipset datasheets. This depends on the mainboard bios binary that you're going to dissect. Some datasheets available at www.rom.by in the PDF-s section. I'm dissecting a VIA693A-596B mainboard. I have all the needed datasheets at my hand.

Prerequisite

There are some stuff that I won't explain here and it's your homework that you should do to comprehend this article :

Now, we proceed to some more hints and conventions that we have to agreed upon throughout this article. In this article I will explain how to inject your own code into Award BIOS by patching the POST jump table. But, before that, let's clarify a few things:

Hacking the POST Jump Table

Now we've already known all the prerequisite knowledge to do the hack. I'd like to formulate the steps that we need to do this type of hack :

By following the above guidelines, we will finally arrive at our hacked BIOS.

1. BIOS Reverse Engineering and Analysis

I have done this, the result can be seen at Pinczakko's Guide to Award BIOS Reverse Engineering. The "POST jump table" location can be seen above (in the Prerequisite section). It's very clear there that we have several candidate of dummy procedure jumps that we can replace with our own procedure jump (it's highlighted with red color).

2. Assembling Our Custom Procedure

The following is the source code of the procedure that I inject into my bios (using nasm syntax):

;---------------- BEGIN TWEAK.ASM --------------------------------------------------------------
BITS 16 ;just to make sure nasm prefix 66 to 32 bit instructions, we're assuming the uP
        ;is in 16 bits mode up to this point (from the boot state)

  section   .text

start:

	pushf
	push eax
	push dx

	mov eax,ioq_reg	 ;patch the ioq register of the chipset
	mov dx,in_port
	out dx,eax
	mov dx,out_port
	in  eax,dx
	or  eax,ioq_mask
	out dx,eax
		
	mov eax,dram_reg ;patch the DRAM controller of the chipset, 
	mov dx,in_port	 ;i.e. the interleaving part
	out dx,eax
	mov dx,out_port
	in  eax,dx
	or  eax,dram_mask
	out dx,eax

	mov eax,bank_reg ;Allow pages of different bank to be active simultanoeusly
	mov dx,in_port
	out dx,eax
	mov dx,out_port
	in  eax,dx
	or  eax,bank_mask
	out dx,eax

	mov eax,tlb_reg	 ;Activate Fast TLB lookup
	mov dx,in_port
	out dx,eax
	mov dx,out_port
	in  eax,dx
	or  eax,tlb_mask
	out dx,eax
	
	pop dx
	pop eax
	popf

	clc		 ;indicate that this POST routine successful
	retn		 ;return near to the header of the rom file

  section .data

  in_port   equ 0cf8h
  out_port  equ 0cfch
  dram_mask equ 00020202h
  dram_reg  equ 80000064h
  ioq_mask  equ 00000080h
  ioq_reg   equ 80000050h
  bank_mask equ 20000840h
  bank_reg  equ 80000068h
  tlb_mask  equ 00000008h
  tlb_reg   equ 8000006ch
;---------------- END TWEAK.ASM --------------------------------------------------------------
The code is assembled using nasm with the invocation syntax :
nasm -fbin tweak.asm -o tweak.bin 
The resulting binary file is tweak.bin. The following is the hex-dump of this binary in hexworkshop v3.02
Address   Hexadecimal Values                        ASCII               
00000000 9C66 5052 66B8 5000 0080 BAF8 0C66 EFBA .fPRf.P......f..
00000010 FC0C 66ED 660D 8000 0000 66EF 66B8 6400 ..f.f.....f.f.d.
00000020 0080 BAF8 0C66 EFBA FC0C 66ED 660D 0202 .....f....f.f...
00000030 0200 66EF 66B8 6800 0080 BAF8 0C66 EFBA ..f.f.h......f..
00000040 FC0C 66ED 660D 4008 0020 66EF 66B8 6C00 ..f.f.@.. f.f.l.
00000050 0080 BAF8 0C66 EFBA FC0C 66ED 660D 0800 .....f....f.f...
00000060 0000 66EF 5A66 589D F8C3                ..f.ZfX...
The dump above shows that we need 0x6A bytes (106 bytes) free space to inject this code in system bios.

3. Injecting The Procedure

Now,extract the system bios by using AwardBios editor. It's very simple, just open the bios file then select the System BIOS tree-item in the left pane, then click the Action|Extract File to save the system bios as a separate uncompressed binary file. As convention in this article, let's name it original.tmp.

Then, open original.tmp using hexeditor. In my original.tmp, I found a lot of padding FFh bytes in the end of segment E000h. Perhaps, this quite confusing at first, let me clarify what I mean: In my previous Award BIOS reverse engineering article, I found that the POST jump table resides in the E000h segment and the jump table contains addresses in Little-Endian 16 bit value. This means that the jump table is only for intra-segment jumps, hence, our injected procedure must reside in the same segment as the POST jump table itself, i.e. segment E000h. So, the "free space" that can be used for our procedure must reside in segment E000h. Most of the time this "free space" is padding bytes.

If you still confused, let me refresh your memory about the mapping between original.tmp in the real system address space and in the hexeditor that we use. Original.tmp size is 128KB, it uses the E000h and F000h segment during it's execution, so, if you see address 0000 0000h in your hexeditor for this file, it's basically address E000:0000h when original.tmp gets executed, and so forth. Due to this fact, we have to look for "free space", i.e. unused area or padding bytes below the address 0001 0000h in the hexeditor.

Below is the snapshot of the beginning of the padding bytes in both IDA Pro 4.50 and Hexworkshop v3.02 for exactly the same address.

In IDA Pro 4.50:

Address   Hex Values         Mnemonic    Comment
E000:EFE0 C3                   db 0C3h ; +
E000:EFE1 00                   db    0 ;  
E000:EFE2 00                   db    0 ;  
E000:EFE3 00                   db    0 ;  
E000:EFE4 00                   db    0 ;  
E000:EFE5 00                   db    0 ;  
E000:EFE6 00                   db    0 ;  
E000:EFE7 00                   db    0 ;  
E000:EFE8 00                   db    0 ;  
E000:EFE9 00                   db    0 ;  
E000:EFEA 00                   db    0 ;  
E000:EFEB 00                   db    0 ;  
E000:EFEC 00                   db    0 ;  
E000:EFED 00                   db    0 ;  
E000:EFEE 00                   db    0 ;  
E000:EFEF 00                   db    0 ;  
E000:EFF0 FF                   db 0FFh ;  
E000:EFF1 FF                   db 0FFh ;  
E000:EFF2 FF                   db 0FFh ;  
E000:EFF3 FF                   db 0FFh ;  
E000:EFF4 FF                   db 0FFh ;  
E000:EFF5 FF                   db 0FFh ;  
E000:EFF6 FF                   db 0FFh ;  
E000:EFF7 FF                   db 0FFh ;  
E000:EFF8 FF                   db 0FFh ;  
E000:EFF9 FF                   db 0FFh ;  
E000:EFFA FF                   db 0FFh ;  
E000:EFFB FF                   db 0FFh ;  
E000:EFFC FF                   db 0FFh ;  
E000:EFFD FF                   db 0FFh ;  
E000:EFFE FF                   db 0FFh ;  
E000:EFFF FF                   db 0FFh ;  

In Hexworkshop 3.02:

Address     Hex values                              ASCII
0000EFE0 C300 0000 0000 0000 0000 0000 0000 0000 ................
0000EFF0 FFFF FFFF FFFF FFFF FFFF FFFF FFFF FFFF ................

Looking at the amount of padding bytes in original.tmp, we know that we have enough space to do the code injection. So, what we need to do is: use the hexeditor to replace 106 bytes beginning at E000:EFF0h (0000EFF0h) with the code that we already assembled (in 16-bit x86 executable binary format) in the previous step. In hexworkshop, this step is trivial, just open original.tmp and tweak.bin in the same hexworkshop, then copy and paste tweak.bin contents to original.bin, that's it :). The result in hexworkshop as follows (the hex-values highlighted in red is the injected code):
Address     Hex values                              ASCII
0000EFD0 C300 0000 0000 0000 0000 0000 0000 0000 ................
0000EFE0 C300 0000 0000 0000 0000 0000 0000 0000 ................
0000EFF0 9C66 5052 66B8 5000 0080 BAF8 0C66 EFBA .fPRf.P......f..
0000F000 FC0C 66ED 660D 8000 0000 66EF 66B8 6400 ..f.f.....f.f.d.
0000F010 0080 BAF8 0C66 EFBA FC0C 66ED 660D 0202 .....f....f.f...
0000F020 0200 66EF 66B8 6800 0080 BAF8 0C66 EFBA ..f.f.h......f..
0000F030 FC0C 66ED 660D 4008 0020 66EF 66B8 6C00 ..f.f.@.. f.f.l.
0000F040 0080 BAF8 0C66 EFBA FC0C 66ED 660D 0800 .....f....f.f...
0000F050 0000 66EF 5A66 589D F8C3 FFFF FFFF FFFF ..f.ZfX.........
0000F060 FFFF FFFF FFFF FFFF FFFF FFFF FFFF FFFF ................
0000F070 FFFF FFFF FFFF FFFF FFFF FFFF FFFF FFFF ................

If you eager to know what the code above accomplished, I provide the snapshot of my chipset datasheet below. Unfortunately, you still need know PCI protocol to make use of it. This is the snapshot for PCI device at address bus 0 - device 0 - function 0, i.e. the hostbridge of my mainboard.

Device 0 Configuration Registers - Host Bridge
These registers are normally programmed once at system
initialization time.
Host CPU Control
Device 0 Offset 50 ERequest Phase Control (00h) ......... RW
7 CPU Hardwired IOQ (In Order Queue) Size
  Default per strap on pin MAB11#During reset. This
  register can be written 0 to restrict the chip to one
  level of IOQ.
  0 1-Level
  1 4-Level
6 Read-Around-Write
  0 Disable ...................................................default
  1 Enable
5 Reserved ........................................ always reads 0
4 Defer Retry When HLOCK Active
  0 Disable ...................................................default
  1 Enable
  Note: always set this bit to 1
3-1 Reserved ........................................ always reads 0
0 CPU / PCI Master Read DRAM Timing
  0 Start DRAM read after snoop complete ...... def
  1 Start DRAM read before snoop complete

DRAM Control
These registers are normally set at system initialization time
and not accessed after that during normal system operation.
Some of these registers, however, may need to be
programmed using specific sequences during power-up
initialization to properly detect the type and size of installed
memory (refer to the VIA Technologies VT82C693A BIOS
porting guide for details).

SDRAM Settings for Registers 67-64
7 Precharge Command to Active Command Period
     0 TRP = 2T
     1 TRP = 3T ............................................... default
6 Active Command to Precharge Command Period
     0 TRAS = 5T
     1 TRAS = 6T ............................................. default
5-4 CAS Latency
     00 1T
     01 2T
     10 3T ...................................... default
     11 reserved
3 DIMM Type
     0 Standard
     1 Registered ............................................. default
2 ACTIVE Command to CMD Command Period /
VCM Prefetch Read Latency
    0 2T / 3T
    1 3T / 4T ................................................... default
1-0 Bank Interleave
    00 No Interleave ......................................... default
    01 2-way
    10 4-way
    11 Reserved

Device 0 Offset 68 - DRAM Control (00h) ...................... RW
7 SDRAM Open Page Control
     0 Always precharge SDRAM banks when
       accessing EDO/FPG DRAMs.................default
     1 SDRAM banks remain active when accessing
       EDO/FPG banks
6 Bank Page Control
     0 Allow only pages of the same bank active.. def.
     1 Allow pages of different banks to be active
5 Reserved ........................................ always reads 0
4 DRAM Data Latch Delay for EDO/FPG DRAM
     0 Latch DRAM data at CCLK rising edge .... def.
     1 Delay latch of DRAM data by ? CCLK
3 EDO Test Mode
     0 Disable ...................................................default
     1 Enable
2 Burst Refresh
     0 Disable ...................................................default
     1 Enable (burst 4 times)
1 System Frequency Divider ..................................RO
    This bit is latched from MAB8# at the rising edge of RESET# (see table below).
0 System Frequency Divider ..................................RO
    This bit is latched from MAB12# at the rising edge of RESET#.
    00 CPU Frequency = 66 MHz
    01 CPU Frequency = 100 MHz
    10 CPU Frequency = 133 MHz
    11 Reserved
Note: See also Rx69[7-6]
Note: MD0 is internally pulled up for EDO detection.

Device 0 Offset 6C - SDRAM Control (00h) ................... RW
7-5 Reserved ........................................ always reads 0
4 CKE Configuration
     0 Rx6B[4]=0 RASA = CSA, RASB = CSB,
                 CKE0=CKE0, CKE1 = CKE1
     x Rx6B[4]=1 RASA = CSA, RASB = Float,
                 CASB = Float, MAB = Float,
                 CKE0 = CKE0, CKE1 = CKE0
     1 Rx6B[4]=0 RASA = CSA, RASB = CSB,
                 CKE3-2 = CSA7-6
                 CKE5-4 = CSB7-6
                 CKE1 = GCKE (Global CKE)
                 CKE0 = FENA (FET Enable)
3 Fast TLB Lookup
     0 Disable ...................................................default
     1 Enable
2-0 SDRAM Operation Mode Select
     000 Normal SDRAM Mode ..........................default
     001 NOP Command Enable
     010 All-Banks-Precharge Command Enable
         (CPU-to-DRAM cycles are converted
         to All-Banks-Precharge commands).
     011 MSR Enable
         CPU-to-DRAM cycles are converted to
         commands and the commands are driven on
         MA[14:0]. The BIOS selects an appropriate
         host address for each row of memory such that
         the right commands are generated on
         MA[14:0].
     100 CBR Cycle Enable (if this code is selected,
         CAS-before-RAS refresh is used; if it is not
         selected, RAS-Only refresh is used)
     101 Reserved
     11x Reserved

After this step, we proceed to next step to patch the jump table.

4. Modifying The Jump Table

Modifying the POST jump table is just a trivial task after we do the reverse engineering in the bios binary. As presented above in the prerequisite section, there are lots of jump table entries that points to "dummy" procedures.

I decided to redirect/replace the jump table entry at E000:61DEh to point to our injected procedure (at E000:EFF0h) instead to the previous "dummy" procedure. Below is the snapshot in both IDA Pro 4.50 and Hexworkshop, before the modification takes place :

In IDA Pro 4.50:

Address   Hex Values         Mnemonic    Comment
E000:61DC 4E 23                dw 234Eh
E000:61DE 53 23                dw 2353h             ; dummy
E000:61E0 55 23                dw 2355h             ; dummy
E000:61E2 57 23                dw 2357h             ; dummy
E000:61E4 59 23                dw 2359h             ; init Programmable Timer (PIT)
E000:61E6 A5 23                dw 23A5h             ; init PIC_1 (programmable Interrupt Ctlr)
E000:61E8 B6 23                dw 23B6h             ; same as above ?
E000:61EA F9 23                dw 23F9h             ; dummy
E000:61EC FB 23                dw 23FBh             ; init PIC_2
E000:61EE 78 24                dw 2478h             ; dummy
E000:61F0 7A 24                dw 247Ah             ; dummy
E000:61F2 7A 24                dw 247Ah
E000:61F4 7A 24                dw 247Ah
E000:61F6 7A 24                dw 247Ah
.........
E000:2353 F8                   clc                  ; Clear Carry Flag
E000:2354 C3                   retn                 ; Return Near from Procedure
.........

In Hexworkshop 3.02:

Address     Hex values                              ASCII
........
000061D0 B817 4B19 BC1A 081B C81D 4223 4E23 5323 ..K.......B#N#S#
000061E0 5523 5723 5923 A523 B623 F923 FB23 7824 U#W#Y#.#.#.#.#x$
000061F0 7A24 7A24 7A24 7A24                     z$z$z$z$
........

Below is the snapshot in both IDA Pro 4.50 and Hexworkshop, after the modification takes place :

In IDA Pro 4.50:

Address   Hex Values                  Mnemonic    Comment
E000:61DC 4E 23                      dw 234Eh
E000:61DE F0 EF                      dw 0EFF0h 	;jump to our injected code
E000:61E0 55 23                      dw 2355h
.........
E000:EFF0 9C                         pushf                ; Push Flags Register onto the Stack
E000:EFF1 66 50                      push eax
E000:EFF3 52                         push dx
E000:EFF4 66 B8 50 00 00 80          mov  eax, 80000050h
E000:EFFA BA F8 0C                   mov  dx, 0CF8h
E000:EFFD 66 EF                      out  dx, eax
E000:EFFF BA FC 0C                   mov  dx, 0CFCh
E000:F002 66 ED                      in   eax, dx
E000:F004 66 0D 80 00 00 00          or   eax, 80h        ; Logical Inclusive OR
E000:F00A 66 EF                      out  dx, eax
E000:F00C 66 B8 64 00 00 80          mov  eax, 80000064h
E000:F012 BA F8 0C                   mov  dx, 0CF8h
E000:F015 66 EF                      out  dx, eax
E000:F017                         
E000:F017                         loc_EF017:              ; DATA XREF: E000:19975
E000:F017                                                 ; E000:1997A
E000:F017 BA FC 0C                   mov  dx, 0CFCh
E000:F01A                         
E000:F01A                         loc_EF01A:              ; DATA XREF: E000:19962
E000:F01A 66 ED                      in   eax, dx
E000:F01C 66 0D 02 02 02 00          or   eax, 20202h     ; Logical Inclusive OR
E000:F022 66 EF                      out  dx, eax
E000:F024 66 B8 68 00 00 80          mov  eax, 80000068h
E000:F02A BA F8 0C                   mov  dx, 0CF8h
E000:F02D 66 EF                      out  dx, eax
E000:F02F BA FC 0C                   mov  dx, 0CFCh
E000:F032 66 ED                      in   eax, dx
E000:F034 66 0D 40 08 00 20          or   eax, 20000840h  ; Logical Inclusive OR
E000:F03A 66 EF                      out  dx, eax
E000:F03C 66 B8 6C 00 00 80          mov  eax, 8000006Ch
E000:F042 BA F8 0C                   mov  dx, 0CF8h
E000:F045 66 EF                      out  dx, eax
E000:F047 BA FC 0C                   mov  dx, 0CFCh
E000:F04A 66 ED                      in   eax, dx
E000:F04C 66 0D 08 00 00 00          or   eax, 8          ; Logical Inclusive OR
E000:F052 66 EF                      out  dx, eax
E000:F054 5A                         pop  dx
E000:F055 66 58                      pop  eax
E000:F057 9D                         popf                 ; Pop Stack into Flags Register
E000:F058 F8                         clc                  ; Clear Carry Flag
E000:F059 C3                         retn                 ; Return Near from Procedure
.........

In Hexworkshop 3.02:

Address     Hex values                              ASCII
........
000061D0 B817 4B19 BC1A 081B C81D 4223 4E23 F0EF ..K.......B#N#..
000061E0 5523 5723 5923 A523 B623 F923 FB23 7824 U#W#Y#.#.#.#.#x$
........
0000EFE0 C300 0000 0000 0000 0000 0000 0000 0000 ................
0000EFF0 9C66 5052 66B8 5000 0080 BAF8 0C66 EFBA .fPRf.P......f..
0000F000 FC0C 66ED 660D 8000 0000 66EF 66B8 6400 ..f.f.....f.f.d.
0000F010 0080 BAF8 0C66 EFBA FC0C 66ED 660D 0202 .....f....f.f...
0000F020 0200 66EF 66B8 6800 0080 BAF8 0C66 EFBA ..f.f.h......f..
0000F030 FC0C 66ED 660D 4008 0020 66EF 66B8 6C00 ..f.f.@.. f.f.l.
0000F040 0080 BAF8 0C66 EFBA FC0C 66ED 660D 0800 .....f....f.f...
0000F050 0000 66EF 5A66 589D F8C3 FFFF FFFF FFFF ..f.ZfX.........
........

By now, we've patched original.tmp to suit our need. The next thing to do is combining it back into one functional bios binary.

5. Recombining BIOS Component and Fixing Checksums

This step is also trivial, just open the previous bios binary from which we extract the original.tmp using awardbios editor. Then select the System BIOS tree-item in the left pane, and proceed to click the Action|Replace File menu. After that select the modified original.tmp as the file used to replace the genuine original.tmp in that bios binary. Then save this change in awardbios editor.

Actually we're done at this point, but some "nasty" Award BIOS sometimes causes awardbios editor failed to fix its checksum. To guard against this possible bug, open this modified bios binary using modbin, then do some minor changes, such as changing the bios string and then saving this change in modbin. This step, will causes modbin to recalculate all checksums and fix the possibly wrong checksums. That's all, voila' we're done :).

6. Testing The Hacked BIOS

Testing is also a trivial task, just flash the modified bios binary. I'm using uniflash to do this in my machine, since the awardflash is unable to handle my Atmel AT29C020C-90 backup-bios chip that I used in my mainboard, whereas uniflash v1.34 can handle flawlessly. Thanks to Ondrej Zary a.k.a Rainbow, who provide us with this great uniflash bios flashing utility. Thumbs up for all uniflash developer and contributor out there :).

Possible Downside and Its Workaround

During my experiment using this method to patch my bios, I encounter a weird situation that confusing at first. The bug that I encounter would hang my machine at boot, but it's very seldom and hard to reproduce, i.e. around 1 out of 30 tries. This bug is in effect if the following jump table modification is carried out.


Note : 1. The modification I explained in the previous sections proved to be bug free after lots of testing and verifications.
2. The code is injected in the same place as explained in the previous sections.

The following is the jump table before the "buggy" patch incorporated :
Address     Hex Values         Mnemonic             Comment
E000:61DE 53 23                dw 2353h             ; dummy
E000:61E0 55 23                dw 2355h             ; dummy
E000:61E2 57 23                dw 2357h             ; dummy
E000:61E4 59 23                dw 2359h             ; init Programmable Timer (PIT)
E000:61E6 A5 23                dw 23A5h             ; init PIC_1 (programmable Interrupt Ctlr)
E000:61E8 B6 23                dw 23B6h             ; same as above ?
E000:61EA F9 23                dw 23F9h             ; dummy
E000:61EC FB 23                dw 23FBh             ; init PIC_2
E000:61EE 78 24                dw 2478h             ; dummy
E000:61F0 7A 24                dw 247Ah             ; dummy
E000:61F2 7A 24                dw 247Ah
E000:61F4 7A 24                dw 247Ah
E000:61F6 7A 24                dw 247Ah
.........
The following is the jump table after the "buggy" patch incorporated :
Address     Hex Values         Mnemonic             Comment
E000:61DE 53 23                dw 2353h             ; dummy
E000:61E0 55 23                dw 2355h             ; dummy
E000:61E2 57 23                dw 2357h             ; dummy
E000:61E4 59 23                dw 2359h             ; init Programmable Timer (PIT)
E000:61E6 A5 23                dw 23A5h             ; init PIC_1 (programmable Interrupt Ctlr)
E000:61E8 B6 23                dw 23B6h             ; same as above ?
E000:61EA F9 23                dw 23F9h             ; dummy
E000:61EC FB 23                dw 23FBh             ; init PIC_2
E000:61EE 78 24                dw 2478h             ; dummy
E000:61F0 F0 EF                dw EFF0h             ; dummy
E000:61F2 7A 24                dw 247Ah
E000:61F4 7A 24                dw 247Ah
E000:61F6 7A 24                dw 247Ah
.........
After further analysis, I conclude that this kind of bug very possibly related to timing issue and race condition during the code execution in POST. If we take a look closely at the jump table redirection, we see that this bug occur if we modify/redirect the jump table entry after the initialization of the Programmable Interrupt Controller (PIC) in the mainboard. Perhaps, the best way to avoid this is to place our jump table modification before the PIC initialization. Based on my testing result, doing so proved to be flawless and successfully eradicate the bug. I summarised some guidelines to avoid this bug in your jump table modification below : That's all about the possible downsides of this method and their workaround. I'm not an experienced hardware hacker, thus it's possible that my explanation in this section is wrong. I really sorry about that, since I'm still in the process of learning about this subject too.


Critical Update

A Very Subtle Bug and Its Patch

After a more thorough testing, the bug that's caused by a race condition as explained in the previous section is not eradicated completely yet. It's true that previous explanation was being written after only up-to 30-40 boot-reboot cycle. With a thorough (a few hundred times) testing I found out that the bug still occured in around once in 50-60 boot-reboot cycle. After analyzing the previous patch that I made, I'm not aware that it has a bug. Only after a careful code-reading and code-execution-timing-scenario analysis I found out that the patch above was the major cause of the bug. The solution is to loosen the timing during the PCI cycles used to initialize the chipset registers. The working and tested solution for exactly the same purpose as the patch described in the above section is provided below in Fasm syntax. It takes more space, but it works perfectly.

;------------------------------ file: mem_optimize.asm -----------------------------------
use16

start:
        pushf
        cli


        mov  cx, 0x50           ;patch the ioq register of the chipset
        call Read_PCI_Bus0_Byte
        or   al, 0x80
        mov  cx, 0x50
        call Write_PCI_Bus0_Byte

        mov  cx, 0x64           ;DRAM Bank 0/1 Interleave = 4-way
        call Read_PCI_Bus0_Byte
        or   al, 2
        mov  cx, 0x64
        call Write_PCI_Bus0_Byte

        mov  cx, 0x65           ;DRAM Bank 2/3 Interleave = 4-way
        call Read_PCI_Bus0_Byte
        or   al, 2
        mov  cx, 0x65
        call Write_PCI_Bus0_Byte

        mov  cx, 0x66           ;DRAM Bank 4/5 Interleave = 4-way
        call Read_PCI_Bus0_Byte
        or   al, 2
        mov  cx, 0x66
        call Write_PCI_Bus0_Byte

        mov  cx, 0x67           ;DRAM Bank 6/7 Interleave = 4-way
        call Read_PCI_Bus0_Byte
        or   al, 2
        mov  cx, 0x67
        call Write_PCI_Bus0_Byte

        mov  cx, 0x68           ;Allow pages of different bank to be active simultanoeusly
        call Read_PCI_Bus0_Byte
        or   al, 0x44
        mov  cx, 0x68
        call Write_PCI_Bus0_Byte

        mov  cx, 0x69           ;Fast DRAM Precharge for Different Bank
        call Read_PCI_Bus0_Byte
        or   al, 0x8
        mov  cx, 0x69
        call Write_PCI_Bus0_Byte

        mov  cx, 0x6C           ;Activate Fast TLB lookup
        call Read_PCI_Bus0_Byte
        or   al, 0x8
        mov  cx, 0x6C
        call Write_PCI_Bus0_Byte


        popf

        clc              ;indicate that this POST routine successful
        retn             ;return near to the header of the rom file


;-- Read_PCI_Byte__ --
;in: cx = dev_func_offset_addr
;out: al = reg_value

Read_PCI_Bus0_Byte:
        mov   ax, 8000h
        shl   eax, 10h
        mov   ax, cx
        and   al, 0FCh
        mov   dx, 0CF8h
        out   dx, eax
        mov   dl, 0FCh ; '?'
        mov   al, cl
        and   al, 3
        add   dl, al
        in    al, dx
        retn


;-- Write_Bus0_Byte --
;in: cx = dev_func_offset addr
;al = reg_value to write

Write_PCI_Bus0_Byte:
        xchg  ax, cx
        shl   ecx, 10h
        xchg  ax, cx
        mov   ax, 8000h
        shl   eax, 10h
        mov   ax, cx
        and   al, 0FCh
        mov   dx, 0CF8h
        out   dx, eax
        add   dl, 4
        or    dl, cl
        mov   eax, ecx
        shr   eax, 10h
        out   dx, al
        retn
;------------------------------ file: mem_optimize.asm -----------------------------------
Assembling the patch source code in fasmw (fasm for windows) is done by pressing CTRL+F9. As simple as that (^__^). This new patch only initializes one register at a time and gives enough "CPU clock-cycle" to the PCI bus intensive routine. Personally, I think that to appropriately initialize a PCI chipset it's not enough just by relaxing the read-write timing, but more importantly we have to initialize only one register at a time in order to minimize the "sudden-load" in the chipset. This is especially true for performance-related registers within the chipset. In my tests for this new patch, I placed the call to the patch in a few places within the POST-jump-table an everyone of them work flawlessly as expected. The testing has been carried out more than 100 boot-reboot cycle for each variant.

Below is the comparison from the latest variant that undergoes code-injection.

If you compare both of the jump-table, the latter has a patched jump-table with the jump into the chipset-patching-procedure located right after the FSB initialization. I've been experimenting with other possibilities, such as inserting the call into the chipset-patching-procedure inside the "Chipset reg Default values (code in awardext.rom, data in Fseg)",i.e. call to E000:178Ah in the jump-table and it worked flawlessly.

Closing Note

Finally we're done. Yeah, this bios hacking method is very possibly my ultimate bios hacking trick to date. I haven't found any new elegant way to accomplish it. But, remember to pay attention to the timing issue for your injected code. I believe that I might have made obscure mistakes in this article. Thus, it's always open for corrections and improvements. Thanks for reading this humble article. I hope it's of some use for you.

I'm waiting for any comments, corrections and suggestions from the reader. Don't hesitate to mail me.


copyright © 2004, 2005, 2006 Darmawan M S a.k.a Pinczakko

Return to main page

1