Pinczakko's Guide to Self-patching Expansion ROM Source Code

1. ISA Expansion ROM structure

It has been known widely that Expansion ROM or Option ROM is a binary file placed inside the ISA expansion card or PCI expansion card. It is also possible to place this ROM as a part of the motherboard BIOS. An ISA expansion ROM has the following structure:

On the other hand, PCI expansion ROM is developed based on the ISA expansion ROM structure with a couple of PCI specific data structure. I won't explain that here and I will only show you how to build an ISA expansion ROM for the moment. You will use only one tools, i.e. the Flat Assembler a.k.a Fasm.

2. Building a Self-patching ISA Expansion ROM

Now, I'll show you an example of a self-patching ISA expansion ROM. This sample can be regarded as a template.

;--------------------------------------------------------------------------------- 

; Pinczakko's ISA ROM patch template ; ; file : isa_rom_template.asm 

;---------------------------------------------------------------------------------  

use16  ; 16-bit by default  

macro PATCH_PCI reg_addr,mask 

{

         mov     eax,reg_addr    ;fetch  addr of the regs to be patched

         mov     dx,in_port      ;fetch input port addr of PCI cfg space

         out     dx,eax

         mov     dx,out_port

          in      eax,dx

         or      eax,mask        ;mask the regs value (activate

                                  ;certain        bits)

         out     dx,eax

}   

;--------------------------------------------------------------------- 

; Expansion ROM Header 

;--------------------------------------------------------------------- 

ROM_SIZE_IN_BLOCK = 1  ; 1 means ROM size is 1 block (512 bytes) 

ROM_SIZE_IN_BYTE = ROM_SIZE_IN_BLOCK * 512

   db  55h                 ; Rom signature byte 1

   db  0AAh                ; Rom signature byte 2

   db  ROM_SIZE_IN_BLOCK   ; Rom Size in multiple of 512-bytes (1bit = 512 bytes)

   call INIT           ; jump to initialization

   retf                ; jump back to system BIOS  

;---------------------------------------------------------------------- 

; equates 

;---------------------------------------------------------------------

         in_port         equ 0cf8h

         out_port        equ 0cfch

         ioq_mask        equ 00000080h

         ioq_reg         equ 80000050h

         bank_mask       equ 20000844h

         bank_reg        equ 80000068h

         tlb_mask        equ 00000008h

         tlb_reg         equ 8000006ch

         dram_mask       equ 00020202h

         dram_reg        equ 80000064h  

INIT:

         pushad         PATCH_PCI ioq_reg, ioq_mask     ; patch the ioq reg

         PATCH_PCI dram_reg, dram_mask   ; patch the DRAM controller i.e. the

                                         ; interleaving part

         PATCH_PCI bank_reg, bank_mask   ; patch bank active page ctl reg

         PATCH_PCI tlb_reg, tlb_mask     ;Activate Fast TLB lookup

         popad

         retn                            ;return to this rom's header

   times (ROM_SIZE_IN_BYTE-$) db 0 ; use 00h as the padding bytes until we reach the ROM size  

; The last byte (512th) will be the patch_byte for the checksum 

; patch_byte is calculated and automagically inserted below

 PREV_CHKSUM = 0

 repeat $

    load CHKSUM byte from %-1

    CHKSUM = (PREV_CHKSUM + CHKSUM) mod 0x100

    PREV_CHKSUM = CHKSUM

 end repeat store byte (0x100 - CHKSUM) at ($-1)  ; store the patch_byte

To compile the code above, do the following steps:

The secret recipe of the source code lies in:

times (ROM_SIZE_IN_BYTE-$) db 0 ; use 00h as the padding bytes until we reach the ROM size  

; The last byte (512th) will be the patch_byte for the checksum 

; patch_byte is calculated and automagically inserted below 

PREV_CHKSUM = 0 

repeat $

    load CHKSUM byte from %-1

    CHKSUM = (PREV_CHKSUM + CHKSUM) mod 0x100

    PREV_CHKSUM = CHKSUM

 end repeat store byte (0x100 - CHKSUM) at ($-1)  ; store the patch_byte

This particular code is parsed by Fasmw "interpreter" after the code has been compiled. To put simply, it zero-extends the result of the compilation to ROM_SIZE_IN_BYTE and then it calculates the 8-bit sum of the binary and then appends a value in the end of the binary. This particular value is the 8-bit checksum of the binary file such that the 8-bit sum of the whole binary is equal to 00h.

The technique explained here is easily extended for PCI expansion ROM. You can use the automatic byte patching and checksum calculation above in your code to create a self-patching PCI expansion ROM. Of course, that's possible only if you use fasm ;-).

Copyright © Darmawan M S a.k.a Pinczakko