/*
 * first-nb.S86  -  MGL netboot loader
 *
 * Copyright (C) 2003-2007 Gero Kuhlmann   <gero@gkminix.han.de>
 *
 *  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: first-nb.S86,v 1.7 2007/01/06 18:31:38 gkminix Exp $
 *
 *====================================================================
 */
#define DEF_HEAP_SIZE
#define DEF_STACK_SIZE

#include <common.i86>
#include <netboot.i86>
#include <system/bios.i86>
#include <system/nbloader.i86>
#include <net/bootp.i86>
#include "headers/osload.i86"
#include "headers/runtime.i86"
#include "first.i86"

.file __FILE__
.line __LINE__



/*
 *====================================================================
 *
 * Definition of heap and stack size
 */

/* Stack size is common for all netboot image loader programs */
__stack_size = MIN_STK_SIZE


/* We dont need a heap */
__heap_size = 0




.line __LINE__
/*
 *====================================================================
 *
 * Define some external routines from the i386 library
 */

	.text

	.extern	nbentry			# routines in nbentry.s86
	.extern	nbexit
	.extern	doerr

	.extern	ldrfnd			# routines in ldrec.s86
	.extern	ldradr



/*
 *====================================================================
 *
 * Boot image loader header
 */

	jmp	nopxe			# PXE entry point

	.org	NBLD_OFFSET

nbld_header:
	.word	__text_end		# total size of text segment
	.word	__bss_end		# total size of data segment
	.word	__heap_size		# minimum heap size
	.word	__stack_size		# minimum stack size
	.word	nbentry			# boot image entry routine
	.word	start			# the main program
	.word	vmagic			# vendor ID
	.word	sigmsg			# copyright message
	.word	0			# no user information



/*
 *====================================================================
 *
 * Now start the game
 */

start:

/*
 * Determine the start address of the MGL program
 */

	mov	si,offset recerr
	mov	al,MGLNUM + LOADER_TAG
	call	ldrfnd			# find MGL program
	jc	doerr
	call	ldradr			# get address
	jc	doerr
	test	eax,0xFFF0000F		# must be on a segment boundary
	jnz	doerr
	shl	eax,12			# compute start address as seg:ofs
	mov	[startadr],eax

/*
 * Prepare the interface structure. We need to pass the address of the
 * bootrom BOOTP buffer to the runtime module. The bootrom always assigns
 * at least BOOTP_MAX_SIZE bytes for the buffer.
 * If the interrupt vector number 3 has been changed so that it points
 * into the base memory (i.e. below 0xA0000), it is an indication that
 * a debugger is active, so we set the TRAP loader flag.
 */

	mov	word ptr rtload_flags[rtload],RTFLAG_RUN + RTFLAG_BOOTP
	mov	word ptr rtload_bsize[rtload],BOOTP_MAX_SIZE
	mov	eax,[btpptr]
	mov	dword ptr rtload_bootp[rtload],eax
	mov	dword ptr rtload_pxe[rtload],0

	push	es
	xor	ax,ax
	mov	es,ax
	mov	eax,es:[NB_SERVICE_INT * 4]
	or	eax,eax
	jz	0f
	mov	ah,NB_SERVICE_CHECK	# check if we have a netboot bootrom
	int	NB_SERVICE_INT		# this call is not supported by an
	cmp	ax,NB_SERVICE_MAGIC	# etherboot bootrom
	jne	0f
	or	word ptr rtload_flags[rtload],RTFLAG_LOAD
	inc	byte ptr cs:[nbrom]
0:	cmp	word ptr es:[3 * 4 + 2],0xA000
	jae	5f
	or	word ptr rtload_flags[rtload],RTFLAG_TRAP
5:	pop	es

/*
 * Now call the MGL program
 */

	mov	si,offset rtload
	lcall	[startadr]		# call the MGL program
	cmp	ax,RTERROR_LOAD
	jne	3f
1:	mov	ax,1			# return to the bootrom with the status
	call	nbexit			# value to reload a new boot image
2:	jmp	2b

3:	cmp	ax,RTERROR_RUN
	je	4f
	xor	ax,ax			# abort the boot process
	jmp	1b

/*
 * The MGL program indicated that we have to copy the loaded image into
 * place. For this we install a short copy program.
 */

4:	cld
	push	es
	xor	ax,ax
	mov	es,ax
	mov	di,OSLOAD_ADDR		# copy boot image loader into low
	mov	si,offset loadstart	# memory
	mov	cx,offset loadend
	sub	cx,si
	rep	movsb
	pop	es

/*
 * Terminate this loader and switch the stack back to the bootrom stack. In
 * case the MGL program terminated the bootrom services, and we dont have a
 * netboot bootrom, we have to use an intermediate stack and setup the return
 * address to the BIOS reboot vector. The netboot bootrom allows returning
 * even if the IP stack has been terminated.
 * In case we can use the old bootrom stack, there are two far pointers on
 * the stack already when this program got called. We can use this space to
 * store the pointers required for the loaded image.
 */

	mov	dx,cs
	shl	edx,16
	mov	dx,offset 5f		# setup return address for nbexit call
	xchg	edx,[romret]
	mov	cs:[retadr],edx

	mov	ax,1
	call	nbexit			# terminate this boot image loader

5:	xor	ax,ax			# we are now running on the bootrom
	mov	fs,ax			# stack
	mov	fs:image_reg_ds[IMAGE_REC_ADDR],ds
	test	byte ptr cs:[nbrom],0xFF
	jnz	0f
	cmp	byte ptr fs:image_hflags1[IMAGE_REC_ADDR],FLAG1_KEEP_ALL
	jne	8f

0:	push	bp
	mov	bp,sp
	mov	eax,fs:image_stack[IMAGE_REC_ADDR + 0]
	or	eax,eax
	jz	6f			# store pointer to image header
	mov	[bp + 2],eax		# or !PXE structure
6:	mov	eax,fs:image_stack[IMAGE_REC_ADDR + 4]
	or	eax,eax
	jz	7f			# store pointer to BOOTP record
	mov	[bp + 6],eax		# if necessary
7:	pop	bp
	mov	eax,cs:[retadr]
	jmp	9f

/*
 * When we got called by a non-netboot bootrom (for example by means
 * of another MGL program), and the bootrom services have been termi-
 * nated, the boot image we are going to call cant return to the
 * bootrom so we have to provide an intermediate stack and a different
 * return address. If the bootrom has been completely terminated we
 * can use the bootstrap vector, otherwise the system has to be reset
 * completely because there might be some interrupt vectors still in
 * use by the UNDI driver.
 */

8:	cli
	mov	ss,ax			# switch to intermediate stack
	mov	sp,OSLOAD_STACK
	sti
	push	dword ptr fs:image_stack[IMAGE_REC_ADDR + 4]
	push	dword ptr fs:image_stack[IMAGE_REC_ADDR + 0]
	mov	eax,fs:[INT_BOOTFAIL * 4]
	cmp	byte ptr fs:image_hflags1[IMAGE_REC_ADDR],0
	je	9f
	mov	eax,(BIOS_RESET_SEG * 65536) + BIOS_RESET_OFS
9:	push	eax
	xor	eax,eax
	ljmp	0,OSLOAD_ADDR		# call OS loader


retadr:	.long	0			# return address
nbrom:	.byte	0			# flag if we have a netboot bootrom




/*
 *====================================================================
 *
 * String and constants definitions
 */
	_rodata


	.extern	loadstart
	.extern	loadend


/* Startup signature */
sigmsg:	.byte	0x0D, 0x0A
	.ascii	"MGL Net Boot Image Loader "
	.ascii	VERSION
	.byte	0x0D, 0x0A
	.ascii	COPYRIGHT
	.byte	0x0D, 0x0A
	.byte	0x0D, 0x0A
	.byte	0


/* Boot image header vendor magic ID */
vmagic:	.asciz	VENDOR_MAGIC


/* Error messages */
recerr:	.asciz	"Error in load record data"




/*
 *====================================================================
 *
 * Variable definitions
 */
	.bss

	.extern	btpptr			# pointer to BOOTP record
	.extern	romret			# bootrom return address

	.lcomm	startadr,4		# start address of MGL program
	.lcomm	rtload,RTLOAD_SIZE	# runtime loader interface structure




/*
 *====================================================================
 */
	.end

