/*
 * doscan.S86  -  Scan memory for boot roms
 *
 * 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: doscan.S86,v 1.6 2007/01/06 18:31:21 gkminix Exp $
 *
 *====================================================================
 */


#include <common.i86>
#include <system/bootrom.i86>
#include "romcheck.i86"

.file __FILE__
.line __LINE__




/*
 *====================================================================
 *
 * Definitions for signature scanning
 */

#define MAXSIGS		16		/* maximum number of ROM signatures */
#define SIGSTEP		0x0020		/* steps for scanning for rom signature */
#define ROMSIG		0xAA55		/* rom signature */

#define MINSIZE		0x04		/* minimum rom size (2kB) */
#define SIGOFS		0x0000		/* offset to rom signature */
#define SIZEOFS		0x0002		/* offset to rom size byte */


/*
 * Definitions for physical memory scanning
 */

#define ROMSTEP		0x0100		/* steps for scanning for physical rom */
#define ROMCHKSIZE	0x0080		/* size of check area for unknown memory */
#define ROMBSIZE	(ENDSEG-STARTSEG) / ROMSTEP	/* rom info buffer size */




.line __LINE__
/*
 *====================================================================
 *
 * Start of text segment and definition of all public entry points
 */

	.text

	.global	_doscan				# define global symbols



/*
 *====================================================================
 *
 * Scan the ROM memory
 * Input:  AX    -  Pointer to scaninfo buffer
 * Output: none
 * Changed registers: AX
 */
_doscan:

	cld
	push	bp
	mov	bp,ax				# get pointer to scaninfo buffer
	push	es				# note that this assumes SS=DS
	push	si
	push	di
	push	ebx
	push	ecx
	push	edx

/*
 * Scan the ROM memory and save all segment addresses. If we found a rom
 * signature, grab the ROM size and check that its OK: it has to be a
 * multiple of 2kB. Then verify the checksum. If thats not OK, it may
 * mean that we dont have a ROM here, or that the ROM is not com-
 * pletely activated. Insert the signatures segment address into the
 * table anyway but mark it as invalid.
 */

	xor	bx,bx
	mov	dx,STARTSEG
1:	cmp	dx,ENDSEG			# reached end of scan memory?
	jae	9f
	mov	es,dx
	cmp	word ptr es:[SIGOFS],ROMSIG
	je	3f				# check rom signature
2:	add	dx,SIGSTEP			# continue with next step
	jmp	1b

3:	mov	al,byte ptr es:[SIZEOFS]	# get size byte
	mov	ah,MINSIZE
4:	cmp	al,ah				# find size in valid values
	je	5f
	add	ah,MINSIZE			# continue with next size
	jnc	4b
	jmp	2b				# size not found, continue scan

5:	mov	sigsegs[bx],dx		# size found, save segment of signature
	xor	ah,ah			# convert rom size into paragraphs
	shl	ax,5
	mov	sigsize[bx],ax		# save rom size
	call	chksum			# verify checksum
	jz	6f
	xor	ax,ax			# mark signature as invalid
	add	dx,SIGSTEP		# continue with next step
	jmp	7f
6:	mov	ax,1			# mark signature as valid
	add	dx,sigsize[bx]		# continue scan past current ROM
7:	mov	sigvalid[bx],ax		# save validity flag
	add	bx,2			# increment signature index
	cmp	bx,MAXSIGS * 2
	jb	1b			# dont continue past buffer size

9:	shr	bx,1			# prepare return values
	mov	si_signum[bp],bx
	mov	dword ptr si_sigsegs[bp],offset sigsegs
	mov	dword ptr si_sigsize[bp],offset sigsize
	mov	dword ptr si_sigvalid[bp],offset sigvalid

/*
 * Now scan for physical ROM. This is similar to the above loop in that
 * the same memory area is scanned, but with a 4kB granularity. If we find
 * an area where its not possible to write in, this doesnt necessarily
 * mean we have a rom. If that area contains just one single value in
 * all bytes its either not a rom at all or an empty rom. We mark it
 * as unknown. This check is only done for the first 2kB of the total
 * 4kB block.
 */

	mov	ax,ds
	mov	es,ax			# ES:DI - pointer into rom buffer
	mov	di,offset rombuf
	mov	si,bx			# SI - end of signature tables
	shl	si,1
	xor	bx,bx			# BX - index into signature tables
	mov	dx,STARTSEG
1:	cmp	dx,sigsegs[bx]		# check if below current signature seg
	jb	4f
	mov	cx,sigsize[bx]		# compute end paragraph of current
	add	cx,sigsegs[bx]		# signature segment
	cmp	dx,cx
	jae	2f			# check if within current signature seg
	test	word ptr sigvalid[bx],0x00FF
	jz	4f			# when invalid do normal memory scan

	call	chkram			# when within a valid rom signature area
	mov	al,TYPE_RAM		# we distinguish only between RAM and
	jz	9f			# ROM and dont check for UNKNOWN
	mov	al,TYPE_ROM
	jmp	9f

2:	cmp	bx,si
	jae	4f			# if above current signature seg advance
	add	bx,2			# to next segment until reaching end
	jmp	1b			# of all segments

4:	call	chkram			# check if paragraph is in ram
	mov	al,TYPE_RAM		# identify ram in info buffer
	jz	9f
	mov	ax,ROMCHKSIZE
	call	chkequ			# check if paragraph contains only
	mov	ah,al			# the same values
	mov	al,TYPE_ROM		# we found some real rom
	jnz	9f
	mov	al,TYPE_EMPTY
	or	ah,ah			# if the area contains all the same
	jz	9f			# byte value, check if thats $00 or
	cmp	ah,0xFF			# $FF. Use a smaller dot for displaying
	je	9f			# these
	mov	al,TYPE_UNKNOWN

9:	stosb				# save ram character into buffer
	add	dx,ROMSTEP		# continue with next step
	cmp	dx,ENDSEG
	jb	1b

	sub	di,offset rombuf	# prepare return values
	mov	word ptr si_romsize[bp],di
	mov	dword ptr si_rombuf[bp],offset rombuf

/*
 * Scan through all signature rom areas and try to identify one with the
 * netboot signature.
 */

	mov	bx,si_signum[bp]
	or	bx,bx			# no netboot rom if no rom areas
	jz	2f
	dec	bx
	shl	bx,1
1:	mov	dx,sigsegs[bx]
	call	fndnb			# identify each rom area in turn
	jnc	8f
	sub	bx,2			# continue with next rom area
	jnc	1b

2:	mov	bx,-1
	xor	ax,ax			# no netboot rom found
	jmp	9f

8:	shr	bx,1
9:	mov	si_nbindex[bp],bx	# save values found
	mov	si_nbmajor[bp],ah
	mov	si_nbminor[bp],al

/*
 * Scan through all signature rom areas and try to find a copyright string.
 */

	mov	bx,si_signum[bp]
	or	bx,bx			# dont scan if no rom areas found
	jz	9f
	dec	bx
	shl	bx,1
1:	mov	dx,sigsegs[bx]
	call	fndcpy			# find a copyright string in rom area
	mov	sigcpyrt[bx],ax
	sub	bx,2			# continue with next rom area
	jnc	1b

9:	mov	dword ptr si_sigcpyrt[bp],offset sigcpyrt
	pop	edx
	pop	ecx
	pop	ebx
	pop	di
	pop	si
	pop	es
	pop	bp
	ret



/*
 *====================================================================
 *
 * Verify ROM checksum
 * Input:  AX  -  ROM size in paragraphs
 *         DX  -  ROM start segment
 * Output: Zero flag cleared if checksum is not correct
 * Registers changed: AX
 */
chksum:	cld
	push	ds
	push	si
	push	bx
	push	cx
	push	dx
	or	ax,ax			# nothing to do for zero size
	jz	9f
	mov	bx,ax
	xor	ah,ah			# checksum goes into AH
1:	mov	ds,dx
	mov	cx,16			# check one paragraph
	xor	si,si
2:	lodsb				# load next byte from paragraph
	add	ah,al			# add it to checksum
	loop	2b
	inc	dx			# continue with next paragraph
	dec	bx
	jnz	1b
	or	ah,ah			# set zero flag according to checksum
9:	pop	dx
	pop	cx
	pop	bx
	pop	si
	pop	ds
	ret



/*
 *====================================================================
 *
 * Check if a paragraph is within a ram
 * Input:  DX  -  paragraph address
 * Output: Zero flag set if paragraph is in ram
 * Registers changed: AX
 */
chkram:	push	cx
	push	es
	cli				# this is sensitive to interrupts
	mov	es,dx
	mov	ax,word ptr es:[0]	# load first word of paragraph
	mov	cx,ax
	not	ax			# negate word and write it back
	mov	word ptr es:[0],ax
	cmp	word ptr es:[0],ax	# check if we could write the new value
	mov	word ptr es:[0],cx	# restore old value in case this was ram
	sti
	pop	es
	pop	cx
	ret



/*
 *====================================================================
 *
 * Check if a ROM area contains all the same byte values
 * Input:  AX  -  ROM size in paragraphs
 *         DX  -  ROM start segment
 * Output: AL  -  Value of first byte of ROM area
 *         Zero flag set if all bytes are the same
 * Registers changed: AX
 */
chkequ:	cld
	push	es
	push	di
	push	bx
	push	cx
	push	dx
	or	ax,ax			# nothing to do for zero size
	jz	9f
	mov	bx,ax
	mov	es,dx
	mov	al,byte ptr es:[0]	# compare value goes into AL
1:	mov	es,dx
	mov	cx,16			# check one paragraph
	xor	di,di
	repe	scasb
	jnz	9f
	inc	dx			# continue with next paragraph
	dec	bx
	jnz	1b
9:	pop	dx
	pop	cx
	pop	bx
	pop	di
	pop	es
	ret


/*
 *====================================================================
 *
 * Identify a rom area as a netboot bootrom.
 * Input:  DX  -  paragraph of rom area
 * Output: AH  -  major version number of bootrom
 *         AL  -  minor version number of bootrom
 *         Carry flag set if bootrom not found
 * Registers changed: AX
 */
fndnb:	cld
	push	bx
	push	cx
	push	si
	push	di
	push	es
	mov	es,dx
	mov	di,ROMIDOFS			# check product name string,
	mov	si,offset nbsig1		# e.g. bootrom signature
	mov	cx,_I(nbsig1_size)
	repe	cmpsb
	je	2f

/*
 * Couldnt find new bootrom signature, so try to find an old identifying
 * string within the first 512 bytes of rom area.
 */

	xor	bx,bx
1:	mov	di,bx
	mov	si,offset nbsig2
	mov	cx,_I(nbsig2_size)
	repe	cmpsb				# find identifying string
	je	2f
	inc	bx
	cmp	bx,0x0200 + _I(nbsig2_size)
	jbe	1b
8:	stc					# signature not found
	jmp	9f

2:	mov	ah,byte ptr es:[ROMMAJOROFS]	# get major version number
	cmp	ah,VER_MAJOR			# has to be identical with
	jne	8b				# current major version number
	mov	al,byte ptr es:[ROMMINOROFS]	# get minor version number
	cmp	al,VER_MINOR			# has to be lower or equal to
	ja	8b				# current minor version number
	clc
9:	pop	es
	pop	di
	pop	si
	pop	cx
	pop	bx
	ret



/*
 *====================================================================
 *
 * Find a copyright string in a rom area
 * Input:  DX  -  paragraph of rom area
 * Output: AX  -  offset to copyright text (0 if not found)
 * Registers changed: AX
 */
fndcpy:	cld
	push	bx
	push	cx
	push	si
	push	di
	push	es
	mov	es,dx
	xor	bx,bx
1:	mov	di,bx
	mov	si,offset cpyrt
	mov	cx,_I(cpyrt_size)
	repe	cmpsb				# find copyright string
	je	9f
	inc	bx
	cmp	bx,0x0200 + _I(cpyrt_size)
	jbe	1b
	xor	bx,bx				# no copyright text found
9:	mov	ax,bx
	pop	es
	pop	di
	pop	si
	pop	cx
	pop	bx
	ret




/*
 *====================================================================
 *
 * Read-only data segment
 */
	_rodata

nbsig1:	.asciz	NBROMID				# signature to identify a
nbsig1_size = . - nbsig1			# netboot bootrom


nbsig2:	.ascii	"Uncompressing... "		# old netboot bootrom strings
	.byte	0
	.ascii	"done"
	.byte	0x0D,0x0A,0
nbsig2_size = . - nbsig2


cpyrt:	.ascii	"Copyright"			# copyright string to look for
cpyrt_size = . - cpyrt




/*
 *====================================================================
 *
 * BSS segment
 */
	.bss

	.lcomm	sigsegs,(MAXSIGS + 1) * 2	# segments of rom signatures
	.lcomm	sigsize,(MAXSIGS + 1) * 2	# size of roms in paragraphs
	.lcomm	sigvalid,(MAXSIGS + 1) * 2	# flags for valid checksums
	.lcomm	sigcpyrt,(MAXSIGS + 1) * 2	# copyright offset buffer
	.lcomm	rombuf,ROMBSIZE	 + 2		# physical rom info buffer




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

