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:
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.
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.
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.
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:
Download Fasm for windows (Fasmw) from http://flatassembler.net
Install Fasmw install fasmw into your computer. Actually all that you need is to copy its files.
Run Fasmw and paste the code above into Fasmw code editor. Then save it to a file by clicking File|Save as...
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