!************************************************************************** !* !* Boot-ROM-Code to load an operating system across a TCP/IP network. !* !* Module: kernel.S !* Purpose: Setup the stack and data areas, and then call the higher !* level routines. !* Entries: Called by loader at offset 3, callpxe, exitrom !* !************************************************************************** !* !* 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: kernel.S,v 1.5 2003/01/25 23:29:41 gkminix Exp $ !* ! !************************************************************************** ! ! Load assembler macros. ! #include #include #include #include #include #include #include #include #include ! !************************************************************************** ! ! Equates for this module: ! STACKSIZE equ 4096 ! Size of stack BIOS_SEG equ $0040 ! BIOS data segment BIOS_FREEMEM equ $0013 ! offset to free base memory kB ! !************************************************************************** ! ! Define the data segment ! .data extrn _end_of_data extrn _tftp_func_tab extrn _udp_func_tab ! Function table for general kernel PXE function: gen_func_tab: .word GEN_MINNUM,GEN_MAXNUM ! first min/max .word unload_base,unload_stack_size ! unload base code .word _pxe_get_binl_info,get_binl_info_size ! get BINL info .word 0,0 ! unused .word _pxe_restart,tftp_read_file_size ! restart TFTP .word 0,0 ! unused .word 0,0 ! start base (invalid) .word stop_base,stop_base_size ! stop base code ! !************************************************************************** ! ! Define the BSS segment ! .bss extrn _end_of_bss .lcomm oldss,2 ! old stack segment .lcomm oldsp,2 ! old stack pointer ! !************************************************************************** ! ! The kernel starts here. At the beginning there is some ! miscellaneous data. ! .text entry start public datseg public callpxe public exitrom extrn _end_of_text extrn _do_boot extrn _pxe_restart extrn _pxe_get_binl_info extrn net_start extrn net_stop extrn prnchar jmp near pxentry ! jump to PXE entry point start: jmp krnstart ! the bootrom loader will call us here .align DATAOFS ! Fill up ! These values can be used by the binary patch program to strip off the ! BSS segment from the final binary. The BSS segment just contains zeroes ! which dont have to be saved in the tight ROM. The totsiz variable will ! be filled in by the binary patch program. .word _end_of_text .word _end_of_data .word romid ! offset to ROM ID structure totsiz: .word 0 ! total number of paragaphs required ! Put the version number into the final kernel image. This allows the ! binary patch program to verify that its using the correct kernel ! image. .byte MAJOR_VER ! major version number .byte MINOR_VER ! minor version number ! Kernel ROM ID structure as defined in the PXE specification. Note that ! this will not be placed into the ROM loader, as its not required there. ! We also dont need to define a loader offset here. romid: .ascii "$BC$" ! structure ID .byte $12 ! length of this structure .byte 0 ! checksum (filled in by makerom) .byte 0 ! revision of this structure .byte $00,$01,$02 ! API revision number .word 0 ! offset to kernel loader (not used) .word STACKSIZE ! stack size required for kernel .word _end_of_bss ! required data segment size .word _end_of_text ! required code segment size ! Network driver data: ! PCI PFA and PnP CSN values as passed by the ROM loader. These values ! dont have to be valid even in systems which do support PCI and PnP. pcipfa: .word $FFFF ! PCI Bus/Device/Function number pnpcsn: .word $FFFF ! PnP Card Select Number pnpptr: .long 0 ! ptr to PnP installation structure ! The following variables have to be accessible using the CS register: retadr: .long 0 ! ROM return address datseg: .word 0 ! our own data segment drvseg: .word 0 ! network driver code segment oldfbm: .word 0 ! old value of free base memory pxeptr: .long 0 ! ptr to PXENV structure pxein: .long 0 ! ptr to PXE entry handler tmpsp: .word 0 ! our own stack pointer stkflg: .byte 1 ! flag if our own stack used ! !************************************************************************** ! ! Now start with the actual kernel code, which sets up the stack, copies ! everything into its place, and then calls the higher level code. ! Input: AX - PCI PFA ! BX - PnP CSN ! DX - first segment of free base memory ! ES:DI - pointer to PnP installation structure ! Output: none ! Registers changed: all ! krnstart: ! First save all values passed to us by the bootrom loader, including the ! return address. For the purposes of this kernel loader, we initially use ! our code segment for DS. push cs pop ds ! set temporary data segment #ifdef IS386 pop dword ptr [retadr] ! save return address #else pop word ptr [retadr + 0] ! save return address pop word ptr [retadr + 2] #endif mov [pcipfa],ax ! save PCI PFA mov [pnpcsn],bx ! save PnP CSN mov [datseg],dx ! save our new code segment mov word ptr [pnpptr + 0],di mov word ptr [pnpptr + 2],es ! Clear the whole memory area used by the bootrom. This is easier ! than clearing the BSS segments seperately. start3: int INT_MEMORY shift (shl,ax,6) ! get the memory end segment cld mov dx,ax mov bx,[datseg] start6: mov es,bx ! since the total area can be larger xor di,di ! than 64kB we have to clear each xor ax,ax ! paragraph seperately. mov cx,#8 rep stosw inc bx cmp bx,dx jb start6 ! Copy the whole kernel code into its final place. It has already been ! checked by the bootrom loader that we have enough memory. Then compute ! the new address of data, BSS and stack segment. mov dx,[datseg] mov ax,dx add ax,[totsiz] mov [drvseg],ax ! save network driver segment mov cx,#_end_of_text + $000F and cx,#$FFF0 ! adjust size of text segment to mov bx,cx ! paragraph alignment shift (shr,bx,4) sub ax,bx ! compute start of text segment mov es,ax xor di,di xor si,si shr cx,#1 ! copy code segment rep movsw jnc start7 movsb start7: mov es,dx xor di,di mov cx,#_end_of_data ! copy data segment shr cx,#1 rep movsw jnc start8 movsb start8: mov es,ax sub ax,dx ! set new stack pointer at end of shift (shl,ax,4) ! data segment sub ax,#2 and ax,#$FFFE seg es mov [tmpsp],ax cli mov ss,dx mov sp,ax ! set new stack pointer sti ! Call the network driver loader to copy the network driver into memory. It ! gets the new network driver code segment in DX. The base code PXE entry ! point will be pointed to by ES:DI. ! The network driver will return with a far return, which will directly go ! into the new copy of the kernel. It has to return a pointer to the PXENV ! structure in ES:DI. #ifdef IS186 push es ! push return address push #start1 #else mov ax,#start1 ! push return address push es push ax #endif mov bx,ds mov ax,si shift (shr,ax,4) ! convert pointer to uncompressed add ax,bx ! copy of network driver so that the push ax ! offset cant overflow and si,#$000F add si,#NETLOADER ! add offset to network driver loader push si mov di,#pxentry ! set kernel PXE entry point address mov dx,[drvseg] ! get new network driver segment retf ! call the network driver loader ! We are now running in the new copy of the bootrom kernel. Set the data ! segment to the code segment again, and save the pointer to the PXENV ! structure. start1: mov bx,cs ! set data segment again mov ds,bx or ax,ax ! check for network driver loader error #ifdef IS386 jnz near fatal1 #else jz start2 jmp near fatal1 #endif start2: mov word ptr [pxeptr + 0],di ! save pointer to PXENV mov word ptr [pxeptr + 2],es ! structure ! Set the amount of free base memory in the BIOS data area. This has to ! come after initializing the network driver. push es mov ax,#BIOS_SEG mov es,ax mov bx,ss ! convert the segment address of the shift (shr,bx,6) ! kernel code into number of kB seg es xchg bx,word ptr [BIOS_FREEMEM] pop es mov [oldfbm],bx ! save old amount of free base memory ! Now set all required values in the PXENV and !PXE structures, which have ! been setup by the network driver loader. push ds lds si,[pxeptr] mov bx,ss mov dx,cs sub dx,bx ! compute size of stack segment shift (shl,dx,4) mov word ptr [si + o_pe_arpa_ss_sel],bx mov word ptr [si + o_pe_arpa_ss_size],dx mov word ptr [si + o_pe_arpa_ds_sel],bx mov word ptr [si + o_pe_arpa_ds_size],dx mov word ptr [si + o_pe_arpa_cs_sel],cs mov word ptr [si + o_pe_arpa_cs_size],#_end_of_text mov byte ptr [si + o_pe_checksum],#0 #ifdef IS386 movzx cx,byte ptr [si + o_pe_length] #else mov cl,byte ptr [si + o_pe_length] xor ch,ch #endif call dochecksum sub byte ptr [si + o_pe_checksum],al lds si,[si + o_pe_pxe_ptr] mov word ptr [si + o_ps_baseromid + 0],#romid mov word ptr [si + o_ps_baseromid + 2],cs mov byte ptr [si + o_ps_descnum],#PXE_DESC_DEF_BASE #ifdef IS386 mov ecx,dword ptr [si + o_ps_pxentry16] seg cs mov dword ptr [pxein],ecx #else mov cx,word ptr [si + o_ps_pxentry16 + 0] seg cs mov word ptr [pxein + 0],cx mov cx,word ptr [si + o_ps_pxentry16 + 2] seg cs mov word ptr [pxein + 2],cx #endif mov cl,#4 mov al,bh ! compute linear base address of data shr al,cl ! segment shl bx,cl mov word ptr [si + o_ps_desc + (PXE_DESC_STACK * desc_size) + o_desc_base_low],bx mov byte ptr [si + o_ps_desc + (PXE_DESC_STACK * desc_size) + o_desc_base_mid],al mov word ptr [si + o_ps_desc + (PXE_DESC_STACK * desc_size) + o_desc_limit_low],dx mov word ptr [si + o_ps_desc + (PXE_DESC_BASE_DATA * desc_size) + o_desc_base_low],bx mov byte ptr [si + o_ps_desc + (PXE_DESC_BASE_DATA * desc_size) + o_desc_base_mid],al mov word ptr [si + o_ps_desc + (PXE_DESC_BASE_DATA * desc_size) + o_desc_limit_low],dx mov bx,cs mov al,bh ! compute linear base address of text shr al,cl ! segment shl bx,cl mov dx,#_end_of_text + $000F and dx,#$FFF0 mov word ptr [si + o_ps_desc + (PXE_DESC_BASE_CODE * desc_size) + o_desc_base_low],bx mov byte ptr [si + o_ps_desc + (PXE_DESC_BASE_CODE * desc_size) + o_desc_base_mid],al mov word ptr [si + o_ps_desc + (PXE_DESC_BASE_CODE * desc_size) + o_desc_limit_low],dx mov word ptr [si + o_ps_desc + (PXE_DESC_BASE_WRITE * desc_size) + o_desc_base_low],bx mov byte ptr [si + o_ps_desc + (PXE_DESC_BASE_WRITE * desc_size) + o_desc_base_mid],al mov word ptr [si + o_ps_desc + (PXE_DESC_BASE_WRITE * desc_size) + o_desc_limit_low],dx mov byte ptr [si + o_ps_checksum],#0 #ifdef IS386 movzx cx,byte ptr [si + o_ps_length] #else mov cl,byte ptr [si + o_ps_length] xor ch,ch #endif call dochecksum sub byte ptr [si + o_ps_checksum],al pop ds ! Call the UNDI start API with the parameters passed to us by the BIOS. This ! will save those values in UNDI internal variables and install the $1A ! interrupt. call do_start_undi or ax,ax jnz fatal ! Initialize the network driver interface. This will also check for a ! correct network driver. call do_undi_startup or ax,ax jnz fatal ! Let the kernel setup its own UNDI interface. This will also redirect the ! network cards hardware interrupt. mov ax,[datseg] ! set new data segment mov ds,ax mov es,ax call net_start or ax,ax jnz fatal ! Now call the higher level C code to continue with the netboot process. call _do_boot ! start the game ! If the main program returns terminate the kernel and UNDI codes. Then ! reclaim all base memory we used so far and return to the BIOS. fatal: push ax mov ax,cs mov ds,ax xor cl,cl ! terminate the kernel and UNDI call terminate ! driver pop ax fatal0: push ax mov bx,#pxemsg ! print first error message call prnstr pop ax call prnhex fatal1: mov bx,#errmsg ! print second error message call prnstr #ifdef IS386 mov eax,[retadr] ! check if return address is valid or eax,eax #else mov ax,word ptr [retadr + 0] or ax,word ptr [retadr + 2] #endif fatal2: jz fatal2 ! if invalid, loop forever jmp far ptr [retadr] ! return to caller ! Messages: pxemsg: .byte $0D,$0A .asciz "Bootrom error: PXE-" errmsg: .byte $0D,$0A .ascii "Network boot failed" .byte $0D,$0A .byte 0 ! !************************************************************************** ! ! Call UNDI_START_UNDI to set all interrupt vectors as required. ! Input: none ! Output: AX - status ! Registers changed: AX ! do_start_undi: push bp sub sp,#start_undi_size mov bp,sp mov word ptr [bp + o_su_status],#0 mov word ptr [bp + o_su_reg_dx],#$FFFF mov ax,[pcipfa] mov word ptr [bp + o_su_reg_ax],ax mov ax,[pnpcsn] mov word ptr [bp + o_su_reg_bx],ax #ifdef IS386 mov eax,[pnpptr] mov dword ptr [bp + o_su_reg_di],eax #else mov ax,word ptr [pnpptr + 0] mov word ptr [bp + o_su_reg_di],ax mov ax,word ptr [pnpptr + 2] mov word ptr [bp + o_su_reg_es],ax #endif mov ax,#UNDI_START_UNDI call callpxe add sp,#start_undi_size pop bp ret ! !************************************************************************** ! ! Call UNDI_STARTUP to initialize the network interface. ! Input: none ! Output: AX - status ! Registers changed: AX ! do_undi_startup: mov ax,#UNDI_STARTUP ustrt1: push bp sub sp,#startup_size mov bp,sp call callpxe add sp,#startup_size pop bp ret ! !************************************************************************** ! ! Call UNDI_STOP_UNDI to reset all interrupt vectors. ! Input: none ! Output: AX - status ! Registers changed: AX ! do_stop_undi: mov ax,#UNDI_STOP_UNDI jmp ustrt1 ! !************************************************************************** ! ! Call UNDI_CLEANUP to remove the network driver from memory. ! Input: none ! Output: AX - status ! Registers changed: AX ! do_undi_cleanup: mov ax,#UNDI_CLEANUP jmp ustrt1 ! !************************************************************************** ! ! Call PXE API. This needs to be a seperate function so that it can get ! exported to the rest of the kernel code. Note that this routine might ! get called with different DS values. ! Input: AX - PXE function number ! SS:BP - pointer to PXE parameter structure ! Output: AX - error code ! Registers changed: AX ! callpxe: push ss push bp push ax seg cs call far ptr [pxein] mov ax,word ptr [bp] add sp,#6 ret ! !************************************************************************** ! ! Terminate the kernel services. This will call the UNDI interface code ! to restore the network cards hardware interrupt vector. Note that this ! routine might get called with different DS and SS settings. Regarding the ! different stack we cant do anything about it but beware of referencing ! any variables with the wrong segment. ! Input: none ! Output: AX - status ! Registers changed: (E)AX, DX, SI ! stop_base: push ds seg cs mov ds,[datseg] call net_stop ! stop network interface services or ax,ax jz stopb9 mov ax,#PXENV_STATUS_KEEP_ALL stopb9: pop ds ret ! !************************************************************************** ! ! Unload the kernel code from memory. However, as per the PXE specification ! this does not change the size of free base memory. Also note that we dont ! have to change the segment descriptors in the !PXE structure, but only ! the base code ROM ID pointer and the number of segment descriptors used ! by the UNDI driver. This routine might get called with different DS register ! values. ! Input: none ! Output: AX - error code ! Registers changed: AX, BX, CX, SI ! unload_base: push es mov bx,#BIOS_SEG mov es,bx seg cs mov ax,[datseg] ! check if base of free memory shift (shr,ax,6) ! has been changed seg es cmp ax,word ptr [BIOS_FREEMEM] pop es mov ax,#PXENV_STATUS_KEEP_ALL jne unld9 push ds seg cs lds si,[pxeptr] ! get PXENV structure pointer xor ax,ax mov word ptr [si + o_pe_arpa_ss_sel],ax mov word ptr [si + o_pe_arpa_ss_size],ax mov word ptr [si + o_pe_arpa_ds_sel],ax mov word ptr [si + o_pe_arpa_ds_size],ax mov word ptr [si + o_pe_arpa_cs_sel],ax mov word ptr [si + o_pe_arpa_cs_size],ax mov byte ptr [si + o_pe_checksum],al #ifdef IS386 movzx cx,byte ptr [si + o_pe_length] #else mov cl,byte ptr [si + o_pe_length] xor ch,ch #endif call dochecksum sub byte ptr [si + o_pe_checksum],al lds si,[si + o_pe_pxe_ptr] xor ax,ax mov word ptr [si + o_ps_baseromid + 0],ax mov word ptr [si + o_ps_baseromid + 2],ax mov byte ptr [si + o_ps_descnum],#PXE_DESC_DEF_UNDI mov byte ptr [si + o_ps_checksum],al #ifdef IS386 movzx cx,byte ptr [si + o_ps_length] #else mov cl,byte ptr [si + o_ps_length] xor ch,ch #endif call dochecksum sub byte ptr [si + o_ps_checksum],al pop ds xor ax,ax unld9: ret ! !************************************************************************** ! ! Print a string onto the console screen. ! Input: CS:BX - pointer to zero terminated string ! Output: none ! Registers changed: AX, BX ! prnstr: prnst1: seg cs mov al,[bx] or al,al ! end of string jz prnst2 call prnchar ! print character inc bx ! proceed with next character jmp prnst1 prnst2: ret ! !************************************************************************** ! ! Print a hex value onto the console screen. ! Input: AL - value to print ! Output: none ! Registers changed: AX ! prnhex: push ax shift (shr,al,4) ! print high nibble call prnnib pop ax prnnib: and al,#$0F ! print low nibble add al,#$30 cmp al,#$39 jbe prnn1 add al,#$07 prnn1: jmp near prnchar ! !************************************************************************** ! ! Compute checksum of a given memory area ! Input: DS:SI - pointer to memory area ! CX - size of memory area ! Output: AL - checksum ! Registers changed: AX, CX ! dochecksum: cld push si xor ax,ax chks1: lodsb add ah,al ! add all bytes together loop chks1 mov al,ah pop si ret ! !************************************************************************** ! ! PXE API entry point for kernel services (aka base code). Since we ! have to change the stack this is not reentrant. The main PXE API ! entry point already saved all registers so we dont have to do it ! ourselves. ! Input: BX - function code ! ES:DI - far ptr to function structure ! Output: AX - error code ! Registers changed: (E)AX, BX, CX, DX, SI, DI, BP, DS, ES ! pxentry: ! For all PXE functions we need our own stack, so we have to switch ! stacks before proceeding. Of course this means that we are not ! reentrant. pushf seg cs mov ds,[datseg] ! set our own data segment mov al,#1 seg cs xchg [stkflg],al ! check reentrancy flag or al,al jz pxent1 mov ax,#PXENV_STATUS_FAILURE jmp near pxentB ! return with error pxent1: cli mov [oldss],ss ! save old stack mov [oldsp],sp seg cs mov ss,[datseg] ! set new stack seg cs mov sp,[tmpsp] sti ! Check for general functions mov si,#gen_func_tab cmp bx,word ptr [si + 0] jb pxent2 cmp bx,word ptr [si + 2] jbe pxent4 ! Check for TFTP functions pxent2: mov si,#_tftp_func_tab cmp bx,word ptr [si + 0] jb pxent3 cmp bx,word ptr [si + 2] jbe pxent4 ! Check for UDP functions pxent3: mov si,#_udp_func_tab cmp bx,word ptr [si + 0] jb pxent7 cmp bx,word ptr [si + 2] ja pxent7 ! Call the function handler with the parameter structure copied to the ! local stack. This is necessary so that the higher level C code is able ! to access it. pxent4: cld sub bx,word ptr [si + 0] ! subtract minimum value from index #ifdef IS186 shl bx,#2 ! convert index into offset #else shl bx,#1 shl bx,#1 #endif lea bx,[bx + si + 4] ! compute pointer into function table mov ax,word ptr [bx + 0] ! check that we have a handler or ax,ax jz pxent7 mov cx,word ptr [bx + 2] ! determine number of bytes to copy mov si,di sub sp,cx ! make space for structure on stack mov di,sp push ds ! save DS for later push cx ! push all values required to copy push si ! the result back push es push di ! push pointer to argument for handler push ds mov ax,es mov ds,ax mov ax,ss mov es,ax rep ! copy parameter structure onto local movsb ! stack pop ds call near ptr [bx + 0] ! call handler routine cld pop si ! restore all parameters for copy pop es pop di pop cx mov dx,ss mov ds,dx ! now copy the parameter structrue back rep ! remember not to change AX, which movsb ! contains the error code pop ds jmp pxent8 ! Upon return we have to restore the stack, clear the reentrancy flag ! and check for an error. pxent7: mov ax,#PXENV_STATUS_BAD_FUNC pxent8: cli mov ss,[oldss] ! restore old stack mov sp,[oldsp] seg cs mov byte ptr [stkflg],#0 ! clear reentrancy flag sti pxentB: popf retf ! !************************************************************************** ! ! Exit the bootrom and call the loaded image. ! Input: AL - function value: ! 0 - unload kernel and UNDI before calling ! 1 - unload kernel before calling ! 2 - dont unload anything ! 3 - dont unload anything and push !PXE pointer ! CX - number of words to copy from old to new stack ! ES:DI - new execution pointer ! DX and SI will be passed to the called image unchanged ! DS will be set to ES before calling the image ! Output: AX - unchanged from called image ! Registers changed: all except BP and DS ! exitrom: push bp mov bp,sp push ds push ax ! save AX before switching stacks ! First setup the new stack. To do this we copy a couple of bytes ! from the old stack to the new stack, and also set the return address ! and the address to call. Then switch to the new stack. push dx push si push di push es seg cs mov bx,[datseg] sub bx,#STACKSIZE / 16 ! set ES:DI with new top-of-stack mov es,bx mov di,#STACKSIZE - 4 lea si,[bp + 2] add si,cx add si,cx std rep ! copy stack contents from old movsw ! to new stack mov cl,al ! save AL for later seg cs lds si,[pxeptr] ! get pointer to PXENV structure cmp cl,#3 jne exit1 mov ax,[si + o_pe_pxe_seg] stosw ! copy pointer to PXE structure mov ax,[si + o_pe_pxe_off] stosw exit1: mov ax,cs stosw ! copy return address mov ax,#exitr stosw pop ax stosw ! copy address to call mov dx,ax pop ax stosw mov ax,ds stosw ! copy pointer to PXENV structure mov ax,si stosw mov ax,dx stosw ! copy new data segment pop ax stosw ! copy old SI pop ax stosw ! copy old DX cli seg cs mov [tmpsp],sp ! save old stack pointer lea ax,[di + 2] mov ss,bx ! switch to new stack mov sp,ax sti seg cs mov byte ptr [stkflg],#0 ! clear stack-in-use flag ! Now check if we have to unload the UNDI driver and/or kernel. When the ! kernel gets unloaded, a later return is not possible. cmp cl,#2 jae exit2 ! terminate kernel and/or UNDI if call terminate ! requested or ax,ax jz exit2 jmp near fatal0 ! Now call the image. We have the pointer to the PXENV structure, the ! address to call and the return address already on the stack exit2: pop dx ! get old DX pop si ! get old SI pop ds ! get new data segment pop bx ! get pointer to PXENV structure pop es retf ! call the loaded image ! The loaded image will return here. First we restore the old stack. Then ! check the action code to see if the kernel has been removed. If it has, ! we cant continue. exitr: mov bx,cs ! set DS to text segment temporarily mov ds,bx mov byte ptr [stkflg],#1 ! set stack-in-use flag mov bx,[datseg] mov dx,[tmpsp] cli mov ss,bx ! set old stack mov sp,dx sti mov es,bx pop cx ! get action flag cmp cl,#2 jae exit9 cmp cl,#1 ! check if UNDI driver already jne exit8 ! terminated mov cl,#2 call terminate ! terminate UNDI driver exit8: jmp near fatal1 exit9: pop ds ! get old data segment pop bp ! get old BP ret ! !************************************************************************** ! ! Terminate bootrom services. Note that this routine might get called ! with different DS and SS settings. ! Input: CL - action code: ! 0 - unload kernel and UNDI driver ! 1 - unload kernel only ! 2 - unload UNDI driver only ! Output: AX - error code ! Registers changed: (E)AX, BX, CX, DX, SI ! terminate: push ds mov ax,cs mov ds,ax or cl,cl ! check if we also have to unload jnz term5 ! the UNDI driver ! Unload both, the kernel and the UNDI driver call stop_base ! stop the base code or ax,ax mov ax,#PXENV_STATUS_STOP_BASE jnz term1 call unload_base ! unload base code or ax,ax jz term1 mov ax,#PXENV_STATUS_UNLOAD_BASE term1: push ax ! save return status call do_stop_undi ! stop UNDI services or ax,ax mov ax,#PXENV_STATUS_STOP_UNDI jnz term2 call do_undi_cleanup ! terminate network driver or ax,ax jz term2 mov ax,#PXENV_STATUS_CLEANUP_UNDI term2: mov bx,ax ! save UNDI error code pop ax ! restore STOP_BASE return status or ax,ax ! check if we can remove the kernel jnz term7 ! from memory mov dx,[oldfbm] mov ax,bx or ax,ax ! if an error occurred during STOP_UNDI jz term4 ! we cant reclaim the UNDI memory, as term3: mov dx,[drvseg] ! there might be some interrupt not shift (shr,dx,6) ! being restored term4: push es mov bx,#BIOS_SEG mov es,bx mov bx,[datseg] ! check that we can free the memory shift (shr,bx,6) seg es cmp bx,word ptr [BIOS_FREEMEM] jne term8 seg es mov word ptr [BIOS_FREEMEM],dx jmp term8 ! Unload the kernel only term5: cmp cl,#1 ! check if we only have to unload jne term6 ! the kernel call stop_base ! stop the base code or ax,ax mov ax,#PXENV_STATUS_STOP_BASE jnz term9 call unload_base ! unload base code or ax,ax jz term3 mov ax,#PXENV_STATUS_UNLOAD_BASE term7: jmp term9 ! Unload the UNDI driver only. This assumes that the kernel has already ! been unloaded. term6: cmp cl,#2 ! check if we only have to unload jne term9 ! the UNDI driver call do_stop_undi ! stop UNDI services or ax,ax mov ax,#PXENV_STATUS_STOP_UNDI jnz term9 call do_undi_cleanup ! terminate network driver or ax,ax mov ax,#PXENV_STATUS_CLEANUP_UNDI jnz term9 push es mov bx,#BIOS_SEG mov es,bx mov ax,[drvseg] ! check that we can free the memory shift (shr,ax,6) seg es cmp ax,word ptr [BIOS_FREEMEM] jne term8 mov ax,[oldfbm] ! restore free base memory seg es mov word ptr [BIOS_FREEMEM],ax xor ax,ax term8: pop es term9: pop ds ret ! !************************************************************************** ! end