;       SCCSID = @(#)selphys.asm        6.12 92/02/04
SCCSID  equ     "@(#)selphys.asm        6.12 92/02/04"

        page    ,132
        title   selphys - Selector Manager

;        IBM/Microsoft Confidential
;
;        Copyright (c) IBM Corporation 1987, 1989
;        Copyright (c) Microsoft Corporation 1987, 1989
;
;        All Rights Reserved
;
;Defect  Flag    ID           Description
;105341  88744   Change Team  Roll defect 88744 into r207 sparse tree.
;                             Modified logic to check for LESS than or EQUAL to.
;
;146867 12/15/95 Change Team  Change PhysToUVirt to panic only in the DEBUG
;                             version and to send an error message back in
;                             the retail version when a selector has not been
;                             freed.


.xlist
include dosseg.inc
include mi.inc
include basemaca.inc
include osmaca.inc
include fnomem.inc
include doshlp.inc              ; DosHlp's info
include defdbug.inc
include osconst.inc
include error.inc
include vmowner.inc
include protmode.inc
include seldesc.inc
include pgexport.inc
include vmexport.inc
include selxport.inc
include virtsym.inc
include error2.inc
include infoseg.inc
include mmpha.inc
.list

CpuMode 386

        ModName , FNO_SELPHYS_ASM

        extrn   dhFrameIP:abs
        extrn   dhFrameCS:abs

        ReferGDT GDT,WORD
        ReferGDT GDT_POOL,WORD

        ReferGlobal InterruptLevel,BYTE

        ReferGlobal CurrIntLevel,WORD       ; in intmgr1.asm
        ReferGlobal DHF,BYTE            ; DosHlp Function Table

        ReferGlobal DekkoPhysAddr,DWORD  ; physical address of dekko card

        ReferFlat _pPTDACur,DWORD
        ReferFlat _SELGDTLaddr,DWORD
        ReferFlat _SELMaxGDT,WORD
        ReferFlat _InterruptLevel,BYTE

;
; These are all the external subroutines needed by this source file.
;

        EXTRNF32        DevhlpAllocGDTSel
        EXTRNF32        DevhlpFreeGDTSel

        EXTRN           _PGMapVirt:near
        EXTRN           _PGPageToPhys:near
        EXTRN           _PGSetAccess:near
        EXTRN           _selAllocPhys:near
        EXTRN           _selFreePhys:near
        EXTRN           _SELGetDescInfo:near
        EXTRN           _SELVirtToLin:near
        EXTRN           _VMAllocMem:near
        EXTRN           _VMFreeMem:near
        EXTRN           _VMLinToPhys:near
;;;;;;; EXTRN           _VMObjectInfo:near
        EXTRN           _VMGetOd:near
        EXTRN           _VMSGSControl:near
        EXTRN           _PGPageListToLin:near
        EXTRN           _VMGetOwner:near

;*  Variables for PhysToVirt

; This define must match the equate in sdata.asm
PTOV_NUMSELS            equ     33*2    ; number of selectors needed for ptov

DATA segment

PtoV_Access     db      D_DATA3
        EVEN
PtoV_RPL        dw      SEL_RPL3

; Moved to sdata.asm for System Anchor Block - f122427
        extrn  PtoV_Selectors:word

DATA ends

        FARCODE HIGH3
HIGH3CODE segment
    assume ds:nothing, es:nothing, ss:nothing, fs:nothing, gs:nothing

;       This is the table of scope/access characteristics that
;       are used in PhysToUVirt and PhysToGDTSel. Note that not all
;       cases are valid for both.

AccessTable dw  (D_UVIRT shl 8) or D_CODE3  ; 0 Ring 3 Code
            dw  (D_UVIRT shl 8) or D_DATA3  ; 1 Ring 3 Data
            dw   0                          ; 2 Free (PToUVIRT only)
            dw  (D_UVIRT shl 8) or D_CODE2  ; 3 Ring 2 Code
            dw  (D_UVIRT shl 8) or D_DATA2  ; 4 Ring 2 Data
            dw  (D_UVIRT shl 8) or D_CODE0  ; 5 Ring 0 Code(PToGDTSel only)
            dw  (D_UVIRT shl 8) or D_DATA0  ; 6 Ring 0 Data(PToGDTSel only)

;       Use this to validate parameters
ATLEN   equ     $-AccessTable
ATMASK  equ     0fh
ATDBIG  equ     080h                        ; Flag for 386 default

HIGH3CODE ends


        FARCODE LOW
CODE segment


sphie:  InternalError <Devhlp called in real mode>

;***EP  VirtToPhys - convert virtual to physical address (protected mode only)
;***    VirtToPhysDM - convert virtual to physical address (dual mode)
;
;       When in real mode, VirtToPhys converts a segment-offset pair
;       to a 32-bit physical address.  When in protected mode, it converts
;       a selector-offset pair to a 32-bit physical address.
;
;       Calling this routine is allowed at interrupt time, but only on
;       GDT selectors.
;
;       Calling in real mode can only happen during system initialization.
;       Callable in user mode and kernel mode.
;
;       Algorithm:
;           - if real mode convert segment:offset to physical address
;           - If protected mode get linear address from descriptor, and
;               call PGPageToPhys
;
;       ENTRY   DS:SI = virtual address:
;                       segment:offset (real mode) or
;                       selector:offset (protected mode)
;
;       EXIT    'C' Clear if no error
;                       AX:BX = 32-bit physical address
;               'C' Set if virtual address error
;
;       USES    EAX, EBX, Flags
;
;               FS and GS must be preserved (DevHelp requirement)
;
;      PseudoCode:
;
;      If (realmode)
;          Convert segment:offset to physical address
;      Else                    ; protected mode
;      Save all registers
;      If (LDT selector)
;          Call GetLDTAddr(Current ptda)
;      Else
;          Get GDT address
;      Endif
;      Convert selector to offset
;      Add selector offset to LDT address
;      Read d_loaddr, d_hiaddr, and d_extaddr fields to eax
;      Add value of si to eax
;      Call VMLinToPhys
;      If (error), return (error code)
;      Save address
;      Restore registers
;      Put physical address in ax:bx
;      return

Procedure  VirtToPhysDM,<near,faronly>                                   ;@xxxx
    assume ds:nothing, es:nothing, ss:nothing, fs:nothing, gs:nothing
        whatmode ax
        jnc     short sphie
        jmp     vtp00
EndProc VirtToPhysDM

Procedure VirtToPhys,<near,faronly,far16>                                ;@xxxx

    assume ds:nothing, es:nothing, ss:nothing, fs:nothing, gs:nothing
    LocalVar    vtp_physaddr,DWORD

vtp00:  EnterProc ,,alignesp
        DBPRT   40h,80h,<VirtToPhys: ds:si=$x:$x  >,<ds,si>

;       protected mode address.

        ; Save registers that will be used

        push    ecx
        push    edx
        push    esi
        push    ds
        push    es

        mov     cx, ds                          ; save ds selector
        FlatContext  <ds, es>, ax

        cmp     byte ptr [_InterruptLevel],-1
        jz      short vtp10                     ; everthing ok at task time

        test    cl,TABLE_MASK                   ; is it ldt?
        jnz     short vtp100                    ; error out if ldt at int time

vtp10:  movzx   esi,si
        CCALL32 _SELVirtToLin <esi, ecx>
        cmp     eax, -1
        jz      short vtp100

;   _VMLinToPhys(laddr, pphysaddr)
;   (eax) = laddr

        SSToDS  ecx, ss:vtp_physaddr
        CCALL32 _VMLinToPhys <eax, ecx>
        or      eax, eax
        .errnz  NO_ERROR
        jnz     short vtp100

        mov     ebx, [vtp_physaddr]
        mov     eax, ebx
        shr     eax, WORDSHIFT          ; (ax:bx) = 32 bit physical address
        clc

        ;   restore saved registers

vtp90:  pop     es
        pop     ds
        pop     esi
        pop     edx
        pop     ecx

        DBPRT   40h,80h,<VirtToPhys out ax:bx=$x:$x\n>,<ax,bx>
;**@xxxx        LeaveProc
;**@xxxx        ret
        ExitProc

;       jump here on error

vtp100: stc
        jmp     vtp90
EndProc VirtToPhys


;***EP  dh_PhysToUVirt - top level worker for DevHlp_PhysToUVirt
;
;       ENTRY:  (AX:BX) = 32-bit physical address
;                       On dh=2 AX is LDT selector to free
;               (CX)    = size of segment, 0 == 64k
;               (DH)    = 0: make segment readable code
;                       1: make segment writable data
;                       2: free selector
;                       3: make segment readable IOPL code
;                       4: make segment read/write IOPL data
;                       5: make semgent read/write data, tag selectors as PVB
;               (SI)    = 0 (used only if dh=5)
;       EXIT:   Carry Flag clear on success
;                       ES:BX = valid virtual address
;               Carry Flag set on error
;
;       USES:   BX, ES, Flags
;
;               FS and GS must be preserved (DevHelp requirement)

Procedure dh_PhysToUVirt,<near,far16>
    assume ds:nothing, es:nothing, ss:nothing, fs:nothing, gs:nothing
        LocalVar        ptu_paddr,DWORD ; physical address
        LocalVar        ptu_size,DWORD  ; size of segment
        LocalVar        ptu_flags,DWORD ; flags
        LocalVar        ptu_callerBP,BP ; caller's BP (used for ownership)

        EnterProc

        TaskTime

        SaveReg         <eax,ecx,edx,esi,edi,ds,fs,gs>

;       If we got here from a devhelp, we may have a user selector in es,
;       fs, or gs.  Clear them before we get a chance to yield.

        FlatContext     <ds,es>,di
        NullContext     <fs,gs>,di      ; clear possible user selectors

        mov     [ptu_paddrl],bx
        mov     [ptu_paddrh],ax
        mov     [ptu_flagsl],dx
        mov     [ptu_flagsh],si

        jcxz    ptu00
        movzx   ecx,cx
ptu10:  mov     [ptu_size],ecx

        movzx   edi,[ptu_callerBP]
        SSToDS  edi
    assume DS:nothing           ; BUGBUG - donh 4/3/90: avoid a linker bug
        movzx   eax,word ptr [edi+dhFrameCS] ;ok to blast eax
        movzx   ecx,word ptr [edi+dhFrameIP] ;we'll restore ecx anyway...
        CCALL32 _VMGetOwner <eax, ecx>

        CALLF32 PhysToUVirt <ptu_paddr, ptu_size, ptu_flags, eax>

        RestoreReg      <gs,fs,ds,edi,esi,edx,ecx,eax>
        LeaveProc
        ret

ptu00:  mov     ecx,64*1024
        jmp     ptu10
EndProc dh_PhysToUVirt


;***EP  AllocPhysDM - Allocate Fixed Block of Physical Memory
;
;       AllocPhysDM is used by device drivers to allocate a
;       block of fixed memory. This routine can not be called in interrupt
;       time.
;
;       ENTRY   (AX:BX) = 32-bit block size in bytes
;               (DH)    = Relative position to 1 MEG
;                         = 0 above 1 MEG
;                         = 1 below 1 MEG
;       EXIT    Carry clear on success
;                   (AX:BX) = 32-bit physical address
;               Carry set on error
;                   (AX) = error code
;       USES    All other registers preserved
;
;               FS and GS must be preserved (DevHelp requirement)
;
;       PseudoCode:
;
;   Ifdef SELSTRICT
;       If (real mode), InternalError
;   Endif
;       Call AllocPhys
;       return

Procedure AllocPhysDM
    assume ds:nothing, es:nothing, ss:nothing, fs:nothing, gs:nothing
    LocalVar  apd_callerBP,BP       ; callers BP (used for ownership)
    LocalVar  apd_phys,DWORD        ; 32 bit physical address

        EnterProc
        TaskTime
        SaveReg         <ecx,edx,esi,ds,es,fs,gs>
        NullContext     <fs,gs>,cx  ; Zero FS and GS in case we block
        whatmode cx
        jnc     sphie

;   Test for invalid input flags

        test    dh, not 1
        jnz     short apde

;   _selAllocPhys(cb, fl, pphys, callers CS)

        FlatContext <ds, es>, cx
        shl     eax, WORDSHIFT
        mov     ax, bx              ; (eax) = 32 bit size
        xor     ecx, ecx
        mov     cl, dh
        SSToDS  edx, apd_phys
        movzx   esi,[apd_callerBP]
        SSToDS  esi
    assume      DS:nothing      ; BUGBUG - donh 4/3/90: hides a linker bug
        push    ebx
        push    edi
        movzx   ebx, word ptr [esi+dhFrameCS]
        movzx   edi, word ptr [esi+dhFrameIP]
        CCALL32 _selAllocPhys <eax, ecx, edx, ebx, edi >
        pop     edi
        pop     ebx
        or      eax, eax
        stc
        .errnz  NO_ERROR
        jnz     short apd60

        mov     ax, [apd_physh]
        mov     bx, [apd_physl]
        clc

apd60:  RestoreReg      <gs,fs,es,ds,esi,edx,ecx>
        LeaveProc
        ret
apde:   mov     ax, ERROR_INVALID_PARAMETER
        stc
        jmp     short apd60

EndProc AllocPhysDM


;***EP  FreePhysDM - Allocate Fixed Block of Physical Memory
;
;       FreePhysDM is used to release memory allocated by the
;       AllocPhys call.
;       In current version, it can only be called in protected mode.
;       This routine can not be called in interrupt time.
;
;       ENTRY   (AX:BX) = 32-bit physical address
;       EXIT    carry set if error, clear otherwise
;       USES    All registers preserved
;
;               FS and GS must be preserved (DevHelp requirement)

Procedure FreePhysDM
    assume ds:nothing, es:nothing, ss:nothing, fs:nothing, gs:nothing
        TaskTime
        SaveReg         <eax,ecx,edx,ds,es,fs,gs>    ; @@
        NullContext     <fs,gs>,cx      ; Zero FS and GS in case we block
        whatmode cx
        jnc     sphie

        FlatContext <ds, es>, cx
        shl     eax, WORDSHIFT
        mov     ax, bx                  ; (eax) = 32 bit physical address
        CCALL32 _selFreePhys <eax>
        shr     ax,1                    ; set carry if error

        RestoreReg      <gs,fs,es,ds,edx,ecx,eax>    ; @@
        ret
EndProc FreePhysDM


;***EP  dh_AllocateGDTSelector - Allocate GDT selectors and linear address space
;***EP  AllocateGDTSelector - Kernel interface for same
;
;       dh_AllocateGDTSelector is used to allocate
;       GDT selectors on behalf of a device driver.
;       This routine can not be called interrupt time.
;
;       ENTRY   (ES:DI) = pointer to array to store selectors
;               (CX)    = number of GDT selectors requested
;       EXIT    Carry Flag clear on success
;               Carry Flag set on failure
;                   (AX) = ERROR_INVALID_ADDRESS
;                          ERROR_ZERO_SELECTORS_REQUESTED
;                          ERROR_NOT_ENOUGH_SELECTORS_AVA
;       USES    Flags
;               also AX on failure
;
;               FS and GS must be preserved (DevHelp requirement)
;
;       PseudoCode:
;
;       Call DevhlpAllocGDTSel
;       return

Procedure AllocateGDTSelector,faronly
    LocalVar ags_cseip,fword
        EnterProc
        push    ax
        mov     ax,[bp+2].Segmt
        mov     [ags_cseip].Segmt32,ax
        mov     ax,[bp+2].Offst
        mov     [ags_cseip].Offst32.loword,ax
        mov     [ags_cseip].Offst32.hiword,0
        pop     ax
        push    bp
        lea     bp,[ags_cseip]
        sub     bp,dhFrameIP
        call    dh_AllocateGDTSelector
        pop     bp
        LeaveProc
        ret
EndProc AllocateGDTSelector

Procedure dh_AllocateGDTSelector
    assume ds:nothing, es:nothing, ss:nothing, fs:nothing, gs:nothing

        LocalVar dag_callerBP,BP
        EnterProc

        TaskTime
        push    ebx                      ; have to save all registers
        movzx   ebx,[dag_callerBP]

        CALLF32 DevhlpAllocGDTSel,,nocheck

        pop     ebx
        LeaveProc
        ret
EndProc   dh_AllocateGDTSelector


;***EP  dh_FreeGDTSelector - Free GDT selectors
;***EP  FreeGDTSelector - kernel interface for same
;
;       dh_FreeGDTSelector frees the GDT selectors allocated via
;       dh_AllocateGDTSelector.
;       This routine can not be called at interrupt time.
;
;       ENTRY   (AX)    = GDT selector to be freed
;       EXIT    Carry clear if success
;               Carry Flag set on failure
;                   (AX) = ERROR_INVALID_PARAMETER
;                          ERROR_ACCESS_DENIED
;       USES    EAX, Flags  (Also DS and ES if they are equal to selector being
;               freed.
;
;       PseudoCode:
;
;       Call DevhlpFreeGDTSel
;       return

Procedure FreeGDTSelector,<near,faronly>
Entry dh_FreeGDTSelector
    assume ds:nothing, es:nothing, ss:nothing, fs:nothing, gs:nothing
        TaskTime

        CALLF32 DevhlpFreeGDTSel,,nocheck
        ret
EndProc FreeGDTSelector


;***EP  dh_PhysToGDTSelector - store physical address in GDT selector
;***EP  PhysToGDTSelector - kernel interface for same
;
;       Converts a physical memory address to a designated GDT selector.
;       The indicated segment must be a fixed block of memory or locked
;       (either long or short term) before issuing this request.
;
;       This routine can be called in interrupt time. It must not
;       block, it must not get any faults.
;
;       Once this call has been issued for a particular selector,
;       that addressability will remain valid until the device driver
;       changes its content via a subsequent dh_PhysToGDTSel request
;       referencing the same GDT selector.  This call can be made in
;       protected mode only.
;
;       ENTRY   (AX:BX) = Address - 32 bit physical address
;               (CX)    = Size - the 32 bit segment size, not the limit
;                         Converted to a limit before editing descriptors
;                         A value of zero indicates 65536 bytes
;               (SI)    = Selector - for the descriptor to be setup
;       EXIT    Carry Flag clear on success
;               Carry Flag set on failure
;                   (AX) = ERROR_INVALID_ADDRESS
;                          ERROR_INVALID_PARAMETER
;                          ERROR_INVALID_SELECTOR
;       USES    EAX on failure
;               Flags
;
;               FS and GS must be preserved (DevHelp requirement)
;
;       PseudoCode:
;
;       Call PhysToGDTSel(Ring 0 data)
;       Return

Procedure PhysToGDTSelector,<near,faronly>
Entry dh_PhysToGDTSelector
    assume ds:nothing, es:nothing, ss:nothing, fs:nothing, gs:nothing

        push    eax
        SaveReg <ecx,edx,esi,fs,gs>

        movzx   ecx, cx
        or      cx, cx                      ; size = 0?
        jnz     short dpg10

        mov     ecx, WORDMASK+1             ; set size to 64Kb
dpg10:  shl     eax, WORDSHIFT
        mov     ax, bx                      ; (eax) = physaddr
        mov     dh, 6                       ; Ring 0 data (286 format)
        CALLFAR PhysToGDTSel

        RestoreReg <gs,fs,esi,edx,ecx>
        jc      short dpg90                 ; jmp if error returned

        pop     eax                         ; no error
        jmp     short   dpgx

dpg90:  add     esp, 4                      ; clear eax from stack
        stc
dpgx:   ret
EndProc PhysToGDTSelector


;***EP  dh_PhysToGDTSel - store physical address and access in GDT selector
;
;       Converts a physical memory address to a designated GDT selector,
;       setting the access byte of the descriptor to desired privilege value.
;       The indicated segment must be a fixed block of memory or locked
;       (either long or short term) before issuing this request.
;
;       This routine can be called in interrupt time. It must not
;       block, it must not get any faults.
;
;       Once this call has been issued for a particular selector,
;       that addressability will remain valid until the device driver
;       changes its content via a subsequent dh_PhysToGDTSel request
;       referencing the same GDT selector.  This call can be made in
;       protected mode only.
;
;       ENTRY   (EAX)   = Address - 32 bit physical address
;               (ECX)   = Size - the 32 bit segment size, not the limit
;                         Converted to a limit before editing descriptors
;                         Must be <= 64Kb
;               (DH)    = Descriptor privilege
;                         0: Make segment readable code (286 format)
;                         1: Make segment writable data (286 format)
;                         3: Make segment readable IOPL code (286 format)
;                         4: Make segment writable IOPL data (286 format)
;                         5: Make segment readable Ring 0 code (286 format)
;                         6: Make segment writable. Ring 0 data (286 format)
;                       128: Add to any of the above to get access privileges
;                           in the 386 chip format (32 bit addressing)
;               (SI)    = Selector - for the descriptor to be setup
;       EXIT    Carry Flag clear on success
;               (SI)    = Selector with the right RPL bits
;               Carry Flag set on failure
;                   (AX) = ERROR_INVALID_ADDRESS
;                          ERROR_INVALID_SELECTOR
;                          ERROR_INVALID_PARAMETER
;       USES    EAX, ESI, Flags
;
;               FS and GS must be preserved (DevHelp requirement)
;
;       PseudoCode
;
;       Call PhysToGDTSel
;       return

Procedure dh_PhysToGDTSel
    assume ds:nothing, es:nothing, ss:nothing, fs:nothing, gs:nothing

        SaveReg <fs,gs>

        CALLFAR  PhysToGDTSel

        RestoreReg <gs,fs>
        ret
EndProc dh_PhysToGDTSel


;***EP  dh_GetDescInfo - Return descriptor information
;
;       dh_GetDescInfo returns descriptor information for the given selector.
;
;       ENTRY       (AX) = selector
;       EXIT    Carry clear if descriptor valid
;                   (AH) = attribute byte(only Big, Granularity flags)
;                           ( = parameter count if call gate)
;                   (AL) = access byte
;                  (ECX) = 32 bit address stored in descriptor
;                           (CX = selector if Call Gate)
;                  (EDX) = 32 bit limit of descriptor
;                           ( = 32 bit offset if call gate)
;               Carry set if descriptor invalid
;       USES    EAX, ECX, EDX, and DS, ES if not set to FLAT
;
;               FS and GS must be preserved (DevHelp requirement)
;
Procedure dh_GetDescInfo
    assume ds:nothing, es:nothing, ss:nothing, fs:nothing, gs:nothing
        LocalVar    dgd_desc,<size seldesc_s>
        EnterProc
        SaveReg <ds, es, fs, gs>

;       _SELGetDescInfo(sel, hptda, &desc)

        FlatContext <ds, es>, cx
        SSToDS  ecx, ss:dgd_desc
        xor     edx, edx
        CCALL32 _SELGetDescInfo <eax, edx, ecx>
        or      eax, eax
        .errnz  NO_ERROR
        stc
        jnz     short dgd90

        mov     ah, [dgd_desc].desc_attr    ; attribute/parm count
        mov     al, [dgd_desc].desc_acc     ; access byte
        test    al, D_SEG
        jz      short dgd80                 ; skip if gate

        and     ah, D_DBIG or D_GRAN4K      ; mask UVIRT flag
dgd80:  mov     ecx, [dgd_desc].desc_laddr  ; address/sel
        mov     edx, [dgd_desc].desc_cb     ; size/offset
        clc
dgd90:  RestoreReg <gs, fs, es, ds>
        LeaveProc
        ret
EndProc dh_GetDescInfo


;***EP  dh_PhysToVirt - Convert physical address to virtual for Device Drivers
;***    PhysToVirtDM  - convert physical address to virtual for kernel.
;***    PhystoVirt  - Convert physical address to virtual for old v1.0 code.
;
;       PhysToVirt converts a physical address to virtual by mapping one of
;       the reserved GDT selectors. This routine can be called at
;       interrupt time. It must not block, must not take a page fault.
;
;       ENTRY       (AX:BX) = Physical address
;                      (CX) = Length
;                      (DH) = 0  put the result in (DS:SI)
;                             1  put the result in (ES:DI)
;       EXIT        CF = 0, ZF = 0 (Will never cause a mode switch)
;                       If input DH = 0, converted address in DS:SI
;                       If input DH = 1, converted address in ES:DI
;       USES    DS, ESI if input DH = 0
;               ES, EDI if input DH = 1
;
;               FS and GS must be preserved (DevHelp requirement)
;
;       PseudoCode:
;
;       Save all registers
;       Get Current Interrupt Level
;       Convert Cur. Int. Level to index into PhysToVirt selector table
;       ; Adjust physical address for page alignment
;       Set pageoffset = (physaddr & PAGEMASK)
;       Set lastaddr = (physaddr + size + pagesize - 1)
;       Set mapphysaddr = physaddr & not PAGEMASK
;       Set mapsize = lastaddr - mapphysaddr
;       If (output goes to ES), increment index into selector pool
;       Set sel= Selector value in selector pool
;       Get GDT Address
;       Read linear address of GDT.sel descriptor, mask off page offset bits
;       Call PGMapVirt          ; map physical pages to linear
;       ; Adjust GDT selector's base linear address
;       Set GDT.sel's d_loaddr =+ pageoffset
;       If (output in DS)
;           Set DS=sel, ESI = 0
;       Else
;           Set ES=sel, EDI=0
;       Endif
;       Clear Carry, zero               ; We don't do mode switches any more
;       restore all registers
;       return

Procedure  PhysToVirtDM                 ; kernel caller entry point
    assume ds:nothing, es:nothing, ss:nothing, fs:nothing, gs:nothing
Entry   dh_PhysToVirt
        whatmode
        jnc  sphie

        call    PhysToVirt
        ret
EndProc PhysToVirtDM


Procedure  PhysToVirt,<near,faronly>
    assume ds:nothing, es:nothing, ss:nothing, fs:nothing, gs:nothing
    LocalVar        ptv_dx,WORD             ; store in DS/ES
    LocalVar        ptv_sel,WORD            ; selector to return

        EnterProc <dx>,,alignesp            ; save dh

        test    dh, not 1
        stc
        jnz     ptvx                    ; jmp if invalid flags

;   Save all registers that will be used

        push    ds
        push    es
        push    eax
        push    ebx
        push    ecx
        push    edx
        DosContext      ds

        MMPHPreDHPhysToVirt()           ; Performance Hook ( 6201h )

;       Maj/Min = 6201H, Data = ax, bx, cx, si, dx, int level+1, dh_CS, dh_IP
;       No comments available inside hook.

        whatmode                        ; check mode
        jnc      sphie                  ; jump if real mode

        shl     eax, WORDSHIFT
        mov     ax, bx                  ; (eax) = 32 bit physical address
        mov     bx, CurrIntLevel        ; get active hardware interrupt level
        inc     bx                      ; get the H/W int level +1
        shl     bl, 1                   ; (BL) = index into sel table
        add     bl, dh                  ; select ES or DS descriptor
        shl     bx, 1                   ; (BX) = offset into sel table
        DosContext ds
        movzx   ebx, PtoV_Selectors[bx] ; (EBX) = selector
        FlatContext  es
        mov     [ptv_sel], bx           ; save selector
        and     ebx, SEL_RPLCLR         ; clear RPL bits (if any)
        add     ebx, es:[_SELGDTLaddr]  ; (ebx) = flat address of descriptor

;   PhysToVirt descriptors already have linear addresses assigned to them
;   stored in descriptor's address field. The address fields should never
;   be erased or changed (except for the page offset adjustment in the lowest
;   12 bits). Here all we do is map the corresponding page table entries
;   to the given physical address, adjust the page offset and update limit.
;   (eax) = 32 bit physical address
;   (ebx) = flat address of descriptor
;   (cx)  = size
;   (dh)  = store in DS/ES
;   _PGMapVirt(pageno, physaddr, npages, fl)

        push    es
        pop     ds
    ASSUME      DS:FLAT
        mov     dx, ax              ; (dx) = low word of address
        and     edx, PAGEMASK       ; (edx) = page offset of physical address
        dec     cx                  ; (CX) = limit
        mov     [ebx].d_limit, cx   ; store new limit in descriptor
        and     [ebx].d_attr, not D_EXTLIMIT
        movzx   ecx, cx
        add     ecx, edx
        add     ecx, PAGESIZE
        shr     ecx, PAGESHIFT      ; (ecx) = no of pages needed to be mapped
        push    ecx                 ; save no of pages
        mov     ch, [ebx].d_extaddr
        mov     cl, [ebx].d_hiaddr  ; (cx) = high word of linear address
        shl     ecx, WORDSHIFT
        mov     cx, [ebx].d_loaddr  ; (ecx) = linear address in descriptor
        and     [ebx].d_loaddr, not PAGEMASK  ; turn off page offset bits
        or      [ebx].d_loaddr, dx  ; store new page offset
        shr     ecx, PAGESHIFT      ; (ecx) = page no.
        pop     edx                 ; (edx) = no of pages
        CCALL32 _PGMapVirt <ecx, eax, edx, -1>
        clc

; set up the output for returning to the caller here;

;   Restore selectors

ptv90:  pop     edx
        pop     ecx
        pop     ebx
        pop     eax
        pop     es
        pop     ds

;   Now store selector to desired place
        jc      short ptvx                    ; skip if error
        test    [ptv_dx].HighByte,1
        jnz     short ptv99

;   Answer goes to DS:SI
        mov     si, [ptv_sel]
        mov     ds, si
        or      si, 1                         ; set CF = 0, ZF = 0
        mov     si, 0
        jmp     short ptvx

ptv99:  ; Answer goes to ES:DI
        mov     di, [ptv_sel]
        mov     es, di
        or      di, 1                         ;set  CF = 0, ZF = 0
        mov     di, 0

ptvx:   MMPHPostDHPhysToVirt()          ; Performance Hook ( 6281H )

        LeaveProc
        ret
EndProc PhysToVirt


;***EP  dh_UnPhysToVirt - UnPhysToVirt for Device drivers.
;***    UnPhysToVirtDM  - UnPhysToVirtDM for kernel.
;
;       This routine, companion of PhysToVirt is actually unnecessary
;       because we never do mode switching. (VDMs run in protected mode)
;       It is supplied for compatibility reasons.
;
;       ENTRY       None
;       EXIT        Carry, zero flag clear
;
;       FS and GS must be preserved (DevHelp requirement)
;
;       PseudoCode:
;
;       return (carry clear, Zero flag clear)

Procedure  dh_UnPhysToVirt
    assume ds:nothing, es:nothing, ss:nothing, fs:nothing, gs:nothing
Entry       UnPhysToVirtDM

        push    ax
        whatmode ax
        jnc      sphie

        MMPHPreDHUnPhysToVirt()         ; Performance Hook ( 6202H )

        or      ax, 1                   ; clear zero, carry
        pop     ax

        MMPHPostDHUnPhysToVirt()        ; Performance Hook ( 6282H )

        ret
EndProc dh_UnPhysToVirt


;***EP  dh_VirtToLin - convert virtual address to linear
;
;       dh_VirtToLin converts given selector:offset pair to flat linear
;       address. It returns error if selector is not valid or if
;       offset is beyond selector limit.
;
;       ENTRY    (AX)   = selector
;                (ESI)  = offset
;       EXIT    C clear
;                   (EAX) = linear address
;               C set if failed
;                   (EAX)   = Error code
;       USES    EAX, EFlags
;
;               FS and GS must be preserved (DevHelp requirement)
;
;       PseudoCode:
;
;       Call VirtToLin
Procedure dh_VirtToLin
    assume ds:nothing, es:nothing, ss:nothing, fs:nothing, gs:nothing

        push    ecx             ; save registers being trashed
        push    edx
        CALLFAR VirtToLin       ; returns eax = laddr
        pop     edx             ; restore trashed registers
        pop     ecx
        ret
EndProc dh_VirtToLin


;***EP  dh_LinToGDTSelector - convert linear address to virtual.
;
;       dh_LinToGDTSelector converts linear address to virtual (selector:offset)
;       address by mapping the passed selector's ptes to the physical memory
;       referred to by the passed linear address.
;       Once this procedure is called for a specific selector, the mapping will
;       remain valid until the selector is mapped to another address range
;       via LinToGDTSelector, PhysToGDTSelector, or PageListToGDTSelector.
;
;       ENTRY   (EBX) = Linear address (must be on a page boundary)
;               (ECX) = Length ( <= 64K)
;                (AX) = Selector returned by dh_AllocGDTSelector
;       EXIT    C clear if conversion performed.
;               C set if not
;                   (EAX) = Error code
;       USES    EAX on error, EFlags
;
;               FS and GS must be preserved (DevHelp requirement)
;
;       PseudoCode:
;
;       Call LinToGDTSel
;
Procedure dh_LinToGDTSelector
    assume ds:nothing, es:nothing, ss:nothing, fs:nothing, gs:nothing

        push    ecx                     ; save trashed registers
        push    edx
        push    eax
        CALLFAR LinToGDTSel
        pop     eax                     ; restore trashed registers
        jnc     short dlt90

        mov     eax, ERROR_INVALID_PARAMETER
dlt90:  pop     edx
        pop     ecx
        ret
EndProc dh_LinToGDTSelector


;***EP  dh_PageListToGDTSelector - map selector to physical pages
;
;       Maps physical addresses described in an array of pagelist_s structures
;       to a GDT selector setting the access byte of the descriptor to the
;       desired privilege value.  The physical pages descibed in the
;       pagelist array must be allocated as resident or locked
;       (either long or short term) before issuing this request.
;
;       This routine can be called in interrupt time. It must not
;       block, it must not get any faults.
;
;       Once this call has been issued for a particular selector,
;       that addressability will remain valid until the device driver
;       changes its content via a subsequent call to dh_PageListToGDTSelector.
;
;       FS and GS must be preserved (DevHelp requirement)
;
;       ENTRY   (AX)    = Selector to map
;               (ECX)   = Sum of the pl_cb fields in the PageList array
;                         Must be less than 64k
;               (DH)    = Descriptor privilege
;                         0: Make segment readable code (286 format)
;                         1: Make segment writable data (286 format)
;                         3: Make segment readable IOPL code (286 format)
;                         4: Make segment writable IOPL data (286 format)
;                         5: Make segment readable Ring 0 code (286 format)
;                         6: Make segment writable. Ring 0 data (286 format)
;                       128: Add to any of the above to get access privileges
;                           in the 386 chip format (32 bit addressing)
;               (EDI)    = pointer to array of pagelist_s structures to map
;                          the selector to.
;       EXIT    Carry Flag clear on success
;                   (AX)  = Selector with the right RPL bits
;               Carry Flag set on failure
;                   (EAX) = error code
;
;       PseudoCode

Procedure dh_PageListToGDTSelector
    assume ds:nothing, es:nothing, ss:nothing, fs:nothing, gs:nothing
        LocalVar pltg_sel,word          ; requested selector
        LocalVar pltg_cb,dword          ; parameter to _PGPageListToGDTSelector
        LocalVar pltg_pdesc,dword       ; pointer descriptor


        EnterProc
        push    ebx
        push    ecx
        push    edx
        push    ds
        push    es
        mov     bx,HIGH3CODE
        mov     ds,bx                   ; (ds) = High3Code for AccessTable
        assume ds:DOSHIGH3CODE
        mov     bl,dh                   ; (bh) = saved access flags

;       verify access flags

        and     dh, not ATDBIG
        cmp     dh, 2                   ; Free request is invalid
        jz      pltg100                 ; jmp if error

        shl     dh, 1                   ; change to AccessTable offset
        cmp     dh, ATLEN               ; past end of table (invalid flags)?
        jae     pltg100                 ; if bad, jmp

;       translate caller's flags to access and attribute bytes

        movzx   edx,dh
        mov     dx,ds:AccessTable[edx]  ; (dx) = attr/access bytes
        and     bl,ATDBIG               ; (bl) = 386 format bit
        shr     bl,1                    ; (bl) = value for D_DBIG bit
        .errnz  ((ATDBIG shr 1) - D_DBIG)
        or      dh,bl                   ; set D_DBIG bit if 386 format request
                                        ; (dx) = attribute/access bytes

;       verify the selector is valid

        FlatContext <ds,es>,bx
        test    ax,SEL_LDTBIT           ; LDT selector?
        jnz     pltg110                 ; if ldt, jmp to error

        and     ax,SEL_RPLCLR           ; (ax) = selector with clear RPL bits
        mov     ss:[pltg_sel],ax        ; save selector
        movzx   eax,ax
        add     eax,[_SELGDTLaddr]      ; (eax) = pointer to descriptor
        mov     [pltg_pdesc],eax        ; save descriptor pointer
        test    [eax].d_attr,D_UVIRT    ; uvirt bit set?
        jz      pltg110                 ; if not uvirt, jmp to error

;       get base linear address out of selector

        mov     bh,[eax].d_extaddr
        mov     bl,[eax].d_hiaddr
        shl     ebx,WORDSHIFT
        mov     bx,[eax].d_loaddr       ; (ebx) = linear address of selector
        shr     ebx,PAGESHIFT           ; (ebx) = first page mapped by sel

;       call PGPageListToLin(ppl, cb, page, flags, maxpages)

        push    dx                      ; save access byte
        movzx   eax,dl                  ; (eax) = access byte of selector
        and     al,D_DPL1               ; bit set if ring 3
        shr     ax,3                    ; (eax) = PG_U if ring 3
        .errnz  ((D_DPL1 shr 3) - PG_U)
        test    dl,D_CODE               ; code selector?
        jnz     short pltg10            ; if code, don't set write bit
                                        ; BUGBUG - didn't set PG_R or PG_X
        or      eax,PG_W                ;          because the chip ignores it
pltg10: SSToDS  edx,ss:pltg_cb          ; max size and returned actual size
        mov     ss:[pltg_cb],64*1024    ;  (64kb)

        CCALL32 _PGPageListToLin <edi, ecx, ebx, eax, edx>
        pop     bx                      ; new access byte
        or      eax,eax                 ; error?
        jnz     short pltg100           ; jmp on error to exit

;       PGPageListToLin worked.  Now set up descriptor and selector and exit.

        mov     eax,[pltg_pdesc]        ; (eax) = flat offset of descriptor
        mov     ecx,[pltg_cb]           ; (ecx) = new selector size
        dec     ecx                     ; (ecx) = limit
        and     [eax].d_loaddr,NOT PAGEMASK ; Clear previous page offset
        mov     dx,word ptr [edi].pl_paddr  ; (dx) = first physical addr
        and     dx,PAGEMASK             ; (dx) = page offset of phys addr
        add     [eax].d_loaddr,dx       ; setup descriptor base
        mov     [eax].d_limit,cx        ; setup descriptor limit
        mov     word ptr [eax].d_access,bx ; setup descriptor access byte
        and     bx,D_PRIV               ; (bx) = dpl
        shr     bx,SELAL_PRIVSHIFT      ; (bx) = rpl
        .errnz ((D_PRIV shr SELAL_PRIVSHIFT) - SEL_RPLMASK)
        mov     ax,[pltg_sel]           ; (ax) = selector w/ clear rpl
        or      ax,bx                   ; (ax) = selector w/ rpl (clears carry)

        ; restore all registers

pltgx:  pop     es
        pop     ds
        pop     edx
        pop     ecx
        pop     ebx
        LeaveProc
        ret

pltg100: mov    eax,ERROR_INVALID_PARAMETER
        stc
        jmp     pltgx

pltg110: mov    eax,ERROR_INVALID_SELECTOR
        stc
        jmp     pltgx
EndProc dh_PageListToGDTSelector


CODE    ends


        FARCODE HIGH3
HIGH3CODE segment
    assume ds:nothing, es:nothing, ss:nothing, fs:nothing, gs:nothing


;***EP  PhysToGDTSel - store physical address and access in GDT selector
;
;       Converts a physical memory address to a designated GDT selector,
;       setting the access byte of the descriptor to desired privilege value.
;       The indicated segment must be a fixed block of memory or locked
;       (either long or short term) before issuing this request.
;
;       This routine can be called in interrupt time. It must not
;       block, it must not get any faults.
;
;       Once this call has been issued for a particular selector,
;       that addressability will remain valid until the device driver
;       changes its content via a subsequent dh_PhysToGDTSel request
;       referencing the same GDT selector.  This call can be made in
;       protected mode only.
;
;       ENTRY   (EAX)   = Address - 32 bit physical address
;               (ECX)   = Size - the 32 bit segment size, not the limit
;                         Converted to a limit before editing descriptors
;                         Must be <= 64Kb
;               (DH)    = Descriptor privilege
;                         0: Make segment readable code (286 format)
;                         1: Make segment writable data (286 format)
;                         3: Make segment readable IOPL code (286 format)
;                         4: Make segment writable IOPL data (286 format)
;                         5: Make segment readable Ring 0 code (286 format)
;                         6: Make segment writable. Ring 0 data (286 format)
;                       128: Add to any of the above to get access privileges
;                           in the 386 chip format (32 bit addressing)
;               (SI)    = Selector - for the descriptor to be setup
;       EXIT    Carry Flag clear on success
;               (SI)    = Selector with the right RPL stamp
;               Carry Flag set on failure
;                   (AX) = ERROR_INVALID_ADDRESS
;                          ERROR_INVALID_SELECTOR
;                          ERROR_INVALID_PARAMETER
;       USES    EAX, ESI, Flags
;
;       PseudoCode
;
;       If (LDT selector), return ERROR_INVALID_SELECTOR
;       Check for invalid flags parameter
;       Read linear address from descriptor
;       If ( not (sel.d_attr && D_UVIRT) )          ; must have UVIRT bit set
;           return (ERROR_INVALID_SELECTOR)
;       Endif
;       Callfar VMGetOd
;       If (not D_UVIRT || owner != PTOGDTOWNER)
;           return (ERROR_INVALID_SELECTOR)
;       Endif
;       If ( (physaddr + size -1) > 4Gb)
;           return (ERROR_INVALID_ADDRESS)
;       Endif
;       ; We have to adjust for the fact that physical address may not
;       ; begin at a page boundary.
;       Set pageoffset = physaddr && PAGEMASK       ; save offset into page
;       Set lastbyte = physaddr + size -1
;       Set mapsize = (lastbyte - physaddr + (pagesize -1)) && not PAGEMASK
;       Set descriptor's loaddr =+ pageoffset   ; adjust descriptor base
;       Set descriptor to given access privilege
;       Set descriptor's limit to size-1
;       Read laddr from descriptor's address fields
;       Call PGMapVIRT(physaddr && PAGEMASK, laddr && not PAGEMASK,
;               npages = mapsize/pagesize, flags)
;       If (error), InternalError
;       Return

Procedure PhysToGDTSel,<near,faronly>
    assume ds:nothing, es:nothing, ss:nothing, fs:nothing, gs:nothing
    LocalVar    ptg_x,DBYTE,ptg_acc,ptg_attr  ; requested access
    LocalVar    ptg_cb,DWORD            ; requested size
ifdef SELSTRICT
;;; LocalVar    ptg_oi,<size vmoi_s>
    LocalVar    ptg_od,<size vmod_s>
endif

        EnterProc <dx, ecx>,,alignesp   ; access, size

        ; save all registers
        push    ebx
        push    ecx
        push    edx
        push    edi
        push    ds
        push    es

        mov     ebx, eax                ; (ebx) = address

        mov     eax, ERROR_INVALID_PARAMETER
        cmp     ecx, 10000h             ; size > 64Kb?
        ja      short ptg20             ; jmp if so
        cmp     ecx, 0                  ; size == 0?
        jne     @F
        mov     ecx,10000h              ; if size == 0, then make it 64k

@@:     and     dh, not ATDBIG
        cmp     dh, 2                       ; Free request is invalid
        jz      short ptg20

        shl     dh, 1                   ; change to table offset
        cmp     dh, ATLEN
        jae     short ptg20

        movzx   eax, dh
        mov     dx, cs:AccessTable[eax]     ; attr/access bytes
        test    [ptg_attr], ATDBIG          ; 386 format requested?
        jz      short  ptg10                ; skip if not

        or      dh, D_DBIG                  ; turn on default big
ptg10:  mov     [ptg_x], dx                 ; attribute and access bytes

        mov     eax, ebx                    ; (ebx) = address
        add     eax, ecx
        mov     ax, ERROR_INVALID_ADDRESS
        jc      short ptg25                 ; jmp if address + size wraps 4G

        mov     ax, ERROR_INVALID_SELECTOR
        test    si,SEL_LDTBIT               ; reject LDT selectors
        jnz     short ptg20

        and     si,SEL_RPLCLR           ; clear RPL bits
        GDTContext DS
        test    [si].d_attr,D_UVIRT     ; must have UVIRT bit set
        jnz     short ptg30

ptg20:  stc
ptg25:  jmp     ptgx

ptg30:  mov     ah,[si].d_extaddr
        mov     al,[si].d_hiaddr
        mov     di, ax
        shl     edi, WORDSHIFT
        mov     di,[si].d_loaddr        ; (edi) = linear address of selector
ifdef SELSTRICT
        and     di, not PAGEMASK

;   _VMGetOd(flags, pod[laddr,pptda])

endif

        FlatContext <ds,es>,ax
ifdef SELSTRICT
;;;;;;; SSToDS  eax, ss:ptg_oi
;;;;;;; xor     ecx, ecx
;;;;;;; CCALL32 _VMObjectInfo <edi, ecx, eax>

        SSToDS  eax, ss:ptg_od
        mov     [eax].od_laddr, edi
        mov     ecx, _pPTDACur
        mov     [eax].od_pPTDA, ecx
        xor     ecx, ecx
        CCALL32 _VMGetOd <ecx, eax>
        or      eax, eax
        .errnz  NO_ERROR
        mov     ax, ERROR_INVALID_SELECTOR
        jnz     short ptg20

;;;;;;; cmp     [ptg_oi].oi_hobowner, PTOGDTOWNER
        mov     ecx, [ptg_od].od_pob
        cmp     [ecx].ob_hobowner, PTOGDTOWNER
        jnz     short ptg20

;;;;;;; cmp     [ptg_oi].oi_va, edi
        cmp     [ptg_od].od_laddr, edi
        jz      short ptg40

        InternalError <PhysToGDTSel:Bad address>

ptg40:
;;;;;;; test    [ptg_oi].oi_fsob, OB_UVIRT
        test    [ecx].ob_fs, OB_UVIRT
        jnz     short ptg50

        InternalError <PhysToGDTSel: Bad UVIRT flag>
ptg50:
endif


; _PGMapvirt(page, physaddr, npages, fl)
;  (ebx) = physical address
;   (si) = selector with clear RPL bits
;  (edi) = linear address

        test    [ptg_acc], D_CODE           ; code selector requested?
        jz      short  ptg70                ; jmp if not

        mov     eax, PG_R or PG_X           ; code permissions
        jmp     short  ptg75

ptg70:  mov     eax, PG_W or PG_R           ; data permissions
ptg75:  test    [ptg_acc], D_DPL1           ; Ring 3 requested
        jz      short ptg80                 ; jmp if not

        or      eax, PG_U
ptg80:  mov     ecx, [ptg_cb]
        mov     edx, ebx                    ; (edx) = physical address
        and     edx, PAGEMASK               ; (edx) = page offset of physaddr
        add     edx, ecx
        add     edx, (PAGESIZE - 1)         ; round up
        shr     edx, PAGESHIFT              ; (edx) = npages
        shr     edi, PAGESHIFT              ; edi = page no
        CCALL32 _PGMapVirt <edi, ebx, edx, eax>

;    (si) = selector with clear RPL bits
;   (ebx) = physical address
; set up descriptor

        movzx   eax, si
        add     eax, [_SELGDTLaddr]         ; (eax) = flat offset of descriptor
        mov     ecx, [ptg_cb]               ; (edi) = requested size
        dec     ecx                         ; (edi) = limit
        pushf                               ; save interrupt flag
        cli
        and     [eax].d_loaddr,NOT PAGEMASK ; Clear previous page offset
        and     bx, PAGEMASK
        add     [eax].d_loaddr, bx
        mov     [eax].d_limit, cx
        mov     cx, [ptg_x]                 ; cx = attr, acc
        mov     word ptr [eax].d_access, cx
        popf                         ; restore interrupt flag

        and     cx, D_PRIV              ; privilege bits
        shr     cx, SELAL_PRIVSHIFT
        .errnz (D_PRIV shr SELAL_PRIVSHIFT - SEL_RPLMASK)
        or      si, cx                  ; clears carry
        xor     eax, eax

        ; restore all registers
ptgx:   pop     es
        pop     ds
        pop     edi
        pop     edx
        pop     ecx
        pop     ebx
        LeaveProc
        ret
EndProc PhysToGDTSel


;***EP  UVirtToPhys - convert a UVIRT address to physical address
;
;       When in protected mode, UVirtToPhys converts a selector-offset
;       pair to a 32-bit physical address.  Callable at interrupt time
;       on GDT selectors only.
;
;       ENTRY   (AX:BX) = UVIRT virtual address
;
;       EXIT    'C' Clear if no error
;                       AX:BX = 32-bit physical address
;               'C' Set if error
;
;       USES    EAX, EBX, DS, Flags
;
;       PseudoCode:
;
;       If (AX is an LDT selector)
;           If (Interrupt time), return (carry )
;           Convert sel:offset to linear address
;       Else                        ; GDT selector
;           Get GDT Address
;       Endif
;       Convert selector to offset
;       Add sel offset to descriptor table's address
;       If (descriptor D_UVIRT bit not set)
;           return (carry)
;       Endif
;       Read address fields in descriptor
;       Call dh_VMLinToPhys
;       Write physical address to AX:BX
;       return
;

Procedure UVirtToPhys,<near,faronly>
    assume ds:nothing, es:nothing, ss:nothing, fs:nothing, gs:nothing
    LocalVar  uvp_desc, <size seldesc_s>
    LocalVar  uvp_physaddr,DWORD            ; physical address

        EnterProc
        ; save registers
        push    esi
        push    ecx
        push    edx
        push    es

        FlatContext     <ds, es>, cx

        test    ax,SEL_LDTBIT           ; is the selector in the LDT?
        jz      short uvp10             ; jump if GDT selector

; LDT selector

        DosContext  es, cx
        cmp     es:[InterruptLevel],-1
        jne     short uvp20              ; can not use LDT sels at int time

        FlatContext  es

;   Convert selector: offset to linear address
;   _SELGetDescInfo(sel, hptda, pdesc)
;   (ax:bx) = virtual address

uvp10:  SSToDS  edx, ss:uvp_desc
        CCALL32 _SELGetDescInfo <eax, 0, edx>
        or      eax, eax
        .errnz  NO_ERROR
        jz      short uvp30

uvp20:  stc
        jmp     short uvpx


uvp30:  test    [uvp_desc].desc_attr, D_UVIRT   ; error if not uvirt
        jz      short uvp20

        mov     eax, [uvp_desc].desc_laddr
        movzx   esi, bx                 ; (esi) = offset
        add     eax, esi                ; (eax) = linear address

; _VMLinToPhys(laddr, pphysaddr)

        SSToDS  ecx, ss:uvp_physaddr
        CCALL32 _VMLinToPhys <eax, ecx>
        or      eax, eax                ; clears carry
        .errnz  NO_ERROR
        jnz     short uvp20

 ; All succeeded, return physical address
        mov     ax, [uvp_physaddrh]
        mov     bx, [uvp_physaddrl]

uvpx:   ; restore registers
        pop     es
        pop     edx
        pop     ecx
        pop     esi
        LeaveProc
        ret
EndProc UVirtToPhys


;***EP  VirtToLin - convert virtual address to linear
;
;       VirtToLin converts given selector:offset pair to flat linear
;       address. It returns error if selector is not valid or if
;       offset is beyond selector limit.
;
;       ENTRY
;              (AX:ESI) = Virtual address (selector:offset)
;       EXIT    C clear
;                   (EAX) = linear address
;               C set if failed
;                   (EAX) = Error code
;       USES    EAX, ECX, EDX, Flags
;
;               FS and GS must be preserved (DevHelp requirement)
;
;       PseudoCode:
;
;       Load access byte
;       If (not segment), return error
;       Load limit
;       If ((expdown && offset <= limit) || (not expdown && offst > limit))
;           return error
;       Call SELVirtToLin(offset, sel)
;       return (laddr)
;
Procedure VirtToLin,<near,faronly,far16>
    assume ds:nothing, es:nothing, ss:nothing, fs:nothing, gs:nothing
        EnterProc ,,alignesp            ; Align ESP for 32 bit callouts
        movzx   eax, ax

;   Check the access byte

        LOADAR  ecx, eax
        jnz     short vtl90

;   ch = access byte

        test    ch, D_SEG
        jz      short vtl90                 ; jmp if not segment

;   Check the limit

        LOADSL  edx, eax                    ; edx = limit (byte granular)
        jnz     short vtl90

        and     ch, (D_CODE or D_EXPDN)
        cmp     ch, (D_DATA or D_EXPDN)
        jz      short   vtl20

; Not expand down

        cmp     esi, edx
        ja      vtl90
        jmp     short   vtl30

;   Expand down

vtl20:  cmp     esi, edx
        jbe     short vtl30

;   All os OK, Now convert to 32 bit address
;   laddr = _SELVirtToLin(offset, sel)

vtl30:  push    ds                      ; save segment registers
        push    es

        FlatContext <ds, es>, dx

        CCALL32 _SELVirtToLin <esi, eax>
        pop     es                      ; restore segment regs
        pop     ds
        cmp     eax,-1
        jz      short vtl90

        clc
vtlx:   LeaveProc                       ; Restore ESP
        ret

vtl90:  mov     eax, ERROR_INVALID_PARAMETER
        stc
        jmp     short vtlx
EndProc VirtToLin


;***EP  LinToGDTSel - convert linear address to virtual.
;
;       LinToGDTSel converts linear address to virtual (selector:offset)
;       address by mapping the passed selector's ptes to the physical memory
;       refered to by the passed linear address.
;
;       ENTRY   (EBX) = Linear address (must be on a page boundary)
;               (ECX) = Length ( <= 64K)
;                (AX) = Selector returned by dh_AllocGDTSelector or PhysToVirt
;       EXIT    C clear if conversion performed.
;               C set if not
;                   (EAX) = Error code
;       USES    EAX, ECX, EDX, Flags
;
;               FS and GS must be preserved (DevHelp requirement)
;
;       PseudoCode:
;
;       Call _GetDescInfo(sel)
;       If (not D_UVIRT), return (Carry)
;       For each page in selector, call PGPageToPhys and PGMapVirt to map to
;               given linear range.
;       If (error), return (carry)
;       Set up descriptor's access byte (ring 0 data)
;       Set up descriptor's limit = size - 1
;       Turn off low 12 bits in descriptor's low address field
;       return
;
Procedure LinToGDTSel,<near,faronly>
    assume ds:nothing, es:nothing, ss:nothing, fs:nothing, gs:nothing
    LocalVar    ltv_laddr,DWORD
    LocalVar    ltv_cb,DWORD
    LocalVar    ltv_paddr,DWORD
        EnterProc <ebx, ecx>                ; laddr, size

        push    ebx
        push    esi                         ; save esi
        push    edi                         ; save edi
        push    ds
        push    es

        cmp     ecx, SEL_64K                ; error if size > 64Kb
        ja      short ltv10

        movzx   edi, ax                     ; (edi) = selector
        test    di,SEL_LDTBIT               ; reject LDT selectors
        jnz     short ltv10

        and     di,SEL_RPLCLR               ; clear RPL bits
        cmp     di,GSEL GDT_POOL            ; must be above POOL base
        jb      short ltv10

        FlatContext <ds, es>, dx

        cmp     di,[_SELMaxGDT]             ; and below GDT limit
;88744  jae     short ltv10
        ja      short ltv10                 ; 88744 - not jump if <=

        mov     esi, ecx                    ; (esi) = size, saved by c conventn
        mov     edx, ebx
        add     edx, esi
        jc      short ltv10                 ; error if address wraps 4Gb

        add     edi, [_SELGDTLaddr]     ; (edi) = flat address of descriptor
        test    [edi].d_attr,D_UVIRT    ; must have UVIRT bit set
        jnz     short ltv40

ltv10:  mov     eax, ERROR_INVALID_PARAMETER
        stc
        jmp     ltvx

ltv20:  pop     edi                     ; clear stack
        jmp     short ltv10

ltv40:  test    [edi].d_access, D_SEG   ; must be segment descriptor
        jz      short ltv10

;   Loop through the pages mapped by the selector and remap them to
;   the physical memory mapped by the given linear address

;       First setup variables used in loop

        push    edi                     ; save descriptor pointer
        mov     edx, ebx
        and     edx, PAGEMASK
        add     esi, edx                ; add page offset to size
        add     esi,PAGESIZE-1          ; (esi) = requested bytes rounded up
        shr     esi,PAGESHIFT           ; (esi) = number of pages to remap
        xor     eax,eax                 ; (eax) = 0 (system ptda context)
        mov     dl, [edi].d_hiaddr
        mov     dh, [edi].d_extaddr
        shl     edx, WORDSHIFT
        mov     dx, [edi].d_loaddr      ; (edx) = laddr of GDT sel
        mov     edi, edx
        shr     edi,PAGESHIFT           ; (edi) = linear page to remap
        shr     ebx,PAGESHIFT           ; (ebx) = linear page to map to

;       Loop through each page

;       _PGPageToPhys(page, pptda, ppaddr)- get physical address of linear page

ltv50:  SSToDS  edx,ss:ltv_paddr
        CCALL32 _PGPageToPhys <ebx, [_pPTDACur], edx>
        or      eax,eax                 ; error?
        .errnz  NO_ERROR
        jnz     short ltv20             ; if error, jmp

;       _PGMapVirt(page, paddr, npages, fl)
;           map uvirt page to physical addr

        CCALL32 _PGMapVirt <edi, [ltv_paddr], dword ptr 1, dword ptr -1>

        inc     edi                     ; (edi) = next page to remap
        inc     ebx                     ; (ebx) = next page to map to
        dec     esi                     ; (esi) = pages remaining to map
        jnz     short ltv50             ; loop for next page if any are left

        pop     edi                     ; loop done, restore descriptor pointer

;   LinToGDTSel succeeded

ltv60:  mov     [edi].d_access, D_DATA0
        mov     eax, [ltv_cb]
        dec     eax
        mov     [edi].d_limit, ax
        and     [edi].d_attr, not D_EXTLIMIT
        mov     ebx, [ltv_laddr]
        and     ebx, PAGEMASK
        and     [edi].d_loaddr, not PAGEMASK
        add     [edi].d_loaddr, bx      ; adjust non page aligned address
        clc
ltvx:   pop     es
        pop     ds
        pop     edi                     ; restore edi
        pop     esi                     ; restore esi
        pop     ebx
        LeaveProc
        ret
EndProc LinToGDTSel

HIGH3CODE ends


        FARCODE INIT
INITCODE segment
    assume ds:nothing, es:nothing, ss:nothing


;***EP  PhysToVirtInit - Initialize PhysToVirt variables.
;
;       Get the GDT selectors needed for PhysToVirt, also reserve 68Kb of
;       linear address space and allocate corresponding page table entries.
;       A 68Kb linear address space is allocated to each selector, in order
;       to account for the fact that we can only map page boundary addresses
;       to page tables and the physical address may not be on a page
;       boundary. In that case we round down the physical address to
;       nearest page boundary while mapping to page table and add the
;       page offset into descriptor's base address field. The linear
;       addresses are assigned to selectors permanently, and should
;       never be erased or changed (except for page offset modifications).
;
;       ENTRY:  NONE
;       EXIT:   NONE
;       USES:   Flags
;
;       PseudoCode:
;
;       Set numsels= PTOV_NUMSELS       ; No of sels to be allocated
;       Set psel = offset of DOSGROUP:PToVSelectors
;       Do (numsels) times
;           Call VMAlloc (GDT space, UVIRT+RESIDENT, 68K, SELMAP, ring3 data,
;               PTOVIRTowner)
;           If (error), InternalError
;           Write Selector value to; psel
;           Increment psel
;       return

PI_VMF equ (VMAC_LOCANY or VMAC_ALIGNPAGE or VMAC_ARENASYS or VMAC_SELALLOC)
PI_PGF equ (PG_UVIRT or PG_W or PG_R or PG_U)
PI_SELF equ (SELAL_SELMAP or SELAL_UVIRT or SELAL_W or (3 shl SELAL_PRIVSHIFT))

Procedure PhysToVirtInit
    assume ds:nothing, es:nothing, ss:nothing, fs:nothing, gs:nothing
    LocalVar  pvi_ac, <size vmac_s>

        EnterProc

;   Save all registers

        pushad
        push    ds
        push    es

        DosContext es, bx
        mov     di, offset es:PtoV_Selectors
        mov     ecx, PTOV_NUMSELS

        FlatContext  ds, ax

; _VMAllocMem(cb, cbCommit, flvm, hptda, owner, hmte, flsel, physaddr, pac)

        SSToDS  esi, ss:pvi_ac

;       (ecx) = loop count
;       (esi) = flat address of pvi_ac
;       (es:di) = address to write selector to

pvi20:  push    es                          ; save es
        FlatContext  es, ax
        xor     eax, eax
        mov     ebx, ecx                    ; save loop count
        CCALL32 _VMAllocMem <<SEL_64K + PAGESIZE>, <SEL_64K + PAGESIZE>, \
                             <PI_VMF or PI_PGF>, eax, PTOVIRTOWNER, \
                             eax, PI_SELF, -1, esi>
        pop     es                          ; restore user segment
        assume es:nothing
        mov     ecx, ebx                    ; (ecx) = loop count
        or      eax, eax
        .errnz  NO_ERROR
        jnz     short pviie                 ; loop if no error

;       Allocation succeeded, Now write the selector
        mov     ax, [pvi_ac].ac_sel
        and     ax, SEL_RPLCLR
        mov     es:[di], ax
        add     di, 2                       ; increment di value
        loop    pvi20
        clc

; Restore all saved  registers

        pop     es
        pop     ds
        popad
        LeaveProc
        ret
pviie:  InternalError  <PhysToVirtProtInit : VMAlloc failed >
EndProc PhysToVirtInit


;***EP  PhysToVirtReinit - Re-initialize PhysToVirt variables
;
;       This routine is called after system initialization to
;       re-initialize any necessary PhysToVirt data.  It sets
;       the dpl to 0 in access bytes for all PhysToVirt descriptors,
;       and forces any later descriptors to be dpl/rpl 0.
;
;       ENTRY:  NONE
;       EXIT:   NONE
;       USES:   All registers preserved         @@

Procedure PhysToVirtReinit
    assume ds:nothing, es:nothing, ss:nothing, fs:nothing, gs:nothing

        pushad
        push    ds
        push    es

        DosContext ds
        mov     [PtoV_Access],D_DATA0
        mov     [PtoV_RPL],SEL_RPL0

;re-initialize dpl/rpl (descriptor/requestor) to 0 for the TEMPORARY pool *

        mov     cx,PTOV_NUMSELS
        mov     si,offset DOSGROUP:PtoV_Selectors

        FlatContext ES
        mov     ebx, [_SELGDTLaddr]

        ; (ebx) = Flat GDT address
        ;  (si) = offset in DOSGROUP into PtoV_Selectors list
        ;  (cx) = loop number
        ;  (ds) = DOSGROUP
        ;  (es) = FLAT

pvr00:  lodsw
        and     eax, SEL_RPLCLR
        add     eax,ebx
        and     es:[eax].d_access,not D_PRIV   ; clear dpl in access byte
    .errnz D_DPL0

    ; Turn off User flag in page tables
    ; _PGSetAccess(ipg, pptda, npages, fl)
    ;   (ebx) = GDT address
    ;    (si) = offset into PtoV_Selectors list
    ;   (eax) = Flat address of PtoV descriptor
    ;    (cx) = loop count

        FlatContext ds, dx
        mov     dh, [eax].d_extaddr
        mov     dl, [eax].d_hiaddr
        shl     edx, WORDSHIFT
        mov     dx, [eax].d_loaddr
        shr     edx, PAGESHIFT              ; (edx) = pageno
        mov     di, cx                      ; save loop count
        CCALL32 _PGSetAccess <edx, [_pPTDACur], <SEL_64K/PAGESIZE + 1>, \
                              <PG_R or PG_W or PG_SKIPFREE>>

        DosContext ds, cx
        mov     cx, di                      ; restore loop count
        loop    pvr00

        pop     es
        pop     ds
        popad

        ret
EndProc PhysToVirtReinit


INITCODE ends

FARCODE HIGH32
HIGH32CODE segment

;***EP  PhysToUVirt - convert physical to protected mode LDT virtual address.
;
;       PhysToUVirt converts a 32-bit address to a valid selector-offset pair.
;
;       Callable in user and kernel modes.
;
;       If the given physical address does not start on a page boundary,
;       we will InternalError for now in order to straighten out all
;       kernel callers, later on we will simply return carry.
;
;       ENTRY:  paddr = 32-bit physical address
;                       On flags=2 high word is LDT selector to free
;               size    = size of segment
;               flags   = 0: make segment readable code
;                       1: make segment writable data
;                       2: free selector
;                       3: make segment readable IOPL code
;                       4: make segment read/write IOPL data
;                       5: Tag selectors PVB, make segment writable data
;               (SI)    = 0 add/remove access from segment (Used only if dh=5)
;               owner   = used for ownership tracking
;       EXIT:   Carry Flag clear on success
;                   (ES:BX) = valid virtual address
;               Carry Flag set on error
;
;       USES:   EAX, EBX, EDX, ES, Flags
;
;       PseudoCode:
;
;       If (free request)
;           If (not user space) return (carry)
;           Get access rights (LoadAR)
;           If (not D_SEG || Ring 0 || not D_W/D_X)
;               return (Carry)
;           Endif
;           Call VMObjInfo
;           If (not UVIRT || owner != PTOUVIRTOWNER)
;               return (Carry)
;           Endif
;           Call VMFreeMem(laddr, current context)
;       Else                        ; allocate
;           If (size == 0), Set size = 64K
;           If (physaddr + size - 1 > 4Gb)
;               return (Carry)
;           Endif
;           If (physaddr is in the SGS control range), set VMAC_SGS flag
;           Call VMAlloc(RESIDENT+UVIRT, SELMAP, privuser space,blockno=physaddr)
;           If (error), return (Carry)
;           If (VMAC_SGS flag is set)
;               Call VMSGSControl
;               If (error), Call VMFree, return error
;           Call SetRPL (laddr to sel)
;           Set UVIRT bit in descriptor
;           Return selector:0 in ES:BX
;
;146867 12/15/95 Change Team  Change PhysToUVirt to panic only in the DEBUG
;                             version and to send an error message back in
;                             the retail version when a selector has not been
;                             freed.

PV_VMF1 equ VMAC_LOCANY or VMAC_ALIGNPAGE or VMAC_ARENAPRV or VMAC_SELALLOC
PV_PGF equ (PG_UVIRT or PG_R)

Procedure PhysToUVirt,far32
    assume ds:nothing, es:nothing, ss:nothing, fs:nothing, gs:nothing

        ArgVar   puv_paddr,DWORD        ; phys addr
        ArgVar   puv_size,DWORD         ; segment size
        ArgVar   puv_flags,DWORD        ; flags
        ArgVar   puv_owner,DWORD        ; owner
        LocalVar puv_sgsf,WORD          ; SGS control flags
        LocalVar puv_access,WORD        ; Segment access/attr
;;;;;;; LocalVar puv_oi,<size vmoi_s>
        LocalVar puv_od,<size vmod_s>
        LocalVar puv_ac,<size vmac_s>

        TaskTime
        EnterProc


        mov     dx,[puv_flagsl]
        cmp     dh,5                    ; can't be > 5!
        ja      puv20

        cmp     dh,2                    ; triage request
        jne     puv30                   ; jump if not a free request

;       Free request:
;       (AX) = UVirt LDT selector to be freed

        test    [puv_paddrh], SEL_LDTBIT
        jz      puv20

        movzx   eax, [puv_paddrh]
        LOADAR  ecx, eax
        jnz     short puv05                 ; jmp if access rights not OK

;       Allow only:
;       Ring 2/3 readable/executable code
;       Ring 2/3 read/write data

        shr     ecx, BYTESHIFT              ; (ch) = attr, (cl) = access
        and     cx,(D_UVIRT shl 8) or (D_PRES+D_DPL2+D_SEG+D_W)
        cmp     cx,(D_UVIRT shl 8) or (D_PRES+D_DPL2+D_SEG+D_W)
        .errnz  (D_W - D_RX)    ; if data, better be r/w, if code, r/x
        .errnz  ((D_DPL1 + D_DPL2) - D_DPL3)    ; allow ring 2 or ring 3
        jnz     short puv05                 ; jmp if not UVIRT, Ring 2 or 3

;   _VMGetOd(flags, pod[laddr,pptda])
;   (ax) = selector

;;;;;;; SSToDS  ecx, ss:[puv_oi]
        SSToDS  ecx, ss:[puv_od]
        xor     edx, edx
        SelToLaTiled ax
        mov     ebx, eax                    ; save address
        mov     [ecx].od_laddr, eax
        mov     eax, _pPTDACur
        mov     [ecx].od_pPTDA, eax
;;;;;;; CCALL32 _VMObjectInfo <eax, edx, ecx>
        CCALL32 _VMGetOd <edx, ecx>
        or      eax, eax
        .errnz  NO_ERROR
        jnz     short puv20

;;;;;;; cmp     [puv_oi].oi_hobowner, PTOUVIRTOWNER
        mov     eax,[puv_od].od_pob
        cmp     [eax].ob_hobowner, PTOUVIRTOWNER
puv05:  jnz     short puv20                 ; error if not PTOUVIRTOWNER

ifdef SELSTRICT
;;;;;;; test    [puv_oi].oi_fsob, OB_UVIRT
        test    [eax].ob_fs, OB_UVIRT
        jz      short puvie0

;;;;;;; cmp     [puv_oi].oi_va, ebx
        cmp     [puv_od].od_laddr, ebx
        jz      short puvex

        Panic "PhysToUVirt: bad selector"
puvie0: Panic "PhysToUvirt: no UVIRT flag"
puvex:
endif
                                                             
                                                                        ;146867
   ;         Change PhysToUVirt to panic only in the DEBUG              ;146867
   ;         version and to send an error message back in               ;146867
   ;         the retail version                                         ;146867


;   _VMFreeMem(laddr, hptda, fl)
;   (ebx) = Flat address

        xor     esi, esi                    ; indicate this is no error path
;;;146867
;;;146867  puv10:  xor     eax, eax
;;;146867          CCALL32 _VMFreeMem <ebx, eax, eax>
;;;146867          or      eax, eax
;;;146867          .errnz  NO_ERROR            ; clears carry
;;;146867          jnz     puvie1
;;;146867
;;;146867          or      esi, esi            ; came from non error path?
;;;146867          jz      short puv21         ; if so, return with carry clear
;;;146867
;;;146867  puv20:  stc                             ; failure
;;;146867  puv21:  jmp      puvx
;;;146867
                                                                        ;146867
puv10:  xor     eax, eax                                                ;146867
        CCALL32 _VMFreeMem <ebx, eax, eax>                              ;146867
        ifdef   VMSTRICT                                                ;146867
          or      eax, eax                                              ;146867
          jz    short @f                                                ;146867 ;146867
          Panic "PhysToVirt : VMFreeMem"                                ;146867
        @@:                                                             ;146867
        endif                                                           ;146867
                                                                        ;146867
        or      esi, esi               ; came from non error path?      ;146867
        jz      short puv25            ; if so, return with carry clear ;146867
        mov     eax, esi                                                ;146867
                                                                        ;146867
puv20:  stc                             ; failure                       ;146867
        jmp puvx                                                        ;146867
                                                                        ;146867
puv25:  or      eax, eax                                                ;146867
        jz      puvx                                                    ;146867
        jmp     short puv20                                             ;146867
                                                                        ;146867

;       Allocation request:
;       (DS) = (ES) = Flat

puv30:  mov     eax,[puv_paddr]
        mov     ebx,[puv_size]
        test    eax, PAGEMASK
        jz      short puv40
        mov     ecx,eax                 ; physaddr NOT on page boundary
        and     ecx,PAGEMASK
        add     ebx,ecx                 ; (ebx) = requested size + page offset
        and     eax,not PAGEMASK        ; (eax) = paddr of page boundary

puv40:  cmp     ebx,64*1024
        ja      puv20                   ; max segment size is 64k       ;b720217
        mov     ecx, eax
        dec     ebx                     ; convert to limit
        add     ecx, ebx
        jc      puvx                    ; error if address+size wraps

        inc     ebx                     ; (ebx) = size

;   (EAX) = physaddr
;   (EBX) = size
;    (DH) = Flags
;   See if this mapping needs to be registered under SGS control

        mov     [puv_sgsf],0            ; initialize to zero
        cmp     dh, 5                   ; flag PVB tag requested?
        jnz     short puv45

        cmp     [puv_flagsh],0          ; extra flags must be 0
        jnz     short puv20

        mov     [puv_sgsf], VMSGS_W or VMSGS_U  ; remove user and write access
        mov     dh, 1                   ; make dh index into table

; _VMAllocMem(cb, cbCommit, flvm, hptda, owner, hmte, flsel, physaddr, pac)

puv45:  xor     esi, esi
        SSToDS  edi, ss:puv_ac
        xor     ecx,ecx
        mov     cl,dh
        shl     ecx,1                   ; word offset into table
        movzx   edx, cs:AccessTable[ecx] ; get access from AccessTable
        mov     [puv_access], dx        ; save for later
        or      edx, SELAL_SELMAP       ; (edx) = SEL flags
        mov     ecx, PV_VMF1 or PV_PGF
        test    dl, D_DPL1              ; is it Ring3 ?
        jz      short puv50

        or      ecx, PG_U               ; turn on user flag
puv50:  test    dl, D_CODE              ; is it code
        jz      short puv60             ; jmp if no

        or      ecx, PG_X               ; code
        jmp     short puv70

puv60:  or      ecx, PG_W               ; data
puv70:  CCALL32 _VMAllocMem <SEL_64K, ebx, ecx, esi, \
                             PTOUVIRTOWNER, [puv_owner], edx, eax, edi>
        or      eax, eax
        .errnz  NO_ERROR
        jnz     puv20                   ; jmp if error


        cmp     [puv_sgsf], 0
        jz      short puv90             ; skip if not SGS control

        movzx   eax, [puv_sgsf]             ; flags
        CCALL32 _VMSGSControl <eax, [puv_ac].ac_va>
        or      eax, eax
        .errnz  NO_ERROR
        jz      short puv90             ; all done if no error

;       Error occured while registering under SGS control

        mov     esi, ERROR_NOT_ENOUGH_MEMORY
        mov     ebx, [puv_ac].ac_va
        jmp     puv10                   ; go free it


puv90:  mov     ax,[puv_ac].ac_sel      ; (ax) = sel

;       Set up return value in ES:BX

        mov     es, ax                  ; Put the selector in ES
        mov     bx, [puv_paddrl]
        and     bx, PAGEMASK            ; AND clears carry

;   Restore registers and leave

puvx:   LeaveProc
        RETC
;;;146867  puvie1: Panic "PhysToVirt : VMFreeMem"
EndProc PhysToUVirt

;***EP  TranslateRAS - convert offset of RAS info in globalinfoseg to
;                      linear address usable by 32 bit callers of
;                      DosQueryRASInfo
;
;       Callable in user modes.
;
;       ENTRY:  RASvirtual = offset of globalinfoseg information
;       EXIT:   (EAX) - contains the 32 bit linear address
;               (EAX) - contains NULL value if error occurs
;
;       USES:   EAX, EBX, ECX, EDX, ESI, Flags
;

;       PseudoCode:
;
;       Save all general purpose registers
;       Save all segment registers
;       Set up RASlinear with null value for error return if not successful
;       If setting up for obtaining Dekko linear address, use actual physical
;          address and do NOT call VIRTTOPHYS but call PHYSTOUVIRT
;       Set up DS with selector information
;       Set up SI with offset inforation
;       Call VIRTTOPHYS to obtain a physical address
;       If Carry flag is set, jump to end to return with NULL value
;       Convert AX:BX to address for PHYSTOUVIRT
;       Set up length in ECX and flags in EDX (0 means read access,100H write)
;       Call PHYSTOUVIRT
;       If Carry flag is set, jump to end to return with NULL value
;       Move ES:BX into AX, ESI respectively
;       Call VIRTTOLIN to obtain a linear address
;       If Carry flag is set, jump to end to return with NULL value
;       Move EAX into RASlinear
;       Cleanup and move RASlinear into EAX for return to caller
;       Return ....
;

Procedure _TranslateRAS,near
    assume ds:nothing, es:nothing, ss:nothing, fs:nothing, gs:nothing
    ArgVar   RASindex, DWORD      ; index for element in infoseg
    LocalVar RASlinear, DWORD     ; holder for linear address
    LocalVar RASoffset, WORD     ; offset for element in infoseg
        EnterProc
        pushad
        push  fs
        push  gs
        push  es
        push  ds

        DosContext <ds>

        mov   RASlinear, NULL

        cmp   RASindex, 0         ; index for MMIOAddr
        je    mmio_indx

        cmp   RASindex, 1         ; index for mec_table
        je    mec_indx

        mov   RASoffset, offset SISData.SIS_SysLog ;index for syslog
        jmp   convert1

mmio_indx:
        mov   eax,DekkoPhysAddr   ; get physical address of dekko card
        cmp   eax,0               ; check to see if the dekko exists
                                  ; it should be at c8000 or above
        pop   ds                  ; pop/push ds since we do not call VirtToPhys
        push  ds
        je    ras_end             ; dekko tracing is not enabled, return null

        mov   edx,0100H           ; writable access for data
        jmp   convert2

mec_indx:
        mov   RASoffset, offset SISData.SIS_mec_table

convert1:
        mov   si, RASoffset
                         ;ds:si = virtual address of RAS info
        CALL16 VirtToPhys
        pop   ds
        push  ds
        jc    ras_end
                         ;ax:bx = physical address
        shl   eax, 16
        mov   ax,  bx    ;eax   = physical address

        xor   edx, edx   ;edx   = readable access for data (dh specifically)

convert2:
        mov   ecx, 14    ;ecx   = length

        CALLF32 PhysToUVirt <eax, ecx, edx, 0>
        jc    ras_end
                         ;es:bx = uvirt virtual address
        mov   ax, es
        movzx esi, bx
        CALL16  VirtToLin
        jc    ras_end
                         ;eax = physical address
        mov   RASlinear, eax

ras_end:
        pop   ds
        pop   es
        pop   gs
        pop   fs
        popad
        mov   eax, RASlinear
        LeaveProc
        RETC

EndProc _TranslateRAS

HIGH32CODE ends

        end
