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:

  • It's a plain binary file.
  • Its size is a multiple of 512 bytes.
  • Its header has the following format:
    1. 55AAh ;this is the 1st and the 2nd byte, its a bootable code sign, it's actually AA55h, but Intel uses little endian that's why it's reversed.
    2. xxh ;this is the 3rd byte, where xx is the hex number that indicate the size of the rom in multiple of 512 bytes, e.g. for a 512 bytes rom it will be 01h.
    3. jmp ;Commonly this is the 4th through 6th byte, usually this is a near jump instruction that invoke the real initialization code of the rom.
    4. ret (far) ;the last byte in the header, it invokes a far return to pass the program execution back to the main bios (original.tmp), you can invoke it in the initialization part as well, so this is not a must have. See the example code for more info.
  • Its byte checksum is exactly zero, i.e. after all of its bytes summed and goes through modulo 100h operation, it's equal to zero.
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:
  1. Download Fasm for windows (Fasmw) from http://flatassembler.net
  2. Install Fasmw install fasmw into your computer. Actually all that you need is to copy its files.
  3. Run Fasmw and paste the code above into Fasmw code editor. Then save it to a file by clicking File|Save as...
  4. Compile the code by pressing Ctlr+F9. The compilation result will be placed in the same directory as the source code file that saved in the previous step. The name of the file is the same as the source code file with a *.bin extension if you are using Fasmw version 1.67 and it will have a *.com extension if you are using Fasmw 1.52. Both kind of files are a valid ISA expansion ROM. You can insert this file into the target BIOS file.

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

Comments