# nbentry.S86 - routines to enter and leave a netboot image # # Copyright (C) 2002-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: nbentry.S86,v 1.2 2003/03/30 10:02:35 gkminix Exp $ # #==================================================================== # #include "common.i86" #include "ldrec.i86" .file "nbentry.S86" .line 30 # #==================================================================== # # Define the text segment and all public entry points # \(.text) .extern ldrini # define external symbols .extern btpini .extern prnstr .global nbentry # define global symbols .global nbexit .global doerr # #==================================================================== # # Startup a netboot image loader program. This routine should only # get called once immediately at the startup of the loader program. # Input: AX - Offset to copyright message # DX - Offset to zero-terminated vendor ID string # Output: none # Changed registers: all # nbentry: # First save some important registers. Especially, save all registers # which are required to return to the bootrom lateron. cld push di push si push bp push ds push es mov si,ax # save offset for later # The loader program can get loaded by a netboot bootrom or a PXE # bootrom at addresses with different offsets. Since the assembler # will always assume the code segment to start at offset 0, we have # to adjust CS accordingly. call nbent1 nbent1: pop bx # compute offset difference sub bx,offset nbent1 mov cx,bx shr cx,1 # use single bit shift here because shr cx,1 # we dont know yet what processor we shr cx,1 # are running on shr cx,1 mov ax,cs add ax,cx mov cx,offset nbent2 push ax # push new segment and offset and load push cx # them by using a far return lret # Now get the bootrom return address from the stack, save all important # bootrom registers into local variables, and switch to new stack. nbent2: push bx mov bx,offset __data_start or bx,bx # if the data segment starts at offset jnz nbent7 # zero, we have seperate text and data mov bx,offset __text_end # segments add bx,0x000F shr bx,1 shr bx,1 # compute real start of data segment shr bx,1 shr bx,1 add ax,bx nbent7: pop bx mov ds,ax mov es,ax mov di,offset __bss_start mov cx,offset __bss_end sub cx,di # clear BSS segment xor al,al rep stosb pop word ptr [oldES] pop word ptr [oldDS] # save old register values in case pop word ptr [oldBP] # we have to return to the boot rom pop word ptr [oldSI] pop word ptr [oldDI] pop cx # get return address to caller sub cx,bx # adjust return address mov bp,sp pop word ptr [retadr + 0] # get return address to bootrom pop word ptr [retadr + 2] mov [oldSP],sp # save boot rom stack pointer mov [oldSS],ss push cx # save return address again call prnstr # print copyright message # Check that we are running on the correct processor. After we have passed # this check, we can be sure to use 32-bit opcodes and registers. mov cl,33 mov ax,0xFFFF # the 8088 and 8086 will actually shl ax,cl # shift over by 33 bits jz nbent3 push sp pop ax cmp ax,sp # 286 or better? jne nbent3 pushf # save original flags pushf pop ax or ax,0x7000 # set IOPL field in status word push ax popf pushf pop ax popf # restore original flags test ax,0x7000 # a 386+ lets us set the IOPL field jnz nbent4 nbent3: mov si,offset cpuerr jmp doerr # Check if we have been called by a PXE bootrom. In that case, the bootrom # pushed a far pointer to the !PXE structure onto the stack right before # calling us. nbent4: lfs di,[bp + 4] # get first stack pointer cmp dword ptr fs:[di],0x45585021 jne nbent5 mov si,offset pxeerr # PXE not supported yet jmp doerr # Check that the boot image header has a correct magic number and vendor # magic ID string. Note that the offset to the vendor magic ID string is # still in DX. Also determine the top of available memory for stack # purposes. nbent5: call ldrini # initialize load record handling or si,si jnz doerr mov eax,fs:BOOT_LD_MLENGTH[di] dec eax and eax,0xFFFFFFFE # get size of memory block allo- test eax,0xFFFF0000 # cated for this boot loader jz nbent6 mov eax,0x0000FFFE # limit to 64 kB nbent6: mov si,offset memerr mov bx,offset __bss_end add bx,MIN_STK_SIZE # check that there is enough jc doerr # space left for a minimum stack cmp ax,bx jb doerr # Check if BOOTP/DHCP record is valid, and copy it out of the bootrom # area into some local memory space. We have to do this before switching # to a new stack because we still need to access the old bootrom stack # to get the pointer to the BOOTP record. push ax # remember to save top of stack lfs si,[bp + 8] call btpini # load BOOTP/DHCP record pop ax or si,si jnz doerr # Switch to a local stack to avoid overflowing the bootrom stack. pop cx # get return address from stack cli mov bx,ds mov ss,bx # set new stack to end of free mov sp,ax # memory segment sti jmp cx # #==================================================================== # # Print error message and return to the bootrom. # Input: SI - Offset to error message # Output: Routine doesnt return # Changed registers: all # doerr: mov ax,ss # restore DS if changed mov ds,ax call prnstr # print error message call prcrlf xor ax,ax # indicate to terminate the boot # fall through to nbexit # #==================================================================== # # Return to the bootrom. # Input: AX - Return code for the bootrom # Output: Routine doesnt return # Changed registers: all # nbexit: cli mov ss,[oldSS] mov sp,[oldSP] # set boot rom stack and return sti # to boot rom with registers set push dword ptr [retadr] # correctly mov si,[oldSI] mov di,[oldDI] mov bp,[oldBP] mov es,[oldES] mov ds,[oldDS] lret # return to bootrom # #==================================================================== # # String and constants definitions # \(.data) # Error messages cpuerr: .asciz "Not running on a 386+ processor" memerr: .asciz "Internal memory error in boot loader" pxeerr: .asciz "PXE not supported yet" # #==================================================================== # # Variable definitions # \(.bss) .lcomm oldDS,2 # old DS from boot rom .lcomm oldES,2 # old ES from boot rom .lcomm oldBP,2 # old BP from boot rom .lcomm oldSI,2 # old SI from boot rom .lcomm oldDI,2 # old DI from boot rom .lcomm oldSP,2 # old SP from boot rom .lcomm oldSS,2 # old SS from boot rom .lcomm retadr,4 # return address to bootrom # #==================================================================== # .end