/*
 * Copyright (C) 1994 by HOSOKAWA, Tatsumi <hosokawa@jp.FreeBSD.org>
 * Copyright (C) 1997 by Poul-Henning Kamp <phk@FreeBSD.org>
 *
 * This software may be used, modified, copied, distributed, and sold,
 * in both source and binary form provided that the above copyright and
 * these terms are retained. Under no circumstances is the author 
 * responsible for the proper functioning of this software, nor does 
 * the author assume any responsibility for damages incurred with its 
 * use.
 *
 * Sep., 1994	Implemented on FreeBSD 1.1.5.1R (Toshiba AVS001WD)
 *
 *	$Id: apm_setup.s,v 1.13 1998/06/03 01:59:34 msmith Exp $
 */

#include "apm.h"
  
#if NAPM > 0

#define ASSEMBLER
#include "assym.s"                    /* system definitions */
#include <machine/asmacros.h>         /* miscellaneous asm macros */
#include <machine/apm_bios.h>
#include <machine/apm_segments.h>
#define PADDR(addr)        addr-KERNBASE
  
	.file	"apm_setup.s"

	.data
_apm_init_image:
	.globl	_apm_init_image

1:
#include "i386/apm/apm_init/apm_init.inc"
2:

_apm_init_image_size:
	.globl	_apm_init_image_size
	.long	2b - 1b

_apm_version:
	.globl	_apm_version
	.long	0

_apm_cs_entry:
	.globl	_apm_cs_entry
	.long	0

_apm_cs16_base:
	.globl	_apm_cs16_base
	.word	0

_apm_cs32_base:
	.globl	_apm_cs32_base
	.word	0

_apm_ds_base:
	.globl	_apm_ds_base
	.word	0

_apm_cs32_limit:
	.globl	_apm_cs32_limit
	.word	0

_apm_cs16_limit:
	.globl	_apm_cs16_limit
	.word	0

_apm_ds_limit:
	.globl	_apm_ds_limit
	.word	0

_apm_flags:
	.globl	_apm_flags
	.word	0
	.globl  _apm_current_gdt_pdesc          /* current GDT pseudo desc. */
_apm_current_gdt_pdesc:
	.word   0, 0, 0

	.globl  _bootstrap_gdt
_bootstrap_gdt:
	.space  SIZEOF_GDT*BOOTSTRAP_GDT_NUM 

	.text
_apm_setup:
	.globl	_apm_setup

	/*
	 * Setup APM BIOS:
	 *
	 * APM BIOS initialization should be done from real mode or V86 mode.
	 *
	 * (by HOSOKAWA, Tatsumi <hosokawa@jp.FreeBSD.org>)
	 */

	/*
	 * Don't trust the value of %fs and %gs (some AT-compatible BIOS 
	 * implementations leave junk values in these segment registers
	 * on bootstrap)
	 */
	xorl	%eax, %eax	/* null selector */
	movw	%ax, %fs
	movw	%ax, %gs

	/* 
         * Copy APM initializer under 1MB boundary:
	 *
	 * APM initializer program must switch the CPU to real mode.
	 * But FreeBSD kernel runs above 1MB boundary. So we must 
	 * copy the initializer code to conventional memory.
	 */
	movl	PADDR(_apm_init_image_size), %ecx	/* size */
	lea	PADDR(_apm_init_image), %esi		/* source */
	movl	$ APM_OURADDR, %edi			/* destination */
	cld
	rep
	movsb

	/* get GDT base */
	sgdt	PADDR(_apm_current_gdt_pdesc)

	/* copy GDT to _bootstrap_gdt */
	xorl	%ecx, %ecx
	movw	PADDR(_apm_current_gdt_pdesc), %cx
	movl	PADDR(_apm_current_gdt_pdesc + 2), %esi
	lea	PADDR(_bootstrap_gdt), %edi
	cld
	rep
	movsb

	/* setup GDT pseudo descriptor */
	movw	$(SIZEOF_GDT*BOOTSTRAP_GDT_NUM), %ax
	movw	%ax, PADDR(_apm_current_gdt_pdesc)
	leal	PADDR(_bootstrap_gdt), %eax
	movl	%eax, PADDR(_apm_current_gdt_pdesc + 2)

	/* load new GDTR */
	lgdt	PADDR(_apm_current_gdt_pdesc)

	/* setup GDT for APM initializer */
	lea	PADDR(_bootstrap_gdt), %ecx
	movl	$(APM_OURADDR), %eax	/* use %ax for 15..0 */
	movl	%eax, %ebx
	shrl	$16, %ebx		/* use %bl for 23..16 */
					/* use %bh for 31..24 */
#define APM_SETUP_GDT(index, attrib) \
	movl	$(index), %si ; \
	lea	0(%ecx,%esi,8), %edx ; \
	movw	$0xffff, (%edx) ; \
	movw	%ax, 2(%edx) ; \
	movb	%bl, 4(%edx) ; \
	movw	$(attrib), 5(%edx) ; \
	movb	%bh, 7(%edx)

	APM_SETUP_GDT(APM_INIT_CS_INDEX  , CS32_ATTRIB)
	APM_SETUP_GDT(APM_INIT_DS_INDEX  , DS32_ATTRIB)
	APM_SETUP_GDT(APM_INIT_CS16_INDEX, CS16_ATTRIB)
	APM_SETUP_GDT(APM_INIT_DS16_INDEX, DS16_ATTRIB)

	/*
	 * Call the initializer:
	 *
	 * direct intersegment call to conventional memory code
	 */
	.byte	0x9a		/* actually, lcall $APM_INIT_CS_SEL, $0 */
	.long	0
	.word	APM_INIT_CS_SEL

	movl	%eax, PADDR(_apm_version)
	movl	%ebx, PADDR(_apm_cs_entry)
	movw	%cx, PADDR(_apm_cs16_base)
	shrl	$16, %ecx
	movw	%cx, PADDR(_apm_cs32_base)
	movw	%dx, PADDR(_apm_ds_base)
	movw	%si, PADDR(_apm_cs32_limit)
	shrl	$16, %esi
	movw	%si, PADDR(_apm_ds_limit)
	movw	%di, PADDR(_apm_flags)

	ret

.text
	.align 2
	.globl	_apm_bios_call
_apm_bios_call:
	pushl	%ebp
	movl	8(%esp),%ebp
	pushl	%esi
	pushl	%edi
	pushl	%ebx
	movl	20(%ebp),%edi
	movl	16(%ebp),%esi
	movl	12(%ebp),%edx
	movl	8(%ebp),%ecx
	movl	4(%ebp),%ebx
	movl	0(%ebp),%eax
	pushl	%ebp
	lcall	_apm_addr
	popl	%ebp
	movl	%eax,0(%ebp)
	jc	1f
	xorl	%eax,%eax
	jz	2f
1:	movl	$1, %eax
2:	movl	%ebx,4(%ebp)
	movl	%ecx,8(%ebp)
	movl	%edx,12(%ebp)
	movl	%esi,16(%ebp)
	movl	%edi,20(%ebp)
	popl	%ebx
	popl	%edi
	popl	%esi
	popl	%ebp
	ret
#endif NAPM > 0
