;       SCCSID = @(#)selinit.asm        6.4 92/03/11
page    ,132

SCCSID  equ     "@(#)selinit.asm        6.4 92/03/11"

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

;   This file includes initialization routines of SELECTOR MANAGER.
;
;   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, FS, GS
;   4) All procedures must receive parameters from stack (4 bytes for each
;       parameter)
;   5) All procedures must return their return code in EAX.


.xlist
include  dosseg.inc
include  basemaca.inc
include  fnomem.inc
include  osmaca.inc
include  ostypes.inc
include  mi.inc
include  osconst.inc
include  seldesc.inc
include  protmode.inc
include  sysinvar.inc
include  doshlp.inc                     ; selSysInitCode and Date def
include  infoseg.inc
include  error.inc
include  ldrvars.inc
include  vmowner.inc
include  virtsym.inc
include  pgexport.inc
include  vmexport.inc
include  selxport.inc
.list

        ModName  , FNO_SELINIT_ASM
        CpuMode 386

        ReferGlobal     DevHlp,DWORD    ; pointer to DeviceHelp
        ReferGlobal     GDTDesc,FWORD
        ReferGlobal     IDTDesc,FWORD
        ReferGlobal     SysInfoSeg,WORD
        ReferGlobal     SISData,WORD    ; InfoSeg data structure
        ReferGlobal     VectorRIPL,DWORD; pointer to RIPL data selector

        ReferGDT        GDT,WORD        ; Base of GDT
        ReferGDT        GDT_DOSALIAS,WORD ; Descriptor for DOSGROUP
        ReferGDT        GDT_POOL,BYTE   ; Marks start of GDT pool
        ReferGDT        GDT_POOLEND,BYTE; Marks end of GDT pool
        ReferGDT        _EndGDTPUBSEG,BYTE      ; Marks end of GDT
        ReferGDT        GDT_IDT,WORD
        ReferGDT        GDT_RM_IDT,WORD
        ReferGDT        GDT_ROMDATA,WORD
        ReferGDT        GDT_RMCODE,WORD
        ReferGDT        GDT_OS2LDR,WORD
        ReferGDT        GDT_R2DS,WORD
        ReferGDT        GDT_R3DS,WORD
        ReferGDT        GDT_R3PDS,WORD
        ReferGDT        GDT_R3CS,WORD
        ReferGDT        GDT_R3THKDS,WORD
        ReferGDT        GDT_SAS,WORD    ; SAS descriptor in GDT
        ReferGDT        GDT_mFSD,WORD   ; Descriptor for mini-FSD
        ReferGDT        GDT_RIPL,WORD   ; Descriptor for remote IPL data
        ReferGDT        GDT_LDT,WORD
        ReferGDT        GDT_PTDA,WORD
        ReferGDT        GDT_R0CSC,WORD
        ReferGDT        GDT_TIB,WORD

        ReferFlat       _pGDTTaskCopy,dword
        ReferFlat       _pGDTThreadCopy,dword
        ReferFlat       _pGDTTIBCopy,dword
        ReferFlat       _Sis2,BYTE      ; infoseg data structure
        ReferFlat       _Lis2,BYTE      ; infoseg data structure
        ReferFlat       _pPTDACur,dword

        extrn   _selLockHandleInit:near
        extrn   _selFixCallGates:near
        extrn   _SELGetDescInfo:near
        extrn   _SELSetDescInfo:near
;;;;;;; extrn   _SELObjectInfo:near
        extrn   _SELGetOd:near
        extrn   _VMAllocMem:near
        extrn   _VMExchangeSel:near
        extrn   _VMFreeMem:near
        extrn   _PGAttach:near
        extrn   _TKSetSSBase:near
        extrn   _PGAllocCRPTE:near

ifdef PMDEBUG
        EXTRN16 DebugMovedGDTIDT
endif
        EXTRNFAR        DeviceHelpRM
        EXTRNFAR        DeviceHelpDM
        EXTRNFAR        GDTAlloc

        extrn   GDTInitTab:word


INITBSS segment
        public _SELGDTLaddrTmp
_SELGDTLaddrTmp         dd      ?               ; flat address of GDT
        public  _vaArenaInfo
_vaArenaInfo    dd      0       ; Flat address of ArenaInfo (after SELInit)

        public  MaxGDTDesc, GDTFreeList
MaxGDTDesc      dw      0       ; highest valid GDT descriptor
GDTFreeList     dw      0       ; GDT free list linked list head

INITBSS ends


_DATA segment
        public _SELGDTLaddr, _SELGDTHandle, _SELMaxGDT,
        public  _SELGDTFreeList
_SELGDTLaddr            dd      ?               ; flat address of GDT
_SELGDTHandle           dw      ?               ; GDT segment handle
_SELMaxGDT              dw      ?               ; maximum GDT desc. value
_SELGDTFreeList         dw      ?               ; Head of free list in GDT

        public _R0DS, _R0CS, _R2DS, _R2CS, _R3DS, _R3PDS, _R3CS, _R3THKDS
        public _R0CSC, _R3TIB
_R0DS           dw  seg FLAT:DGROUP
_R0CS           dw  seg FLAT:DOSHIGH32CODE
_R2DS           dw  GSEL GDT_R2DS    + SEL_RPL2
_R2CS           dw  GSEL GDT_R3CS    + SEL_RPL2
_R3DS           dw  GSEL GDT_R3DS    + SEL_RPL3
_R3PDS          dw  GSEL GDT_R3PDS   + SEL_RPL3
_R3CS           dw  GSEL GDT_R3CS    + SEL_RPL3
_R3THKDS        dw  GSEL GDT_R3THKDS + SEL_RPL3
_R0CSC          dw  GSEL GDT_R0CSC
_R3TIB          dw  GSEL GDT_TIB     + SEL_RPL3

_DATA ends



       FARCODE HIGH32
CINIT_TEXT segment

flushcs     macro    vaddr     ;; update cs register
    local   labl
    push    cs
    push    offset FLAT:labl
    RETFD
labl:
endm


;***EP  _SELInit - Initialize Selector Manager
;
;       _SELInit is called by PGInit to update descriptor base addresses
;       after the objects are moved into high end of linear address space.
;       It also reformats 32-bit flat-relative call gates.
;
;       ENTRY   parena - Pointer to the ArenaInfo array (flat address)
;       EXIT    (EAX)  = New flat address of ArenaInfo array
;       USES    EAX, ECX, EDX, Flags
;
;       laddr_t _SELInit(struct arena_s *parena_s)
;
;       PseudoCode:
;
;       Save registers for C calling conventions
;       Set DS = DOSGDTDATA
;       While (not (parena_s->a_aflags & AF_PEND) ) {
;           If (a_oflags & OBJALIAS16 && a_aflags & AF_LADDR) {
;               Set sel = parena_s->a_sel
;               set laddr = parena_s->a_laddr
;               Set sel descriptor address fields = laddr
;               If (sel == GDT selector), save its address
;           }
;           parena_s += size arena_s
;       }
;       Set base address of R0CS and R0DS to 0
;       ; Now all kernel descriptors have been updated. Pop and push all
;       ; segment registers to refresh descriptor caches
;       Push and then pop  cs, ds, ss, es, fs, gs
;       Call _TKSetSSBase to update SSBase value
;       ; First update aliases to kernel code segments. Since all segments
;       ;   are moving in linear address space once, calculate the mov amount
;       ; and add it to every alias base address
;       Save new parena_s address
;       While (a_oflags & OBJALIAS16 && a_aflags & AF_LADDR)
;           parena_s += size arena_s    ; find any valid map
;       Set basemove = laddr - paddr
;       Set tabOffst = offset INITCODE:GDTInitTab
;       While (tabOffst != 0)  {
;           Set sel= [tabOffst]
;           Read address fields from sel descriptor
;           Add basemove to address
;           Store the new address back into the descriptor
;           If (sel == GDT_IDT)
;               Update IDTDesc
;           Endif
;           Set tabOffst += 4                   ; get next selector
;       }
;       Set up GDTDesc                          ; new GDT address
;       Load    GDTR
;       Load    IDTR
;       restore saved registers
;       call _selFixCallGates to reformat 32-bit flat-relative call gates
;       return new arena info address

Procedure _SELInit, near
    assume ds:FLAT, es:FLAT, fs:nothing, gs:nothing, ss:nothing
    ArgVar   ini_parena,DWORD           ; flat offset of ArenaInfo Array

    LocalVar ini_GDTlimit,WORD          ; Limit of GDT
    LocalVar ini_basebias,DWORD
    LocalVar ini_SysInfoSeg,WORD        ; Tiled infoseg

        EnterProc
        push    esi
        push    ebx

;       Walk the arena info array to update the base field of
;       descriptors that need to be updated
;
;       (ES) = FLAT

        GDTContext ds,ax
        mov     eax,[ini_parena]
        mov     cx,es:[eax].a_aflags    ; cx = flags
ini10:  test    cx,AF_LADDR             ; is it to be updated?
        jz      ini50                   ; jmp if not
        test    es:[eax].a_oflags,OBJALIAS16 ; does it have a selector?
        jz      ini50                   ; jmp if not

        movzx   ebx,es:[eax].a_sel      ; ebx = selector
        and     ebx,not SEL_RPLMASK     ; ds:ebx = offset of descriptor

;       Update descriptor's base address to new parameter
;
;       (EAX) = arena_s pointer
;       (EBX) = GDT selector, RPL bits clear
;       (CX)  = arena flags
;       (DS)  = DOSGDTDATA
;       (ES)  = FLAT

        mov     edx,es:[eax].a_laddr    ; get new virtual address
        and     cx,AF_SRCMASK
        cmp     cx,AF_OS2KRNL
        jnz     short ini20             ; jmp if not a kernel object

        push    edx
        mov     ecx,es:[eax].a_paddr
        sub     edx,ecx                 ; This is the base shift amount
        mov     [ini_basebias],edx
        pop     edx                     ; restore the address
ini20:  cmp     bx,DOSGDTDATA           ; is it GDT Data
        jnz     short ini30

;       Is GDT Data, save its virtual address and limit for later use

        mov     [_SELGDTLaddr],edx

        mov     [_pGDTTaskCopy],GSEL GDT_LDT   ; save start of GDT area updated
        add     [_pGDTTaskCopy],edx            ;   during inter-task switches
        mov     [_pGDTThreadCopy],GSEL GDT_PTDA; save start of GDT area updated
        add     [_pGDTThreadCopy],edx          ;   during intra-task switches
        mov     [_pGDTTIBCopy],GSEL GDT_TIB    ; save start of GDT area updated
        add     [_pGDTTIBCopy],edx             ;   during FS:TIB load

        mov     cx,[ebx].d_limit
        mov     [ini_GDTlimit],cx

;       (EAX) = arena_s pointer
;       (EBX) = GDT selector, RPL bits clear
;       (EDX) = laddr from arena_s entry
;       (DS)  = DOSGDTDATA
;       (ES)  = FLAT
;
;       WARNING!  Do not single-step or set breakpoints in this critical
;       section where the kernel GDT descriptor linear addresses are updated.
;       Note that even a breakpoint on the first instruction will confuse
;       the debugger when we're updating its code and data descriptors, since
;       a breakpointed instruction is single-stepped by the debugger (in order
;       to restore the breakpoint).

ini30:  mov     [ebx].d_loaddr,dx
        ror     edx,WORDSHIFT
        mov     [ebx].d_hiaddr,dl
        mov     [ebx].d_extaddr,dh

;       End critical section.  We just updated the descriptor. If it is
;       DosHlp selector we must also update its data descriptor.

        rol     edx,WORDSHIFT           ; restore base address just in case
        cmp     ebx,DOSHLP_CODESEL      ; check for DosHlp code selector
        jnz     short ini35             ; no - continue
        mov     ebx,DOSHLP_DATASEL      ; set up for DosHlp data selector
        jmp     short ini30             ; update descriptor

;       Check to see if it was os2ldr selector.  If so we must update
;       the selSysInitCode and selSysInitData selectors.

ini35:  cmp     ebx,GSEL GDT_OS2LDR     ; check for os2ldr selector
        jnz     short ini37             ; no - continue
        mov     ebx,selSysInitCode      ; set up for selSysInitCode selector
        jmp     short ini30             ; update descriptor

ini37:  cmp     ebx,selSysInitCode      ; check for selSysInitCode selector
        mov     ebx,selSysInitData      ; set up for selSysInitData selector
        jz      short ini30             ; update descriptor

;       Descriptor updated, now get the next descriptor
;
;       (EAX) = arena_s pointer
;       (DS)  = DOSGDTDATA
;       (ES)  = FLAT

ini50:  add     eax,size arena_s        ; get to next element
        mov     cx,es:[eax].a_aflags    ; get the flags
        test    cx,AF_PEND              ; last element?
        jz      ini10                   ; no, go update it

        GDTContext ds,bx                ; refresh ds register
        push    ss
        pop     ss                      ; refresh ss register

;       All kernel code segments updated, now update R0CS and R0DS
;       CAN'T TRACE TILL END OF UPDATE

        xor     eax,eax
        mov     bx,seg FLAT:DOSHIGH32CODE
        movzx   ebx,bx
        mov     [ebx].d_loaddr,ax       ; base addr = 0 for R0DS and R0CS
        mov     [ebx].d_hiaddr,al
        mov     [ebx].d_extaddr,al
        mov     bx,seg FLAT:DGROUP
        mov     [ebx].d_loaddr,ax       ; base addr = 0 for R0DS and R0CS
        mov     [ebx].d_hiaddr,al
        mov     [ebx].d_extaddr,al

;       Update flat segment registers

        flushcs                         ; update cs
        DosInitContext es,ax
        mov     ebx,[_vaArenaInfo]      ; Get new virtual address of AI
        mov     [ini_parena],ebx        ; save it for later use
        FlatContext es,ax               ; updates es

;       Now we have to update the alias selectors in GDTInitTab

        mov     eax,[ini_basebias]
        mov     cx,DOSCODE
        mov     es,cx                   ; es = INITCODE
    assume es:DOSCODE

        mov     esi,offset es:GDTInitTab
ini60:  movzx   ebx,word ptr es:[esi]   ; get alias selector
        and     ebx,SEL_RPLCLR          ; turn selector to offset in GDT
        jz      short ini80             ; jmp if done

        cmp     bx,GSEL GDT_RMCODE      ; skip GDT_RMCODE
        jz      short ini70

;       (eax) = base move amount
;       (ds:ebx) = descriptor
;       (es:esi) = GDTInitTab table entry

ini65:  mov     dh,[ebx].d_extaddr
        mov     dl,[ebx].d_hiaddr
        shl     edx,WORDSHIFT
        mov     dx,[ebx].d_loaddr       ; edx = address in descriptor
        add     edx,eax                 ; edx = new address to put in desc.
        mov     ecx,edx
        mov     [ebx].d_loaddr,dx
        shr     edx,WORDSHIFT
        mov     [ebx].d_hiaddr,dl
        mov     [ebx].d_extaddr,dh

        cmp     bx,GSEL GDT_IDT         ; is it IDT selector?
        jnz     short ini70             ; jmp if not

;       IDT selector updated, save its new values
;       (eax) = base move amount
;       (ds:ebx) = DOSGDTDATA:GDT_IDT
;       (es:esi) = GDTInitTab table entry
;       (ecx) = new address of IDT

        push    gs
        DosContext gs,dx                ; gs = DOSGROUP
        mov     edx,offset gs:IDTDesc
        mov     gs:[edx+2],ecx          ; store new linear address
        mov     cx,[ebx].d_limit
        mov     gs:[edx],cx             ; store limit
        mov     bx,gs:[SysInfoSeg]      ; get Global Infoseg's value
        pop     gs
    assume gs:nothing
        jmp     short ini65             ; Go update SysInfoSeg too.

ini70:  add     esi,4                   ; get next selector in GDTInitTab
        jmp     short ini60

;       Now all the descriptors have been updated, reload IDTR and GDTR
;       IDTDesc contents already updated

ini80:  DosContext ds,bx
        FlatContext es,bx
        mov     ebx,offset GDTDesc
        mov     cx,[ini_GDTlimit]
        mov     [ebx],cx
        mov     ecx,[_SELGDTLaddr]
        mov     [ebx+2],ecx

        lgdt    [GDTDesc]
        lidt    [IDTDesc]

        FlatContext ds,bx
        CCALL32 _TKSetSSBase

;       All done, Now initialize _SELGDTFreeList to make _SELAllocGDT work.

        DosInitContext  es,bx
        mov     bx,es:[GDTFreeList]
        mov     [_SELGDTFreeList],bx
        mov     bx,es:[MaxGDTDesc]
        mov     [_SELMaxGDT],bx
        FlatContext es

;       Reformat the 32-bit flat-relative call gates

        CCALL32 _selFixCallGates

;       Have to return the new flat address of ArenaInfo

        mov     eax,[ini_parena]
        pop     ebx
        pop     esi
        LeaveProc
        RETC                            ; caller cleans stack
EndProc _SELInit


;***EP  _SELReinit - Complete initialization of Selector Manager
;
;       _SELReinit is called by VMInit to move GDT into its final address
;       and initialize lockhandle BMP32 records object.
;
;       ENTRY   NONE
;       EXIT    NONE
;       USES    EAX, ECX, EDX, Flags
;
;       PseudoCode:
;
;       Call PGAllocCRPTE - allocate user page tables
;       Call VMAlloc(sparse, 64K, system area, resident) - GDT
;       Map GDT, read GDT descriptor size
;       Call PGAttach       ; attach GDT pages to newly allocated high memory
;       If (size & PAGEMASK)  ; size wasn't on a page boundary
;           Panic     ; size must be on a page boundary
;       Endif
;       Set up DOSGDTDATA descriptor
;       Load GDT register
;       Set _SELGDTLaddr = new flat address of GDT
;       Call VMFreeMem(old GDT pointer)
;       Call _SELLockHandleInit
;       return

;       Use "realmode" arg to force the __FILE__ string into DOSGROUP

VM_SKEW_GDT equ 5
reiie1: Panic   <'SELReinit: VMAlloc'>,,,realmode
reiie2: Panic   <'SELReinit: PGAttach'>

Procedure   _SELReinit,far32
    assume ds:FLAT, es:FLAT, fs:nothing, gs:nothing, ss:nothing
    LocalVar    rei_ac, <size vmac_s>   ; VMAlloc return info

    EnterProc

;       Save registers that need to be saved for C calling conventions.

        push    es
        push    ebx
        push    esi

        ; b730502 - Allocate the GDT below the 2G boungary. To insure this can
        ; be done call PGAllocCRPTE (allocates the 2G linear memory for the
        ; user page tables) and place the GDT 64K below the page tables.

        CCALL32 _PGAllocCRPTE       ; on return eax = page table liner address
        sub     eax, (_128KB - (VM_SKEW_GDT * 4 * _1KB))
        mov     [rei_ac].ac_va, eax ; linear address for GDT

        ; Push parameters needed for _VMAllocMem and call _VMAllocMem
        ; VMAllocMem(cb, cbcommit, fl, hobptda, hobmte, flsel, blkno, &ac)

        xor     eax, eax
        SSToDS  ecx, ss:rei_ac      ; flat address of rei_ac
        mov     edx, VMAC_ARENASYS + VMAC_ALIGNPAGE + VMAC_LOCSPECIFIC \
                + PG_ZEROFILL + PG_RESIDENT + PG_R + PG_W       ; alloc flags
        CCALL32 _VMAllocMem <SEL_64K, eax, edx, eax, SELGDTOWNER, eax, eax, \
                    eax, ecx>
        or      eax, eax
        .errnz  NO_ERROR
        jnz     short reiie1        ; Panic

        ; Linear memory reserved, Now attach the existing pages of GDT to it.
        ; PGAttach(pgnew, pptdanew, pgold, pptdaold, npages, fl)

        mov     ebx, [_SELGDTLaddr] ; get GDT linear address
        xor     ecx, ecx            ; clear high word
        mov     cx, DOSGDTDATA      ; get GDT selector
        add     ecx, ebx            ; ecx = flat offset of GDTData
        movzx   esi, [ecx].d_limit  ; esi = current GDT limit
        mov     eax, esi
        add     eax, PAGEMASK       ; round up to nearest page size
        shr     eax, PAGESHIFT      ; eax = no. of valid GDT pages
        mov     edx, ebx
        shr     edx, PAGESHIFT      ; (edx) = old pageno of GDT
        mov     ecx, [rei_ac].ac_va     ; eax = linear address of new GDT
        shr     ecx, PAGESHIFT
        CCALL32 _PGAttach <ecx, 0, edx, 0, eax, <PG_W or PG_R>>
        or      eax, eax
        .errnz  NO_ERROR
        jnz     reiie2

        mov     ecx, esi
        sub     ecx, (size desctab - 1)  ; ecx = highest valid GDT value
        mov     [_SELMaxGDT], cx

        ; We allocated new address space and successfully attached the
        ;   new pages.
        ;   esi = Current limit of GDT
        ;   ebx = Old flat offset of GDT

        mov     ax, [rei_ac].ac_hob     ; handle to GDT segment
        mov     [_SELGDTHandle], ax     ; save for later use
        DosInitContext es, ax
        movzx   eax, es:[MaxGDTDesc]  ; initialize max. committed GDT desc value
        mov     ecx, esi
        or      ecx, PAGEMASK
        sub     ecx, (size desctab - 1)
        cmp     cx, ax              ; need to link new descriptors to freelist?
        je      short rei30         ; jmp if not

        Panic   <'SELReinit: GDT size'> ; size must have been page mult.

        ; GDTFreeList updated, now update GDT descriptor and reload GDTR
        ;   WARNING: We also need to update contents of GDTDesc here, because
        ;   the leftover value in it is used later on to reload the GDT
        ;   once again.
        ;   ebx = old offset of GDTData
        ;   esi = old limit of GDTData

rei30:  FlatContext es, cx
        xor     ecx, ecx                    ; clear high word
        mov     cx, DOSGDTDATA
        add     ecx, ebx                    ; ecx = flat offset of GDT
        mov     edx, [rei_ac].ac_va
        DosContext  ds, ax
        mov     eax, offset [GDTDesc]

        mov     [eax], esi                  ; GDT limit
        mov     [eax+2], edx                ; new GDT address
        lgdt    [GDTDesc]                   ; load GDTR

        ; WARNING: Don't trace from here to end of DebugMovedGDTIDT call

        FlatContext  ds, ax
        mov     [_SELGDTLaddr], edx         ; update to new GDT address

        mov     [_pGDTTaskCopy],GSEL GDT_LDT   ; save start of GDT area updated
        add     [_pGDTTaskCopy],edx            ;   during inter-task switches
        mov     [_pGDTThreadCopy],GSEL GDT_PTDA; save start of GDT area updated
        add     [_pGDTThreadCopy],edx          ;   during intra-task switches
        mov     [_pGDTTIBCopy],GSEL GDT_TIB    ; save start of GDT area updated
        add     [_pGDTTIBCopy],edx             ;   during FS:TIB load

        mov     [ecx].d_loaddr, dx
        shr     edx, WORDSHIFT
        mov     [ecx].d_hiaddr, dl
        mov     [ecx].d_extaddr, dh
        mov     [ecx].d_limit, si

ifdef PMDEBUG
        CALL16  DebugMovedGDTIDT
endif

        ; Now remove GDT selector from the old object and attach it to the
        ; new one
        ; VMExchangeSel(ipgold, ipgnew)

        mov     eax, [rei_ac].ac_va
        shr     eax, PAGESHIFT
        mov     ecx, ebx
        shr     ecx, PAGESHIFT
        CCALL32 _VMExchangeSel <ecx, eax>

        ; Now we can free the old GDT pages
        ;   ebx = old address of GDTData

        xor     eax, eax
        CCALL32 _VMFreeMem <ebx, eax, eax>  ; also frees GDT selector
        or      eax, eax
        .errnz  NO_ERROR
        jnz     short reiie3                ; Panic if error

        CCALL32 _selLockHandleInit

        ; Move linear addresses of GDT_RM_IDT and GDT_ROMDATA high. Their
        ; physical addresses should still be low

        mov     ax, GSEL GDT_RM_IDT   ; Selector to be mapped high
        xor     edx, edx
        CCALL32 selMapHigh
        mov     edx, PG_U
        mov     ax, GSEL GDT_ROMDATA
        CCALL32 selMapHigh

        ; pop all registers saved in the beginning
        pop     esi
        pop     ebx
        pop     es

        LeaveProc
        RETC
reiie3: Panic   <'SELReinit: VMFreeMem'>
EndProc   _SELReinit


;***EP  _SELEndInit - Free OS2LDR data segment (GDT_OS2LDR)
;
;       _SELEndInit is called by Sysinit to free the OS2LDR data segment
;       It also sets Device Drivers FLAT code segment to nonconforming.
;
;       ENTRY   NONE
;       EXIT    NONE
;       USES    EAX, ECX, EDX, Flags
;
;       PseudoCode:
;
;       Call _SELGetOd(GDT_OS2LDR)
;       Call _VMFreeMem()
;;;;;;;seiie1: Panic   <'SELEndInit: SELObjectInfo'>
seiie1: Panic   <'SELEndInit: SELGetOd'>
seiie3: Panic   <'SELEndInit: OS2LDR data owner'>
seiie4: Panic   <'SELEndInit: OS2LDR data address'>

Procedure   _SELEndInit,far32
    assume ds:FLAT, es:FLAT, fs:nothing, gs:nothing, ss:nothing
        LocalVar    sei_desc,<size seldesc_s>
;;;;;;; LocalVar    sei_oi,<size vmoi_s>
        LocalVar    sei_od,<size vmod_s>

        EnterProc

        push    esi
        push    edi

;   _SELGetOd(fl, sel, pseldesc, pod)

;;;;;;; SSToDS  ecx, ss:sei_oi              ; Flat address of sei_oi
        SSToDS  ecx, ss:sei_od              ; Flat address of sei_oi
        mov     edx, _pPTDACur
        mov     [ecx].od_pPTDA, edx
        SSToDS  edx, ss:sei_desc            ; Flat address of sei_desc
;;;;;;; xor     esi, esi
        mov     ax, GSEL GDT_OS2LDR         ; selector for OS2LDR DATA
;;;;;;; CCALL32 _SELObjectInfo <VMGS_VERIFY, eax, esi, edx, ecx>
        CCALL32 _SELGetOd <VMGS_VERIFY, eax, edx, ecx>
        or      eax, eax
        .errnz NO_ERROR
        jnz     short seiie1

;;;;;;; cmp     [sei_oi].oi_hobowner, OS2LDROWNER
        mov     eax, [sei_od].od_pob
        cmp     [eax].ob_hobowner, OS2LDROWNER
        jnz     short seiie3

        mov     edi, [sei_desc].desc_laddr  ; address in descriptor
;;;;;;; cmp     [sei_oi].oi_va, edi         ; Must equal to descriptor address
        cmp     [sei_od].od_laddr, edi      ; Must equal to descriptor address
        jnz     short seiie4

;   _VMFreeMem(laddr, hptda, fl)
;       (edi) = object address

        xor     eax,eax
;;;;;;; CCALL32 _VMFreeMem <edi, esi, eax>
        CCALL32 _VMFreeMem <edi, eax, eax>
        or      eax, eax
        .errnz  NO_ERROR
        jnz     short seiie6

;   GDT_OS2LDR selector is freed. Take it from the free list to avoid
;   having it reassigned.

        movzx   eax, [_SELGDTFreeList]
        cmp     ax, GSEL GDT_OS2LDR
        jnz     short seiie7

        add     eax, [_SELGDTLaddr]     ; (eax) = flat address of GDT_OS2LDR
        xor     cx, cx
        xchg    cx, [eax].d_flink
        mov     [_SELGDTFreeList], cx

;   Device Driver initialization is over. Now set R0CSC as nonconforming.

        movzx   eax, _R0CSC
        add     eax, [_SELGDTLaddr]     ; (eax) = Flat address of R0CSC
        and     [eax].d_access, not D_CONFORM

ifdef  SELSTRICT

;   All done, now make sure the thunk selector is tiled

        mov     si, GSEL GDT_R3THKDS
        cmp     si, (R3THKDS and SEL_RPLCLR)
        jnz     short seiie8
endif

        pop     edi
        pop     esi
        LeaveProc
        RETC
seiie6: Panic   <'SELEndInit: VMFreeMem'>
seiie7: Panic   <'SELEndInit: GDTFreeList'>
ifdef SELSTRICT
seiie8: Panic   <'SELEndInit:R3THKDS'>
endif
EndProc _SELEndInit


;***LP  selMapHigh - Map given selectors into high linear addresses
;
;       selMapHigh allocates UVIRT page tables, maps them to descriptor's
;       physical address, and maps the given selector to the returned
;       linear address.
;
;       ENTRY       AX - Selector
;                   EDX - User access flag
;                           0 Turn User access bit off
;                           PG_U Turn user bit on
;       RETURN      NONE
;       USES        EAX, ECX, EDX, Flags
;
;       PseudoCode:
;
;       Read size from descriptor, round up to page boundary
;       Call VMAlloc(UVIRT+SYSTEM+PAGEALIGN, size, SELUVIRTOWNER)
;       Read address from descriptor
;       Call PGMapPhys                  ; map pte's to physical addresses
;       Store linear address in descriptor
MPH_VMF equ VMAC_LOCANY or VMAC_ALIGNPAGE or VMAC_ARENASYS
MPH_PGF equ PG_UVIRT or PG_W or PG_R

Procedure   selMapHigh,,local
    assume ds:FLAT, es:FLAT, fs:nothing, gs:nothing, ss:nothing
        LocalVar    mph_sel,WORD            ; selector
        LocalVar    mph_fl,DWORD             ; User bit flag
        LocalVar    mph_seldesc,<size seldesc_s>
        LocalVar    mph_ac,<size vmac_s>

        EnterProc <ax, edx>

        ; Get descriptor information

        SSToDS  ecx, ss:mph_seldesc
        CCALL32 _SELGetDescInfo <eax, eax, ecx>
        or      eax, eax
        .errnz  NO_ERROR
        jnz     short mphie1

;   _VMAllocMem(cb, cbcommit, fl, hobptda, hobowner, hobmte, flsel, blkno, &ac)

        push    edi                 ; save edi
        mov     edi, [mph_seldesc].desc_laddr
        shr     edi, PAGESHIFT      ; page number to map pages to.
        SSToDS  edx, ss:mph_ac
        mov     ecx, MPH_VMF or MPH_PGF
        or      ecx, [mph_fl]       ; alloc flags
        mov     eax, [mph_seldesc].desc_laddr
        and     eax, PAGEMASK       ; page offset
        add     eax, [mph_seldesc].desc_cb  ; size + page offset
        add     eax, PAGESIZE - 1
        and     eax, PAGESHIFT      ; no. of pages needed
        CCALL32 _VMAllocMem <eax, eax, ecx, 0, SELUVIRTOWNER, 0, 0, edi, edx>
        pop     edi                 ; restore edi
        or      eax, eax
        .errnz  NO_ERROR
        jnz     short mphie2        ; Panic

        mov     edx, [mph_seldesc].desc_laddr
        and     edx, PAGEMASK       ; page offset of address
        add     edx, [mph_ac].ac_va ; new address to store in descriptor
        mov     ax, [mph_sel]
        and     eax, SEL_RPLCLR     ; turn into offset
        add     eax, [_SELGDTLaddr] ; offset of descriptor
        mov     [eax].d_loaddr, dx  ; low word of address
        shr     edx, WORDSHIFT
        mov     [eax].d_hiaddr, dl  ; high word of address
        mov     [eax].d_extaddr, dh

        LeaveProc
        RETC
mphie1: Panic   <'selMapHigh: Bad descriptor'>
mphie2: Panic   <'selMapHigh: VMAlloc failed'>
EndProc selMapHigh


CINIT_TEXT ends


        FARCODE INIT
INITCODE segment


;***EP  SELInitRM - Initialize GDT
;
;       SELInitRM initializes system's GDT.  Uses a parameter
;       table to initialize the static entries in the GDT.
;
;       ENTRY   (ES:DI) = ArenaInfo array
;       EXIT    (ES)    = DOSINITDATA paragraph address
;               (EAX)   = Base address in R0CS and R0DS
;               (EBX)   = Offset of ArenaInfo w.r.t. R0DS, R0CS
;                           right after we switch to protmode
;       USES    EAX, EBX, ECX, EDX, ESI, EDI, DS, ES, Flags
;
;       Algorithm:
;
;       Find and save the physical addresses of dosmte,
;           initdataseg, highdataseg, and GDT.
;       Complete static descriptor initialization by adding the physical
;           address of the group to the alias descriptors for DFTSS. DFSTACK,
;           GDT, IDT, SAS, TSS, INTSTACK and INFOSEG.
;       Find the first element in the arena info array with a high virtual
;           address. Calculate (paddr - vaddr) and store this value for
;           later use as base address of R0CS and R0DS
;       Calculate the flat address of ArenaInfo array in Protected mode, twice;
;           once for before paging is initialized, once after PG initialized
;           and kernel segments moved into high memory.
;       Call GDTMapInit to walk the arena info array to map every segment
;           that requires mapping. Store (paddr -vaddr) as calculated above in
;           R0CS and R0DS as base address. For other selectors, store
;           given physical addresses.
;       Set DOSGROUP and DOSINITDATA to ring 3.
;       Initialize DevHlp call gate
;       Initialize GDTDesc and IDTDesc (for loading into GDTR and IDTR)
;       Call GDTAllocInit to initialize the GDT free selector link list.

TILEMASK        equ     ((8*16)-1)      ; can tile on 8 paragraph boundaries

ifdef SELSTRICT
sirie0: InternalError <SELInitRM: GDT offset>
endif

Procedure SELInitRM
    assume ds:nothing, es:nothing, fs:nothing, gs:nothing, ss:nothing
    LocalVar sir_sysinfoseg,WORD
    LocalVar sir_initdataseg,WORD
    LocalVar sir_baseaddr,DWORD
    LocalVar sir_arenainfo,DWORD

        EnterProc
        TaskTime

ifdef SELSTRICT
        xor     ax,ax                           ; GSEL GDT must be 0!
        cmp     ax,GSEL GDT
        jnz     short sirie0
endif

        mov     ax,DOSINITDATA
        call    ArenaSelToSeg                   ; get DOSINITDATA paragraph
        mov     [sir_initdataseg],ax

        mov     ax,DOSGDTDATA
        call    ArenaSelToSeg                   ; get DOSGDTDATA paragraph
        mov     ds,[sir_initdataseg]
    assume ds:DOSINITDATA
        mov     [_SELGDTLaddrTmp],esi           ; set DOSGDTDATA linear address
        mov     ds,ax                           ; (ds) = DOSGDTDATA
    assume ds:DOSGDTDATA

;       Complete the static GDT descriptor initialization by adding in
;       the base physical address of the group specified in the table.
;       Only tiled groups and the DOSGDTDATA group are supported.
;
;       (ES:DI) = ArenaInfo structure
;       (DS)    = GDT data

        mov     si,offset cs:GDTInitTab
sir10:  lods    word ptr cs:[si]                ; (ax) = selector number
        or      ax,ax
        jz      short sir30                     ; if zero selector, all done
        mov     bx,ax
        lods    word ptr cs:[si]                ; (ax) = tiled segment/selector
        cmp     ax,DOSGDTDATA                   ; is it the GDT?
        jnz     short sir20
        mov     ax,ds                           ; (ax) = GDT
sir20:  mov     dl,ah                           ; (dl:ax) = 24-bit address
        shl     ax,4
        shr     dl,4
        add     [bx].d_loaddr,ax                ; add to base of segment
        adc     [bx].d_hiaddr,dl
        adc     [bx].d_extaddr,0
        jmp     short sir10

sirie1: InternalError <SELInitRM: InfoSegGDT not tiled>

;       Initialize sir_baseaddr by finding any kernel code segment to be
;       moved high, and calculating (physical address - virtual address)
;       from the arena info.
;
;       (ES:DI) = ArenaInfo
;       (DS)    = DOSGDTDATA

sir30:  push    di                      ; save ArenaInfo address
        sub     di,size arena_s
sir40:  add     di,size arena_s
        mov     ax,es:[di].a_aflags
        test    ax,AF_LADDR             ; does it have valid virtual address?
        jz      short sir40             ; no, get next one
        and     ax,AF_SRCMASK           ; mask out source field
        cmp     ax,AF_OS2KRNL           ; is it a kernel object?
        jnz     short sir40             ; no, get next one

        mov     eax,es:[di].a_laddr     ; get virtual address
        mov     ecx,es:[di].a_paddr     ; get physical address
        sub     ecx,eax                 ; ecx = ppage - vpage
        mov     [sir_baseaddr],ecx      ; save given address
        pop     di                      ; restore address of ArenaInfo array

;       Now set up R0CS and R0DS descriptors as flat selectors.

        mov     si,seg FLAT:DOSHIGH32CODE
        mov     al,D_CODE0
sir50:  call    GDTMapAlloc             ; Allocate specific selector
        mov     [si].d_access,al
        mov     [si].d_attr,D_EXTLIMIT + D_DBIG + D_GRAN4K
        mov     [si].d_limit,WORDMASK   ; limit = 0xfffff plus D_GRAN4K
        mov     eax,[sir_baseaddr]
        mov     [si].d_loaddr,ax
        shr     eax,WORDSHIFT           ; (ax) = high word of address
        mov     [si].d_hiaddr,al
        mov     [si].d_extaddr,ah

        cmp     si,seg FLAT:DGROUP
        jz      short sir60             ; jmp if both are updated

        mov     si,seg FLAT:DGROUP      ; update R0DS
        mov     al,D_DATA3              ; set to DPL 0 in SELReinitDPL
        jmp     short sir50

;       Now, find the offset of ArenaInfo array with respect to R0CS and
;       R0DS

sir60:  push    ds                      ; save GDT segment address
        xor     eax,eax
        mov     ax,es                   ; segment address
        shl     eax,4                   ; convert to physical address
        movzx   edi,di
        add     eax,edi                 ; physical address of ArenaInfo
        mov     cx,[sir_initdataseg]
        mov     ds,cx
    assume ds:DOSINITDATA
        mov     [_vaArenaInfo],eax      ; store physical address of ArenaInfo
                                        ; converted to virtual address later.
        pop     ds                      ; restore GDT segment address
    assume ds:DOSGDTDATA
        mov     ecx,[sir_baseaddr]
        sub     eax,ecx                 ; (eax) = offset of ArenaInfo wrt R0DS
        mov     [sir_arenainfo],eax

;       Now examine the arenainfo array and map each segment that
;       requires mapping into the GDT using the specified selectors.
;
;       (DS)    = DOSGDTDATA
;       (ES:DI) = ArenaInfo

        call    GDTMapInit                      ; GDTMapInit(es:di=arenainfo)

;       Initialize tiled InfoSeg selector

sir70:  mov     ax,offset DOSGROUP:SISData      ; InfoSeg structure
        test    ax,TILEMASK                     ; On tiled boundary?
        jnz     sirie1                          ; No, punt

        shr     ax,4
        add     ax,DOSGROUP                     ; (ax) = Selector to tile
        mov     [sir_sysinfoseg],ax             ; Save for DevHlp/GetDOSVar
        mov     si,ax                           ; (si) = sel
        mov     dl,ah
        shr     dl,4
        shl     ax,4                            ; (dl:ax) = 24-bit phys address

        call    GDTMapAlloc                     ; GDTMapAlloc(si=sel)

        mov     [si].d_limit,SIS_LEN-1          ; set limit
        mov     [si].d_loaddr,ax                ; set address
        mov     [si].d_hiaddr,dl
        mov     [si].d_extaddr,0
        mov     [si].d_access,D_DATA3-D_W       ; set access
        mov     [si].d_attr,D_UVIRT             ; set attribute

;       Set DOSGROUP to ring 3 (for DevHlp_GetDOSVar) and set
;       GDT_DOSALIAS and GDT_SAS limits to DOSGROUP's limit.

        mov     bx,DOSGROUP
        mov     ax,[bx].d_limit
        mov     [bx].d_access,D_DATA3
        mov     [GDT_DOSALIAS].d_limit,ax
        mov     [GDT_SAS].d_limit,ax

;       Set DOSINITR3CODE and DOSINITDATA to ring 3 (for syiProcess)

        mov     bx,DOSINITR3CODE
        mov     [bx].d_access,D_CODE3
        mov     bx,DOSINITDATA
        mov     [bx].d_access,D_DATA3

;       Initialize the DevHlp call gate

        MOVFAROFFSET si,DeviceHelpRM,DOSCODE    ; (ax:si) = DeviceHelpRM label
        mov     ax,DOSCODE
        mov     bx,si                           ; (bx) = DeviceHelpRM offset
        and     si,not TILEMASK
        shr     si,4
        add     si,ax                           ; (si) = tiled DeviceHelpRM sel
        call    GDTMapAlloc                     ; GDTMapAlloc(si=sel)

;       Store DeviceHelpDM label in the call gate,
;
;       (AX) = DOSCODE
;       (BX) = offset of DeviceHelpDM
;       (SI) = CallGate selector

        MOVFAROFFSET <word ptr [si].g_handler>,DeviceHelpDM,DOSCODE
        mov     word ptr [si].g_handler+2,ax
        xor     ax,ax
        mov     [si].g_extoffset,ax
        mov     [si].g_parms,al
        mov     [si].g_access,D_GATE3

        DosContext es
        mov     word ptr [DevHlp],bx            ; set DevHlp pointer
        mov     word ptr [DevHlp+2],si
        mov     ax,[sir_sysinfoseg]             ; stuff for DevHlp/GetDOSVar
        mov     [SysInfoSeg],ax

;       Initialize GDTDesc and IDTDesc

        xor     ax,ax
        mov     si,DOSGDTDATA
        mov     di,offset DOSGROUP:GDTDesc
        movsw
        movsw
        movsw
        dec     di
        stosb                   ; store highest byte of address

        mov     si,GSEL GDT_IDT
        mov     di,offset DOSGROUP:IDTDesc
        movsw
        movsw
        movsw
        dec     di
        stosb                   ; store highest byte of address

        push    fs
        mov     fs,[sir_initdataseg]
    assume fs:DOSINITDATA
        mov     si,DOSGDTDATA
        mov     ax,ds:[si].d_limit
        inc     ax                      ; convert limit to size
        sub     ax,(size desctab)
        mov     fs:[MaxGDTDesc],ax

;       GDTAllocInit(ds=DOSGDTDATA, es:DOSGROUP, fs=DOSINITDATA)

        call    GDTAllocInit

        pop     fs
        mov     es,[sir_initdataseg]    ; (ES) = DOSINITDATA
    assume ds:nothing, es:nothing, fs:nothing
        mov     eax,[sir_baseaddr]      ; set up exit parameters
        mov     ebx,[sir_arenainfo]

        LeaveProc
        ret
EndProc SELInitRM


;***LP  ArenaSelToSeg - Convert selector to physical paragraph address
;
;       ArenaSelToSeg maps a selector to a physical address/16
;
;       ENTRY   (AX)    = protected mode selector to translate
;               (ES:DI) = arenainfo array
;       EXIT    (AX)  = matching paragraph address
;               (ESI) = matching linear address
;       USES    EAX, ESI, Flags
;
;       PseudoCode:
;
;       Given first element of arena info array:
;       If (a_oflags & OBJALIAS16)
;           If (a_sel == given selector)
;               read 32 bit physical address
;               convert it to paragraph address
;               return paragraph address
;           Endif
;       Endif
;       get next element in arena info array
;       go back to checking for OBJALIAS16 flag

PARASHIFT       equ     4

Procedure ArenaSelToSeg
    assume ds:nothing, es:nothing, fs:nothing, gs:nothing, ss:nothing
        mov     si,di
        sub     si,size arena_s                 ; start from the first one
ass00:  add     si,size arena_s
        test    es:[si].a_aflags,AF_PEND+AF_VEND
        jnz     short assie                     ; must be in arenainfo
        test    es:[si].a_oflags,OBJALIAS16
        jz      short ass00
        cmp     es:[si].a_sel,ax
        jnz     short ass00
        mov     eax,es:[si].a_paddr
        shr     eax,PARASHIFT                   ; (eax) = paragraph address
        test    eax,0ffff0000h
        jnz     short assie                     ; must be in low memory
        test    es:[si].a_aflags,AF_LADDR
        jz      short assie                     ; must have laddr assigned
        mov     esi,es:[si].a_laddr
        ret
assie:  InternalError <ArenaSelToSeg>
EndProc ArenaSelToSeg


;***LP  GDTMapInit - map initially allocated OS2KRNL and OS2LDR segments
;
;       Walk the ArenaInfo array to find all segments that need selector
;       mapping, and map the selectors given in the ArenaInfo array.
;       GDTMapInit maps selected DOS, OS2LDR, mini-FSD, remote IPL data,
;       and other segments as indicated by the arenainfo array built
;       by OS2LDR.
;
;       For R0CS or R0DS segments, adjust the selector's base address
;       so that the offset values will correspond to same values as it would
;       when they move to the high end of the linear arena and have 0 base
;       addresses.
;
;       The memory map will be as follows:
;       +-------------+ Top of High Memory
;       |  Ripl data  |
;       +-------------+
;       |  mFSD.FSD   |
;       +-------------+
;       |    free     |
;       +-------------+
;       |   invalid   |         optional invalid physical address range(s)
;       +-------------+
;       |    free     |         optional secondary free range(s)
;       +-------------+
;       | OS2KRNL(hi) |         high kernel memory objects
;       +-------------+ bottom of High Memory (at 1Meg)
;       |     :       |         screen, ROMs, etc.
;       +-------------+ Top of Low Memory     (usually 639k or 640k)
;       |   OS2LDR    |
;       +-------------+
;       |   unused    |
;       +-------------+
;       | OS2KRNL(lo) |         low kernel memory objects
;       +-------------+ 2000h
;       |   DosHlps   |
;       +-------------+ 1000h
;       |   unused    |         rest of page 0
;       +-------------+
;       |  reserved   |         ROM data area, RM IDT
;       +-------------+ 0
;
;       ENTRY   (DS)    = GDT
;               (ES:DI) = ArenaInfo array
;               SELInitRM local variable frame
;       EXIT    NONE
;       USES    EAX, EBX, ECX, EDX, SI, DI, Flags

Procedure GDTMapInit,,local
    assume ds:DOSGROUP, es:nothing, fs:nothing, gs:nothing, ss:nothing
        TaskTime
        sub     di,size arena_s         ; don't skip the first one
gmi00:  add     di,size arena_s
        mov     dx,es:[di].a_aflags
        test    dx,AF_PEND              ; end of physical arena info?
        jnz     short gmix_j            ; jmp if so - all done
        test    es:[di].a_oflags,OBJALIAS16 ; selector mapping required?
        jz      short gmi00             ; skip if not

        mov     si,es:[di].a_sel        ; get selector from arenainfo
        or      si,si                   ; did os2ldr specify selector?
        jnz     short gmi20             ; jmp if so

        mov     ax,dx
        and     ax,AF_SRCMASK           ; mask out source field

;       if mini-FSD, set si = GDT_mFSD

        mov     si,GSEL GDT_mFSD
        cmp     ax,AF_FSD               ; is it mini-fsd?
        jz      short gmi30             ; jmp if so

;       if OS2LDR, set si = GDT_OS2LDR

        mov     si,GSEL GDT_OS2LDR      ; (si) = os2ldr's data sel
        cmp     ax,AF_OS2LDR            ; OS2LDR?
        jnz     short gmi05             ; jmp if not

;       Found OS2LDR's selector; calculate the virtual address of ArenaInfo
;       and store in _vaArenaInfo.  _vaArenaInfo now has physical address
;       of ArenaInfo.

        mov     es:[di].a_sel,si        ; fixup os2ldr's arenainfo entry
        push    ds                      ; save GDT address
        mov     cx,[sir_initdataseg]
        mov     ds,cx
    assume ds:DOSINITDATA
        mov     ecx,es:[di].a_paddr     ; get physical address of os2ldr
        sub     [_vaArenaInfo],ecx      ; [_vaArenaInfo] = offset of ArenaInfo
                                        ; in os2ldr segment
        mov     ecx,es:[di].a_laddr     ; virtual address of os2ldr
        add     [_vaArenaInfo],ecx      ; Add it to offset of ArenaInfo
        pop     ds                      ; restore ds
    assume ds:DOSGDTDATA
        jmp     short gmi40             ; jmp for os2ldr

gmi05:  cmp     ax,AF_RIPL              ; is it remote ipl data?
        jz      short gmi10             ; jmp if so
        InternalError <GDTMapInit: zero sel>

gmix_j: jmp     gmix

;       If it is remote IPL data, set si = GDT_RIPL
;       and VectorRIPL to point to remote IPL data

gmi10:  mov     si,GSEL GDT_RIPL
        push    ds
        DosContext ds
        mov     [VectorRIPL].Segmt,si   ; set ptr to ripl data
        mov     [VectorRIPL].Offst,0
        pop     ds
    assume ds:DOSGDTDATA
        jmp     short gmi30             ; Don't call mapalloc for ripl (CDB)

;       call GDTMapAlloc to allocate a specific GDT selector

gmi20:  call    GDTMapAlloc             ; GDTMapAlloc(si=sel)
gmi30:  mov     es:[di].a_sel,si        ; save selector in arena info

;       If arena item flag == code, set selector access byte to code
;       else set selector access byte to data
;       (es:di) = Element of ArenaInfo array to be mapped
;       (dx) = Flags in ArenaInfo array

gmi40:  mov     cl,D_CODE0              ; (cl) = access (assume code)
        test    es:[di].a_oflags,OBJEXEC
        jnz     short gmi50

gmi45:  mov     cl,D_DATA0              ; oops, is data
gmi50:  mov     [si].d_access,cl

;       selector address = physical address in arena item

        mov     eax,es:[di].a_paddr     ; (eax) = physical address
        mov     ebx,eax
        mov     [si].d_loaddr,ax
        shr     eax,WORDSHIFT           ; (ax) = high word of address
        mov     [si].d_hiaddr,al
        mov     [si].d_extaddr,ah

;       selector size = virtual size in arena item

        mov     ecx,es:[di].a_size      ; (ecx) = size
gmi60:  dec     ecx                     ; sel_limit = sel_size - 1
        cmp     si,DOSGDTDATA
        jnz     short gmi65
        or      ecx,PAGEMASK            ; BUGBUG: GDT size must be page multiple
gmi65:  mov     [si].d_limit,cx

;       assume no size can be larger than 1 Meg. (We are in real mode)

        shr     ecx,WORDSHIFT           ; cx = high word of limit
        test    cx,not D_EXTLIMIT
        jnz     short gmiie
        test    es:[di].a_oflags,OBJBIGDEF
        jz      short gmi70
        or      cl,D_DBIG

gmi70:  mov     [si].d_attr,cl          ; store high 4 bits of limit

;       If we just got through mapping DosHlp's code, repeat the loop
;       for DosHlp's data selector.

        cmp     si,DOSHLP_CODESEL       ; is it DosHlp code?
        jnz     gmi75
        mov     si,DOSHLP_DATASEL       ; set up for DosHlp data
        call    GDTMapAlloc             ; GDTMapAlloc(si=sel)
        jmp     short gmi45

;       If we just got through mapping GDT_OS2LDR process
;       selSysInitCode and selSysInitData

gmi75:
        cmp     si,GSEL GDT_OS2LDR      ; is it GDT_OS2LDR
        jnz     gmi80
        mov     si,selSysInitCode       ; set up for os2ldr code
        call    GDTMapAlloc             ; GDTMapAlloc(si=sel)
        mov     cl,D_CODE0              ; access code
        jmp     short gmi50
gmi80:
        cmp     si,selSysInitCode       ; is it selSysInitCode
        jnz     gmi00
        mov     si,selSysInitData       ; set up for os2ldr data
        call    GDTMapAlloc             ; GDTMapAlloc(si=sel)
        jmp     short gmi45


gmix:   ret
gmiie:  InternalError <GDTMapInit: seg gt 1 meg>
EndProc GDTMapInit


;***LP  GDTMapAlloc - allocate a GDT selector for GDTMapInit and SELInitRM.
;
;       GDTMapAlloc allocates specific selectors as requested by the
;       caller, and moves a dynlink call gate selector if necessary.
;
;       ENTRY   (SI) = selector to allocate
;               (DS) = GDT
;               SELInitRM local variable frame
;       EXIT    NONE
;       USES    Flags

Procedure GDTMapAlloc,,local
    assume ds:DOSGDTDATA, es:nothing, fs:nothing, gs:nothing, ss:nothing
        TaskTime
        cmp     si,GSEL GDT_POOL
        jb      short gmaie0
        cmp     si,GSEL GDT_POOLEND     ; cannot allocate outside of the pool
        jae     short gmaie1
        cmp     [si].d_access,0
        jnz     short gmaie2
        ret
gmaie0: InternalError <GDTMapAlloc: bad selector>
gmaie1: InternalError <GDTMapAlloc: pool too small>
gmaie2: InternalError <GDTMapAlloc: sel busy>
EndProc GDTMapAlloc


;***LP  GDTAllocInit - Initialize the GDT pool of free descriptors.
;
;       GDTAllocInit links up the free descriptors in the GDT pool,
;       and remaining descriptors at the end of GDT.
;
;       ENTRY   (DS) = GDT
;               (FS) = DOSINITDATA
;       EXIT    NONE
;       USES    AX, BX, CX, DX, SI, Flags
;
;       PseudoCode:
;
;       Link up the free descriptors, put head of link list in GDTFreeList.

Procedure GDTAllocInit,,local
    assume ds:DOSGDTDATA, es:nothing, fs:DOSINITDATA, gs:nothing, ss:nothing

        TaskTime
        xor     ax,ax                   ; 0 terminates GDTALIAS free list
        mov     si, GSEL _EndGDTPUBSEG
        add     si, PAGEMASK            ; round up to page boundary
        and     si, not PAGEMASK
        mov     cx, si
        sub     cx, GSEL _EndGDTPUBSEG
        shr     cx, SEL_SHIFT           ; no of descs between _EndGDTPUBSEG
                                        ; and next page boundary
        sub     si, size desctab        ; offset of last descriptor

;       (AX) = offset of last linked up descriptor
;       (SI) = offset of current descriptor

gai00:  cmp     [si].d_access,0         ; Being used to tile?
        jnz     short gai10

        mov     [si].d_flink,ax
        mov     ax,si
gai10:  sub     si,size desctab
        loop    short gai00

        cmp     si, GSEL GDT_POOLEND    ; have we finished generic linkup?
        jb      short gai20             ; Yes, go do exit cleanup

;       here we link up the descriptors in the pool

        mov     cx, GDTPOOLSIZE
        mov     si,GSEL GDT_POOLEND-(size desctab)
        jmp     short gai00

gai20:  mov     fs:[GDTFreeList],ax     ; set head of GDT free list
        ret

EndProc GDTAllocInit


;***LP  GDTAllocN - Allocate GDT selector(s).
;
;       GDTAllocN allocates selectors (and descriptor) from the GDT.
;
;       ENTRY   (ES:DI) = pointer to array to store allocated selectors.
;               (CX)    = number of GDT selectors to be allocated.
;       EXIT    NONE
;       USES    AX, CX, DI, Flags

Procedure GDTAllocN,hybrid
    assume ds:nothing, es:nothing, fs:nothing, gs:nothing, ss:nothing
        TaskTime

gan00:  CALLFAR GDTAlloc                ; allocate a selector
        jc      short ganie
        stosw                           ; store it in array
        loop    short gan00
        ret
ganie:  Panic <"GDTAllocN">
EndProc GDTAllocN


;***EP  SELReinitDPL    - remap DPLs at the end of init time.
;
;       This routine changes a bunch of descriptors from their init time
;       state of DPL 3 to their final resting place at DPL 0.
;
;       ENTRY:  none
;
;       EXIT:   none
;
;       USES:   EAX,EBX,DS,ES,FS
;
Procedure SELReinitDPL,near
        LocalVar    srd_sel,WORD            ; selector
        LocalVar    srd_fl,DWORD             ; User bit flag
        LocalVar    srd_seldesc,<size seldesc_s>

        EnterProc <ds,es>
;       Change the ROM data area and DOSGROUP selectors to DPL 0

        GDTContext ds
        DosContext es,bx
        mov     [GDT_ROMDATA].d_access,D_DATA0  ; ROM data
        mov     ([GDT+bx]).d_access,D_DATA0     ; DOSGROUP, too

;       Resize the System Anchor Segment

        mov     ax,[GDT+bx].d_limit             ; copy limit from DOSGROUP's
        mov     [GDT_SAS].d_limit,ax            ; descriptor

        FlatContext fs,bx
        and     ([GDT+bx]).d_access,not D_PRIV  ; flat data -> DPL 0
        mov     bx,fs:[_Lis2].is_sel
        and     ([GDT+bx]).d_access,not D_PRIV  ; local infoseg -> DPL 0
        mov     bx,fs:[_Sis2].is_sel
        and     ([GDT+bx]).d_access,not D_PRIV  ; RW global infoseg -> DPL 0
        mov     bx,es:[SysInfoSeg]
        and     ([GDT+bx]).d_access,not D_PRIV  ; RO global infoseg -> DPL 0
        assume fs:nothing

;       Change the DevHlp callgate to DPL 0


        ; Get descriptor information

        FlatContext <ds,es>,ax
        xor     eax,eax
        mov     ax,DOSCODE
        SSToDS  ecx, ss:srd_seldesc
        CCALL32 _SELGetDescInfo <eax, eax, ecx>
        or      eax, eax
        .errnz  NO_ERROR
        jnz     short srdie1

;  _SELSetDescInfo(scope, attr/access, sel, size, laddr, handle, hptda,
;      oldsize)
        xor     ecx, ecx
        .errnz SELSD_ALL
        movzx   edx,BYTE ptr [srd_seldesc].desc_acc
        mov     dh,BYTE ptr [srd_seldesc].desc_attr
        DosContext ds,ax                ; ds = DOSGROUP
        movzx   eax,word ptr [DevHlp+2]         ; get DevHlp call gate selector

        FlatContext ds
        pushf
        cli      ;need to disable interrupts so DD won't use selector
                 ;while we are modifying it.
        CCALL32 _SELSetDescInfo <ecx, edx, eax,[srd_seldesc].desc_cb , \
                [srd_seldesc].desc_laddr, ecx, ecx, ecx>
        jc      srdie2
        popf
        LeaveProc
        RETP
srdie1: Panic   <'SELReinitDPL:Bad descriptor'>
srdie2: Panic   <'SELReinitDPL:Set descripter info failed'>

EndProc SELReinitDPL

INITCODE ends

        end
