SCCSID equ "@(#)selmgra.asm     6.11 92/01/16"

;        IBM/Microsoft Confidential
;
;        Copyright (c) IBM Corporation 1987, 1989
;        Copyright (c) Microsoft Corporation 1987, 1989
;
;        All Rights Reserved

;   This file includes c callable versions of SELECTOR MANAGER procedures
;   and any other procedure that needed to be written in assembly.
;
;   In order to be callable from c code, all exported procedures in this
;   file must obey the following interface rules:
;   1) All procedure names must start with '_'
;   2) Upon entry all procedures have DS = ES = FLAT
;   3) All procedures must preserve EBX, ESI, EDI, SS, ESP, EBP, DS, ES
;   4) All procedures must receive parameters from stack (4 bytes for each
;       parameter)
;   5) All procedures must return their return code in EAX.
;
; 09/19/91  Khoa Dang Huynh
;   CP20 DCR 1486  Provide selector packing capability.
;


.xlist
include  dosseg.inc
include  fnomem.inc
include  osconst.inc
include  mi.inc
include  dossymc.inc
include  sigdef.inc
include  except.inc
include  basemaca.inc
include  osmaca.inc
include  seldesc.inc
include  protmode.inc
include  error.inc
include  vmowner.inc
include  pgexport.inc
include  vmexport.inc
include  selxport.inc
include  tkexport.inc
include  infoseg.inc
include  curdir.inc
include  dirent.inc
include  find.inc
include  screen.inc
include  sem.inc
include  tkptda.inc
include  sellocal.inc
.list

        ModName  selmgra, FNO_SELMGRA_ASM

CpuMode 386

        ReferGlobal     GDTDesc,FWORD
        ReferGDT        GDT_DOSALIAS,WORD
        ReferGDT        GDT_SAS,WORD
        ReferGDT        GDT_PTDA,WORD      ; ptr b728108
        ReferGDT        GDT_LDT,word

        extrn   _VMEachMap:near
        extrn   _VMGetPTDAAddr:near
        extrn   _VMGetOd:near
;;;;;;; extrn   _VMObjectInfo:near
        extrn   _VMGetSem:near
        extrn   _VMClearSem:near
        extrn   _VMGetAliasOffset:near
        extrn   _PGAlloc:near

        ReferFlat       _pPTDACur,dword
        ReferFlat       _TKSSBase,dword
        ReferFlat       _SELGDTHandle,word
        ReferFlat       _SELGDTLaddr,dword
        ReferFlat       _SELMaxGDT,word
        ReferFlat       _SELGDTFreeList,word
        ReferFlat       _VMProtectedMem,dword
        ReferFlat       _pGDTTaskCopy,dword


    _selGDTDesc_s struc               ; Used to update GDTR after GDT grow
        gd_limit    dw  ?
        gd_laddr    dd  ?
    _selGDTDesc_s ends


      FARCODE HIGH32
HIGH32CODE segment

;***LP  _selGetLDTAddr - Get linear address of LDT
;
;       _selGetLDTAddr returns linear address of LDT given context handle.
;
;       ENTRY   hptda   - context handle (0 if current)
;       RETURN  laddr   - Linear address of LDT belonging to ptda
;       USES    EAX, ECX, EDX, Flags
;
;       PseudoCode:
;
;       If(current context)
;           Get [ptda_ldtaddr]        /* From stack */
;       Else
;           Call VMGetPTDAAddr
;           get [ptda_ldtaddr]
;       Endif
; ifdef SELSTRICT
;       Call VMGetObjectInfo
;       If(he_owner != SELLDTOWNER) Panic
; endif
;       return linear address
Procedure   _selGetLDTAddr, near
    assume ds:FLAT, es:FLAT , fs:nothing, gs:nothing, ss:nothing
        ArgVar      gla_hptda,WORD

ifdef  SELSTRICT
        LocalVar    gla_od,<size vmod_s>        ; Object information buffer
endif

        EnterProc

        mov     eax, _pPTDACur
        mov     cx, [gla_hptda]
        or      cx,cx                   ; current context?
        jz      short gla20             ; jmp if so

;       PTDA address = _VMGetPTDAAddr(hptda)

        CCALL32 _VMGetPTDAAddr <ecx>    ; eax = PTDA address

gla20:

ifdef   SELSTRICT

;       errcode = _VMGetOd(NA, pod)

        mov     ecx, [eax].ptda_ldtaddr
        mov     gla_od.od_pPTDA,eax
        mov     gla_od.od_laddr,ecx
        xor     ecx,ecx
        lea     eax, ss:gla_od
        add     eax, [_TKSSBase]
        CCALL32 _VMGetOd <ecx, eax>
        or      eax, eax
        .errnz  NO_ERROR
        jnz     short glaie2

        mov     eax,gla_od.od_pob
        cmp     [eax].(ob_hobowner - obu0), SELLDTOWNER
        jnz     short glaie3
        mov     eax, gla_od.od_laddr
else
        mov     eax, [eax].ptda_ldtaddr
endif

        LeaveProc
        RETC                            ; caller cleans stack
ifdef SELSTRICT
glaie2: Panic   <'selGetLDTAddr : VMGetOd'>
glaie3: Panic   <'selGetLDTAddr : not an LDT'>
endif
EndProc _selGetLDTAddr


;***EP  _SELAllocGDT - Allocate GDT selector
;
;       _SELAllocGDT allocates GDT selector, growing the GDT if necessary.
;
;       ENTRY   psel - Address to return selector
;       RETURN  int     - error code (NO_ERROR if no error)
;
;       SELAllocGDT (sel *psel)
;
;       PseudoCode:
;
;       Save all registers
;       If (GDTFreeList != 0)       ; still selectors on freelist?
;           Set desc=[GDTFreeList]
;           Set desc.d_limit=0      ; clear limit field
;           Set [GDTFreeList] = desc.d_flink
;           If ([GDTFreeList] != 0)     ; freelist still not empty?
;               set [GDTFreeList].d_blink = 0 ; clear back link field
;           Endif
;           return(selector)
;       Endif
;       ; Free list empty, we have to grow GDT
;       Set nextdesc == [MaxGDTDesc] + size desctab
;       If (maxdesc >= 64K)         ; GDT already grown to full size?
;           return(Carry, ERROR_NOT_ENOUGH_MEMORY)
;       Endif
;       Set pg=(nextdesc) >> PAGESHIFT
;       Call PGAlloc(resident, zeroinit, pg, 1 page) ;allocate one more page
;       If (error), return (error code)
;       Set [MaxGDTDesc] = nextdesc + PAGESIZE - size desctab
;       ; set up freelist
;       Set oldhead= [GDTFreeList]
;       Set [GDTFreeList] = nextdesc
;       Do (PAGESIZE/sizeof desctab - 1) times
;           set [nextdesc].d_flink = nextdesc+size desctab
;           Set nextdesc =+ size desctab
;       Set [nextdesc].d_flink = oldhead     ; new block points to old freelist
;       Reload GDT register to update the limit.
;       Go back to beginning
;

SAG_FLPG equ PG_ZEROFILL or PG_RESIDENT or PG_R or PG_W

Procedure   _SELAllocGDT, near
    assume ds:FLAT, es:FLAT , fs:nothing, gs:nothing, ss:nothing

        ArgVar  ag_psel, DWORD              ; place to write the selector value
        EnterProc

        ; Save registers for c calling conventions
        push    ebx
        push    esi
        push    edi

alg10:  movzx   eax, [_SELGDTFreeList]
        or      eax, eax                ; empty freelist?
        jz      short alg30             ; jmp if GDT needs growing

        ; Free list is not empty, just remove the selector from link list

        mov     edx, [_SELGDTLaddr]
        xor     ecx, ecx
        xchg    [edx+eax].d_flink, cx   ; limit = 0, cx = next sel on list
        mov     [_SELGDTFreeList], cx   ; reset free list
        mov     ecx, [ag_psel]
        mov     [ecx], ax               ; return selector
        xor     eax, eax
        .errnz  NO_ERROR                    ; return NO_ERROR
alg90j: jmp     alg90

ifdef SELSTRICT
algie:  Panic   <'SELAllocGDT : Bad Max sel'>
endif
        ; Need to grow the GDT, first obtain GDT handle semaphore.
        ; _VMGetSem(hob, fl, pp)

alg30:  xor     edi, edi
        CCALL32 _VMGetSem <dword ptr [_SELGDTHandle], VMGS_TAKESEM, edi>
        or      eax, eax
        jnz     short alg90j            ; return if error

        ; Did somebody already grow the GDT while we blocked?

        or      [_SELGDTFreeList], 0    ; is it still empty?
        jnz     alg80                   ; go clear semaphore

        movzx   ebx, [_SELMaxGDT]
        add     ebx, size desctab       ; must be offset of next page
ifdef SELSTRICT
        test    ebx, PAGEMASK           ; must be on page boundary
        jnz     short algie
endif
        cmp     ebx, WORDMASK + 1       ; Is GDT already 64Kb?
        mov     eax, ERROR_NOT_ENOUGH_MEMORY
        jae     alg80                   ; return if so

        mov     ecx, ebx
        add     ecx, [_SELGDTLaddr]     ; (ecx) = address of page to allocate

        ; Need to grow the GDT, just allocate another page at the end
        ; _PGAlloc(ipg, pptda, npages, blockno, hob, obpg, fl)

        xor     eax, eax
        shr     ecx, PAGESHIFT
        mov     edx, ebx
        shr     edx, PAGESHIFT          ; object relative page number
        CCALL32 _PGAlloc <ecx, eax, 1, eax, <dword ptr [_SELGDTHandle]>, edx, SAG_FLPG>
        or      eax, eax
        .errnz  NO_ERROR
        jnz     short alg80                 ; return error if failed

        ; New page committed, now update data
        ; (ebx) = Last valid desc + size desctab

        add     ebx, PAGESIZE -1            ; (ebx) = new GDT limit
        mov     eax, [_SELGDTLaddr]
        mov     [GDTDesc].gd_limit, bx
        mov     [GDTDesc].gd_laddr, eax
        lgdt    [GDTDesc]                   ; update GDT register

        xor     ecx, ecx
        mov     cx, DOSGDTDATA
        mov     [eax+ecx].d_limit, bx       ; update DOSGDTDATA
        sub     ebx, (size desctab - 1)     ; offset of last valid descriptor
        mov     [_SELMaxGDT], bx
        mov     ecx, (PAGESIZE shr SEL_SHIFT)   ; no. of descs in a page

        ; link descriptors to free list
        ; (eax) = Flat GDT address
        ; (ebx) = offset of last GDT desc
        ; (ecx) = no. of descs in a page

        mov     dx, [_SELGDTFreeList]           ; somebody may have freed
                                                ; a selector while we blocked
alg60:  mov     [eax+ebx].d_flink, dx
        mov     edx, ebx
        sub     ebx, size desctab
        loop    alg60
        mov     [_SELGDTFreeList], dx           ; store head of free list
        xor     eax, eax
        .errnz  NO_ERROR

        ; Now free the semaphore and go back to beginning
        ; _VMClearSem(hob)
        ; (eax) = error code

alg80:  push    eax
        CCALL32 _VMClearSem <dword ptr [_SELGDTHandle]>
        pop     eax                             ; error code
        or      eax, eax
        jz      alg10                           ; jmp if grow succeeded

        ; Restore all registers
alg90:  pop     edi
        pop     esi
        pop     ebx

        LeaveProc
        RETC
EndProc     _SELAllocGDT


;***LP  _selFreeGDTSub - Free GDT selector, return committed size
;
;       _selFreeGDTSub frees the GDT selector and returns its existing
;       descriptor size. It generates an Internal Error under any error
;       condition.
;
;       ENTRY   sel - selector to be freed
;       RETURN  ulong - Existing descriptor size
;               WARNING: Invalid if it is an expand down segment.
;       USES    EAX, ECX, EDX, EFlags
;
;       cbDesc = selFreeGDTSub(sel)
;
;       PseudoCode:
;
;       clear GDT descriptor access field
;       Read/calculate descriptor size
;       link onto free list
;
Procedure   _selFreeGDTSub, near
    assume ds:FLAT, es:FLAT , fs:nothing, gs:nothing, ss:nothing

        ArgVar  fg_sel, WORD              ; selector
        EnterProc

        push    ebx
        mov     ebx, [_SELGDTLaddr]
        movzx   edx, [fg_sel]
        test    dx, SEL_LDTBIT                      ; LDT selector?
        jnz     short fgsie                         ; Panic if so

        movzx   ecx, [_SELMaxGDT]
        cmp     cx, dx                              ; is sel > MaxGDT ?
        jb      short fgsie                         ; Panic if so

        and     edx, not (SEL_RPLMASK)              ; convert sel to offset
ifdef  SELSTRICT
        CCALL32 selCheckFreeList                    ; Check free list
endif
        or      [ebx+edx].d_access, 0               ; invalid ?
        mov     eax, -1                             ; no committed size if so
        jz      short fgs60

        mov     al, [ebx+edx].d_attr                ; get the ext. limit
        mov     cl, al
        and     ax, D_EXTLIMIT
        shl     eax, WORDSHIFT
        mov     ax,[ebx+edx].d_limit
        test    cl, D_GRAN4K
        jz      short fgs40

        shl     eax, PAGESHIFT                      ; adjust for 4K granularity
        or      eax, PAGEMASK

fgs40:  inc     eax                                 ; (eax) = size
fgs60:  xor     ecx, ecx
        mov     [ebx+edx].d32_low, ecx              ; clear desc fields
        mov     [ebx+edx].d32_high, ecx
        mov     ecx, edx                            ; ecx = sel to be freed
        xchg    [_SELGDTFreeList], dx               ; set new link head, get old
        mov     [ebx+ecx].d_flink, dx

        pop     ebx
        LeaveProc
        RETC
fgsie:  Panic   <'selFreeGDTSub: bad selector'>
EndProc     _selFreeGDTSub


;***EP  _SELFreeGDT - Free GDT selector
;
;       _SELFreeGDT puts the given GDT selector back to free list.
;
;       ENTRY   sel - selector to be freed
;       RETURN  int - error code (NO_ERROR if no error)
;       USES   EAX, ECX, EDX, EFlags
;
;       SELFreeGDT(SEL sel)
;
;       PseudoCode:
;
;       Call _selFreeGDTSub
;
Procedure   _SELFreeGDT, near
    assume ds:FLAT, es:FLAT , fs:nothing, gs:nothing, ss:nothing

        ArgVar  fg_sel, WORD              ; selector
        EnterProc

        movzx   eax,[fg_sel]
        CCALL32 _selFreeGDTSub <eax>
        xor     eax, eax
        .errnz  NO_ERROR                            ; return code
        LeaveProc
        RETC
EndProc     _SELFreeGDT


ifdef   SELSTRICT
;***LP  selCheckFreeList - Check the GDT Free List to make sure selector
;           to be freed is not already free.
;
;       selCheckFreeList checks to make sure the selector to be freed is not
;       already on the free list.
;
;       ENTRY       EDX - Selector offset to be freed.
;                   ECX - Highest valid selector value
;                   EBX - Linear address of GDT.
;       EXIT        NONE (Panic if selector already free)
;       USES        Flags
;
;       PseudoCode:
;
;       Walk the whole free list to see if any linked descriptor matches
;           the selector value passed.
;
Procedure   selCheckFreeList, near, local
    assume ds:FLAT , es:FLAT , fs:nothing, gs:nothing, ss:nothing

        push        eax                 ; save
        movzx       eax, [_SELGDTFreeList]

cfl10:  or          eax, eax            ; end of free list/ free list empty?
        jz          short cflx          ; jmp if so

        cmp         eax, edx
        jz          short cflie1

        mov         ax, [ebx+eax].d_flink
        cmp         ecx, eax            ; is it beyond valid GDT limit?
        jae         short cfl10

        Panic   <'selCheckFreeList: GDT Free List broken'>

cflx:   pop         eax
        RETC
cflie1: Panic   <'selCheckFreeList: GDT selector already free'>
EndProc selCheckFreeList

endif


;***LP  selConvertToExpandUp - Convert expand down base and limit to expand up.
;
;       Given values of limit and address from an expand down descriptor,
;       selConvertToExpandUp returns the corresponding expand up values
;       for address and size (NOT limit!)
;
;       ENTRY    (AH) = Attribute byte (Only G and B bits needed)
;               (ESI) = 32 bit expand down limit (byte granular)
;               (EDX) = Expand down base address
;
;       EXIT    (ESI) = 32 bit expand regular size
;               (EDX) = Linear address of first byte of segment
;
;       USES    EDX, ESI, Flags
;
;       Pseudocode:
;
;       Ifdef SELSTRICT
;           If (state of G bit != B bit), Panic
;           If ( G bit == 0)
;               If (limit > fffe), Panic
;           Endif
;       Endif
;       Expandup address = expdown address + limit + 1
;       If (G bit == 0)
;           Size = ffff - limit         ; negate, dec si
;       Else                            ; G bit == 1
;           Size = ffffffff - limit     ; negate, dec esi
;       Endif
;       return
;
ifdef   SELSTRICT
ceuie:  Panic   <'ConvertToExpandUp: Bad parameters'>
endif

Procedure   selConvertToExpandUp, near, local
    assume ds:FLAT , es:FLAT , fs:nothing, gs:nothing, ss:nothing

ifdef SELSTRICT                 ; State of D_DBIG and D_GRAN4K must be same
        test    ah, D_GRAN4K            ; is granularity set?
        jz      short ceu10

        test    ah, D_DBIG              ; This must also be set
        jz      short ceuie
        jmp     short ceu20             ; all OK, exit

ceu10:  test    ah, D_DBIG              ; This must also be clear
        jnz     short ceuie

        cmp     esi, WORDMASK           ; limit must be < ffff
        jae     short ceuie

ceu20:
endif

        push    ecx                     ; save eax
        mov     ecx, edx                ; expanddown address
        add     ecx, esi                ; ecx = expandup address - 1
        inc     ecx                     ; ecx = expandup address
        test    ah, D_GRAN4K
        jnz     short ceu30

        neg     si                      ; negate si
        dec     si                      ; esi = expandup size
        jmp     short ceu50

ceu30:  neg     esi                     ; negate esi
        dec     esi                     ; esi = expandup size
ceu50:  mov     edx, ecx                ; edx = expandup address
        pop     ecx
        RETC
EndProc selConvertToExpandUp


;***LP  selConvertToExpandDown - Convert expand up base and size to expand down.
;
;       Given the starting address, size and G bit, selConvertToExpandDown
;       returns the expand down descriptor address and limit values.
;
;       ENTRY    (AH) = Attribute byte (Only G bit needed)
;               (ECX) = 32 bit regular size (byte granular)
;               (EDX) = Expand up base address
;
;       EXIT    (ECX) = 32 bit expand down limit  (NOT size!)
;               (EDX) = Expand down base address
;
;       USES    ECX, EDX, Flags
;
;       Pseudocode:
;
;       Ifdef SELSTRICT
;           If (G bit == 0)
;               If (size > ffff) Panic
;           Endif
;       Endif
;       If (G bit == 0)
;           Limit = ffff - size
;       Else                    ; G bit set
;           Limit = ffffffff - size
;       Endif
;       ExpanddownAddress = ExpandupAddress - 1 - limit
;       Return
;
ifdef   SELSTRICT
cedie:  Panic   <'ConvertToExpandDown: Bad parameters'>
endif

Procedure   selConvertToExpandDown, near, local
    assume ds:FLAT , es:FLAT , fs:nothing, gs:nothing, ss:nothing

        test        ah, D_GRAN4K
        jnz         short ced10

ifdef SELSTRICT                 ; State of D_DBIG and D_GRAN4K must be same
        cmp         ecx, WORDMASK       ; size can't be > ffff
        ja          short cedie
endif
        neg     cx
        dec     cx                      ; ecx = limit
        jmp     short  ced20

ced10:  neg     ecx
        dec     ecx                     ; ecx = limit
ced20:  sub     edx, ecx
        dec     edx                     ; edx = expand down address
        RETC
EndProc  selConvertToExpandDown


;***EP  _SELVirtToLin - Convert 16:16 pointer to 0:32
;
;       _SELVirtToLin converts 16:16 pointer to 0:32. It handles
;       expand down addresses correctly. No checking is done for LDT
;       selectors, minimal checking for GDT selectors.
;
;       ENTRY   offset  - 32 bit offset
;               sel     - selector
;               DS = ES = Flat
;       EXIT    laddr   - 32 bit linear address (-1 if invalid)
;       USES    EAX, ECX, EDX, Flags
;               FS and GS must be preserved (DevHelp requirement)
;
;       PseudoCode
;
;       If (LDT selector) use SelToLa
;       Else    read descriptor's address from the GDT
;       Add the offset returned
Procedure   _SELVirtToLin
    assume  ds:FLAT, es:FLAT, fs:nothing, gs:nothing, ss:nothing
        ArgVar  vtl_off,DWORD           ; offset
        ArgVar  vtl_sel,WORD            ; selector

        EnterProc

        movzx   eax, [vtl_sel]          ; ax = selector
        mov     ecx,eax
        and     ecx, SELECTOR_MASK      ; (ecx) = descriptor offset

        test    al, SEL_LDTBIT
        jz      short   vtl20           ; jmp if GDT sel

        mov     eax,[_pPTDACur]
        add     ecx,[eax].ptda_ldtaddr  ; (ecx) = pointer to descriptor

        mov     [vtl_offh], word ptr 0  ; clear high word of offset if LDT
        jmp     short vtl30

vtl20:  add     ecx, [_SELGDTLaddr]   ; (ecx) = pointer to descriptor
        cmp     ax,GSEL GDT_PTDA      ; b728108 is this the ring 0 stack
        jnz     short vtl25           ; b728108 if so return tkssbase + offset
        mov     eax, [_TKSSBase]      ; b728108
        jmp     short vtl80           ; b728108

vtl25:  cmp     ax, [_SELMaxGDT]        ; error if sel > GDT size
        mov     eax, -1
        ja      short vtl99

vtl30:  test    [ecx].d_access, D_SEG
        mov     eax,-1
        jz      short vtl99             ; jmp if not segment selector

        mov     ah, [ecx].d_extaddr
        mov     al, [ecx].d_hiaddr
        shl     eax, WORDSHIFT
        mov     ax, [ecx].d_loaddr      ; eax = base address
vtl80:  add     eax, [vtl_off]          ; add the offset
vtl99:  LeaveProc
        RETC
EndProc  _SELVirtToLin


;***EP  _SELGetDescInfo - Get segment descriptor information
;
;       _SELGetDescInfo returns all of the information contained in the
;       specified segment descriptor.
;       WARNING: This procedure will work for descriptors only after
;       SEL initialization is complete, i.e. after SELInit is called.
;
;       ENTRY   sel    -  selector
;               hptda  -  ptda handle (required only if LDT selector)
;               pseldesc_s - flat address of location to return information
;       RETURN  int     -  error code   (NO_ERROR if no error)
;                   pseldesc_s->desc_cb = size ( = 0 if 4Gb )
;                   pseldesc_s->desc_laddr = linear address
;                   pseldesc_s->desc_attr  = attribue (D_UVIRT,D_B, D_G)
;                   pseldesc_s->desc_acc  = access byte
;
;       SELGetDescInfo(SEL sel, VMHOB hptda, struct seldesc_s *pseldesc_s)
;
;       Pseudocode:
;
;       Mask off RPL and table bits
;       If (GDT selector)
;           If (selector value out of GDT range), return error
;           Get GDT Address
;       Else    /* LDT selector */
;           Call selGetLDTAddr
;       Endif
;       Read descriptor fields
;       If (G bit set) convert limit to byte value
;       If (expand down segment)
;           Call ConvertToExpandUp      /* Convert base and limit to base and size */
;       Else
;           Change limit to size        /* Increment */
;       Endif
;       return
;
Procedure   _SELGetDescInfo, near
    assume ds:FLAT , es:FLAT , fs:nothing, gs:nothing, ss:nothing

        ArgVar      gdi_sel,WORD        ; selector
        ArgVar      gdi_hptda,WORD      ; context handle
        ArgVar      gdi_pseldesc,DWORD  ; address of location to return info

        EnterProc

        ; Save registers for c calling conventions
        push        ebx
        push        esi

        movzx       eax, [gdi_sel]              ; eax = selector
        mov         ebx, eax                    ; eax = ebx = selector
        and         ebx, not (SEL_LDTBIT + SEL_RPLMASK) ; ebx = offset in table
        test        eax, SEL_LDTBIT             ; LDT selector?
        jnz         short gdi20                 ; jmp if so

        ; GDT selector

        mov     cx, [_SELMaxGDT]
        cmp     cx, bx
        jb      short gdie

        mov     ecx, [_SELGDTLaddr]             ; get GDT address
        add     ebx, ecx                        ; ebx points to descriptor
        jmp     short   gdi40

        ;   LDT selector
        ;   ebx = selector's offset into descriptor table

gdi20:  mov     ax, [gdi_hptda]             ; ax = hptda
        CCALL32 _selGetLDTAddr <eax>    ; returns eax = laddr of LDT
        add     ebx, eax                ; ebx points to descriptor

gdi40:  mov     ecx, [ebx].d32_low      ; ecx = base15-0, limit 15-0
        mov     edx, [ebx].d32_high     ; edx = extaddr, attr, acc, hiaddr
        mov     ebx, [gdi_pseldesc]
        mov     eax, edx
        mov     [ebx.desc_acc], ah
        or      ah, ah                  ; is access = 0 ?
        jnz     short gdi45

        ;   Bad selector value, return error

gdie:   mov     eax, ERROR_INVALID_PARAMETER
        jmp     short gdix

gdi45:  and     dh, D_SEG + D_GATE      ; is it a gate descriptor?
        cmp     dh, D_GATE
        jz      short gdi70             ; jmp if gate

        rol     edx, BYTESHIFT          ; edx = attr, acc, hiaddr, extaddr
        xchg    dh, dl                  ; edx = attr, acc, extaddr, hiaddr
        shld    edx, ecx, WORDSHIFT     ; edx = laddr
        mov     esi, eax                ; esi = eax = NA, attr, acc, NA
        and     esi, (D_EXTLIMIT shl WORDSHIFT)
        mov     si, cx                  ; esi = 20 bit limit
        shr     eax, BYTESHIFT          ; ah = attr, al = acc
        test    ah, D_GRAN4K
        jz      short gdi50

        shl     esi, PAGESHIFT          ; esi = limit
        or      esi, PAGEMASK           ; fill last 12 bits with ones
gdi50:  inc     esi                     ; esi = size
        and     al, (D_SEG + D_CODE + D_EXPDN)
        cmp     al, (D_SEG + D_DATA + D_EXPDN)  ; is it expand down data?
        jnz     short gdi60

        dec     esi                     ; esi = limit
        CCALL32 selConvertToExpandUp    ; returns expand up address and size

gdi60:  mov     [ebx].desc_laddr, edx
        mov     [ebx].desc_cb, esi
        and     ah, not D_EXTLIMIT
        mov     [ebx].desc_attr, ah
        jmp     short gdi90             ; all done, exit

        ; it is a GATE descriptor
        ; eax = high dword of descriptor = high offs.,access, parm count
        ; ecx = low dword of descriptor  = selector, low offs
        ; ebx = [gdi_pseldesc]

gdi70:  xchg    ax, cx                  ; eax=32 bit off, ecx=sel,acc,cparm
        mov     [ebx].desc_off, eax
        and     cl, D_WCMASK            ; cl= parameter count
        mov     [ebx].desc_cparm, cl
        shr     ecx, WORDSHIFT          ; ecx=selector
        mov     [ebx].desc_sel, ecx
gdi90:  xor     eax, eax
        .errnz  NO_ERROR                ; successful return

        ; Restore saved registers

gdix:   pop         esi
        pop         ebx
        LeaveProc
        RETC
EndProc     _SELGetDescInfo


;***EP  _SELSetDescInfo - Setup segment descriptor information
;
;       _SELSetDescInfo optionally sets or updates the limit, base address,
;       access and attribute fields in the specified segment descriptor(s).
;       For a GDT selector, it sets up the descriptor to point to entire
;       size of the memory object. For an LDT selector, it maps consecutive
;       64K chunks of the object into selectors that are SEL_INCR apart.
;       This routine manages LDT, GDT, shared, and aliased
;       descriptors. VMM alias descriptors are not updated automatically
;       after a memory object limit update. Only the Selector Manager
;       aliases are updated(cs and MemMap aliases).
;
;       WARNING: Any change in the first five parameters of this procedure
;       must also be reflected in _SELSetDescAll.
;
;       ENTRY   (SS:SP)     Scope
;               (SS:SP+4)   Access byte
;               (SS:SP+5)   Attribute (D_BIG, D_UVIRT only, D_GRAN4K if expand
;                           down)
;                           If (SELSD_GATE), parameter count (<= 31)
;               (SS:SP+8)   Base selector to be set up
;               (SS:SP+12)  Size of memory object
;                           If (SELSD_GATE), offset
;               (SS:SP+16)  Linear address of base(Needed only if GDT selector)
;                           If (SELSD_GATE), selector to store
;               (SS:SP+20)  Handle of memory object
;                           Ignored if SELSD_GATE
;               (SS:SP+24)  hptda of the selector to be set up
;                               If zero, the current task's LDT will be used.
;                               Interpreted only if Selector is an LDT selector.
;               (SS:SP+28)  Oldsize (SELSD_LIMIT and LDT selector only)
;       RETURN  NONE
;
;       SELSetDescInfo(ulong scope, ushort attr_acc, SEL sel, ulong size,
;           ulong laddr, VMHOB handle, VMHOB hptda, ulong oldsize)
;
;       PseudoCode:
;
;  Scope parameters
;  SELSD_GATE  Set up descriptor as a (call, interrupt or trap) gate
;  SELSD_ALL   Set up all fields of the descriptor
;  SELSD_LIMIT Update only the limit field
;       SELSD_SHARED Shared memory, update all other contexts (SELSD_LIMIT only)
;       SELSD_ALIAS  Update SELMGR alias selectors (SELSD_LIMIT only)
;   SELSD_BASEALL Base (LDT) selector maps all (SELSD_ALL and SELSD_LIMIT only)
;
;   Ifdef SELSTRICT
;       If (SELSD_ALL || SELSD_GATE)
;           If (SELSD_SHARED || SELSD_ALIAS) Panic
;       Endif
;       IF (SELSD_GATE && SELSD_BASEALL) Panic
;   Endif
;       Convert size to limit and store in stack
;       Set up helper in stack (sdhgate, sdhLDTall, sdhGDTall, sdhLDTlimit,
;           sdhGDTlimit)
;       If (LDT sel), set laddr = selector >> 3 << 16
;       If (hptda == 0), set hptda = current hptda
;       If (D_DATA && D_EXPDN)
;           increment limit
;           Call ConvertToExpandDown
;           Save expanddown values on stack
;       Endif
;       If (SELSD_SHARED or SELSD_ALIAS)
;           Call VMEachMap(&selEachDesc)
;       Else                    ; limit update for shared LDT selector
;           Call selEachDesc
;       Endif
;       If (selector == DOSGROUP)
;           Call Helper(GDT_DOSALIAS)
;           Call Helper(GDT_SAS)
;       Endif
;       return
;
SELSD_FMASK     equ     30000h
SELSD_FSHIFT    equ     0dh
SELSD_NEXTHELPER   equ     4h

ALIGN 4                                     ; align on a dword boundary
HelperTab:
        .errnz ($-HelperTab)-((SELSD_ALL and SELSD_FMASK) shr SELSD_FSHIFT)
        dd      offset  FLAT:selGDTall
        dd      offset  FLAT:selLDTall
        .errnz ($-HelperTab)-((SELSD_LIMIT and SELSD_FMASK) shr SELSD_FSHIFT)
        dd      offset  FLAT:selGDTlimit
        dd      offset  FLAT:selLDTlimit

Procedure   _SELSetDescInfo, near
    assume ds:FLAT , es:FLAT , fs:nothing, gs:nothing, ss:nothing

        ArgVar  sd_Scope, DWORD                 ; first parameter, pushed last
        ArgVar  sd_x,DBYTE,sd_access, sd_attr
        ArgVar  sd_selector,WORD
        ArgVar  sd_limit,DWORD                  ; size converted to limit later
        ArgVar  sd_laddr,DWORD
        ArgVar  sd_hob,WORD
        ArgVar  sd_hptda,WORD
        ArgVar  sd_oldsize,DWORD

        LocalVar    sd_helper,DWORD             ; pointer to helper

        EnterProc

        ; Save variables to conform to c calling conventions

        push    ebx
        push    esi
        push    edi

        mov         eax,[sd_Scope]
ifdef   SELSTRICT                               ; check validity of scope
        test        eax, SELSD_LIMIT
        jnz         short sdi00

        test        eax, (SELSD_SHARED or SELSD_ALIAS)
        jz          short sdi00

sdiie:  Panic   <'SELSetDescInfo: bad scope parameter'>

sdi00:  test        eax, SELSD_GATE
        jz          short sdi05

        test        eax, SELSD_BASEALL
        jnz         short sdiie
sdi05:
endif
        mov         esi, eax                    ; esi = scope
        test        eax, SELSD_GATE
        mov         ecx, offset FLAT:selGate
        jnz         short sdi25                 ; skip if gate

        or          [sd_limit], 0               ; is size = 0 ?
        jz          short sdi10                 ; skip if so

        sub         [sd_limit],1                ; convert size to limit

        ;   Set up helper in stack
sdi10:  and         eax, SELSD_FMASK            ; get function flags
        shr         eax, SELSD_FSHIFT           ; turn into offset from HelperTab

        test        [sd_selector], SEL_LDTBIT   ; LDT selector?
        jz          short sdi20                 ; jump if not

        add         eax, SELSD_NEXTHELPER       ; get LDT helper

        mov         ecx, [sd_laddr]             ; see is base addr passed
        or          ecx,ecx
        jnz         short sdi15

        mov         cx, [sd_selector]
        and         ecx, SELECTOR_MASK
        shl         ecx, SEL_LADDR_SHIFT        ; convert selector to laddr
        mov         [sd_laddr], ecx             ; store in stack parameter
sdi15:  mov         dx, [sd_hptda]              ; correct ptda if current
        or          dx,dx                       ; is it 0 ?
        jnz         short sdi20

        mov         ecx, _pPTDACur
        mov         dx,[ecx].ptda_handle        ; get current ptda handle
        mov         [sd_hptda],dx               ; store in stack
sdi20:  mov         ecx, dword ptr [HelperTab + eax]
sdi25:  mov         [sd_helper], ecx            ; store helper in stack

        ; Check for expanddown

        mov         al, [sd_access]
        and         al, (D_SEG or D_CODE or D_EXPDN)
        cmp         al, (D_SEG or D_DATA or D_EXPDN)
        jnz         short sdi30                 ; jmp if not expand down

        mov         ecx, [sd_limit]
        inc         ecx                         ; ecx = size
        mov         edx, [sd_laddr]             ; edx = address
        mov         ah, [sd_attr]
        CCALL32     selConvertToExpandDown      ; convert values to expand down
        mov         [sd_limit], ecx             ; store expand down values
        mov         [sd_laddr], edx

sdi30:  mov         eax, [sd_laddr]
        shr         eax, PAGESHIFT              ; (eax) = ipg
        and         [sd_selector], SEL_RPLCLR   ; mask off RPL bits

        test        [sd_Scope], (SELSD_SHARED or SELSD_ALIAS)
        jnz         short sdi40                 ; jmp if alias/context update

;   No alias or context update needed

        test        [sd_selector], SEL_LDTBIT
        jz          short sdi70                 ; skip if GDT sel

;   call _selEachDesc directly
;   _selEachDesc(EBP, ipg, sel, hptda, -1)
;   (eax) = ipg

        CCALL32     _selEachDesc <EBP, eax, dword ptr [sd_selector], \
                    dword ptr [sd_hptda], -1>
        jmp         short sdi80

sdiie2: Panic   <'_SELSetDescInfo: Bad selector'>

;   There are other contexts or SELMGR aliases that also need to be
;   updated. VMEachMap needs to be called
;   _VMEachMap(pworker, pdata, ipg, hptda, cpg, fl, perrworker, perrdata)
;   (eax) = ipg

sdi40:  xor         edx, edx
        mov         ecx, VMEM_CONTEXT           ; always update nonaliases
        test        [sd_Scope], SELSD_ALIAS
        jz          short sdi50

        or          ecx, VMEM_SELALIAS          ; SELMGR alias update needed
sdi50:  mov         esi, [sd_limit]
        add         esi, PAGESIZE               ; convert to size, round up to
                                                ;  page boundary
        shr         esi, PAGESHIFT              ; cpg
        CCALL32     _VMEachMap < offset FLAT:_selEachDesc, EBP, eax, \
                    dword ptr [sd_hptda], esi, ecx, edx, edx>
        jmp         short sdi80

;    Update GDT selector with no alias

sdi70:  movzx       ebx, [sd_selector]
        add         ebx, [_SELGDTLaddr]
        CCALL32     [sd_helper]

sdi80:  cmp         [sd_selector], DOSGROUP     ; Is it DosGroup ?
        jnz         short sdi90                 ; return if not

        ; DOSGROUP selector, Apply helper to GDT_SAS and GDT_DOSALIAS

        mov         esi, [_SELGDTLaddr]
        xor         ebx, ebx
        mov         bx, GSEL GDT_SAS            ; ebx = GDTSAS selector
        add         ebx, esi                    ; ebx = Flat offset of GDTSAS
        CCALL32     [sd_helper]                 ; preserves esi
        xor         ebx, ebx
        mov         bx, GSEL GDT_DOSALIAS       ; ecx = GDT_DOSALIAS selector
        add         ebx, esi                    ; ebx = flat offset of DOSALIAS
        CCALL32     [sd_helper]

sdi90:  ; All done. Restore saved variables and exit

        pop         edi
        pop         esi
        pop         ebx
        LeaveProc
        RETC
EndProc _SELSetDescInfo


;***LP  _selEachDesc - Worker function for VMEachMap.
;
;       _selEachDesc sets/updates the descriptor(s) to match the
;       parameters pointed to by _SELSetDescInfo argument stack frame.
;       If alias update is requested, only the SELMGR alias update is needed,
;       because VMM aliases do not get descriptor updates after a realloc,
;       commit or decommit.
;       This procedure must remain in resident code, because it may be
;       updating aliases in the GDT
;       ENTRY   (SS:ESP) = pdata  _SELSetDescInfo argument stack frame
;               (SS:ESP + 4) = Starting page address, ipg
;               (SS:ESP + 8) = cpg if from VMEachMap,
;                              GDT sel if called from SELSetDescInfo directly
;               (SS:ESP + 12)= pod, pointer to object description structure
;               (SS:ESP + 16)= pgoff if from VMEachMap,
;                              -1 if called from SELSetDescInfo directly
;       EXIT    NONE
;       USES    EAX, ECX, EDX, Flags
;
;       _selEachDesc(pdata, ipg, cpg/sel, hptda, pgoff/-1)
;
;       PseudoCode:
;
;       Load ipg, hptda, pgoff into registers
;       Set stack frame to SetDescInfo stack frame
;       If (ipg < 512Meg)
;           Call GetLDTAddr(hptda)
;           Convert selector to offset into Descriptor Table
;           If (ipg == sd_laddr >> PAGESHIFT)
;               Call helper
;           Else                    ; it's a cs alias limit upgrade
;               if (sd_limit > 64Kb)
;                   set descriptor's limit = 0xffff
;               else
;                   set descriptor's limit = sd_limit
;               endif
;           Endif
;       Else                        ; address in GDT area
;           if (pgoff = -1)         ; called directly from SELSetDescInfo?
;               set sel = cpg/sel   ; use given selector if so
;               call helper
;           else                    ; called from VMEachMap,
;                                   ; this is a MemMapAlias
;               set sel = SELConvertToSelector  ; find selector
;               call selGDTlimit
;           endif
;       Endif
;       return
;
Procedure   _selEachDesc
    assume ds:FLAT , es:FLAT , fs:nothing, gs:nothing, ss:nothing

        ArgVar  sed_sdistack,DWORD          ; EBP value for _SELSetDescInfo
        ArgVar  sed_ipg,DWORD               ; laddr >> PAGESHIFT
        ArgVar  sed_sel,DWORD               ; cpg or GDT sel
        ArgVar  sed_pod,DWORD
        ArgVar  sed_pgoff,DWORD             ; 0 or -1

        EnterProc

        ; Save all registers that need to be saved for c calling conventions

        push    ebx
        push    esi
        push    edi
        push    ebp

        mov     ebx, [sed_pod]              ; ebx = poi (if called from
                                            ;  VMEachMap, else ebx=hptda)
        mov     esi, [sed_ipg]              ; esi = ipg
        shl     esi, PAGESHIFT              ; esi = passed laddr
        mov     edi, [sed_sel]              ; selector(cpg if from VMEachMap)
        mov     eax, [sed_pgoff]            ; -1 if from SELSetDescInfo
        mov     ecx, [sed_sdistack]
        mov     ebp, ecx                    ; WARNING: sed_ variables are no
                                            ; longer valid! sd_ are valid.

        inc     eax                         ; sets zero flag if was -1
        jz      short sed10

;       Call originated from VMEachMap. See if it is a SELBaseAll type of
;       update.
;       BUGBUG: VMBaseSelMapAllOi and VMIsAliasOi in assembly

        mov     edi, [ebx].od_par
        mov     edi, dword ptr [edi].ar_union0 ; ar_ipg, ar_f
        and     di,  AR_SELMASK
        cmp     di,  AR_SELBASEALL
        jnz     short sed05                 ; no it's not, skip

        or      [sd_Scope], SELSD_BASEALL
        mov     edi, [ebx].od_par
        or      [edi].ar_hal,0
        jz      short sed07                 ; not an alis

        cmp     [edi].ar_hal, VMHAL_PRIV    ; not an alias
        jz      short sed07

;       SelBaseAll alias is being updated.
;       (SS:BP) = SetDescInfo stack frame
;       (EBX)   = pointer to object descriptor

        call    selBaseAllAlias
        jmp     sed90                       ; go to return

sed05:
        and     [sd_Scope], not SELSD_BASEALL
sed07:
        mov     ebx, [ebx].od_pPTDA
        mov     bx,  [ebx].ptda_handle       ; bx = hptda

;       Call originated from VMEachMap, have to find the selector.
;       sel = _SELConvertToSelector(laddr, rpl, hptda)

        xor     edi, edi
        CCALL32 _SELConvertToSelector <esi, edi, edi>
        mov     edi, eax            ; (edi) = selector with clear RPL bits
ifdef SELSTRICT
        cmp     eax, -1
        jz      sedie
endif

sed10:  test    di, SEL_LDTBIT              ; LDT selector?
        jz      short sed60                 ; jmp if not

        ;  (di) = selector or 0
        ; (esi) = passed laddr
        ;  (bx) = hptda
        ; linear address of LDT  = _selGetLDTAddr(hptda)

sed20:  CCALL32 _selGetLDTAddr <ebx>
        mov     ebx, eax                    ; (ebx) = LDT address
        and     edi, SELECTOR_MASK          ; convert sel to offset
        add     ebx, edi                    ; ebx = flat offset of desc
        mov     ecx, [sd_laddr]
        and     ecx, not PAGEMASK           ; matters if call gate!
        cmp     ecx, esi                    ; is it same address?
        jnz     short sed40                 ; jmp if not

;   Same address, shared descriptor or direct call from SELSetDescInfo.

        CCALL32 [sd_helper]                 ; apply helper to selector
        jmp     sed90

;   LDT descriptor with a different laddr, Must be cs alias update

sed40:
ifdef SELSTRICT
        test    [sd_Scope], SELSD_LIMIT     ; must be limit update
        jz      short sedie

        test    [sd_Scope], SELSD_ALIAS     ; must be alias limit update
        jz      short sedie
endif

;       (ebx) = flat offset of descriptor
;       cs alias can only be up to 64Kb, and has only one selector, so
;       do a quick limit update

        mov     eax, [sd_limit]
        cmp     eax, WORDMASK               ; larger than 64Kb?
        jbe     short sed50                 ; jmp if limit < 64Kb

        mov     ax, WORDMASK
sed50:  mov     [ebx].d_limit, ax           ; update limit
        jmp     short sed90

        ; GDT selector
        ;  (di) = selector or 0
        ; (esi) = laddr

sed60:  mov     ebx, edi
        and     ebx, SELECTOR_MASK          ; convert selector to offset
        add     ebx, [_SELGDTLaddr]         ; (ebx) = flat address of desc.

;       See if we are updating an alias
        mov     eax, [sd_laddr]
        and     eax, not PAGEMASK           ; matters if call gate!
        cmp     esi, eax                    ; aliases have different laddr
        jnz     sed70                       ; jmp if alias

        CCALL32 [sd_helper]
        jmp     short sed90

;       We are updating MemMap alias, call originated from VMEachMap

sed70:
ifdef SELSTRICT
        test    [sd_Scope], SELSD_LIMIT     ; must be limit update
        jz      short sedie

        test    [sd_Scope], SELSD_ALIAS     ; must be alias limit update
        jnz     short sed80

sedie:  Panic   <'_selEachDesc'>
sed80:
endif
        ; (ebx) = Flat address of descriptor
        ; (esi) = alias laddr

        mov     ecx, [sd_laddr]             ; save sd_laddr value
        mov     [sd_laddr], esi             ; put new  base address
        CCALL32 selGDTlimit                 ; saves ecx
        mov     [sd_laddr], ecx             ; restore sd_laddr

        ;   Restore all variables and exit

sed90:  pop     ebp
        pop     edi
        pop     esi
        pop     ebx
        xor     eax, eax                ; make VMEachMap happy
        LeaveProc
        RETC
EndProc _selEachDesc


;***LP  selGate - Set up given selector as gate descriptor (SD_GATE)
;
;       selGate sets the passed LDT or GDT descriptor as a gate.
;
;       ENTRY   (SS:EBP) = pointer to SetDescInfo ArgVar stack frame
;               (DS:EBX) = pointer to LDT or GDT descriptor
;       EXIT    Carry clear if operation complete
;               Carry set otherwise
;
;       PseudoCode:
;
;  Ifdef SELSTRICT
;       If (dwordcount > 31) Panic
;   Endif
;       Set eax register = selector << 16 | offset.low
;       Set [ebx] = register
;       Set up eax = offset.high << 16 | access << 8 | dwordcount
;       Set [ebx + 4] = eax
;       return
;
Procedure   selGate,near,local
    assume ds:FLAT , es:FLAT , fs:nothing, gs:nothing, ss:nothing

sd_cparms   equ     BYTE ptr sd_attr    ; parameter count
sd_gatesel  equ     WORD ptr sd_laddr   ; selector of the gate
sd_Offst    equ     DWORD ptr sd_limit  ; offset in the segment

        mov     al, [sd_cparms]         ; al = parameter count
ifdef   SELSTRICT
        test    al, not D_WCMASK
        jz      short sgt10

        Panic   <'selGate: bad gate parameter count'>
sgt10:
endif
        mov     ah, [sd_access]         ; ax = access, parameter count
        mov     word ptr [ebx].g_parms, ax  ; set up g_access, g_parms
        mov     eax, [sd_Offst]
        mov     [ebx].g_lowoffset, ax   ; set up low offset field
        shr     eax, WORDSHIFT          ; ax = high word of offset
        mov     [ebx].g_extoffset, ax
        mov     ax, [sd_gatesel]
        mov     [ebx].g_sel, ax         ; set up g_sel
        clc
        RETC
EndProc     selGate


;***LP  selSwitchLDTsub - switch LDT selector for a VDM
;
;       selSwitchLDTsub switches the LDT for for the current VDM to the LDT
;       specified in the TSD.
;
;       ENTRY   NONE
;       EXIT    NONE
;       USES    EAX

Procedure _selSwitchLDTsub,near
    assume ds:FLAT, es:FLAT, fs:nothing, gs:nothing, ss:nothing

        push    edi                     ; save registers
        push    esi
        pushfd                          ; save flags

        cld
        cli
        mov     esi,[_pPTDACur].TCBpTSD ; (esi) = thread's TSD
        lea     esi,[esi].TSDdescLDT    ; (esi) = ptr to source descriptor
        mov     edi,[_pGDTTaskCopy]     ; (edi) = pointer to GDT destination
        movsd
        movsd

        mov     eax,GSEL GDT_LDT
        lldt    ax                      ; load ldt register

        popfd
        pop     esi
        pop     edi

        RETC
EndProc _selSwitchLDTsub

HIGH32CODE ends

        FARCODE HIGH32
SELSWAP_TEXT segment


;***LP  selBaseAll - Set up base selector to cover all of the object
;
;       selBaseAll is called by selLDTAll and selLDTLimit to set up
;       the first selector of the object to map all of the object instead
;       of the first 64Kb. This rarely used fetature is needed for
;       Windows 3.0 support.
;       The descriptor's access, attribute and address fields must already
;       be set. This procedure only updates limit, extlimit and granularity
;       field.
;       WARNING: This procedure is in swappable code.
;
;       ENTRY   (SS:EBP) = pointer to SetDescInfo ArgVar stack frame
;               First 5 parameters needed.
;               (DS:EBX) = pointer to LDT descriptor
;       EXIT    NONE
;       USES    EAX, ECX, EFlags
Procedure   selBaseAll,near,local
    assume ds:FLAT , es:FLAT , fs:nothing, gs:nothing, ss:nothing

        and     [ebx].d_attr, not (D_GRAN4K or D_EXTLIMIT)
                                            ; turn off granularity and extlimit
        mov     eax, [sd_limit]             ; eax = limit
        cmp     eax, D_GRANMASK             ; size > 1 Meg?
        jbe     short sba10                 ; jmp if not

        or      [ebx].d_attr, D_GRAN4K      ; limit must be 4K granular
        shr     eax, PAGESHIFT
sba10:  mov     [ebx].d_limit, ax           ; set d_limit field
        shr     eax, WORDSHIFT
        or      [ebx].d_attr, al            ; set extended limit field

        ; If the first selector is set up as code selector, the others have
        ; to be set up as data.

        test    [ebx].d_access, D_CODE
        jz      short sbae              ; skip if already data

        mov     ecx, [sd_limit]
        shr     ecx, WORDSHIFT          ; no of sels-1
        jcxz    short sbae

sba95:  add     ebx, SEL_INCR           ; next selector of the object
        and     [ebx].d_access, not D_CODE  ; make selectors data
        and     [ebx].d_access, not D_DBIG
        loop    sba95

sbae:   RETC
EndProc     selBaseAll


;***LP  selBaseAllAlias - Set up selectors of an 'base selector map all' alias
;
;       selBaseAllAlias is called by selEachDesc to update
;       the selectors of the 'base sel. map all' type of alias
;       after a realloc or SetMem. This rarely used feature is needed for
;       Windows 3.0 support.
;       WARNING: This procedure is in swappable code.
;
;       ENTRY   (SS:EBP) = pointer to SetDescInfo ArgVar stack frame
;               (EBX)    = pointer to object descriptor
;       EXIT    NONE
;       USES    EAX, EBX, ECX, EDX, ESI, EDI, EFlags
Procedure   selBaseAllAlias,near,local
    assume ds:FLAT, es:FLAT, fs:nothing, gs:nothing, ss:nothing

;       Modify the the needed SetDescInfo parameters and call selLDTLimit

        push    [sd_x]
        push    [sd_selector]
        push    [sd_laddr]
        push    [sd_limit]
        push    [sd_oldsize]


        xor     esi, esi
        mov     eax, [sd_limit]
        inc     eax                 ; eax = newsize
        cmp     eax, [sd_oldsize]
        jc      short baa10

        inc     esi                 ; indicate it is a growth request

baa10:

;   Alias may not be covering the complete object. We have to find the
;   offset of the alias from the start of the object.
;   (eax) = offset of the alias  = _VMGetAliasOffset(pod)

        CCALL32 _VMGetAliasOffset <ebx>
        sub     [sd_limit], eax
        jc      baa70               ; skip if alias outside of new limit.

        mov     ecx, eax            ; save page offset

        mov     eax, [ebx].od_laddr
        mov     [sd_laddr], eax

        LaToSelTiled  eax                            ; (ax) = selector
        mov     [sd_selector], ax

        mov     edi, [ebx].od_pPTDA
        mov     ebx, [edi].ptda_ldtaddr
        and     eax, SELECTOR_MASK
        add     ebx, eax

;   (DS:EBX) = points to the descriptor

        mov     ax, word ptr [ebx].d_access     ; (ah) = attr, (al) = access
        and     ah, D_DBIG or D_UVIRT
        mov     [sd_x], ax

;   Obtain the alias old size

        mov     al, [ebx].d_attr
        and     eax, D_EXTLIMIT
        shl     eax, WORDSHIFT
        mov     ax, [ebx].d_limit
        test    [ebx].d_attr, D_GRAN4K      ; 4k granular?
        jz      short   baa50

        shl     eax, PAGESHIFT
        add     eax, PAGEMASK               ; (eax) = old total limit of alias
baa50:  inc     eax

        or      esi, esi
        jz      short baa55                 ; skip if it is a shrink request

        add     ecx, eax                    ; (ecx) = offset + old limit
        cmp     ecx, [sd_oldsize]
        jc      short baa70                 ; growth request and old size does
                                            ; not map the whole object, skip
                                            ; desc update

baa55:  mov     [sd_oldsize], eax

        or      esi, esi
        jnz     short baa60                 ; skip if it is a growth request

        dec     eax
        cmp     eax, [sd_limit]
        jc      short baa70                 ; shrink request and old size
                                            ; already less than new size
                                            ; skip desc. update.


baa60:  CCALL32 selLDTlimit

;       Restore old stack frame

baa70:  pop     [sd_oldsize]
        pop     [sd_limit]
        pop     [sd_laddr]
        pop     [sd_selector]
        pop     [sd_x]

        RETC
EndProc     selBaseAllAlias

SELSWAP_TEXT ends
SWAP32_5 segment

;***LP  selLDTall - Set raw descriptor information (SD_ALL)
;
;       selLDTall sets the passed LDT descriptor to the passed values.
;       If the limit is larger than 64K, It sets up consecutive
;       selectors with 64K in size, and the last one with the remainder
;       of size. The descriptors must be allocated in advance.
;       WARNING: This procedure is in swappable code.
;
;       ENTRY   (SS:EBP) = pointer to SetDescInfo ArgVar stack frame
;               First 5 parameters needed.
;               (DS:EBX) = pointer to LDT descriptor
;       EXIT    Carry clear if operation complete
;               Carry set otherwise
;       USES    EAX, EBX, ECX, EDX, Flags
;
;       PseudoCode:
;
;       Set eax = extaddr, attr(D_BIG only), acess, hiaddr
;   Ifdef SELSTRICT
;       If (requested attr & not (D_BIG+D_UVIRT)), Panic
;       If (laddr.low), Panic   ; must be on 64K boundary
;   Endif
;       If (limit >= 64K)
;           Set edx = laddr.low, limit
;       Else
;           Set edx = laddr.low, ffff   ; d_loaddr and d_limit fields
;       Endif
;       Set numdesc = limit.high
;       Do (numdesc) times {
;  Ifdef SELSTRICT
;           If ([ebx.d_access] && D_VALIDMASK) Panic ; desc must be unused
;   Endif
;           Set [ebx] = edx
;           Set [ebx+4] = eax
;           Set ebx = ebx + SEL_INCR
;           Increment d_hiaddr in al
;           If (carry), Increment d_extaddr in eax
;       }
;       If (limit.low)
;           Set dx = limit.low
;           Set [ebx] = edx
;           Set [ebx+4] = eax
;       Endif
;       return
;
Procedure   selLDTall,near,local
    assume ds:FLAT , es:FLAT , fs:nothing, gs:nothing, ss:nothing

        push    ebx                         ; save first selector's address
        mov     eax, [sd_laddr]
        mov     edx,eax
        shl     edx,WORDSHIFT               ; high word (EDX) = low addr
        mov     dx, word ptr [sd_access]    ; edx = d_loaddr, attrib, access
ifdef   SELSTRICT
        test    dh, not(D_DBIG+D_UVIRT)     ; only D_BIG or D_UVIRT allowed
        jz      short sla10

        Panic   <'selLDTall: bad descriptor parameters'>
sla10:
endif
        and     dh, not D_DBIG              ; don't set D_DBIG for 64Kb sels
        mov     ecx, eax
        shl     ecx, BYTESHIFT              ; ecx = d_hiaddr, 0,0,0
        shr     eax, BYTESHIFT              ; eax = 0, d_extaddr, ?, ?
        mov     ax, dx                  ; eax = 0, d_extaddr, d_attr, d_access
        shld    eax, ecx, BYTESHIFT    ; eax=d_extaddr,a_attr,d_access,d_hiaddr
        mov     ecx, [sd_limit]
        shr     ecx, WORDSHIFT          ; cx = no of 64K selectors
        or      cx, cx                  ; is size < 64K ?
        jz      short sla50             ; jump if so

        mov     edx, WORDMASK           ; edx = d_loaddr (=0), d_limit(=ffff)
        ; eax = high dword of descriptor
        ; edx = low dword of descriptor
        ; ecx = no of 64K descriptors to set up
        ; ebx = address of first descriptor
sla20:  mov     [ebx].d32_low, edx
        mov     [ebx].d32_high, eax     ; Update access last! We may block!
        add     al, 1                   ; add 64K to address, need Carry
        jnc     short sla25

        add     eax, (1 shl (WORDSHIFT+BYTESHIFT)) ; increment d_extaddr
sla25:  add     ebx, SEL_INCR
        loop    sla20

        ; 64K selectors have been set up, Now see if there is another one
sla50:
        ;ebx = address of next descriptor to set up
        ;eax = high dword of descriptor

        mov     dx, word ptr [sd_limit] ; edx = d_loaddr, d_limit
        mov     [ebx].d32_low, edx
        mov     [ebx].d32_high, eax     ; update access byte last!
        pop     ebx                     ; (ebx) = first sel's address
        test    [sd_Scope], SELSD_BASEALL   ; 1st selector map all?
        jnz     short sla80             ; jmp out of line if so

slae:   clc
        RETC

        ; We need to map the first selector to all of the object. This is a
        ; rare case and handled out of line.

sla80:  test    [sd_attr], D_DBIG       ; big bit needs to be set?
        jz      short sla90

        or      [ebx].d_attr, D_DBIG
sla90:  call    selBaseAll              ; set up limit, granularity

        jmp     short slae

EndProc     selLDTall

SWAP32_5 ENDS


    FARCODE HIGH32
HIGH32CODE segment

;***LP  selGDTall - Set raw descriptor information (SD_ALL)
;
;       selGDTall sets the passed GDT descriptor to the passed values.
;       It assumes that expand down values are passed if the descriptor
;       is expand down.
;
;
;       ENTRY   (SS:EBP) = pointer to SetDescInfo ArgVar stack frame
;               First 5 parameters are needed.
;               (DS:EBX) = pointer to GDT descriptor
;       EXIT    Carry clear if operation complete
;               Carry set otherwise
;       USES    EAX, ECX, EDX, Flags
;
;       PseudoCode:
;
;       Set eax = extaddr, attr, access, hiaddr
;       If (limit > 1Meg && not (D_DATA && D_EXPDN))
;           turn on D_GRAN4K in attr
;       Endif
;       If (D_GRAN4K)
;           shift left limit by 12
;       Endif
;       Set eax = eax & not D_EXTLIMIT
;       Turn on ext limit field in eax by high bits of limit
;       Set edx = loaddr, limit.low
;       Set [ebx] = edx
;       Set [ebx + 4] = eax
;       Return
;
Procedure   selGDTall,near,local
    assume ds:FLAT , es:FLAT , fs:nothing, gs:nothing, ss:nothing

        mov     eax, [sd_laddr]             ; get linear address
        movzx   ecx, word ptr [sd_access]   ; get access, attr
        and     ch, (D_GRAN4K + D_DBIG + D_UVIRT)
        mov     dl, cl                      ; dl = access byte
        and     dl, (D_DATA+D_EXPDN)
        cmp     dl, (D_DATA+D_EXPDN)        ; is it expand down data?
        mov     edx, [sd_limit]             ; edx = unshifted limit
        jz      short sga20                 ; jmp if so

        and     ch, not D_GRAN4K            ; turn off D_GRAN4K bit
        cmp     edx, D_GRANMASK             ; is it above 1 Meg?
        jbe     short sga20                 ; jmp if not

        or      ch, D_GRAN4K                ; turn on D_GRAN4K bit
sga20:  test    ch, D_GRAN4K
        jz      short sga30                 ; jump if not set

        shr     edx, PAGESHIFT              ; shift limit right if GRAN4K
sga30:  ; cx = attr, access
        ; eax = laddr
        ; edx = limit (shifted right if GRAN4K)
        ; ebx = offset of selector to be set up
        mov     [ebx].d_limit, dx           ; low word of limit
        mov     [ebx].d_loaddr, ax          ; low word of address
        shr     edx, WORDSHIFT              ; dl = highest 4 bits of limit
        or      ch, dl                      ; set up extended limit fields
        mov     [ebx].d_access, cl          ; set up d_access, d_attr bytes
        mov     [ebx].d_attr, ch
        shr     eax, WORDSHIFT               ; ah = extaddr field
        mov     [ebx].d_hiaddr, al          ; set up  hiaddr byte
        mov     [ebx].d_extaddr, ah         ; set up extaddr byte
        clc
        RETC
EndProc     selGDTall

HIGH32CODE ends


    FARCODE HIGH32
SWAP32_8 segment

;***LP  selLDTlimit - Set LDT descriptor's limit field (SD_LIMIT)
;
;       selLDTlimit sets the passed descriptor to the passed values.
;       If given newlimit is larger than 64K, it sets up consecutive
;       selectors with 64k-1 limits and the last one with the remainder
;       of the size.
;       WARNING: This procedure is in swappable code
;
;       ENTRY   (SS:EBP) = pointer to SetDescInfo ArgVar stack frame
;               (DS:EBX) = pointer to LDT descriptor
;       EXIT    Carry clear if operation complete
;               Carry set otherwise
;       USES    EAX, EBX, ECX, EDX, EDI, Flags
;
;       PseudoCode:
;
;       Call selSDMatch           ; check base laddr, D_SEG,
;       If (error), return error
;       Set numdesc = newlimit.high
;       Do (numdesc) times
;           Set ebx.d_limit = 64K-1
;           Set ebx = ebx + SEL_INCR
;       If (newlimit.low)
;           set [ebx.d_limit] = newlimit.low
;           increment numdesc
;           Set ebx = ebx + SEL_INCR
;       Endif
;       Set olddesc = oldlimit.high
;       If (oldlimit.low) Increment olddesc
;       If (olddesc > numdesc)
;           Do (olddesc - numdesc) times
;               Set [ebx] = 0
;               Set [ebx + 4] = 0       ; zero out the escriptor
;               Set ebx = ebx + SEL_INCR
;       Else if (numdesc > olddesc)
;           Set ebx = ebx - (numdesc - olddesc) *SEL_INCR   ; first new desc
;           Set highdw = [ebx + 4]
;           Set lowdw  = [ebx]
;   Ifdef SELSTRICT
;           If (lowdw.high) Panic
;   Endif
;           Do (numdesc - olddesc) times
;               Set ebx = ebx + SEL_INCR
;               Increment highdw.lowbyte        ; adjust d_hiaddr
;               If (carry)
;                   Set highdw = highdw + 1 << 24   ; adjust d_extaddr
;               Endif
;               Set [ebx + 2] = lowdw.high
;               Set [ebx + 4] = highdw
;   Ifdef SELSTRICT
;               If ([ebx.d_attr] && D_EXTLIMIT+D_GRAN4K) Panic
;   Endif
;       Endif
;       return
;
Procedure selLDTlimit,near,local
    assume ds:FLAT , es:FLAT , fs:nothing, gs:nothing, ss:nothing

ifdef SELSTRICT
        CCALL32 selSDMatch
        jnc      short sll00

        Panic <'selLDTLimit'>
sll00:
endif

        push    ebx                         ; save selector's address
        push    ebx                         ; save selector's address
        mov     eax, [sd_limit]             ; eax = new limit
        xor     edx, edx

        xor     ecx, ecx
        shld    ecx, eax, WORDSHIFT         ; ecx = limit.high, eax=limit
        or      ecx, ecx                    ; is limit < 64Kb?
        jz      short sll15                 ; jmp if so

        mov     edx, ecx                    ; edx =ecx = high word of new limit
sll10:  mov     [ebx].d_limit, WORDMASK     ; limit = 64K - 1
        add     ebx, SEL_INCR               ; get next selector
        loop    sll10                       ; repeat for all 64K descriptors

        ; eax = new limit
        ; edx = no of descriptors that have been changed
        ; ebx = SEL_INCR+last descriptor that has been changed

sll15:  mov     [ebx].d_limit, ax               ; set up last valid desc.
        add     ebx, SEL_INCR                   ; point to next desc.
        inc     edx                             ; inc no of descs changed

sll20:  mov     ecx, [sd_oldsize]               ; get old size
        dec     ecx                             ; convert to limit
        jz      short sll25                     ; jmp if size = 1 byte

        mov     edi, ecx
        shr     ecx,WORDSHIFT

sll25:  inc     ecx                             ; ecx = no. of old descriptors

        ; edi = old limit
        ; edx = no of descriptors limit is applied
        ; ecx = old no of descriptors
        ; ebx = SEL_INCR + last changed descriptor

        xchg    ecx, edx                        ; ecx = new, edx = old
        sub     ecx, edx                        ; ecx= new - old no of desc
        pop     eax                             ; eax = base selector's address
        jz      short sll80                     ; done if old = new
        jnc     short sll50                     ; jmp if old < new

        ; old > new, remaining descriptors need to be invalidated
        neg     ecx                             ; ecx = old - new
sll40:  mov     [ebx].d32_high, 0               ; zero out the descriptor,
                                                ; do access byte first!
        mov     [ebx].d32_low, 0
        add     ebx, SEL_INCR
        loop    sll40

        jmp     short   sll80

sll50:
        ; new > old, new descriptors are added, fill in their other fields
        ; edx = old no of descriptors
        ; eax = base selector's offset
        ; ecx = no of descriptors to be set up

        dec     edx
        shl     edx, SEL_SHIFT
        add     eax, edx                    ; eax = last desc with valid fields
        xor     ebx, ebx                    ; (bx) = low address = 0
        mov     edx, [eax].d32_high  ; edx = d_hiaddr,d_access,d_attr,d_extaddr

        ; Keep adding 64K to base address and setting up the next desc.
        ; eax = last descriptor with valid access, address, attribute
        ; ecx = no of descriptors to be set up
        ; edx = hiaddr, access, attr, extaddr fields of last valid descriptor
        ; bx = loaddr field of last valid descriptor = 0

sll60:  add     dl, 1                       ; increment d_hiaddr, need Carry!
        jnc     sll65
        add     edx, (1 shl (WORDSHIFT+BYTESHIFT)) ;increment d_extaddr
sll65:  add     eax, SEL_INCR               ; get next desc
        mov     [eax].d_loaddr, bx          ; d_limit already set up
        mov     [eax].d32_high, edx         ; hhiaddr,access, attr, extaddr
                                            ; update access byte last!
        loop    sll60
sll80:  pop     ebx
        test    [sd_Scope], SELSD_BASEALL   ; first selector map all?
        jz      short slle

        CCALL32 selBaseAll                  ; update first sel's limit, gran.

slle:   clc
sllx:   RETC
EndProc selLDTlimit
SWAP32_8  ENDS


    FARCODE HIGH32
HIGH32CODE segment

;***LP  selGDTlimit - Set GDT descriptor's limit field (SD_LIMIT)
;
;       selGDTlimit sets the passed descriptor's limit to the passed value.
;       It assumes that expand down values are passed if the descriptor
;       is expand down.
;
;
;       ENTRY   (SS:EBP) = pointer to SetDescInfo ArgVar stack frame
;               (DS:EBX) = pointer to GDT or LDT descriptor
;       EXIT    Carry clear if operation complete
;               Carry set otherwise
;       USES    EAX, Flags
;
;       PseudoCode:
;
;       Call selSDMatch
;       If (carry) return
;       If (not (D_DATA && D_EXPDN))
;           Set [ebx.d_attr] =& not (D_GRAN4K)
;           If (newlimit > 1 Meg)
;               Set [ebx.d_attr] =| D_GRAN4K
;           Endif
;       Endif
;       If (D_GRAN4K)
;           Shift left limit by 12
;       Endif
;       Set [ebx.d_limit] = limit.low
;       Set [ebx.d_attr] =& not D_EXTLIMIT
;       Set [ebx.d_attr] =| limit.high
;       return
;
Procedure selGDTlimit,near,local
    assume ds:FLAT , es:FLAT , fs:nothing, gs:nothing, ss:nothing

ifdef SELSTRICT
        CCALL32 selSDMatch
        jnc     short sgl00

        Panic <'selGDTLimit'>
sgl00:
endif

        mov     al,[ebx].d_access
        and     al,D_DATA+D_EXPDN
        cmp     al,D_DATA+D_EXPDN
        mov     eax,[sd_limit]              ; eax = limit
        jz      short sgl10                 ; jmp if expand down data

        ; descriptor is not expand down, set up granularity bit

        and     [ebx].d_attr, not D_GRAN4K  ; turn off granularity bit
        cmp     eax, D_GRANMASK             ; is it >= 1 Meg?
        jb      short sgl10                 ; no, granularity must be clear

        or      [ebx].d_attr, D_GRAN4K      ; turn on granularity bit
sgl10:  test    [ebx].d_attr, D_GRAN4K      ; do we need to shift limit?
        jz      short sgl20                 ; jmp if not

        shr     eax, PAGESHIFT              ; shift limit right
sgl20:  mov     [ebx].d_limit,ax
        and     [ebx].d_attr, not D_EXTLIMIT
        shr     eax, WORDSHIFT            ; al = highest 4 bits of limit
        or      [ebx].d_attr, al            ; store extended limit
        clc
sglx:   RETC
EndProc  selGDTlimit


ifdef SELSTRICT
;***LP  selSDMatch - determine whether the descriptor matches the request.
;
;       Algorithm:
;       - Check if segment descriptor
;       - Check if address fields match old address
;       - Return carry if any one doesn't match
;
;       ENTRY   (SS:EBP) = pointer to SetDescInfo ArgVar stack frame
;               (DS:EBX) = pointer to GDT or LDT descriptor
;       EXIT    Carry flag clear if descriptor matches
;               Carry flag set if not
;       USES    EAX, flags
;
;       PseudoCode:
;
;       If (not ([ebx.d_access] && D_SEG)
;           return (carry)
;       Endif
;       Set base.low = [ebx.d_loaddr]
;       Set base.high.lowbyte = [ebx.d_hiaddr]
;       Set base.high.highbyte = [ebx.d_extaddr]
;       If (base != laddr) return (Carry)
;   Ifdef SELSTRICT
;       If (not([ebx.d_access] && D_PRES)) Panic
;   Endif
;       return
;
Procedure  selSDMatch,near,local
    assume ds:FLAT , es:FLAT , fs:nothing, gs:nothing, ss:nothing

        mov     al,[ebx].d_access
        test    al,D_SEG
        jz      short smte                  ;jmp if not a segment

        mov     al,[ebx].d_access
        test    al,D_PRES
        jnz     short smt10
        Panic   <'selSDMatch: descriptor not present'>
smt10:
        mov     ax, word ptr [ebx].d_attr   ; ah=extended address
        mov     al, [ebx].d_hiaddr          ; ah/al = d_extaddr/d_hiaddr
        shl     eax, 16                     ; eax.high = high word of address
        mov     ax, [ebx].d_loaddr          ; eax = base address in descriptor
        cmp     eax, [sd_laddr]             ; clears carry
        jz      short smtx

        cmp     [sd_Scope],SELSD_LIMIT  ; if adjusting only the limit,
        jne     short smte
        cmp     [sd_laddr],0            ; also allow unspecified laddr
        je      short smtx

smte:   stc
smtx:   RETC
EndProc selSDMatch
endif


;***EP  _SELConvertToSelOff - Find the selector and offset, in the LDT
;                             corresponding to the given linear address.
;
;       ENTRY   (SS:SP)     - laddr, linear address
;               (SS:SP+4)   - rpl bits (-1 if to copy from access byte)
;                               0 - Ring 0
;                               2 - Ring 2
;                               3 - Ring 3
;               (SS:SP+8)   - hptda, handle to the PTDA
;                               0 if GDT sel or current ptda
;       EXIT    (EAX)       - Selector and Offset
;                             ( = 0 if error)
;                             SELECTOR is high order 16 bits
;                             OFFSET   is low  order 16 bits
;
;       USES    EAX, ECX, EDX, Flags
;
;       ulong_t SELConvertToSelOff(ulong_t laddr, uchar_t rpl, VMHOB hptda)
;
;       PseudoCode:
;
;       SEL   SELConvertToSelector(ulong_t laddr, uchar_t rpl, VMHOB hptda)
;
;       if (return value is zero)
;           exit this routine
;       else
;           shift eax 16 bits to left
;           add eax to edx (which has the offset from the above call)
;       endif
;
;       return
Procedure   _SELConvertToSelOff
    ArgVar      cts_laddr,DWORD                 ; linear address
    ArgVar      cts_x,DWORD                     ; rpl bit
    ArgVar      seloff_hptda,DWORD              ; hptda

        assume ds:FLAT , es:FLAT , fs:nothing, gs:nothing, ss:nothing

        EnterProc

        CCALL32 _SELConvertToSelector <cts_laddr, cts_x, seloff_hptda>

        or      eax, eax                        ; 16 bit selector from call
        jz      short cts0x                     ; if zero exit caller gets zero

        shl     eax,16                          ; put selector in high 16 bits

IFDEF   selstrict
        test    edx,0ff00h
        jz      cts01
        InternalError <SELConvertToSelOff: bad offset>
cts01:
ENDIF
        or      eax,edx                         ; put offset in low 16 bits

cts0x:  LeaveProc                               ; EAX returned to caller
        RETC
EndProc     _SELConvertToSelOff


;***EP  _SELConvertToSelector - Find the selector corresponding to the address.
;
;       _SELConvertToSelector converts given linear address to its selector
;       value and stamps the selector with the given privilege.
;
;       ENTRY   (SS:SP)     - laddr, linear address
;               (SS:SP+4)   - rpl bits (-1 if to copy from access byte)
;                               0 - Ring 0
;                               2 - Ring 2
;                               3 - Ring 3
;               (SS:SP+8)   - hptda
;                               0 if GDT sel or current ptda
;       EXIT    (EAX)       - Selector with desired RPL stamp
;                             ( = 0 if error)
;               (EDX)       - Offset (passed back to _SELConvertToSelOff)
;       USES    EAX, ECX, EDX, Flags
;
;       SEL     SELConvertToSelector(ulong_t laddr, uchar_t rpl, VMHOB hptda)
;
;       PseudoCode:
;
;       if (LDT sel)
;           LaToSel(laddr)
;       else
;           Call VMGetOd
;           read selector
;       endif
;       if (rpl == -1)
;           read access byte
;       else
;           verify that rpl = (0, 2, 3)
;       endif
;       SELStampRPL(sel, access)
;       return
Procedure   _SELConvertToSelector
    ArgVar      cts_laddr,DWORD                 ; linear address
    ArgVar      cts_x,DBYTE,cts_rpl,cts_y       ; rpl bit
    ArgVar      cts_hptda,WORD                  ; hptda

;;; LocalVar    cts_oi,<size vmoi_s>        ; return info from VMObjectInfo
    LocalVar    cts_od,<size vmod_s>        ; object descriptor
    LocalVar    cts_desc, <size seldesc_s>  ; return info from SELGetDescInfo

        assume ds:FLAT , es:FLAT , fs:nothing, gs:nothing, ss:nothing

        EnterProc

        CCALL32 _VMGetPTDAAddr <[cts_hptda]>
        cmp     [cts_laddr], SEL_512MEG
        jae     short cts00                     ; jmp if system address

        CCALL32 _SELLaToSel  <[cts_laddr], eax>  ; convert it
        jmp     short cts30

;   Address > 512Meg, read selector from VM data structures
;   _VMGetOd(flags, pod[laddr,pptda])

cts00:
;;;;;;; lea     ecx, ss:cts_oi
        lea     ecx, ss:cts_od
        add     ecx, [_TKSSBase]
        mov     [ecx].od_pPTDA, eax
        mov     eax, [cts_laddr]
        mov     [ecx].od_laddr, eax
        xor     eax, eax
        CCALL32 _VMGetOd < eax, ecx >
;;;;;;; CCALL32 _VMObjectInfo <[cts_laddr], eax, ecx>
        or      eax, eax
        .errnz  NO_ERROR
        jnz     short cts10

;;;;;;; test    [cts_oi].oi_fsar, AR_SELMAP     ; selector mapped ?
        mov     eax,[cts_od].od_par
        test    word ptr [eax].ar_union0, AR_SELMAP
        jnz     short cts20                     ; error if no selector mapping

cts10:  mov     eax, 0                          ; error case, return 0
        jmp     short  ctsx

cts20:
;;;;;;; movzx   eax, [cts_oi].oi_sel            ; (ax) = selector
        movzx   eax, word ptr [eax].ar_union4+2 ; (ax) = selector

;       (ax) = selector with right table bit

cts30:  cmp     [cts_rpl], -1
        jz      short cts50

        mov     cl, [cts_rpl]
ifdef SELSTRICT
        test    cl, not SEL_RPLMASK
        jnz     short cts10                     ; error if invalid rpl

        cmp     cl, SEL_RPL1                    ; Ring 1 is illegal
        jz      short cts10
endif

        and     ax, SEL_RPLCLR
        or      al, cl                          ; add rpl bits
        jmp     short ctsx

;       (eax) = selector with right table bit


;   _SELGetDescInfo(sel, hptda, &desc)
;   (eax) = selector with the right table bit

cts50:  push    eax                        ; save selector
        SSToDS  ecx, ss:cts_desc
        mov     dx, [cts_hptda]
        CCALL32 _SELGetDescInfo <eax, edx, ecx>
        or      eax, eax
        pop     eax                         ; restore selector
        jnz     short cts10

        mov     cl, [cts_desc].desc_acc
        SELRplStamp ax, cl
        mov     edx, [cts_laddr]            ; linear address passed in
        sub     edx, [cts_desc].desc_laddr  ; put offset in EDX
ctsx:   LeaveProc
        RETC
EndProc     _SELConvertToSelector


;***EP  _SELConvertToLaddr - Find the linear address of the selector,
;                            for the CURRENT PTDA
;
;       _SELConvertToLaddr converts given selector to its linear address.
;
;       ENTRY   (SS:SP+4)   - sel, selector
;       EXIT    (EAX)       - Linear address
;                             ( = -1 if error)
;       USES    EAX, ECX, EDX, Flags
;
;       ulong_t  SELConvertToLaddr(SEL)
;
;       PseudoCode:
;
;       Call SELConvertToLinear (selector, pointer to current PTDA)
;
;       return
Procedure   _SELConvertToLaddr
    ArgVar      ctl_sel,DWORD                   ; selector

        assume ds:FLAT , es:FLAT , fs:nothing, gs:nothing, ss:nothing

        EnterProc

        CCALL32 _SELConvertToLinear <[ctl_sel], [_pPTDACur]>

        LeaveProc
        RETC
EndProc     _SELConvertToLaddr


;***EP  _SELConvertToLinear - Find the linear address of the selector
;                             for the passed PTDA
;
;       ENTRY   (SS:SP+4)   - sel, selector
;               (SS:SP+8)   - pptda, pointer to target tasks ptda
;
;       EXIT    (EAX)       - Linear address
;                             ( = -1 if error)
;       USES    EAX, ECX, EDX, Flags
;
;       ulong_t  SELConvertToLinear(SEL pPtda)
;
;       PseudoCode:
;
;       if (LDT sel)
;           SelToLa(sel)
;       else
;           Call GetDescInfo
;           if (error), return error
;           read address from descriptor info
;       return
Procedure   _SELConvertToLinear
    ArgVar      ctl_sel,DWORD                   ; selector
    ArgVar      ctl_pptda,DWORD                 ; PTDA ADDRESS

    LocalVar    ctl_desc,<size seldesc_s>   ; return info from SELGetDescInfo
        assume ds:FLAT , es:FLAT , fs:nothing, gs:nothing, ss:nothing

        EnterProc

        test    [ctl_sel], SEL_LDTBIT
        jz      short ctl15                     ; jmp if GDT selector

        mov     eax,[ctl_sel]
        CCALL32 _SELSelToLa <eax, [ctl_pptda]>
        jmp     short ctlx2

;   Not an LDT selector, have to use SELGetDescInfo to find the address
;   _SELGetDescInfo(sel, hptda, pdesc)
;   (eax) = 0

ctl15:  lea     ecx, ss:ctl_desc
        add     ecx, [_TKSSBase]
        CCALL32 _SELGetDescInfo <dword ptr [ctl_sel], eax, ecx>
        or      eax, eax
        .errnz  NO_ERROR
        mov     eax, -1                     ; preserve zero flag
        jnz     short ctlx2

        mov     eax, [ctl_desc].desc_laddr
ctlx2:  LeaveProc
        RETC
EndProc     _SELConvertToLinear


;***EP  _SELSelToLa - converts an arbitrary ldt selector to flat address
;
;       ENTRY:  sel - selector to convert
;               pptda - pointer to target tasks ptda
;       EXIT:   (eax) = base of selector
;       USES:   eax,ecx
;
Procedure _SELSelToLa,,,,esp
        assume ds:FLAT , es:FLAT , fs:nothing, gs:nothing, ss:nothing

        ArgVar  stl_sel,   DWORD
        ArgVar  stl_pptda, DWORD

        mov     eax,[stl_sel]                   ; (eax) = selector to convert

ifdef   SELSTRICT
        ; ldt selectors only
        test    al,TABLE_MASK
        jz      stlie
endif

; KDH - We want to check if the selector to convert is packed or not.  We have
;       to check for two "fences":  one in the private arena, and one in the
;       shared arena.  Currently, we assume that the selector to convert is
;       NOT in the protected region.  But if it is, this code still works,
;       but the performance would be slower.

        mov     ecx,[stl_pptda]                 ; (ecx) = pptda
        cmp     [ecx].ptda_infoseg.LIS_PackSel,ax  ; is selector packed ?
        ja      short stl10                     ; jump if packed
        cmp     [ecx].ptda_infoseg.LIS_PackShrSel,ax  ; is selector packed?
        jb      short stl10                     ; jump if packed

        SelToLaTiled ax                         ; if not packed, use tiling
        RETC

;       we get to this code if the selector is in the packed region

stl10:  mov     ecx,[ecx].ptda_ldtaddr          ; (ecx) = pointer to ldt
        and     eax,RPL_CLR - TABLE_MASK        ; clear rpl and table bit
        add     ecx,eax                         ; (ecx) = pointer to descriptor

;       if the selector is not present, load the segment here to avoid
;       touching the thing for the first time through a flat address and
;       not taking a segment fault and thus getting no data.  If we get
;       error_interrupt, try again, since the caller won't be expecting
;       error on a simple address translation routine.  If we get some
;       other sort of error (disk hard error or exe format problem)
;       return some invalid flat address so when the guy touches the
;       thing he will get a fatal fault.  BUGBUG - bs - this is probably not
;       the best way to go about this, since we won't get descriptive
;       info about the problem, but I can't think of a better way now.
;
;       This tci_Fault0 thing below is used to inform dynamic trace that
;       it is OK to take a page fault here.  See task\dynci.asm.

   tci_Fault0:
        public tci_Fault0

; KDH - Here we need to get the complete 32-bit base address of the LDT
;       descriptor.

        mov     ah,[ecx].d_extaddr
        mov     al,[ecx].d_hiaddr
        shl     eax,16
        mov     ax,[ecx].d_loaddr               ; (eax) = base of selector

        RETC

ifdef SELSTRICT
stlie:  Panic <"_SELSelToLa">
endif
EndProc _SELSelToLa


;***EP  _SELLaToSel - returns selector for a given user space flat address
;
;       ENTRY:  laddr - linear address to convert
;               pptda - pointer to target tasks ptda
;       EXIT:   (eax) = selector
;       USES:   eax, ecx
;
Procedure _SELLaToSel,,,,esp
        assume ds:FLAT , es:FLAT , fs:nothing, gs:nothing, ss:nothing

        ArgVar  lts_laddr, DWORD
        ArgVar  lts_pptda, DWORD

; KDH - We want to check if the selector to convert is packed or not.  We have
;       to check for two "fences":  one in the private arena, and one in the
;       shared arena.

        LaToSelTiled [lts_laddr]                ; (eax) = selector by tiling

        mov     ecx,[lts_pptda]                 ;
        cmp     [ecx].ptda_infoseg.LIS_PackSel,ax ; is is packed (private) ?
        ja      lts10                           ; jump if packed
        cmp     [ecx].ptda_infoseg.LIS_PackShrSel,ax ; is is packed (shared)?
        jb      lts10                           ; jump if packed

lts05:  RETC

; KDH - we get to here if the given address is within the packed region of
;       the private or shared arena.  The macro LaToSelTiled returns a selector
;       with the LDT bit set, but not the privilege bits.  If the macro is
;       changed, this code should be adjusted as well.
;
;       Another issue that needs to be addressed here is the fact that a
;       descriptor in the PACKSHR region can be invalidated or freed as a
;       result of freeing a DLL.  As a result, we need to check if the base
;       address of the descriptor is 0 or not.

lts10:  cmp     ax,SEL_INFOSEGSEL1              ; Infosegs are tiled !
        je      lts05                           ; (Privilege bits are not set)
        cmp     ax,SEL_INFOSEGSEL2              ; Infosegs are tiled !
        je      lts05                           ; (Privilege bits are not set)

        cmp     _VMProtectedMem,0       ; Is protected region enabled?
        je      lts15                   ; If not, go to lts15.
        cmp     ax,SEL_LASTPACKSHR      ; Selector in protected region ?
        ja      lts05                   ; If so, return.
        jmp     lts19

lts15:  mov     eax,SEL_LASTLDTOFF      ;
        add     eax,[ecx].ptda_ldtaddr  ;
        mov     ch,[eax].d_extaddr
        mov     cl,[eax].d_hiaddr
        shl     ecx,16
        mov     cx,[eax].d_loaddr       ; (ecx) = base of selector
        or      ecx,ecx                 ; Is it 0 (invalid descriptor) ?
        jz      lts18                   ; If so, exit the loop.

        cmp     ecx,[lts_laddr]         ; is this our match?
        ja      lts18                   ; Not last descriptor, resume normally.
        mov     eax,SEL_LASTLDTSEL      ;
        RETC                            ;

lts18:  LaToSelTiled [lts_laddr]        ; Restore EAX and ECX.
        mov     ecx,[lts_pptda]         ;

lts19:  and     eax,RPL_CLR - TABLE_MASK
        or      eax,eax                 ; null selector?
        jz      ltsx                    ; exit if null
        add     eax,[ecx].ptda_ldtaddr  ; (eax) = pointer to highest potential
                                        ;         match descriptor

;       Let the search begin.  For now we will do a linear search up from
;       (eax) which is a pointer to the computed descriptor for this address
;       using the tiling algorithm (which is the lowest possible descriptor
;       it could be).

lts20:  add     eax,size desctab        ; (eax) = next higher descriptor

;       This tci_Fault1 thing below is used to inform dynamic trace that
;       it is OK to take a page fault here.  See task\dynci.asm.

   tci_Fault1:
        public tci_Fault1

        mov     ch,[eax].d_extaddr
        mov     cl,[eax].d_hiaddr
        shl     ecx,16
        mov     cx,[eax].d_loaddr       ; (ecx) = base of selector
        or      ecx,ecx                 ; Is it 0 (invalid descriptor) ?
        jz      lts30                   ; If so, exit the loop.

        cmp     [lts_laddr],ecx         ; is this our match?
        jae     lts20                   ; jump if still too low

;       if we fall through to here, we hit a descriptor whose base is
;       above what we are looking for.  That means that the previous selector
;       is our answer.

lts30:  mov     ecx,[lts_pptda]
        sub     eax,[ecx].ptda_ldtaddr  ; (eax) = offset in ldt of next desc
        sub     eax,size desctab-SEL_LDTBIT   ; (eax) = answer selector

ltsx:   RETC

ifdef SELSTRICT
ltsie:  Panic <"SELLaToSel">
endif
EndProc _SELLaToSel

HIGH32CODE ends

    FARCODE HIGH32
SWAP32_4 segment

;***EP  _SELSetDescAll - Set up a descriptor's contents
;
;       _SELSetDescAll sets up a descriptor's contents IN ONE CONTEXT ONLY!
;       No shared context, no limit, alias, expand down, or gate descriptor
;       supported! For all these combinations, use _SELSetDescInfo.
;       WARNING: This procedure is in swappable code
;       WARNING: First five parameters of this procedure must be exactly
;           the same as those of _SELSetDescInfo.
;
;       ENTRY   (SS:SP)     Scope, must be SELSD_ALL
;               (SS:SP+4)   Access byte
;               (SS:SP+5)   Attribute (D_BIG, D_UVIRT only)
;               (SS:SP+8)   Base selector to be set up
;               (SS:SP+12)  Size of memory object
;               (SS:SP+16)  Linear address of base
;               (SS:SP+20)  pptda - Address of ptda
Procedure   _SELSetDescAll, near
    assume ds:FLAT , es:FLAT , fs:nothing, gs:nothing, ss:nothing

        ArgVar  sda_Scope, DWORD            ; first parameter, pushed last
        ArgVar  sda_x,DBYTE,sda_access, sda_attr
        ArgVar  sda_selector,WORD
        ArgVar  sda_limit,DWORD             ; size converted to limit later
        ArgVar  sda_laddr,DWORD
        ArgVar  sda_pptda,DWORD             ; Address of PTDA

        EnterProc

     ;  .errnz (sd_Scope - sda_Scope)
     ;  .errnz (sd_x - sda_x)
     ;  .errnz (sd_selector - sda_selector)
     ;  .errnz (sd_limit - sda_limit)
     ;  .errnz (sd_laddr - sda_laddr)

        push    ebx                             ; save ebx

ifdef SELSTRICT
        mov     eax, [sda_Scope]
        and     eax, not SELSD_BASEALL
        cmp     eax, SELSD_ALL
        jz      short sda20

sdaie:  Panic <'SELSetDescAll:bad parameter'>
sda20:

endif
        movzx   eax, [sda_selector]
        or      [sda_limit], 0
        jz      short sda30

        dec     [sda_limit]                     ; convert size to limit
sda30:  test    ax, SEL_LDTBIT
        jnz     short   sda50

        ; GDT descriptor update

        mov     ebx, [_SELGDTLaddr]
        and     eax, SEL_RPLCLR
        add     ebx, eax
        CCALL32 selGDTall
        jmp     short sda90

sda50:  mov     ebx, [sda_pptda]
        mov     ebx, [ebx].ptda_ldtaddr
        and     al, SEL_LOW_MASK            ; mask LDT, RPL bits
        add     ebx, eax                    ; ebx = flat address of selector
        CCALL32 selLDTall

sda90:  pop     ebx
        LeaveProc
        RETC
EndProc _SELSetDescAll

SWAP32_4 ends
        end
