# flash.S86 - FlashCard flash EPROM programmer # # Copyright (C) 1997-2003 Gero Kuhlmann # # This program is free software; you can redistribute it and/or modify # it under the terms of the GNU General Public License as published by # the Free Software Foundation; either version 2 of the License, or # any later version. # # This program is distributed in the hope that it will be useful, # but WITHOUT ANY WARRANTY; without even the implied warranty of # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the # GNU General Public License for more details. # # You should have received a copy of the GNU General Public License # along with this program; if not, write to the Free Software # Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. # # $Id: flash.S86,v 1.1 2003/03/09 00:43:08 gkminix Exp $ # #==================================================================== # #include "common.i86" #include "flash.i86" .file "flash.S86" .line 30 # #==================================================================== # # Definitions local to this module # ASK_TIMEOUT .equ 20 # number of seconds to wait for key ASK_KEY .equ H'1B # ASCII code for escape key .line 42 # #==================================================================== # # Define some external routines from the i386 library # \(.text) .extern nbentry # routines in nbentry.S86 .extern nbexit .extern doerr .extern ldrlen # routines in ldrec.S86 .extern ldradr .extern ldrfnd .extern btptag # routines in bootp.S86 .extern prnstr # routines in scnio.S86 .extern prcrlf .extern prndwd .extern prnwrd .extern prnbyt .extern prndec .extern keywait # routines in kbdio.S86 .extern keyget .extern getmem # routines in misc.S86 .extern movmem .extern play # #==================================================================== # # Now start the game # .global _start _start: jmp start1 .word __text_end # total size of text segment .word __bss_end + MIN_STK_SIZE # specify total size of data # and BSS segments .word istruct_list # offset to interface list # Call the i386 library startup routine. It will automatically care # for all arguments given by the bootrom, save all registers and setup # the load record and BOOTP information. start1: mov ax,offset sigmsg mov dx,offset vmagic call nbentry # setup everything mov si,offset start_snd call play # Next get the address of the boot rom image, and determine the size of # the bootrom loader. mov si,offset recerr mov al,VENDOR_ROMIMAGE call ldrfnd # find load record for rom image jc jmperr call ldradr # get address of rom image jc jmperr mov [imgaddr],eax mov bx,ax # convert linear image address into and bx,0x000F # seg:ofs pointer, it should start at jnz jmperr # a paragraph boundary shr eax,4 mov word ptr [imgptr + 0],bx mov word ptr [imgptr + 2],ax mov eax,fs:BOOT_LD_ILENGTH[di] test eax,0xFFFE01FF # maximum for ROM image size is 128 kB jnz jmperr # it also has to be a multiple of 512 mov [imgsize],eax # bytes call getmem # get amount of lower memory shl eax,10 mov edx,fs:BOOT_LD_MLENGTH[di] mov ebx,edx add ebx,[imgaddr] cmp ebx,eax # image should end within lower memory jae jmperr sub edx,[imgsize] jc jmperr # the memory size of the rom image cmp edx,WINMAXSIZE # has to be large enough to optionally jb jmperr # include a page switching rom window mov bl,fs:BOOT_LD_LENGTH[di] test bl,0xF0 jz jmperr and bx,0x000F # get offset of vendor infor- shl bx,2 # mation within load record mov edx,fs:[bx + di] # get bootrom loader size test edx,0xFFFF0000 jnz jmperr mov [ldsize],edx # Try to find a supported flash EPROM interface. Scan through the list # of possible interfaces to find an installed one. mov si,offset istruct_list start2: cld lodsw # get pointer to next list entry or ax,ax jnz start3 # check if at end of list mov si,offset eprerr jmp jmperr start3: push si push ax mov di,ax # call interface detection routine lfs si,[imgptr] call [di + ISTRUCT_DETECT] pop bx pop si jc start2 mov [curif],bx # save interface definition pointer mov [curchip],dx # save Flash EPROM definition pointer mov [romaddr],eax # save return values mov [romsize],ecx mov [romofs],edi # Let the user know what we found mov si,offset fndmsg call prnstr mov si,[curchip] mov si,[si + FSTRUCT_NAME] call prnstr mov edx,[romaddr] or edx,edx jz start4 mov si,offset adrmsg call prnstr mov eax,edx call prndwd start4: mov si,offset ifmsg call prnstr mov bx,[curif] mov si,[bx + ISTRUCT_ID] call prnstr call prcrlf # If the Flash EPROM is not mapped into the address space entirely, we # have to install a special Flash EPROM loader into the bootrom binary. # For this, we copy the compressed bootrom image to the beginning of the # second Flash EPROM page, and then copy the Flash EPROM loader at the # end of the bootrom image loader. mov bx,[bx + ISTRUCT_LOADER] or bx,bx # check if we need to install a Flash jz start5 # EPROM loader at all mov edx,[bx + LSTRUCT_WINSIZE] cmp edx,[imgsize] # check if the full bootrom image can jae start5 # be mapped into the address space mov si,offset winerr test edx,0x000001FF # the window size has to be a multiple jnz jmperr # of 512 bytes, and a maximum of 64kB cmp edx,WINMAXSIZE ja jmperr mov ax,[bx + LSTRUCT_ENDOFS] sub ax,[bx + LSTRUCT_INIOFS] and eax,0x0000FFFF # check if boot image loader fits into add eax,[ldsize] # Flash EPROM window add eax,0x000F # add some safety margin cmp eax,edx ja jmperr call instload # install Flash EPROM image loader mov si,offset ilderr jc jmperr # Setup image for programming start5: call imgprep # prepare ROM image for programming mov si,offset sizerr mov eax,[imgsize] add eax,[romofs] cmp eax,[romsize] # check if image fits into Flash EPROM ja jmperr # Give the user a final chance to abort all of this mov si,offset wrnmsg call prnstr # print a warning message mov cx,ASK_TIMEOUT start6: mov si,offset k1msg call prnstr mov ax,cx # print number of seconds left call prndec mov si,offset k2msg call prnstr mov ax,1 call keywait # wait for one second jnc start7 mov al,0x0D call prnchr # return cursor to beginning of line loop start6 # continue next round jmp start8 start7: cmp al,ASK_KEY # check if escape pressed jne start8 call prcrlf mov si,offset abmsg # print abort message jmp doerr start8: call prcrlf call prcrlf mov si,offset prog_snd call play # Initialize Flash EPROM access mov bx,[curif] call [bx + ISTRUCT_INIT] # call interface initialization # Erase the flash EPROM mov si,offset eramsg call prnstr mov edi,[romofs] mov ecx,[imgsize] call [bx + ISTRUCT_ERASE] # erase the chip mov si,offset eraerr jc start9 # Program the flash EPROM mov si,offset prgmsg call prnstr # print programming message lfs si,[imgptr] mov ecx,[imgsize] mov edi,[romofs] call [bx + ISTRUCT_PROG] # program the Flash EPROM mov dx,si mov si,prgerr jc start9 # Verify that the written image has really been programmed into the # Flash EPROM mov si,offset vfymsg call prnstr # print verifying message mov si,dx call [bx + ISTRUCT_VERIFY] # verify the image mov si,vfyerr # in case of error fall through with # carry flag # Terminate the programming and reboot the system start9: pushf call [bx + ISTRUCT_CLEAN] # call interface cleanup popf jc jmperr mov si,offset donmsg call prnstr mov si,offset end_snd call play mov si,offset endmsg call prnstr # print end message call keyget # wait for a key press xor ax,ax mov ds,ax mov word ptr [0x0472],0x1234 ljmp 0xFFFF,0 # reboot the system # #==================================================================== # Return to the bootrom in case of error # Input: SI - Pointer to error message # Output: routine does not return # Changed registers: all # jmperr: push si mov si,offset error_snd call play # play error sound pop si jmp doerr # #==================================================================== # Install Flash EPROM loader into bootrom image # Input: BX - Pointer to loader definition structure # Output: Carry flag set if error # Changed registers: EAX, EBX, ECX, EDX, ESI, EDI # instload: push es mov di,bx les si,[imgptr] # get size of compressed image mov ecx,es:[si + ROMCOMPSIZE] mov eax,[imgaddr] # move the compressed bootrom image mov ebx,eax # to the beginning of the first Flash add eax,[ldsize] # EPROM page add ebx,[di + LSTRUCT_WINSIZE] add ebx,WINID_LEN # leave some bytes for the signature call movmem jc instl9 add ecx,ebx sub ecx,[imgaddr] # compute new bootrom image size mov [imgsize],ecx mov bx,di cld mov edi,[imgaddr] add edi,[bx + LSTRUCT_WINSIZE] mov eax,edi and di,0x000F # move the window magic ID at the shr eax,4 # beginning of the compressed bootrom mov es,ax # image mov si,offset lmagic mov cx,WINID_LEN rep movsb mov edi,[imgaddr] add edi,[ldsize] mov eax,edi and di,0x000F # move the Flash EPROM loader into shr eax,4 # the bootrom image mov es,ax mov si,[bx + LSTRUCT_INIOFS] mov cx,[bx + LSTRUCT_ENDOFS] sub cx,si push ds mov ax,cs mov ds,ax mov ax,cx add ax,0x000F # adjust size to paragraph boundary and ax,0xFFF0 mov [si + 2],ax # save size of Flash EPROM loader mov ax,cx # remember actual size for later rep movsb pop ds and eax,0x0000FFFF add eax,[ldsize] mov ecx,[bx + LSTRUCT_WINSIZE] sub ecx,eax # fill the remaining window with 0xFF mov dx,es mov ax,di shr ax,4 add dx,ax mov es,dx and di,0x000F mov al,0xFF rep stosb # 0x8080 should always be at end of rom mov word ptr es:[di - 2],0x8080 mov byte ptr es:[di - 3],0 les di,[imgptr] # set Flash EPROM loader flag in rom or byte ptr es:[di + ROMFLAGS],FLAG_FLOADER mov ecx,[bx + LSTRUCT_WINSIZE] mov eax,ecx shr eax,9 # compute new ROM size - even though the and ax,0x00FF # size of the new ROM might be smaller mov es:[di + ROMSIZEVAL],al # than the window size we need the ROM mov bx,es:[di + ROMPCIHDR] # to be as large as the full window so or bx,bx # that it gets mapped into the processor jz instl1 # address space entirely for page switch mov es:[bx + di + PCIROMLEN],ax instl1: mov bx,[curif] or word ptr [bx + ISTRUCT_FLAGS],IFLAGS_NEEDMEM clc instl9: pop es ret # #==================================================================== # # Prepare the ROM image for programming. This sets the DDIM flag in the # PnP header and re-computes all checksums. # Input: none # Output: none # Changed registers: EAX, EBX, ECX, EDX, ESI, EDI # imgprep: push es mov bx,[curif] test word ptr [bx + ISTRUCT_FLAGS],IFLAGS_NEEDMEM jz imgp2 # Needing direct memory access can be for two reasons: # 1.) The Flash EPROM programmer needs to directly write into the ROM # for programming. In this case, the IFLAGS_NEEDMEM flag is set by # default the interface definition structure. # 2.) The bootrom needs a Flash loader with page switching. In this case # the IFLAGS_NEEDMEM flag has been set by the Flash loader installer # routine above. # In both cases, we have to turn off DDIM support with the bootrom. DDIM # allows the BIOS to copy the bootrom into RAM, which gets switched to read- # only lateron. This prevents any direct write access to the physical Flash # EPROM chip. For the second case, DDIM is not necessary anyway since the # bootrom footprint is reduced by the Flash loader already. However, turning # off DDIM is unfortunate for case number 1. Direct memory access is only # required for Flash EPROM programming, but not during normal operation. # However, once DDIM is activated, everytime the system gets booted up, # direct memory access will be prohibited by the BIOS, and there is no way # to regain access to re-program the Flash EPROM. There are three solutions # to this problem: # # 1.) Use a network card which provides I/O access to the Flash EPROM (like # the 3Com 3C90xB/C series or the AMD PCNet series). For those cards we # dont need direct memory write access and can therefore activate DDIM. # The IFLAGS_NEEDMEM flag is not set in the interface definition struc- # ture. # 2.) Use some way of modifying the motherboard chipset so that it enables # write access to the Flash EPROM temporarily. This way, we can use DDIM # during normal boot, but can turn it off when required for programming # the Flash EPROM. Also, it is necessary to turn off caching of the Flash # EPROM region, which is much more complicated as it means to either to # modify the chipset, or reprogram the MTRR registers of the CPU if they # exist. Due to the variety of different chipsets and CPU configurations, # this solution is not yet implemented. But it is the better way instead # of turning off DDIM permanently, because DDIM is quite useful. # 3.) For network cards needing direct write access to the Flash EPROM to get # re-programmed, turn off DDIM permanently. This is the worst of all solu- # tions, but the easiest one to implement (besides using a different net- # work card). # # In order to turn off DDIM support, we have to reset a bit in the PnP header of # the bootrom and recalculate the PnP header checksum. les di,[imgptr] mov bx,es:[di + ROMPNPHDR] or bx,bx # check if we do have a PnP header jz imgp2 # no PnP header means no DDIM support add di,bx mov al,FLAG_DDIM # we do have to negate the flag like not al # this because GAS doesnt understand ~ and byte ptr es:[di + PNPFLAGS],al movzx cx,byte ptr es:[di + PNPLENGTH] shl cx,4 xor bx,bx xor al,al imgp1: add al,es:[bx + di] # compute checksum of PnP header inc bx loop imgp1 sub es:[di + PNPCHECKSUM],al # Up till now, the bootrom image might have been changed significantly. Before # we can program it into a Flash EPROM we have to set the checksum correctly. # If we have installed a Flash EPROM loader, only the first page of the bootrom # image is visible to the client at bootup, so we should only checksum that # first page. imgp2: les di,[imgptr] mov cl,es:[di + ROMSIZEVAL] and ecx,0x000000FF # compute size of physically visible shl ecx,9 # rom image (can be max. 128kB) xor al,al imgp3: add al,es:[di] # compute bootrom checksum inc di test di,0x8000 jz imgp4 # avoid any DI overflow and di,0x7FFF mov bx,es add bx,0x0800 mov es,bx imgp4: cmp ecx,4 jne imgp5 # the third byte from the end of the mov dx,es # bootrom image receives the checksum mov si,di # we just remember its pointer imgp5: data32 loop imgp3 or al,al # if checksum is zero already nothing jz imgp9 # needs to be done mov es,dx sub es:[si],al # make total checksum zero imgp9: pop es ret # #==================================================================== # # String and constants definitions # \(.data) # Startup signature sigmsg: .byte 0x0D, 0x0A .ascii "Netboot Flash EPROM programmer " .ascii "\&VERSION" .byte 0x0D, 0x0A .ascii "\©RIGHT" .byte 0x0D, 0x0A .byte 0x0D, 0x0A .byte 0 # Boot image header vendor magic ID vmagic: .asciz "\&VENDOR_MAGIC" # Magic ID at the beginning of the compressed bootrom image lmagic: .ascii "\&WINID_MAGIC" # Error messages recerr: .asciz "Error in load record data" sizerr: .asciz "Netboot image too large for Flash EPROM" winerr: .asciz "Netboot image loader too large" ilderr: .asciz "Error while installing Flash EPROM loader" eprerr: .ascii "Could not find a Flash EPROM. Make sure that you really have a supported" .byte 0x0D, 0x0A .ascii "network card installed, and that it contains a supported Flash EPROM." .byte 0x0D, 0x0A .ascii "Also make sure that shadow RAM and caching are disabled for the Flash" .byte 0x0D, 0x0A .ascii "EPROM using the BIOS setup, and that the Flash EPROM is activated using" .byte 0x0D, 0x0A .asciz "the network card setup program." eraerr: .byte 0x0D, 0x0A .asciz "Unable to erase Flash EPROM" prgerr: .byte 0x0D, 0x0A .asciz "Timeout while programming Flash EPROM" vfyerr: .byte 0x0D, 0x0A .asciz "Verify failed" # Messages to be printed for user information fndmsg: .asciz "Found " adrmsg: .asciz " at address " ifmsg: .asciz " on " wrnmsg: .byte 0x0D, 0x0A .ascii "We are now going to program the netboot bootrom image into the Flash" .byte 0x0D, 0x0A .ascii "EPROM. This will erase all current contents. This is your last chance" .byte 0x0D, 0x0A .ascii "to abort this process. Once the programming process has been started," .byte 0x0D, 0x0A .ascii "you should not reboot the system or turn off the power." .byte 0x0D, 0x0A, 0x00 k1msg: .asciz "You have " k2msg: .asciz " seconds to press Escape to abort! " abmsg: .byte 0x0D, 0x0A .asciz "Flash EPROM programming aborted by user request" eramsg: .asciz "Erasing... " prgmsg: .asciz "Programming... " vfymsg: .asciz "Verifying... " donmsg: .asciz "done" endmsg .byte 0x0D, 0x0A .asciz "Press any key to reboot and activate the new rom image..." # The following sequence of sounds tells the user about what is going on. start_snd: .word 5, 6541 # sound when program starts .word 3, 0 .word 5, 6541 .word 3, 0 .word 10, 11000 .word 0 error_snd: .word 15, 3270 # sound in case of error .word 6, 0 .word 15, 3270 .word 6, 0 .word 15, 3270 .word 0 prog_snd: .word 5, 11000 # start of programming .word 3, 0 .word 5, 11000 .word 3, 0 .word 10, 6541 .word 0 end_snd: .word 3, 6541 # finished programming .word 1, 0 .word 3, 7342 .word 1, 0 .word 3, 8241 .word 1, 0 .word 3, 8731 .word 1, 0 .word 3, 9800 .word 1, 0 .word 3, 11000 .word 1, 0 .word 3, 12347 .word 1, 0 .word 3, 13081 .word 0 # List of pointers to flash EPROM interface structures. A flash EPROM # interface is the FlashCard for example, but can also be a NIC with # an integrated flash EPROM. The structures pointed to by the following # list point to various routines to access the flash EPROM built onto # any of these interfaces. Note that the FlashCard interface pointer has # to come last. .extern istruct_3c905xbc .extern istruct_rtl8139 .extern istruct_i82595 .extern istruct_fc istruct_list: .word istruct_3c90xbc # 3C905xB/C interface definition .word istruct_rtl8139 # RTL8139 interface definition .word istruct_i82595 # EtherExpress interface definition .word istruct_fc # FlashCard interface definition .word 0 # end of list # #==================================================================== # # Variable definitions # \(.bss) .extern bootpbuf # space to hold BOOTP record .extern header # pointer to load record header .lcomm imgptr,4 # pointer to beginning of rom image .lcomm imgaddr,4 # adress of rom image .lcomm imgsize,4 # size of rom image .lcomm ldsize,4 # size of rom image loader .lcomm curif,2 # current interface definition .lcomm curchip,2 # current Flash EPROM definition .lcomm romaddr,4 # linear address of Flash EPROM .lcomm romsize,4 # size of Flash EPROM .lcomm romofs,4 # start offset into Flash EPROM # #==================================================================== # .end