        title  "Abios Support Assembly Routines"
;++
;
; Copyright (c) Microsoft Corporation. All rights reserved. 
;
; You may only use this code if you agree to the terms of the Windows Research Kernel Source Code License agreement (see License.txt).
; If you do not agree to the terms, do not use the code.
;
;
; Module Name:
;
;    abiosa.asm
;
; Abstract:
;
;    This module implements assembly code for ABIOS support.
;
;--
.386p
        .xlist
include ks386.inc
include callconv.inc                    ; calling convention macros
include i386\kimacro.inc
include irqli386.inc
        .list

extrn   _DbgPrint:proc

extrn _KiStack16GdtEntry:DWORD

;
; This should be either 0 or 1, if it's greater than 1, then we've re-entered the BIOS.
;
extrn _KiInBiosCall:DWORD
extrn _FlagState:DWORD
extrn _KiBiosFrame:DWORD

OPERAND_OVERRIDE        equ     66h
ADDRESS_OVERRIDE        equ     67h
KGDT_CDA16              equ     0E8h

LocalStack                              equ     16          ; 4 DWORDS of slop for PnPBioses.

if DBG
extrn  KiBiosReenteredAssert:DWORD
endif

; Macro change note:
;
;   This macro pair used to do an unconditional sti coming back from the 16-bit
;   side, this potentially caused problems in APM. Now we save and restore the
;   flag state
;

;++
;
;   STACK32_TO_STACK16
;
;   Macro Description:
;
;       This macro remaps current 32bit stack to 16bit stack.
;
;   Arguments:
;
;       None.
;
;--

STACK32_TO_STACK16      macro

        pushfd
        mov     ecx,[esp]
        mov     _FlagState,ecx
        popfd
        mov     eax, PCR[PcPrcbData+PbCurrentThread] ; get current thread address
        mov     eax, [eax]+ThStackLimit ; get thread stack base
        mov     edx, eax
        mov     ecx, _KiStack16GdtEntry
        mov     word ptr [ecx].KgdtBaseLow, ax
        shr     eax, 16
        mov     byte ptr [ecx].KgdtBaseMid, al
        mov     byte ptr [ecx].KgdtBaseHi, ah
        cli
        sub     esp, edx
        mov     eax, KGDT_STACK16
        mov     ss, ax

;
; NOTE that we MUST leave interrupts remain off.
; We'll turn it back on after we switch to 16 bit code.
;

endm

;++
;
;   STACK16_TO_STACK32
;
;   Macro Description:
;
;       This macro remaps current 32bit stack to 16bit stack.
;
;   Arguments:
;
;       None.
;
;--

STACK16_TO_STACK32      macro   Stack32

        db      OPERAND_OVERRIDE
        db      ADDRESS_OVERRIDE
        mov     eax, PCR[PcPrcbData+PbCurrentThread] ; get current thread address
        db      OPERAND_OVERRIDE
        db      ADDRESS_OVERRIDE
        mov     eax, [eax]+ThStackLimit ; get thread stack limit
        cli
        db      OPERAND_OVERRIDE
        add     esp, eax
        db      OPERAND_OVERRIDE
        mov     eax, KGDT_R0_DATA
        mov     ss, ax
        db      OPERAND_OVERRIDE
        db      ADDRESS_OVERRIDE
        push ds:_FlagState
        db      OPERAND_OVERRIDE
        popfd
endm

COPY_CALL_FRAME macro FramePtr

        mov     [FramePtr].TsEax,eax
        mov     [FramePtr].TsEbx,ebx
        mov     [FramePtr].TsEcx,ecx
        mov     [FramePtr].TsEdx,edx
        mov     [FramePtr].TsEsi,esi
        mov     [FramePtr].TsEdi,edi
        mov     [FramePtr].TsEbp,ebp
        mov     [FramePtr].TsHardwareEsp,esp
        mov     [FramePtr].TsSegFs,fs
        mov     [FramePtr].TsSegCs,cs
endm
        page ,132
        subttl  "Abios Support Code"
_TEXT   SEGMENT DWORD PUBLIC 'CODE'
        ASSUME  DS:FLAT, ES:FLAT, SS:NOTHING, FS:NOTHING, GS:NOTHING

;
; BBT cannot instrument code between this label and BBT_Exclude_Selector_Code_End
;
        public  _BBT_Exclude_Selector_Code_Begin
_BBT_Exclude_Selector_Code_Begin  equ     $
        int 3


;++
; ULONG
; KiAbiosGetGdt (
;     VOID
;     )
;
; Routine Description:
;
;     This routine returns the starting address of GDT of current processor.
;
; Arguments:
;
;     None.
;
; Return Value:
;
;     return Pcr->GDT
;
;--

cPublicProc _KiAbiosGetGdt,0

        mov     eax, PCR[PcGdt]
        stdRET    _KiAbiosGetGdt

stdENDP _KiAbiosGetGdt

;++
; VOID
; KiI386CallAbios(
;     IN KABIOS_POINTER AbiosFunction,
;     IN KABIOS_POINTER DeviceBlockPointer,
;     IN KABIOS_POINTER FunctionTransferTable,
;     IN KABIOS_POINTER RequestBlock
;     )
;
; Routine Description:
;
;     This function invokes ABIOS service function for device driver.  This
;     routine is executing at DIAPTCH_LEVEL to prevent context swapping.
;
;     N.B. We arrive here from the Ke386AbiosCall with a 32bit CS. That is,
;     we're executing the code with cs:eip where cs contains a selector for a
;     32bit flat segment. We want to get to a 16bit cs. That is, cs:ip.
;     The reason is that ABIOS is running at 16 bit segment.
;     Before we can call ABIOS service we must load ss and cs segment
;     registers with selectors for 16bit segments.  We start by pushing a far
;     pointer to a label in the macro and then doing a retf. This allows us
;     to fall through to the next instruction, but we're now executing
;     through cs:ip with a 16bit CS. Then, we remap our 32-bit stack to 16-bit
;     stack.
;
; Arguments:
;
;     AbiosFunction - a 16:16 pointer to the abios service function.
;
;     DeviceBlockPointer - a 16:16 pointer to Device Block.
;
;     FunctionTransferTable - a 16:16 pointer to Function Transfer Table.
;
;     RequestBlock - a 16:16 pointer to device driver's request block.
;
; Return Value:
;
;     None.
;--

KacAbiosFunction        equ     [ebp + 8]
KacDeviceBlock          equ     [ebp + 12]
KacFunctionTable        equ     [ebp + 16]
KacRequestBlock         equ     [ebp + 20]

cPublicProc _KiI386CallAbios,4

;
; We're using a 32bit CS:EIP - go to a 16bit CS:IP
; Note the base of KiAbiosCallSelector is the flat address of _KiI386AbiosCall
; routine.
;

        push    ebp
        mov     ebp, esp
        push    ebx

        COPY_CALL_FRAME _KiBiosFrame
        sub     esp,LocalStack          ; After C style frame
        CurrentIrql                             ; Local Variable
        push    eax                             ; Local Variable

        cmp     al, DISPATCH_LEVEL              ; Is irql > Dispatch_level?
        jae     short Kac00

; Raise to Dispatch Level
        RaiseIrql DISPATCH_LEVEL
        mov     [esp], al

Kac00:

;
; Set up parameters on stack before remapping stack.
;

        push    word ptr KGDT_CDA16             ; CDA anchor selector
        push    KacRequestBlock                 ; Request Block
        push    KacFunctionTable                ; Func transfer table
        push    KacDeviceBlock                  ; Device Block
        mov     ebx, KacAbiosFunction           ; (ebx)-> Abios Entry

;
; Remap current stack to 16:16 stack.  The base of the 16bit stack selector is
; the base of current kernel stack.
;

        inc     _KiInBiosCall                         ; Set the 'In Bios' flag
if DBG
        cmp   _KiInBiosCall,2
        jb  @F
        push    offset FLAT:KiBiosReenteredAssert
        call    _dbgPrint
        add     esp, 4
@@:
endif

        STACK32_TO_STACK16                      ; Switch to 16bit stack
        push    word ptr KGDT_CODE16
        push    word ptr (offset FLAT:Kac40 - offset FLAT:_KiI386CallAbios@16)
        push    KGDT_CODE16
        push    offset FLAT:Kac30 - offset FLAT:_KiI386CallAbios@16
        retf

Kac30:

;
; Stack switching (from 32 to 16) turns interrupt off.  We must turn it
; back on.
;

        sti
        push    bx                              ; Yes, BX not EBX!
        retf
Kac40:
        add     esp, 14                         ; pop out all the parameters

        STACK16_TO_STACK32                      ; switch back to 32 bit stack

;
; Pull callers flat return address off stack and push the
; flat code selector followed by the return offset, then
; execute a far return and we'll be back in the 32-bit code space.
;

        db      OPERAND_OVERRIDE
        push    KGDT_R0_CODE
        db      OPERAND_OVERRIDE
        push    offset FLAT:Kac50
        db      OPERAND_OVERRIDE
        retf
Kac50:
        pop     ecx                             ; [ecx] = OldIrql
        pop     ebx                             ; restore ebx
        cmp     cl, DISPATCH_LEVEL
        jae     short Kac60

        LowerIrql cl

Kac60:

        dec     _KiInBiosCall                          ;Clear 'In Bios' Flag

        add     esp,LocalStack                           ; subtract off the scratch space
        pop     ebp
        stdRET    _KiI386CallAbios

stdENDP _KiI386CallAbios


;; ********************************************************
;;
;; BEGIN - power_management
;;
;;

;++
; VOID
; KeI386Call16BitFunction (
;     IN OUT PCONTEXT Regs
;     )
;
; Routine Description:
;
;     This function calls the 16 bit function specified in the Regs.
;
; Parameters:
;
;     Regs - supplies a pointer to register context to call 16 function.
;
;   NOTE: Caller must be at DPC_LEVEL
;
;--

cPublicProc _KeI386Call16BitFunction,1

    ;  verify CurrentIrql
    ;  verify context flags

        push    ebp                             ; save nonvolatile registers
        push    ebx
        push    esi
        push    edi
        mov     ebx, dword ptr [esp + 20]       ; (ebx)-> Context

        COPY_CALL_FRAME _KiBiosFrame

        sub     esp,LocalStack          ; After prolog

        inc    _KiInBiosCall                         ; Set the 'In Bios' flag
if DBG
        cmp   _KiInBiosCall,2
        jb  @F
        push    offset FLAT:KiBiosReenteredAssert
        call    _dbgPrint
        add     esp, 4
@@:
endif

;
; We're using a 32bit CS:EIP - go to a 16bit CS:IP
; Note the base of KiAbiosCallSelector is the flat address of _KiI386AbiosCall
; routine.
;

;
; Remap current stack to 16:16 stack.  The base of the 16bit stack selector is
; the base of current kernel stack.
;

        STACK32_TO_STACK16                      ; Switch to 16bit stack
    ;
    ; Push return address from 16 bit function call to kernel
    ;

        push    word ptr KGDT_CODE16
        push    word ptr (offset FLAT:Kbf40 - offset FLAT:_KiI386CallAbios@16)

        ;
        ; Load context to call with
        ;

        push    word ptr [ebx].CsEFlags
        push    word ptr [ebx].CsSegCs
        push    word ptr [ebx].CsEip

        mov     eax, [ebx].CsEax
        mov     ecx, [ebx].CsEcx
        mov     edx, [ebx].CsEdx
        mov     edi, [ebx].CsEdi
        mov     esi, [ebx].CsEsi
        mov     ebp, [ebx].CsEbp
        push    [ebx].CsSegGs
        push    [ebx].CsSegFs
        push    [ebx].CsSegEs
        push    [ebx].CsSegDs
        mov     ebx, [ebx].CsEbx
        pop     ds
        pop     es
        pop     fs
        pop     gs

    ;
    ; Switch to 16bit CS
    ;
        push    KGDT_CODE16
        push    offset FLAT:Kbf30 - offset FLAT:_KiI386CallAbios@16
        retf

Kbf30:
    ;
    ; "call" to 16 bit function
    ;
        iretd

Kbf40:
    ;
    ; Push some of the returned context which will be needed to
    ; switch back to the 32 bit SS & CS.
    ;
        db      OPERAND_OVERRIDE
        push    ds

        db      OPERAND_OVERRIDE
        push    es

        db      OPERAND_OVERRIDE
        push    fs

        db      OPERAND_OVERRIDE
        push    gs

        db      OPERAND_OVERRIDE
        push    eax

        db      OPERAND_OVERRIDE
        pushfd

        db      OPERAND_OVERRIDE
        mov     eax, KGDT_R0_PCR
        mov     fs, ax

        db      OPERAND_OVERRIDE
        mov     eax, KGDT_R3_DATA OR RPL_MASK
        mov     ds, ax
        mov     es, ax

        xor     eax, eax

    ;
    ; Switch back to 32 bit stack
    ;

        STACK16_TO_STACK32

;
; Push the flat code selector followed by the return offset, then
; execute a far return and we'll be back in the 32-bit code space.
;


        db      OPERAND_OVERRIDE
        push    KGDT_R0_CODE
        db      OPERAND_OVERRIDE
        push    offset FLAT:Kbf50
        db      OPERAND_OVERRIDE
        retf

Kbf50:
    ;
    ; Return resulting context
    ;

        mov     eax, dword ptr [esp+44+LocalStack]     ; (eax) = Context Record
        pop     [eax].CsEflags
        pop     [eax].CsEax
        pop     [eax].CsSegGs
        pop     [eax].CsSegFs
        pop     [eax].CsSegEs
        pop     [eax].CsSegDs

        mov     [eax].CsEbx, ebx
        mov     [eax].CsEcx, ecx
        mov     [eax].CsEdx, edx
        mov     [eax].CsEdi, edi
        mov     [eax].CsEsi, esi
        mov     [eax].CsEbp, ebp

;
; Restore regs & return
;
        dec     _KiInBiosCall                         ; Clear  the 'In Bios' flag

        add     esp,LocalStack                                          ;remove scratch space
        pop     edi
        pop     esi
        pop     ebx
        pop     ebp
        stdRET    _KeI386Call16BitFunction

stdENDP _KeI386Call16BitFunction

;++
; USHORT
; KeI386Call16BitCStyleFunction (
;     IN ULONG EntryOffset,
;     IN ULONG EntrySelector,
;     IN PUCHAR Parameters,
;     IN ULONG Size
;     )
;
; Routine Description:
;
;     This function calls the 16 bit function which supports C style calling convention.
;
; Parameters:
;
;     EntryOffset and EntrySelector - specifies the entry point of the 16 bit function.
;
;     Parameters - supplies a pointer to a parameter block which will be
;         passed to 16 bit function as parameters.
;
;     Size - supplies the size of the parameter block.
;
;   NOTE: Caller must be at DPC_LEVEL
;
; Returned Value:
;
;     AX returned by 16 bit function.
;
;--

cPublicProc _KeI386Call16BitCStyleFunction,4

;
;  verify CurrentIrql
;  verify context flags
;

        push    ebp                             ; save nonvolatile registers
        push    ebx
        push    esi
        push    edi

        COPY_CALL_FRAME _KiBiosFrame

        inc     _KiInBiosCall                         ; Set the 'In Bios' flag
if DBG
        cmp   _KiInBiosCall,2
        jb  @F
        push    offset FLAT:KiBiosReenteredAssert
        call    _dbgPrint
        add     esp, 4
@@:
endif

        mov     edi, esp
        sub     esp,LocalStack          ;  now, add in some scratch space
        mov     esi, dword ptr [esp + LocalStack +28]       ; (esi)->BiosParameters
        or         esi, esi
        jz         short @f

        mov    ecx, [esp + LocalStack +32]                 ; (ecx) = parameter size
        sub    esp, ecx                        ; allocate space on TOS to copy parameters

        mov   edi, esp
        rep     movsb                           ; (edi)-> Top of nonvolatile reg save area
        add    edi, LocalStack           ; edi now points to original stack

@@:

;
; We're using a 32bit CS:EIP - go to a 16bit CS:IP
; Note the base of KiAbiosCallSelector is the flat address of _KiI386AbiosCall
; routine.
;

;
; Remap current stack to 16:16 stack.  The base of the 16bit stack selector is
; the base of current kernel stack.
;

        STACK32_TO_STACK16                      ; Switch to 16bit stack

;
; Push return address from 16 bit function call to kernel
;

        push    word ptr KGDT_CODE16
        push    word ptr (offset FLAT:Kbfex40 - offset FLAT:_KiI386CallAbios@16)

        push    word ptr 0200h                  ; flags
        push    word ptr [edi + 24 ]             ; entry selector
        push    word ptr [edi + 20 ]             ; entry offset

;
; Switch to 16bit CS
;
        push    KGDT_CODE16
        push    offset FLAT:Kbfex30 - offset FLAT:_KiI386CallAbios@16
        retf

Kbfex30:
;
; "call" to 16 bit function
;
        iretd

Kbfex40:
;
; Save return value.
;

        db      OPERAND_OVERRIDE
        push    eax

;
; Restore Flat mode segment registers.
;

        db      OPERAND_OVERRIDE
        mov     eax, KGDT_R0_PCR
        mov     fs, ax

        db      OPERAND_OVERRIDE
        mov     eax, KGDT_R3_DATA OR RPL_MASK
        mov     ds, ax
        mov     es, ax

        xor     eax, eax

;
; Switch back to 32 bit stack
;

        STACK16_TO_STACK32

;
; Push the flat code selector followed by the return offset, then
; execute a far return and we'll be back in the 32-bit code space.
;


        db      OPERAND_OVERRIDE
        push    KGDT_R0_CODE
        db      OPERAND_OVERRIDE
        push    offset FLAT:Kbfex50
        db      OPERAND_OVERRIDE
        retf

Kbfex50:
        pop     eax

;
; Restore regs & return
;
        dec    _KiInBiosCall                         ; Clear  the 'In Bios' flag

        mov     esp, edi                                 ; Also removes the scratch space!
        pop     edi
        pop     esi
        pop     ebx
        pop     ebp
        stdRET    _KeI386Call16BitCStyleFunction

stdENDP _KeI386Call16BitCStyleFunction

;
; BBT cannot instrument code between BBT_Exclude_Selector_Code_Begin and this label
;

        public  _BBT_Exclude_Selector_Code_End
_BBT_Exclude_Selector_Code_End  equ     $
        int 3

;;
;; END - power_management
;;
;; ********************************************************


        public  _KiEndOfCode16
_KiEndOfCode16  equ     $



_TEXT   ends
        end

