!************************************************************************** !* !* Boot-ROM-Code to load an operating system across a TCP/IP network. !* !* Module: rom.S !* Purpose: Decompress the rom image and copy everything into place !* Entries: Called by BIOS at offset $0003, getbyte, putbyte !* !************************************************************************** !* !* Copyright (C) 1995-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: rom.S,v 1.7 2003/03/09 00:43:06 gkminix Exp $ !* ! !************************************************************************** ! ! Load assembler macros. ! #include #include #include #include #include #include #include "ldram.inc" #if !defined(NODDIM) && !defined(IS386) #define NODDIM 1 /* DDIM is only available with 386+ */ #endif ! !************************************************************************** ! ! Equates for this module: ! COMPADR equ FLOPMEM ! segment for compressed image LOADADR equ FLOPEND ! segment where to load the boot code LOADEND equ OSENDMEM ! end segment of this boot code BOOTLOAD equ $7C00 ! boot load area in segment 0 STACKSIZE equ 4096 ! size of bootrom kernel stack BIOS_SEG equ $F000 ! BIOS code segment BIOS_BOOTSTRAP equ $E6F2 ! BIOS bootstrap entry point FLOADSIZE equ $0002 ! Offset to size of flash loader if (LOADEND - LOADADR) < $1000 .err "invalid boot loader size" endif ! !************************************************************************** ! ! Definitions for accessing the high memory area using the BIOS ! GDTSRCLEN equ $0010 ! source segment length GDTSRCADR equ $0012 ! source segment linear address GDTSRCRIGHT equ $0015 ! source segment access rights GDTSRCHIADR equ $0017 ! source segment linear addr high byte GDTDESTLEN equ $0018 ! destination segment length GDTDESTADR equ $001A ! destination segment linear address GDTDESTRIGHT equ $001D ! destination segment access rights GDTDESTHIADR equ $001F ! dest segment linear addr high byte GDTSTRUCTLEN equ $0030 ! length of GDT BIOS structure GDTACCESS equ $93 ! segment access rights ! !************************************************************************** ! ! Definitions used to setup the PnP option ROM expansion header as ! defined per PnP BIOS Specification, Version 1.0A. ! ! Vendor and Device ID: This is only required for PnP compatible operating ! systems to load the necessary drivers. For the bootrom, we use 0 for ! both values. If it really is required, makerom can add it lateron. ! PNP_VENDID equ 0 ! vendor ID not used PNP_DEVID equ 0 ! device ID not used ! Device Indicators: ! Bit 7 = 1 --> ROM supports DDIM (if selected) ! Bit 6 = 0 --> ROM may not be shadowed in RAM ! Bit 5 = 1 --> ROM is read cacheable ! Bit 4 = 1 --> ROM is only required if this device is selected for boot ! Bit 3 = 0 --> reserved ! Bit 2 = 1 --> this is an IPL device ! Bit 1 = 0 --> not an input device ! Bit 0 = 0 --> not an output device ! #ifdef NODDIM PNP_DEVIND equ %00110100 ! device indicators #else PNP_DEVIND equ %10110100 ! device indicators #endif ! Device Type Codes: These are used by the BIOS to prioritize the boot ! devices. ! PNP_BASETYPE equ 2 ! network interface controller PNP_SUBTYPE equ 0 ! ethernet PNP_IFTYPE equ 0 ! unused ! !************************************************************************** ! ! Definitions used to setup the PCI data structure. ! ! Device Indicator: Set to $80 if this is the last PCI structure within ! the physical ROM. ! PCI_DEVIND equ $80 ! device indicator ! Code type: We are only able to run on x86 Intel architecture. ! PCI_CODETYPE equ 0 ! Device Type Codes: These are the same as used by the PnP expansion ! header. ! PCI_BASETYPE equ PNP_BASETYPE PCI_SUBTYPE equ PNP_SUBTYPE PCI_IFTYPE equ PNP_IFTYPE ! Revision level of code/data: This is just the major/minor version ! number of the netboot bootrom. ! PCI_REVISION equ ((MAJOR_VER * 100) + MINOR_VER) ! !************************************************************************** ! ! The physical rom starts here. At the beginning there is some ! miscellaneous data. ! .text entry start ! define entry point for rom code extrn _end_of_text extrn inflate public getbyte public putbyte .word $AA55 ! rom signature romlen: .byte 0 ! rom size (filled in by checksum prg) start: jmp near romstart ! the BIOS calls us here ! The following variables contain the program version number and some ! other useful values. Dont change the order of any of these values as ! the binary patch program depends on them. Especially, the pointer to ! the PnP structure has to be at offset $001A as per the BBS spec. ! Note on the UNDI rom structure pointer: this structure is only required ! in the header for using the UNDI driver from the BIOS (called early ! UNDI API usage in Intels PXE specification). Since this is optional ! according to the PXE specification, and we dont support it, there is ! no need to have a UNDI rom structure here. We are just running as a ! normal IPL, so the structure offset is zero here. .align DATAOFS .byte MAJOR_VER ! major version number .byte MINOR_VER ! minor version number .long 0 ! serial number botvec: .word 0 ! offset to bootrom interrupt vector romsiz: .long 0 ! compressed rom size totsiz: .word 0 ! total no. of paragraphs required .word 0 ! pointer to UNDI rom structure .word pcihdr ! pointer to PCI data structure .word pnphdr ! pointer to PnP expansion header .asciz "netboot" ! romcheck searches for this string botflg: .byte 0 ! boot flags flash: .word 0 ! offset to flash EPROM loader mannam: .asciz MANUFACTURER ! manufacturer name prdnam: .asciz PRODUCT ! product name copyrt: .ascii ", " .asciz COPYRIGHT ! copyright message crlf: .byte $0D,$0A,0 .align 2 ! Define the PCI data structure to make the netboot bootrom compatible ! with the PCI local bus specification 2.1. This header is actually only ! needed when the bootrom is physically located on the PCI network card. ! Note that the order of the class codes has to be reversed compared to ! what the spec says. pcihdr: .ascii "PCIR" ! signature for PCI data structure pcivid: .word 0 ! vendor ID (filled in later) pcidid: .word 0 ! device ID (filled in later) .word 0 ! ptr to vital product data (unused) .word $0018 ! length of PCI data structure .byte $01 ! structure revision 0.1 .byte PCI_BASETYPE ! base device type code .byte PCI_SUBTYPE ! device subtype code .byte PCI_IFTYPE ! device programming interface pcilen: .word 0 ! rom image length (filled in later) .word PCI_REVISION ! version number of code/data .byte PCI_CODETYPE ! code type .byte PCI_DEVIND ! device indicator .word 0 ! reserved ! Define the expansion header to make the netboot bootrom Plug and Play ! compatible according to the Plug and Play BIOS Specification 1.0A. This ! will allow the system BIOS to select whether to boot from the network or ! not, and we dont have to redirect interrupt 18h or 19h by ourselves. ! Note that the order of the class codes has to be reversed compared to ! what the spec says. pnphdr: .ascii "$PnP" ! signature for PnP expansion header .byte $01 ! structure revision 0.1 .byte 2 ! length of header in 16-byte-blocks .word 0 ! pointer to next header (0 = none) .byte 0 ! reserved .byte 0 ! checksum (filled in later) .word PNP_VENDID ! compressed manufacturer code .word PNP_DEVID ! product number and revision, device ID .word mannam ! ptr to manufacturer string .word prdnam ! ptr to product name .byte PNP_BASETYPE ! base device type code .byte PNP_SUBTYPE ! device subtype code .byte PNP_IFTYPE ! device programming interface .byte PNP_DEVIND ! device indicators .word 0 ! boot connection vector (unused) .word 0 ! disconnect vector (unused) .word dorom ! bootstrap entry vector .word 0 ! reserved .word 0 ! static resource info vector (unused) ! These variables are writable when we are using DDIM. They hold the ! values required to resize the ROM and to handle interrupt $15. #ifndef NODDIM oldi15: .long $DEADBEEF ! old interrupt $15 newsiz: .word 0 ! new extended memory size newcks: .byte 0 ! new ROM checksum #endif ! !************************************************************************** ! ! Now start with the actual boot rom code. The startup code just installs ! a new bootrom interrupt vector, and then returns to the BIOS. This is ! necessary so that the BIOS gets a chance to also initialize other installed ! roms. The bootrom vector is used for the ROM BASIC in original IBM PCs ! and gets called by the BIOS whenever there is no bootable disk in any ! of the drives. ! In case we have a PnP capable BIOS, we dont have to redirect the bootrom ! vector here, as the BIOS will call us lateron with a far call, not an ! interrupt. If the BIOS is PnP compatible, this routine gets called with ! the PCI PFA in AX or the PnP CSN in BX. Save these values for later use. ! Also, if we can write into this code we are running under the Device ! Driver Initialization Model (DDIM). In that case, we move the compressed ! bootrom image into extended memory and can therefore reduce the bootrom ! image accordingly. This way, more space is provided for other option ROMs ! in the system. See the BIOS boot specification and PXE specification for ! a further explanation on DDIM. ! romstart: push ds push es pushall mov dx,ax ! save AX for later call checkram ! check if we have been initialized #ifdef IS386 jnc near romstE #else jc romst0 jmp near romstE #endif ! Setup our private data area within the interrupt table. The checksum ! gets written at the end of this routine. romst0: mov si,#RAM_START mov cx,#RAM_SIZE / 2 romst1: mov word ptr [si],#0 ! fill RAM area with zeroes add si,#2 loop romst1 mov word ptr [RAM_START + RAM_ID],#RAM_SIGNATURE ! Check for the PnP installation structure. This has also to be present with ! the 16-bit code because that code can also be used on a 386+. With this ! we just check if we have been called according to the BBS. call checkpnp jc romst2 mov word ptr [RAM_START + RAM_PNPSTRUCT + 0],di mov word ptr [RAM_START + RAM_PNPSTRUCT + 2],es mov ch,#RAMFL_BBS jmp romst3 ! We dont have a PnP BIOS, so we have to redirect the necessary interrupts. Also ! there is no PCI PFA or PnP CSN available, so set them both to $FFFF. romst2: seg cs mov si,[botvec] mov ax,ds mov es,ax mov di,#RAM_START + RAM_OLDINT mov bx,#int18 call intset mov dx,#$FFFF mov bx,dx xor ch,ch ! no flags defined romst3: mov word ptr [RAM_START + RAM_PCIPFA],dx mov word ptr [RAM_START + RAM_PNPCSN],bx mov byte ptr [RAM_START + RAM_FLAGS],ch ! We can now handle DDIM support. For this, we copy the compressed bootrom ! image into extended memory. If we can use PMM, we can simply copy the ! compressed bootrom image into the space provided by the PMM allocation ! routine. Only if no PMM BIOS is available, use interrupt $15 for copying. ! In that case, we also have to redirect interrupt $15 so that it will ! return the size of extended memory less than what we used for the bootrom ! copy. When using DDIM, we are actually running in RAM, so we can write ! into variables stored in the code segment. After redirecting the interrupt, ! set the new ROM size. ! The PMM specification states that when the Boot Entry Vector is called ! lateron, no PMM services are available anymore. However, the extended ! memory contents should be left untouched by PMM so we can still use it, ! even though its not quite clean, but unfortunately, the PXE specification ! requires the use of DDIM and PMM if available. #ifndef NODDIM mov cl,#$FF seg cs xchg cl,[romlen] ! set rom length to $FF seg cs cmp cl,[romlen] ! check if we could write into je near romst9 ! the ROM area seg cs mov [romlen],cl ! restore rom length value or byte ptr [RAM_START + RAM_FLAGS],#RAMFL_DDIM seg cs mov ebx,dword ptr [romsiz] ! get size of compressed rom image or ebx,ebx ! check if we have a compressed image jz near romst8 ! at all add ebx,#$03FF ! and convert it into kB shr ebx,#10 mov di,cs ! compute linear address of compressed shl edi,#4 ! bootrom image add edi,#_end_of_text + $000F and edi,#$000FFFF0 call findpmm ! find PMM entry point or eax,eax jz romst5 ! check if PMM entry point found push word #PMM_FLAGS ! push flags push dword #PMM_RAMSIG ! push memory block signature shl ebx,#6 ! convert size into paragraphs push ebx ! push size of memory block push word #PMM_ALLOCATE ! push PMM function code push cs push #romst4 ! push return address push eax ! push PMM entry point retf ! call PMM romst4: add sp,#12 ! cleanup stack mov si,dx shl esi,#16 mov si,ax ! check if we got a valid memory block or esi,esi ! in case the memory allocation routine jz romst8 ! returned with an error we cant divert cmp esi,#$FFFFFFFF ! to using int $15 because thats pro- je romst8 ! hibited when PMM is available mov dword ptr [RAM_START + RAM_EXTADR],esi seg cs mov ebx,[romsiz] ! get size of compressed rom image add ebx,#$0003 rcr ebx,#1 ! convert it into number of double- shr ebx,#1 ! words romstA: mov eax,[edi + ebx * 4] ! we have to do the copying routine mov [esi + ebx * 4],eax ! by hand (instead of using movsd) dec ebx ! because movsd just uses si/di, not jnz romstA ! esi/edi. or byte ptr [RAM_START + RAM_FLAGS],#RAMFL_PMM jmp romst6 romst5: clc mov ah,#$88 int INT_MISC ! get size of extended memory from jc romst8 ! the BIOS sub ax,bx ! compute new extended memory size jnc romst7 romst8: and byte ptr [RAM_START + RAM_FLAGS],#~RAMFL_DDIM jmp romst9 romst7: push ax xor ebx,ebx mov bx,ax ! compute linear address of new bootrom shl ebx,#10 ! image copy add ebx,#$00100000 mov dword ptr [RAM_START + RAM_EXTADR],ebx mov edx,edi seg cs mov ecx,[romsiz] ! get size of compressed rom image call copy_highmem ! copy image into extended memory pop ax jc romst8 seg cs ! save new extended memory size mov [newsiz],ax mov ax,cs mov es,ax mov di,#oldi15 ! set new interrupt mov si,#INT_MISC * 4 mov bx,#int15 call intset romst6: push ds mov ax,cs mov ds,ax xor si,si mov cx,#_end_of_text + $07FF and cx,#$F800 shr cx,#9 ! compute new bootrom size mov [romlen],cl mov [pcilen],cx shl cx,#9 call dochksum ! compute checksum for the bootrom sub [newcks],ah pop ds #endif ! Finally compute the checksum of the RAM data area and return to the ! caller. romst9: mov si,#RAM_START mov cx,#RAM_SIZE call dochksum sub byte ptr [RAM_START + RAM_CHKSUM],ah romstE: popall pop es pop ds mov ax,#$0020 ! identify this option ROM as an IPL retf ! device #ifndef NODDIM ! !************************************************************************** ! ! New $15 interrupt. It has been redirected to report the new reduced ! size of extended memory when using DDIM, but not PMM. ! int15: pushf cmp ah,#$88 ! check if we have to report the je int151 ! amount of free extended memory popf seg cs jmp far ptr [oldi15] ! jump to old interrupt handler int151: popf seg cs mov ax,[newsiz] ! return new memory size push bp mov bp,sp ! clear carry flag on the stack and word ptr [bp + 6],#$FFFE pop bp iret #endif ! !************************************************************************** ! ! Handle a redirected bootstrap interrupt vector. ! int18: sti seg cs cmp word ptr [botvec],#INT_BOOTSTRAP * 4 je int19 push cs call dorom ! start IPL process iret ! We have been started as the bootstrap vector. Therefore we first check ! to see if we have a floppy in drive A, and boot from it if we have. int19: xor dx,dx xor ax,ax int INT_DISK ! reset the disk system mov cx,#5 ! loop counter mov bx,#BOOTLOAD ! put loading address into ES:BX mov es,dx floop: push cx mov ax,#$0201 mov cx,#$0001 ! try to load first sector of first int INT_DISK ! track on floppy drive A pop cx jnc doflop ! if error, we dont have a floppy loop floop ! try up to 5 times jmp int191 doflop: cld mov di,bx seg es mov al,byte ptr [di] ! check that the boot sector doesnt mov cx,#$0080 ! contain only one value repe scasb jcxz int191 ! boot sector is empty so try network xor di,di jmpi BOOTLOAD,0 ! call floppy boot sector int191: push cs call dorom ! start IPL process #ifdef IS386 xor eax,eax ! restore bootstrap interrupt in case mov ds,ax ! this hasnt been done already mov ax,cs shl eax,#16 mov ax,#int18 cmp dword ptr [INT_BOOTSTRAP],eax jne int192 mov dword ptr [INT_BOOTSTRAP],#BIOS_SEG << 16 + BIOS_BOOTSTRAP #else xor ax,ax ! restore bootstrap interrupt in case mov ds,ax ! this hasnt been done already mov ax,cs cmp word ptr [INT_BOOTSTRAP * 4 + 0],#int18 jne int192 cmp word ptr [INT_BOOTSTRAP * 4 + 2],ax jne int192 cli mov word ptr [INT_BOOTSTRAP * 4 + 0],#BIOS_BOOTSTRAP mov word ptr [INT_BOOTSTRAP * 4 + 2],#BIOS_SEG sti #endif int192: int INT_BOOTSTRAP jmp int192 ! !************************************************************************** ! ! Start the bootrom. This is called UNDI IPL in the PXE specification. ! While the code above is just for the BIOS interface, now decompress ! the bootrom and start the game. ! dorom: cld pushall push ds push es ! Print the startup message and check that our RAM area hasnt been touched ! by anyone. mov si,#prdnam call prnstr ! print bootrom startup message mov si,#copyrt call prnstr mov si,#crlf call prnstr call checkram #ifdef IS386 mov si,#datmsg ! print error message and terminate jc near error #else jnc dorom1 mov si,#datmsg ! print error message and terminate jmp near error #endif ! Now restore all interrupts which have been redirected previously. The ! data area, which resides in the interrupt vector table, doesnt have to ! be restored. If we cant restore the $15 interrupt vector its not such a ! problem because this code will remain in ROM and the vector will therefore ! remain valid. But we cant reclaim the used memory in that case. ! Remember to mark our RAM area invalid, so that we can start the bootrom ! again if this boot failed. dorom1: mov word ptr [RAM_START + RAM_ID],#0 seg cs mov si,[botvec] mov dx,cs #ifdef IS386 mov ax,dx shl eax,#16 mov ax,#int18 ! check that the interrupt cmp dword ptr [si],eax ! has been changed jne dorom2 mov ecx,dword ptr [RAM_START + RAM_OLDINT] mov dword ptr [si],ecx ! restore it dorom2: # ifndef NODDIM mov ax,#int15 ! check that the interrupt cmp dword ptr [INT_MISC * 4],eax ! vector has been changed jne dorom3 seg cs mov ecx,[oldi15] mov dword ptr [INT_MISC * 4],ecx ! restore it # endif #else /* IS386 */ cmp word ptr [si + 2],dx ! check that the interrupt jne dorom3 ! vector has been changed cmp word ptr [si + 0],#int18 ! since we have started jne dorom3 cli mov ax,word ptr [RAM_START + RAM_OLDINT + 0] mov word ptr [si + 0],ax mov ax,word ptr [RAM_START + RAM_OLDINT + 2] mov word ptr [si + 2],ax sti #endif ! Check if the user really wants to start the netboot process. If not we ! can simply return to the BIOS. Note that we should not change DX here. ! Also note that we need to store the timeout value onto the stack since ! we are still running within a ROM (even when using DDIM the BIOS has ! turned us read-only by now). dorom3: seg cs mov al,[botflg] test al,#ROMFLG_ASKNET jz dorom4 #ifndef NOTIMEOUT push bx ! make space on stack for timeout push bx ! value mov bp,sp ! BP points to timeout value and al,#ROMFLG_ASKTIME jz dorom7 ! check if no timeout call settimeout ! set keyboard timeout mov si,#askmsg ! print message call prnstr dorom6: mov ah,#$01 ! check if keystroke available int INT_KBD jnz dorom7 call chktimeout ! check if timeout reached jc dorom6 mov al,#$4E ! assume 'N' if timeout reached jmp dorom8 #else mov si,#askmsg ! print message call prnstr #endif dorom7: xor ah,ah ! get keystroke int INT_KBD dorom8: pop bx ! restore stack pop bx push ax call prnchr ! print entered character mov si,#crlf call prnstr pop ax cmp al,#$59 ! check for 'Y' je dorom4 cmp al,#$79 ! check for 'y' je dorom4 cmp al,#$5A ! check for 'Z' (e.g. 'Y' on German kbd) je dorom4 cmp al,#$7A ! check for 'z' #ifdef IS386 jne near romend ! return to BIOS #else je dorom4 jmp near romend ! return to BIOS #endif ! Copy this code into RAM so that we can use variables with the decompression ! code. Also we set our own new stack and save the ROM address. dorom4: push ds mov bx,#LOADADR mov cx,#_end_of_text + $000F and cx,#$FFF0 #ifdef IS386 shr cx,#2 #else shr cx,#1 #endif mov ds,dx mov es,bx ! move boot loader into lower ram xor si,si xor di,di rep #ifdef IS386 movsd #else movsw #endif pop ds cli seg es mov [oldss],ss ! save old stack pointer seg es mov [oldsp],sp mov ss,bx ! set new stack mov sp,#(LOADEND - LOADADR) * 16 - 2 sti cmp dx,#MEMEND ! check if we are running within a jb dorom5 ! physical ROM seg es mov [romseg],dx ! save physical ROM segment dorom5: jmpi #fload,LOADADR ! jump to new copy ! We are now in RAM. If a Flash EPROM loader is installed, copy it into ! RAM as well. fload: seg cs test byte ptr [botflg],#ROMFLG_FLOADER jz docopy ! check if flash loader installed push ds mov ds,dx mov cx,[si + FLOADSIZE] shr cx,#1 ! copy flash loader from ROM into rep ! RAM at the end of this bootrom movsw ! loader pop ds jmp fndpnp ! skip DDIM checking ! Copy the compressed image from extended memory and save the values necessary ! to access the compressed image. docopy: #ifndef NODDIM test byte ptr [RAM_START + RAM_FLAGS],#RAMFL_DDIM jz docpy1 mov edx,dword ptr [RAM_START + RAM_EXTADR] mov ebx,#COMPADR * 16 seg cs mov ecx,[romsiz] ! copy the compressed bootrom image call copy_highmem ! out of extended memory mov si,#datmsg ! if we have an error, the DDIM was jc near error ! incorrect, therefore: data error xor si,si ! let DX:SI point to new compressed mov dx,#COMPADR ! image copy #endif docpy1: seg cs mov word ptr [inptr+0],si ! set pointer to compressed image seg cs mov word ptr [inptr+2],dx ! Check that we have a PnP BIOS and find the PnP BIOS entry point structure. ! If we have been called according to the BBS, we already know the address ! of the PnP BIOS entry structure. Otherwise, we have to scan the BIOS memory ! area. fndpnp: les di,[RAM_START + RAM_PNPSTRUCT] call checkpnp jnc fndpci mov word ptr [RAM_START + RAM_PNPSTRUCT + 0],#0 mov word ptr [RAM_START + RAM_PNPSTRUCT + 2],#BIOS_SEG fndpn1: les di,[RAM_START + RAM_PNPSTRUCT] call checkpnp jnc fndpci inc word ptr [RAM_START + RAM_PNPSTRUCT + 2] jnz fndpn1 ! If we are running on a PCI device, but havent been called according to ! the BBS, we have to determine the PCI bus/dev/func number (PFA) in order ! to pass valid values to the bootrom. fndpci: seg cs mov ax,[pcivid] ! check if we have a PCI vendor and seg cs ! device ID. then we have a PCI or ax,[pcidid] ! network card. jz fndpc2 mov bx,word ptr [RAM_START + RAM_PCIPFA] cmp bx,#$FFFF je fndpc1 ! check that we dont have a PFA already mov di,#PCI_REG_VENDOR_ID mov ax,#PCI_FUNC_READ_WORD ! read PCI vendor ID int INT_PCI jc fndpc1 ! if error, the known PFA is incorrect seg cs cmp cx,[pcivid] ! if vendor ID is invalid, we dont have jne fndpc1 ! a valid PFA mov di,#PCI_REG_DEVICE_ID mov ax,#PCI_FUNC_READ_WORD ! read PCI device ID int INT_PCI jc fndpc1 ! if error, the known PFA is incorrect seg cs cmp cx,[pcidid] ! if device ID is valid, we finally je chkmem ! have a valid PFA fndpc1: mov ax,#PCI_FUNC_FIND_DEV seg cs mov cx,[pcidid] ! get device ID seg cs mov dx,[pcivid] ! get vendor ID xor si,si ! search for first device int INT_PCI jnc fndpc3 ! found a device mov si,#pcimsg ! print error message if device not jmp decom3 ! found fndpc2: mov bx,#$FFFF fndpc3: mov word ptr [RAM_START + RAM_PCIPFA],bx ! Check that we have enough base memory for the bootrom kernel and the ! network driver. The binary patch program should have calculated the ! required size already and written it into the bootrom header. chkmem: int INT_MEMORY shift (shl,ax,6) ! get number of free paragraphs seg cs sub ax,[totsiz] ! compute base of bootrom memory mov si,#memmsg jc decom4 cmp ax,#OSENDMEM ! check that it doesnt go into our jb decom4 ! own stack segment ! Some network cards offer a flash EPROM for the bootrom. However, various ! cards dont map the full flash EPROM into the processor space, but just a ! small window (8kB or 16kB are common) from the start of the flash EPROM. ! In those cases, the flash programmer can choose to put the compressed ! bootrom image into the non-visible flash EPROM area. It then adds an ! access routine at the end of this bootrom loader and puts its offset ! into the bootrom header. We check this offset, and if set call it with ! BX = PCI PFA and DX = PnP CSN. It returns an offset to the get-byte routine ! in AX. If AX is returned zero, the flash EPROM could not be accessed. ! Otherwise, we can then use the get-byte routine to decompress the bootrom ! image. If the flash EPROM loader vector is zero, there is no flash EPROM ! accessing driver available, and we can simply decompress the bootrom ! image. decomp: mov dx,word ptr [RAM_START + RAM_PNPCSN] push ds push ax mov ax,ss ! set segment registers mov ds,ax mov es,ax mov di,#_end_of_text + $000F and di,#$FFF0 test byte ptr [botflg],#ROMFLG_FLOADER jz decom2 ! check if flash loader installed mov si,#romsiz call di ! call flash init routine or ax,ax ! check for error mov si,#flsmsg ! print error message jz decom3 mov [getfls],ax ! save offset to get-byte routine decom2: call inflate ! decompress the block or al,al ! check for error jz decom5 mov si,#fmtmsg ! print error message decom3: pop ax pop ds decom4: jmp near error decom5: pop dx ! restore base segment pop ds ! Print the bootrom BIOS options mov si,#optmsg ! print options message call prnstr mov cl,byte ptr [RAM_START + RAM_FLAGS] mov ch,cl or ch,ch mov si,#nonmsg ! print 'none' jz popt4 popt1: test ch,#RAMFL_BBS ! check for BBS calling sequence jz popt2 mov si,#bbsmsg ! print BBS message call prnstr and ch,#~RAMFL_BBS jz popt5 mov si,#commsg ! print comma call prnstr popt2: test ch,#RAMFL_DDIM ! check for DDIM option jz popt3 mov si,#dimmsg ! print DDIM message call prnstr and ch,#~RAMFL_DDIM jz popt5 mov si,#commsg ! print comma call prnstr popt3: test ch,#RAMFL_PMM ! check for PMM option jz popt5 mov si,#pmmmsg ! print PMM message popt4: call prnstr popt5: mov si,#crlf call prnstr mov si,#crlf call prnstr ! Finally start the bootrom image loader, called base code loader in the ! PXE specification. However, we dont need to use the BC loader interface ! defined in the PXE specification, as our bootrom kernel will never be ! started and used by an UNDI option ROM other than ours. The bootrom ! loader gets the following parameters: ! ! AX - PCI PFA ! BX - PnP CSN ! DX - first segment of free base memory for the bootrom ! ES:DI - pointer to PnP installation structure ! ! Set the return address so that the bootrom can return to us savely. How- ! ever, we can only have a return address if this code has NOT been started ! off a floppy, and it has to return into physical ROM, not our own RAM ! copy. seg cs mov ax,[romseg] push ax ! push ROM segment or ax,ax ! check if we are running off a jz dobc1 ! floppy mov ax,#bbsret ! use BBS return address test cl,#RAMFL_BBS ! check if we are running under BBS jnz dobc1 mov ax,#nobbs ! use non-BBS return address dobc1: push ax ! push return offset mov ax,word ptr [RAM_START + RAM_PCIPFA] mov bx,word ptr [RAM_START + RAM_PNPCSN] les di,[RAM_START + RAM_PNPSTRUCT] jmpi KRNLOADER,DECOMPMEM ! call decompressed image ! If the bootrom returns, we cant simply get back to the BIOS since we ! dont know if the old BIOS stack is still valid. Therefore, depending on ! whether we have BBS or not, use interrupts to get back to the BIOS. ! Note that these routines can only be called within a physical ROM. If ! we have been started from a floppy, the bootrom is never allowed to ! return. ! ! With BBS, we can use the BOOTFAIL interrupt after a short pause. Other- ! wise just get back using the BOOTSTRAP interrupt. bbsret: mov ah,#$86 mov cx,#$002D ! wait 3 seconds mov dx,#$C6C0 int INT_MISC int INT_BOOTFAIL ! get back to BBS BIOS nobbs: mov si,#keymsg call prnstr ! wait for key press xor ax,ax int INT_KBD int INT_BOOTSTRAP ! call the bootstrap vector jmp nobbs ! If we have a decompression error, print an error message and return ! to the BIOS. error: push si mov si,#errmsg call prnstr pop si call prnstr ! print error message mov si,#crlf call prnstr mov si,#keymsg call prnstr xor ax,ax ! wait until user presses a key int INT_KBD romend: seg cs mov bx,[oldss] seg cs mov cx,[oldsp] mov ax,bx ! if not switched to new stack yet, or ax,cx ! we can return directly jz error1 cli mov ss,bx ! reset old BIOS stack mov sp,cx sti error1: pop es pop ds popall retf ! return to BIOS ! Messages: errmsg: .asciz "Loader error: " datmsg: .asciz "data" fmtmsg: .asciz "format" memmsg: .asciz "memory" flsmsg: .asciz "flash" pcimsg: .asciz "PCI ID" keymsg: .asciz "Press a key to continue" optmsg: .asciz "BIOS options: " nonmsg: .asciz "none" bbsmsg: .asciz "BBS" dimmsg: .asciz "DDIM" pmmmsg: .asciz "PMM" commsg: .asciz ", " askmsg: .asciz "Boot from network (Y/N)? " ! !************************************************************************** ! ! Print a string onto the console screen ! Input: CS:SI - Pointer to string ! Output: none ! Registers changed: AX, SI ! prnstr: seg cs mov al,[si] ! get next character or al,al jz prnst9 call prnchr ! send character to screen inc si jmp prnstr prnst9: ret ! !************************************************************************** ! ! Print a character onto the console screen ! Input: AL - Character to print ! Output: none ! Registers changed: AX ! prnchr: push bx mov ah,#$0E ! BIOS command for character output mov bx,#$0007 ! screen page and colors int INT_VIDEO ! send character to screen pop bx ret ! !************************************************************************** ! ! Check that the interrupt table contains our data structure ! Input: none ! Output: DS - segment of interrupt table ! Carry set if data structure not found ! Registers changed: AX, DS ! checkram: push cx push si xor ax,ax mov ds,ax cmp word ptr [RAM_START + RAM_ID],#RAM_SIGNATURE jne chkr8 mov si,#RAM_START mov cx,#RAM_SIZE ! the data signature has to be valid call dochksum ! and the checksum has to be correct or ah,ah ! this will clear the carry bit jz chkr9 chkr8: stc ! return with error chkr9: pop si pop cx ret ! !************************************************************************** ! ! Check for $PnP installation structure ! Input: ES:DI - pointer to installation structure ! Output: Carry flag set if pointer does not show to a PnP structure ! Registers changed: AX, (E)CX ! checkpnp: mov cx,es ! check if we have a pointer to a or cx,di ! system BIOS PnP installation check jz chkp8 ! structure #ifdef IS386 seg es ! check that the installation check mov ecx,dword ptr [di] ! structure starts with the proper seg cs ! 4-byte signature cmp ecx,dword ptr [pnphdr] jne chkp8 #else seg es mov cx,word ptr [di + 0] seg cs cmp cx,word ptr [pnphdr + 0] jne chkp8 ! check that the installation check seg es ! structure starts with the proper mov cx,word ptr [di + 2] ! 4-byte signature seg cs cmp cx,word ptr [pnphdr + 2] jne chkp8 #endif push ds mov cx,es ! compute checksum of PnP installation mov ds,cx ! check structure xchg si,di #ifdef IS386 movzx cx,byte ptr [si + PNPE_LENGTH] #else mov cl,byte ptr [si + PNPE_LENGTH] xor ch,ch #endif call dochksum xchg si,di pop ds or ah,ah ! if its ok, then we have a PnP BIOS jz chkp9 chkp8: stc chkp9: ret ! !************************************************************************** ! ! Compute checksum of a given memory area ! Input: DS:SI - pointer to memory area ! CX - size of memory area ! Output: AH - checksum ! Registers changed: AX, CX ! dochksum: cld push si xor ah,ah chks1: lodsb add ah,al ! add all bytes together loop chks1 pop si ret #ifndef NODDIM ! !************************************************************************** ! ! Copy into and out of extended memory. ! Input: EDX - source linear address ! EBX - destination linear address ! ECX - number of bytes to copy ! Output: Carry flag set if error ! Registers changed: EAX, EBX, ECX, EDX ! copy_highmem: push bp push es push si inc ecx shr ecx,#1 ! convert byte count into word count cpyh1: push ecx cmp ecx,#$00008000 ! copy a maximum of $8000 words at one jbe cpyh2 ! time mov cx,#$8000 cpyh2: mov si,#GDTSTRUCTLEN ! get space for GDT on stack sub sp,si mov bp,sp cpyh3: dec si mov byte ptr [bp + si],#0 ! fill GDT with zeroes jnz cpyh3 ! Setup source segment address in GDT mov word ptr [bp + GDTSRCADR + 0],dx ror edx,#16 mov byte ptr [bp + GDTSRCADR + 2],dl mov byte ptr [bp + GDTSRCHIADR],dh ror edx,#16 mov word ptr [bp + GDTSRCLEN],#$FFFF mov byte ptr [bp + GDTSRCRIGHT],#GDTACCESS ! Setup destination segment address in GDT mov word ptr [bp + GDTDESTADR + 0],bx ror ebx,#16 mov byte ptr [bp + GDTDESTADR + 2],bl mov byte ptr [bp + GDTDESTHIADR],bh ror ebx,#16 mov word ptr [bp + GDTDESTLEN],#$FFFF mov byte ptr [bp + GDTDESTRIGHT],#GDTACCESS ! Actually copy the memory using the BIOS mov ax,ss ! let ES:SI point to the GDT mov es,ax ! CX is already set with the number mov si,bp ! of words to copy mov ah,#$87 clc int INT_MISC ! call the BIOS to do the copy lahf add sp,#GDTSTRUCTLEN ! restore stack shl ecx,#1 add ebx,ecx ! set address to next block add edx,ecx pop ecx ! restore copy count sahf jc cpyh9 ! check for error sub ecx,#$00008000 ja cpyh1 ! continue with next block to copy clc ! return without error cpyh9: pop si pop es pop bp ret #endif #ifndef NODDIM ! !************************************************************************** ! ! Find POST Memory Manager entry point ! Input: none ! Output: EAX - PMM entry point, zero if not found ! Registers changed: EAX, CX, SI ! findpmm: push ds mov ax,#PMM_STARTSEG - 1 ! start scanning for PMM structure fpmm1: inc ax jz fpmm8 ! check if at end of memory mov ds,ax cmp dword ptr [PMM_SIGOFS],#PMM_SIGNATURE jne fpmm2 xor si,si movzx cx,byte ptr [PMM_SLEN] ! check if checksum is correct call dochksum or ah,ah jnz fpmm2 mov eax,dword ptr [PMM_ENTRY] or eax,eax jnz fpmm9 ! the entry point should not be zero fpmm2: mov ax,ds jmp fpmm1 ! continue with next segment fpmm8: xor eax,eax ! PMM entry not found fpmm9: pop ds ret #endif #ifndef NOTIMEOUT ! !************************************************************************** ! ! Set timer timeout in 1 second intervalls ! Input: AL - Timeout value ! BP - Pointer to 4-byte save space ! Output: none ! Registers changed: AX ! settimeout: push cx push dx mov ah,#18 ! compute number of timer ticks mul ah push ax xor ah,ah int INT_TIME pop ax add dx,ax adc cx,#0 mov word ptr [bp + 0],dx mov word ptr [bp + 2],cx pop dx pop cx ret #endif #ifndef NOTIMEOUT ! !************************************************************************** ! ! Check if timeout value reached ! Input: BP - Pointer to 4-byte save space ! Output: Carry flag set if timeout not reached ! Registers changed: AX ! chktimeout: push cx push dx xor ah,ah int INT_TIME cmp cx,word ptr [bp + 2] jne chktm9 cmp dx,word ptr [bp + 0] chktm9: pop dx pop cx ret #endif ! !************************************************************************** ! ! Set new interrupt. Dont delete the old vector if it has been saved ! already. ! Input: DS:SI - pointer to interrupt vector ! CS:BX - pointer to new handler routine ! ES:DI - pointer to old vector repository ! Output: none ! Registers changed: (E)AX ! intset: pushf cli #ifdef IS386 mov ax,cs shl eax,#16 mov ax,bx cmp dword ptr [si],eax je ints9 xchg eax,dword ptr [si] seg es mov dword ptr [di],eax #else /* IS386 */ cmp word ptr [si + 0],bx jne ints1 mov ax,cs cmp word ptr [si + 2],ax je ints9 ints1: mov ax,word ptr [si + 0] seg es mov word ptr [di + 0],ax mov ax,word ptr [si + 2] seg es mov word ptr [di + 2],ax mov word ptr [si + 0],bx mov word ptr [si + 2],cs #endif /* IS386 */ ints9: popf ret ! !************************************************************************** ! ! Return the next byte from the compressed image. ! Input: none ! Output: AL - next byte ! Zero flag set if end of file, AL is zero in that case ! Changed registers: AX ! getbyte: jmp word ptr [getfls] ! call get-byte routine readbyte: mov ax,word ptr [romsiz + 0] or ax,word ptr [romsiz + 2] ! check if at end of file jz readb9 pushf ! remember zero flag push si push ds lds si,[inptr] lodsb ! get next byte from compressed pop ds ! image test si,#$000F jnz readb1 shift (shr,si,4) ! if offset is on paragraph add word ptr [inptr+2],si ! boundary, increase segment xor si,si ! and reset offset readb1: mov word ptr [inptr+0],si #ifdef IS386 dec dword ptr [romsiz] #else sub word ptr [romsiz + 0],#1 sbb word ptr [romsiz + 2],#0 #endif pop si popf ! restore zero flag readb9: ret ! !************************************************************************** ! ! Write a byte to the decompressed image. ! Input: AL - output byte ! Output: none ! Registers changed: none ! putbyte: push di push es les di,[outptr] stosb ! write byte into output buffer area test di,#$000F jnz putb1 shift (shr,di,4) ! adjust segment add word ptr [outptr+2],di xor di,di putb1: mov word ptr [outptr+0],di pop es pop di ret ! !************************************************************************** ! ! Define the data segment ! outptr: .word 0 ! ptr into decompressed memory .word DECOMPMEM inptr: .long 0 ! ptr into compressed memory oldsp: .word 0 ! old BIOS stack pointer oldss: .word 0 ! old BIOS stack segment romseg: .word 0 ! physical ROM segment getfls: .word readbyte ! get-byte routine ! !************************************************************************** ! end