; 	[]==========================================================[]
;
;	    This source code is classified as confidential and 
;	    contains trade secrets owned by Award Software, Inc.
;
;	    		Copyright 1984, 1997
;			Award Software, Inc.
;			All rights reserved.
;
; 	[]==========================================================[]

;----------------------------------------------------------------------------
;Rev	Date	Name	Description
;----------------------------------------------------------------------------
;R12	10/28/98 DNL	Added APIC plamform MP support for NT 5.0
;R11	09/19/98 TNY	Always set "FACP_Pointer" to be public.
;R10	09/19/98 TNY	Set "SetRealModeLimit" hook to be public.
;R09	04/02/98 DNL	Fixed coding mistake
;R08	02/19/98 DNL	Add simple boot spec. v0.99b support
;R07	02/16/98 RIC	Add "Special_ACPI_Table_Update" define.
;R06	12/04/97 DNL	Added misc.equ include file
;R05	12/01/97 DNL	Added Thermal Zone support
;R04	11/25/97 DNL	Fixed ACPI HCT testing error
;R03A 	08/08/97 DNL	Fixed ACPI Table setup failure problem
;R03	08/07/97 DNL	Add ACPI Table size runtime assign function
;R02	03/28/97 LRY	Fixed system hang at post code 3Dh
;R01	03/11/97 KVN	Fixed system hang from SETUP exit or run warmboot
;	012996	ATT	Add handler for wake event
;	012796	ATT	Move FACS table to NVS area instead of reclaim area
;R00	123096	ATT	First rev for ACPI
;			This file contains RSD PTR table and routines to
;			copy other ACPI tables from below 1Mb to top of
;			extended memory.  Also, fill in each table's pointer
;			and checksum.
;----------------------------------------------------------------------------
		
		PAGE	56,132
		.386p
		include	bios.cfg
ifdef	ACPI_Support
		include	common.mac
		include	acpi.inc
		include	acpi_ct.inc
		include	misc.equ		;R06
		extrn	F000_Shadow_W:near
		extrn	F000_Shadow_R:near
		extrn	ACPITableAddress:near
		extrn	ACPINVSAddress:near
		extrn	A20_On:near
		extrn	A20_Off:near
ifdef	Special_ACPI_Table_Update			;R07
		extrn	Ct_ACPI_Table_Update:Near	;R07
endif;	Special_ACPI_Table_Update			;R07
if	CPU_THERMAL_SUPPORT	EQ	1		;R06
		extrn	Ct_FillTempPointer:near		;R05
endif	;CPU_THERMAL_SUPPORT	EQ	1		;R06
ifdef	MP_SUPPORT					;R12
		extrn	If_MP_PLUGGED:near		;R12
		MPoffset	=	38h		;R12
endif	;MP_SUPPORT					;R12

;R04 - start
CK_STRING_LENGTH	MACRO	SOFFSET
IF2
	IF ($ - SOFFSET NE 6)
		%OUT "ACPI_OEMID" IS 6 BYTE STRING. !!
		.ERR
	ENDIF
ENDIF
		ENDM
;R04 - end

DGROUP		GROUP	FCODE
FCODE		SEGMENT	PARA PUBLIC 'CODE'

		ASSUME	CS:DGROUP,DS:DGROUP
;-----------------------------------------------------------------------
;Build RSD PTR	table
;
		ALIGN	16
		Public	RSD_PTR
RSD_PTR:	db	"RSD PTR "		;RSD PTR Signature
RSD_CKSM:	db	0			;Checksum, fill in at POST	
;R04 		db	6 dup (?)		;OEMID
ACPIID_STR	db	ACPI_OEMID		;R04 OEMID
		CK_STRING_LENGTH <offset ACPIID_STR>	;R04
		db	0			;Reserved
RSDT_Ptr:	db	4 dup (?)		;RSDT physical address, fill in at POST
RSDPTRLength	EQU	($-RSD_PTR) 		;Total length of RSD_PTR table

;Keep the pointer for ACPI tables after move up to ACPI reclaim area
;so we don't have to scan for them again. These could be locate in E000.
;Since the code enable F000 shadow to writeable, it's just better to save them 
;in F000 (only 16bytes)
;Note: The oder is important. It's one-to-one corresponding to ACPITables
;
		ALIGN	4
		Public	TempBuffArea
TempBuffArea:
RSDT_Pointer	dd	0		;Store RSDT pointer
;R11 ifdef	Special_ACPI_Table_Update	;R07
		Public	FACP_Pointer		;R07
;R11 endif;	Special_ACPI_Table_Update	;R07
FACP_Pointer	dd	0		;Store FACP pointer
DSDT_Pointer	dd	0		;Store DSDT pointer
ifdef	SIMPLE_BOOT_SUPPORT		;R08
BOOT_Pointer	dd	0		;R08 Store BOOT pointer
endif	;SIMPLE_BOOT_SUPPORT		;R08
ifdef	MP_SUPPORT			;R12
APIC_Pointer	dd	0		;R12
endif	;MP_SUPPORT			;R12
		Public	FACS_Pointer
FACS_Pointer	dd	0		;Store FACS pointer
FCODE		ENDS

;--------------------------------------------------------------
;----- The following codes will be located at E000 ! ----------
;--------------------------------------------------------------
.386p
EGROUP		GROUP	ECODE			
ECODE		SEGMENT USE16 PARA PUBLIC 'ECODE'
		ASSUME	CS:EGROUP, DS:NOTHING, ES:NOTHING
;		include	ACPI.INC
RepMovsb32	Macro
		db	0f3h	;REP
		db	067h	;32bit addressing (ESI/EDI)
		db	0a4h	;MOVSB
		Endm

		ALIGN	4
		extrn	f000_call_proc:near

		Public	ACPITables
ACPITables:
RSDT		dd	"TDSR"
FACP		dd	"PCAF"
DSDT		dd	"TDSD"
ifdef	SIMPLE_BOOT_SUPPORT			;R08
BOOT		dd	"TOOB"			;R08
endif	;SIMPLE_BOOT_SUPPORT			;R08
ifdef	MP_SUPPORT				;R12
APIC		dd	"CIPA"			;R12
endif	;MP_SUPPORT				;R12
NoACPITables	EQU	($ - offset ACPITables)/4
FACS		dd	"SCAF"

;[]========================================================================[]
;
;
; Entry: 
;
; Exit:  NONE
;
; Destroy : AX,DX
;
;[]========================================================================[]
;[]========================================================================[]
; SetupACPI
;
; Entry:After extended memory test, copy all ACPI tables to ACPI reclaim
;	area (just below top of extended memory, see ATBASE INT15 function
;	0e820h).  Then fill in physical address and checksum for all ACPI
;	tables 
;
; Exit:  NONE
;
; Destory : All reserved
;
;[]========================================================================[]
		Public	SetupACPI
SetupACPI	Proc	Near
		push	ds
		push	es
		pushad
;R03A		push	edx			;R03
		mov	al, 1			
		call	SetRealModeLimit	;Enter big real mode
		F000_Call F000_Shadow_W		;Set F000 shadow to writeable
;R03A;R03 - start
;R03A		pop	edx			
;R03A		test	edx,03ffh		
;R03A		jz	short @f		
;R03A		add	edx,400h		
;R03A@@:						
;R03A		and	edx,not 03ffh		
;R03A		mov	edi,0f0000h		
;R03A		add	edi,offset ACPITableAddress + 8
;R03A		mov	es:[edi],edx
;R03A;R03 - end
		call	GetACPIReclaimArea	;Return EAX=physical address of
						;reclaim memory area
		call	MoveACPIToReclaim	;Move RSDT, FACP, DSDT tables to reclaim area
		call	GetACPINVSArea		;Return EAX=physical address of 
						;ACPI NVS area
		call	MoveACPItoNVS		;Move FACS table to NVS area.
		call	FillACPIAddress		;Fill in the pointers for all tables

ifdef	Special_ACPI_Table_Update		;R07
		call	Ct_ACPI_Table_Update	;R07
endif;	Special_ACPI_Table_Update		;R07

		call	FillACPIChecksum	;Fill in check sum for all tables
if	CPU_THERMAL_SUPPORT	EQ	1	;R06
		call	Ct_FillTempPointer	;R05
endif	;CPU_THERMAL_SUPPORT	EQ	1	;R06
		F000_Call F000_Shadow_R		;Set F000 shadow to readable
		xor	al, al			;
		call	SetRealModelimit	;Leave big real mode
NoACPITable:
		popad
		pop	es
		pop	ds
		ret
SetupACPI	Endp
;[]========================================================================[]
; SearchACPITables
; Search ACPI table and return physical address
; Entry:EAX = ACPI table signature e.g "RSDT" 
;	EBX = Start physical address
;	EDX = End physical address
; Exit: ESI = Physical address of table found
;	NC  = ACPI table found
;	CY  = Error, ACPI table not found	
;
; Destroy : ESI
;
;[]========================================================================[]
SearchACPITables	Proc	Near
		clc					;clear the flag
		mov	esi, ebx			;start from here
SearchACPILoop:
		cmp	dword ptr ds:[esi], eax		;ACPI table?
		je	short @f			;Found it, 
		inc	esi	  			;Next byte
		cmp	esi, edx			;Reach the end?
		jb	SearchACPILoop
		stc
		jmp	SearchACPIExit			;Set CF and jump out
@@:		clc
SearchACPIExit:
		ret
SearchACPITables	Endp
;[]========================================================================[]
; GetACPIReclaimArea
; Return starting physical address of ACPI reclaim area
; Entry: NONE
;
; Exit:  EAX
;
; Destroy : EAX
;
;[]========================================================================[]
GetACPIReclaimArea	Proc	Near
		push	ds
		mov	ax, 0f000h
		mov	ds, ax
		mov	eax, dword ptr ds:[ACPITableAddress]
		and	eax,not 0ffh		;R09 clear end maker
		pop	ds
		ret
GetACPIReclaimArea	Endp
;[]========================================================================[]
; GetACPINVSArea
; Return starting physical address of ACPI NVS area
; Entry: NONE
;
; Exit:  EAX
;
; Destroy : EAX
;
;[]========================================================================[]
GetACPINVSArea	Proc	Near
		push	ds
		mov	ax, 0f000h
		mov	ds, ax
		mov	eax, dword ptr ds:[ACPINVSAddress]
		and	eax,not 0ffh		;R09 clear end maker
		pop	ds
		ret
GetACPINVSArea	Endp
;[]========================================================================[]
; MoveACPIToReclaim
; Move copies of ACPI tables from somewhere below 1Mb to top of extended
; memory. Looping from source starting address, look for valid header signature
; Get the length at byte offset 4, and move the each table up one by one.
; RSDT is the first table to look for (see ACPITables.)
; Entry: EAX = ACPI reclaim memory destination 
; Exit:  None
; Destroy : None
;
;[]========================================================================[]
		Public	MoveACPIToReclaim
MoveACPIToReclaim	Proc	Near
		push	eax
		push	cx
		push	ebp
		xor	ebp, ebp
;R02		mov	cl, NoACPITables		;Get total number of tables
		mov	cx, NoACPITables		;Get total number of tables ;R02
		mov	edi, eax			;Get ACPI reclaim address
MoveAnother:
		mov 	eax, dword ptr cs:[ACPITables+bp];Get table signature
		mov	ebx, ACPISearchStart		;Look from here
		mov	edx, ACPISearchEnd		;To here
		call	SearchACPITables		;Return ESI			
		jc	short NotFound
;DSDT table need to be 64 byte aligned.
;Just align all table to make it easier.
		test	di, 03fh			;aligned?
		jz	short @f		    	;Yes, jump over
		add	edi, 40h			;
		and	di, 0ffc0h			;Make it align
@@:
		push	esi				;Need to use ESI for awhile								
		mov	esi, ACPISeg			;Save current ACPI pointer
		or	esi, offset TempBuffArea
		add	esi, ebp 			;Get buffer pointer
		mov	dword ptr ds:[esi], edi		;Save ACPI pointer
		pop	esi 				;Get ESI=source pointer
		call	MoveTables			;Use current ESI and EDI
NotFound:
		add	bp, 4 				;Add pointer to next table
		loop	MoveAnother			;Loop until end		
		pop	ebp
		pop	cx
		pop	eax
		ret
MoveACPIToReclaim	Endp
;[]========================================================================[]
; MoveACPIToNVS
; Move copies of ACPI tables from somewhere below 1Mb to top of extended
; memory. Looping from source starting address, look for valid header signature
; Get the length at byte offset 4, and move the each table up one by one.
; RSDT is the first table to look for (see ACPITables.)
; Entry: EAX = ACPI reclaim memory destination 
; Exit:  None
; Destroy : None
;
;[]========================================================================[]
		Public	MoveACPIToNVS
MoveACPIToNVS	Proc	Near
		push	eax
		mov	edi, eax			;Get ACPI reclaim address
		mov 	eax, FACS
		mov	ebx, ACPISearchStart		;Look from here
		mov	edx, ACPISearchEnd		;To here
		call	SearchACPITables		;Return ESI			
		jc	short FACSNotFound
		push	esi				;Need to use ESI for awhile								
		mov	esi, ACPISeg			;Save current ACPI pointer
		or	esi, offset FACS_Pointer
		mov	dword ptr ds:[esi], edi		;Save ACPI pointer
		pop	esi 				;Get ESI=source pointer
		call	MoveTables			;Use current ESI and EDI
FACSNotFound:
		pop	eax
			ret
MoveACPItoNVS		Endp
;[]========================================================================[]
; MoveTables
; Similiar to REP MOVSB, but works with 32 bits ESI and EDI
;
; Entry:ESI = source starting physical address
; 	EDI = destination starting physical address
; Exit:  NONE
; Destroy : ESI, EDI
;
;[]========================================================================[]
		Public	MoveTables
MoveTables	Proc	Near
		push	ecx
		cld
		mov	ecx, dword ptr ds:[esi]+LengthOffset
		RepMovsb32
		pop	ecx
		ret
MoveTables	Endp
;[]========================================================================[]
; FillACPIAddress
; For each ACPI table, fill in the pointer of other table.
; e.g RSDT needs a pointer to FACP.  FACP needs a pointer to FACS and DSDT
; Entry: Pointers are saved in TempBuffArea
;
; Exit:  None
;
; Destroy : None
;
;[]========================================================================[]
FillACPIAddress	Proc	Near
		push	edi
		push	esi				
		mov	esi, ACPISeg
;RSD_PTR table need RSDT pointer
;
		mov	eax, dword ptr ds:[esi]+offset RSDT_Pointer	;RSD_PTR
		or	eax, eax
		jz	FillExit
		mov	dword ptr ds:[esi]+offset RSDT_Ptr, eax		;
;FACP table need FACS and DSDT pointers
;
		mov	eax, dword ptr ds:[esi]+offset DSDT_Pointer	;
		or	eax, eax
		jz	FillExit
		mov	edi, dword ptr ds:[esi]+offset FACP_Pointer	;FACP
		mov	dword ptr es:[edi]+DSDTOffset, eax		;

		mov	eax, dword ptr ds:[esi]+offset FACS_Pointer	;
		or	eax, eax
		jz	FillExit
		mov	edi, dword ptr ds:[esi]+offset FACP_Pointer	;FACP
		mov	dword ptr es:[edi]+FACSOffset, eax		;
;RSDT table need FACP pointers
;
		mov	eax, dword ptr ds:[esi]+offset FACP_Pointer	;
		or	eax, eax
		jz	FillExit
		mov	edi, dword ptr ds:[esi]+offset RSDT_Pointer	;RSDT
		mov	dword ptr es:[edi]+FACPOffset, eax		;
;R08 - start
ifdef	SIMPLE_BOOT_SUPPORT
		mov	eax, dword ptr ds:[esi]+offset BOOT_Pointer	;
		or	eax, eax
		jz	short @F
		mov	edi, dword ptr ds:[esi]+offset RSDT_Pointer	;RSDT
		mov	dword ptr es:[edi]+BOOTOffset, eax		;
@@:
endif	;SIMPLE_BOOT_SUPPORT
;R08 - end
;R12 - start
ifdef	MP_SUPPORT
		mov	eax, dword ptr ds:[esi]+offset APIC_Pointer	;
		or	eax, eax
		jz	short @F
		mov	edi, dword ptr ds:[esi]+offset RSDT_Pointer	;RSDT
		mov	dword ptr es:[edi]+APICOffset, eax		;
		mov	edi,eax
		push	es
		Call	If_MP_PLUGGED
		pop	es
		jnc	short @f
		and	dword ptr es:[edi]+MPOffset, Not 01h
@@:
endif	;MP_SUPPORT
;R12 - end
FillExit:
		pop	esi
		pop	edi
		ret
FillACPIAddress	Endp

;[]========================================================================[]
; FillACPIChecksum
; For each ACPI table, caculate the checksum of entire table and fill in
; according to table type (RSD_PTR is different with the rest)
;
; Entry: 
;
; Exit:  None
;
; Destroy : None
;
;[]========================================================================[]
FillACPIChecksum	Proc	Near
		push	ax
		push	cx
		push	esi
		push	ebp
		xor	ecx, ecx
		xor	ebp, ebp
;Do RSD_PTR checksum first because it's different than others
;
		mov	esi, ACPISeg
		or	esi, offset RSD_PTR
		mov	cl, RSDPTRLength
		xor	al, al			;Zero out checksum byte first
		mov	byte ptr ds:[esi]+RSDPTRChksumOffset, al
		call	GetCheckSum
		mov	byte ptr ds:[esi]+RSDPTRChksumOffset, al
;Other ACPI tables checksum
;
		xor	ecx, ecx
		mov	cl, NoACPITables
ChksumAgain:
		push	ecx			;Save number of tables to do checksum
		mov	esi, ACPISeg
		or	esi, offset TempBuffArea
		add	esi, ebp
		mov	esi, dword ptr ds:[esi]			;Get start address
		mov	ecx, dword ptr ds:[esi]+LengthOffset	;Get total length
		xor	al, al					;Zero out checksum byte first
		mov	byte ptr ds:[esi]+ChksumOffset, al	;
		call	GetCheckSum
		mov	byte ptr ds:[esi]+ChksumOffset, al	;Fill checksum for this table
SkipChecksum:
		pop	ecx
		add	bp, 4 					;Next table
		loop	ChksumAgain		
		pop	ebp
 		pop	esi
		pop	cx
		pop	ax
		ret
FillACPIChecksum	Endp
;[]========================================================================[]
; GetCheckSum		
; Calculate the checksum for a block of data
;
; Entry: ECX =Length
;	 ESI=Start physical address of block to do checksum
; Exit:  AL =checksum byte
;
; Destroy : AX
;
;[]========================================================================[]
GetCheckSum	Proc	Near
		push	ecx
		push	esi
		xor	al, al
chkSumLoop:
		add	al, byte ptr ds:[esi]	; get a byte and add it
		inc	esi			; Update pointer
		loop	chkSumLoop		; Loop till done
		neg	al			; Make zero compliment
		pop	esi
		pop	ecx
		ret
GetCheckSum	Endp

;[]========================================================================[]
; SetRealModeLimit
; Setup ES and DS segment limit for real mode
; Entry:AL=1	ES and DS will set to 4Gb segment limit
;	AL=0	ES and DS will set back to 64Kb segment limit
;
; Exit:  NONE
;
; Destroy : ES, DS
;
;[]========================================================================[]
		public	SetRealModeLimit	;R10
SetRealModeLimit	Proc	Near
		push	eax
		or	al, al	 		;Set up as 64Kb limit?
		jz	short @f		;
		F000_call A20_On		;enable A20 for 4Gb limit
		jmp	short Enable4Gb
@@:
		F000_call A20_Off		;disable A20 for 64Kb limit
Enable4Gb:
                mov     ax,cs
                mov     es,ax
		lea	di, GDTR
		cli				;clear interrupt
                lgdt    fword ptr es:[di]	;Load Global Descriptor Table
                mov     eax,cr0
                or      al,1
                mov     cr0,eax			;Enter protected mode
 		jmp  	@f			;jump to clear CPU instruction queue
@@:
		pop	eax			;Get AL 
		push	eax			;Save EAX again
		or	al, al	 		;Set up as 64Kb limit?
		jz	short @f		;
                mov     ax, DATA4G_INDEX    	;No, set DS, ES to 4Gb limit
		jmp	short Enable4Gb_
@@:						
		mov	ax, DATA64K_INDEX	;Set DS, ES back to 64kb limit
Enable4GB_:
		mov	es,ax			;
		mov	ds,ax
;Switch back to real mode, DS and ES limits are set
;
    		mov  eax,cr0
    		and  al,NOT 1
    		mov  cr0,eax
    		jmp  @f				; clear prefetch queue.
@@:
		xor	ax, ax
		mov	ds, ax			;DS and ES limit are set
		mov	es, ax
		pop	eax
		ret
SetRealModeLimit	Endp
		ALIGN	4
GDTR:						; global descriptor table register
		dw	8*3			; LIMIT
		dw	offset GDT1
		dw	0eh			; in 0e000h segment
GDT1:						; null descriptor
		dw	0			; limit
		dw	0			; base
		db	0			; hibase
		db	0			; access
		db	0			; hilimit
		db	0			; msbase
DATA64K:					; cs - prom code segment
DATA64K_INDEX	=	((offset DATA64K - offset GDT1)/8) SHL 3
		dw	0ffffh			; limit
		dw	0			; base	
		db	0			; hibase   
		db	93h			; access
		db	0			; hilimit
		db	0			; msbase   
DATA4G:
DATA4G_INDEX	=	((OFFSET DATA4G - OFFSET GDT1)/8) SHL 3
		dw	0ffffh			; limit
		dw	0			; base data segment points to
		db	0			; hibase	; 00000000
		db	93h			; access
		db	08fh			; hilimit (4GB)
		db	0			; msbase
ECODE		ENDS
;--------------------------------------------------------------------------
;
;R01DGROUP		GROUP	@DATAC
;R01@DATAC		SEGMENT	USE16 DWORD COMMON 'DATAC'
;R01		ASSUME	CS:DGROUP,DS:DGROUP
;R01		extrn	NotACPIWakeEvent:near
;R01		Public	CheckACPIWakeEvent
;R01CheckACPIWakeEvent:
;R01		mov	dx, PM1a_CNT_BLK	;Get PM1 Control register
;R01		in	al, dx
;R01		test	al, SCI_En		;Is SCI bit enabled?
;R01		jz	NotACPIWakeEvent	;If not, this is normal reset. 
;R01		inc	dx			;Next byte
;R01		in	al, dx			;Read this register			
;R01		cmp	al, 0ffh		;If not valid IO, jump out.
;R01		je	NotACPIWakeEvent
;R01		and	al, SLP_TYPE_MASK	;Isolate sleep type bits
;R01		call	RestoreContext 		;Restore context for this state
;R01		call	GetWakingVector		;Get the waking vector
;		jmp	word ptr ds:[si]	;Pass control to OS 		

;R01;[]========================================================================[]
;R01;RestoreContext
;R01; Entry: AL=Sleep Type 
;R01; Exit:  NONE
;R01; Destroy : 
;R01;[]========================================================================[]
;R01RestoreContext	Proc	Near
;R01		ret
;R01RestoreContext	Endp
;R01;[]========================================================================[]
;R01;
;R01; Entry:Nothing 
;R01; Exit: DS=Segment of waking vector
;R01;	SI=Offset of waking vector
;R01; Destroy : AX,DX
;R01;[]========================================================================[]
;R01GetWakingVector	Proc	Near
;R01;		mov	eax, dword ptr cs:FACS_Pointer			;Get start address
;R01;		add	eax, WakingVector
;R01;Need to go into protect mode to access FACS table to get waking vector.
;R01;		call	EnterProtectMode
;R01;		mov	eax, dword ptr ds:[eax]		
;R01		ret
;R01GetWakingVector	Endp
;R01@DATAC		ENDS

endif;	ACPI_Support
		END
