;       SCCSID = @(#)ldrinita.asm       6.4 92/03/17
page    ,132
title   ldrinita
name    ldrinita

SCCSID  equ     "@(#)ldrinita.asm       6.4 92/03/17"

;***    Loader init routines
;
;       This file contains init routines for the loader

.xlist
include dosseg.inc
include basemaca.inc
include fnomem.inc
include osmaca.inc
include osconst.inc
include argframe.inc
include error.inc
include mi.inc
include kmode.inc
include sysentry.inc
include vmowner.inc
include protmode.inc
include seldesc.inc
include pgexport.inc
include vmexport.inc
include selxport.inc
include sysinvar.inc
include virtsym.inc
include inicfg.inc
include ldrvars.inc
include ldrxport.inc
include doshlp.inc
.list

        ModName  , FNO_LDRINITA_ASM
        CpuMode 386

        extrn           _LDRClearSem:near
        extrn           _LDREnableCVK:near
        extrn           _LDRInit:near
        extrn           _PGFree:near
        extrn           _PGSetType:near
        extrn           SELSegRealloc:near
        extrn           _VMAllocKHB:near
        extrn           _VMAllocMem:near
        extrn           _VMDonateKHB:near
        extrn           _VMGetHandle:near
        extrn           _VMMapAlias:near
        extrn           _VMSetMem:near

        EXTRN16         UCase
        EXTRN16         DescInvalidate

        ReferFlat       _DosModMte,BYTE
        ReferFlat       _DosModMteSwappable,BYTE
        ReferFlat       _cbKrnlInstance,dword
        ReferFlat       _pKrnlInstance,dword
if 0
        ReferFlat       _pchIOPLlist,dword
        ReferFlat       _pchProt16List,dword
        ReferFlat       _IOPLEnabled,byte
        ReferFlat       _VMProtectedMem,dword
endif
        ReferFlat       _mte_h,dword
        ReferFlat       _global_h,dword
        ReferFlat       _global_l,dword
        ReferFlat       _cbKrnlInstSwap,dword
        ReferFlat       _pKrnlInstSwap,dword
        ReferFlat       dcm_entrytblstart,dword
        ReferFlat       dcm_length,word
        ReferFlat       dcm_ote_start,dword
        ReferFlat       vdd_entrytblstart,dword
        ReferFlat       vdd_length,word
        ReferFlat       ABIOS_present,byte
        ReferFlat       _BasicHWConfig,WORD
        ReferFlat       _LDRLibPath,DWORD
        ReferFlat       _LDRLibPathLen,DWORD
        ReferFlat       _LDRLieList,DWORD

;       Segment start/end labels:

        ReferFlat       _StartMVDMINSTDATA,byte
        ReferFlat       _StartSWAPINSTDATA,byte
        ReferFlat       _EndDOSMTEENTRY,byte
        ReferFlat       _EndVDDMTEENTRY,byte
        ReferFlat       _StartHIGH32SWAP_TEXT,byte
        ReferFlat       _StartCINIT_TEXT,byte
        ReferFlat       _StartENDHIGH32CODE,byte
        ReferFlat       _StartHIGH4SWAPCODE,byte
        ReferFlat       _StartENDHIGH4CODE,byte
        ReferFlat       _EndLOGSWAPCODE,byte
        ReferFlat       _StartLOGLOCKCODE,byte
        ReferFlat       _EndLOGLOCKCODE,byte
        ReferFlat       _StartRASLOCKCODE,byte
        ReferFlat       _EndRASLOCKCODE,byte
        ReferFlat       _StartABIOSCODE,byte
        ReferFlat       _EndABIOSCODE,byte
        ReferFlat       _EndCINIT_DATA,byte
        ReferFlat       _StartCINIT_DATA,byte
        ReferFlat       _EndCSWAP_DATA,byte
        ReferFlat       _StartINITCODE,byte

        ReferGDT        GDT_PTDA,byte

        ReferInitGlobal Config_Sys_Data,word

        ReferGlobal     DHF,dword

if RAS
        ReferGlobal     ras_stda_addr,word
endif ;RAS

ifdef FSSTRICT
        ReferGlobal     fFSSwapStarted,byte
endif

_DATA segment
        public  RsrcHead
RsrcHead        dd      0
_DATA ends


INITBSS segment

        public  FSHelperModHdl
FSHelperModHdl  dw      0


INITBSS ends


        FARCODE HIGH32
_TEXT segment



;***EP  ldrGetObjOff - get kernel object number and offset
;
;       LDRGetObjOff attempts to translate a selector:offset into a kernel
;       MTE object number and offset.  This procedure can only be called
;       for kernel objects.
;
;       ENTRY   (AX)  = selector
;               (EBX) = offset
;       EXIT    Carry Flag clear on success
;                   (AX)  = object number
;                   (EBX) = offset
;               Carry Flag set on failure
;                   Not a valid kernel MTE address
;       USES    EAX, EBX, ECX, EDX, EDI, Flags

Procedure ldrGetObjOff,far32
    assume ds:FLAT, es:FLAT, fs:nothing, gs:nothing, ss:nothing

;       Loop through the OTEs, looking for a matching object.

        mov     ecx,[_DosModMteSwappable].smte_objcnt   ; traverse each object
        mov     edx,offset FLAT:dcm_ote_start
lgo10:  cmp     [edx].ote_selector,ax
        jnz     short lgo40
        test    [edx].ote_flags,OBJBIGDEF
        jz      short lgo20

        mov     edi,ebx                         ; (edi) = offset
        sub     edi,[edx].ote_base
        jb      short lgo40                     ; jmp if wrong object
        cmp     edi,[edx].ote_size
        jae     short lgo40                     ; jmp if wrong object
        mov     ebx,edi                         ; (ebx)=object relative offset

;       Found the object; compute the object number.  The offset is ok.

lgo20:  mov     eax,[_DosModMteSwappable].smte_objcnt
        sub     eax,ecx
        inc     eax
        clc
        jmp     short lgox

lgo40:  add     edx,size ldrote_s
        loop    lgo10                           ; keep looking
        stc                                     ; error if no match
lgox:   RETC
EndProc ldrGetObjOff


_TEXT ends


        FARCODE HIGH32
CINIT_TEXT segment

if 0

;***LP  LoadInit - initializes data for the loader
;
;       ENTRY   NONE
;
;       EXIT    IOPL list and Protect list initailised
;
;       USES    ALL except BP

Procedure LoadInit,far32
        ASSUME DS:FLAT, ES:FLAT, SS:NOTHING

        cmp     _IOPLEnabled,0          ; Is IOPL Enabled ?
        jne     short li1               ; Yes, don't bother making IOPL list
        CCALL32 Make_IOPL_list          ; Construct list of IOPL modules
li1:   ;cmp     _VMProtectedMem,0       ; Is Protection enabled ?
       ;je      short li2               ; No, don't bother making PROT16 list
       ;CCALL32 Make_Protect_list       ; Construct list of PROT16 modules.
li2:    RETC
EndProc LoadInit

endif


;***LP  PreLoadInit - initializes data for the loader
;
;       ENTRY   NONE
;
;       EXIT    loader data structures initialized
;
;       USES    EAX, EBX, ECX, EDX, ESI, EDI, FS, GS, Flags

Procedure PreLoadInit,far32
    assume ds:FLAT, es:FLAT, fs:nothing, gs:nothing, ss:nothing
        CCALL32 InitMTEs                ; initialize kernel MTEs
        CCALL32 _LDRInit
        RETC
EndProc PreLoadInit



;***EP  LDRLibPathAlloc - Allocate swappable buffer to store libpath in.
;
;       ENTRY   ECX                     - length of buffer to allocate
;
;       EXIT    EAX                     - pointer to buffer
;
;       USES

        assume  ds:nothing,es:nothing,ss:nothing
Procedure LDRLibPathAlloc,far32
LocalVar paddr,DWORD
        EnterProc

        SaveReg <ds,es,si,di>

        FlatContext <ds,es>,eax         ; set up for C calls

        mov     _LDRLibPathLen,ecx      ; save length of libpath

        SSToDS  eax,paddr

;       Allocate the memory
;       rc = _VMAllocKHB(heap hdl, size, flags, selflags, hobowner, &address)

        CCALL32 _VMAllocKHB,<VM_HKH_PUB_SWAPRW, ecx, LDRLIBPATHOWNER, NA, eax>

        or      eax,eax
        jz      short llpa

        Panic <'LDRLibPathAlloc'>

;       Return to caller

llpa:   mov     eax,paddr

        mov     _LDRLibPath,eax

        RestoreReg <di,si,es,ds>

        LeaveProc
        ret

EndProc LDRLibPathAlloc



;***EP  ldrLieListAlloc - Allocate swappable buffer to store lielist in,
;                         and copy from input syi buffer, processing it
;                         as we go.
;
;       ENTRY   ECX                     - length of buffer to allocate
;                AX                     - selector of file in memory, offset 0
;
;       EXIT     AX                     - selector unchanged, to be freed
;
;       USES

        assume  ds:nothing,es:nothing,ss:nothing
Procedure ldrLieListAlloc,far32
LocalVar paddr,DWORD
        EnterProc

        SaveReg <ds,es,esi,edi,ax>
        push    ax
        push    ecx

        FlatContext <ds,es>,eax

        ; Allocate memory from swappable heap to hold lie list
        SSToDS  eax,paddr
        CCALL32 _VMAllocKHB,<VM_HKH_PUB_SWAPRW, ecx, LDRLIELISTOWNER, NA, eax>
        or      eax,eax
        jz      short llla

        Panic <'ldrLieListAlloc'>   ; can not return error to caller

llla:   mov     edi,paddr
        mov     _LDRLieList,edi
        mov     esi,edi          ; temp save

        ; zero the storage - heap is not zero filled
        pop     ecx
        push    ecx
        rep     stosb

        ; Copy the date from the file buffer to the swappable heap, taking
        ; out "20=" and "cr,lf", and separating with ";" instead.

        pop     ecx        ; length of input string (will be <64K)
        dec     ecx

        cld

        mov     edi,esi    ; es:edi = target - ldrlielist

        pop     ax         ; ds:si = source - segment allocated by syiOS2VER
        mov     ds,ax
        xor     esi,esi

NewLine:
        cmp     cx,si       ; check not off end of input buffer
        jc      short NoMore

;161596        lodsb
;161596        cmp     al,'2'
;161596        jne     short BadLine
;161596        cmp     cx,si       ; check not off end of input buffer
;161596        jc      short NoMore

;161596        lodsb
;161596        cmp     al,'0'
;161596        jne     short BadLine
;161596        cmp     cx,si       ; check not off end of input buffer
;161596        jc      short NoMore

	lodsb                  ;161596
	cmp	al,'0'         ;check for version format (0-9)
	jb	short BadLine  ;161596
	cmp	al,'9'         ;161596
	ja	short BadLine  ;161596
        cmp     cx,si          ;check not off end of input buffer
        jc      short NoMore   ;161596
	stosb		       ;161596


	lodsb                  ;161596
	cmp	al,'0'         ;check for version format (0-9)
	jb	short BadLine  ;161596
	cmp	al,'9'         ;161596
	ja	short BadLine  ;161596
        cmp     cx,si          ;check not off end of input buffer
        jc      short NoMore   ;161596
	stosb		       ;161596
	

        lodsb
        cmp     al,'='
        jne     short BadLine
        cmp     cx,si       ; check not off end of input buffer
        jc      short NoMore
	stosb		       ;161596

NxtChar:
        lodsb
        cmp     al,13       ; cr
        je      short DoneLine
        cmp     al,10       ; lf
        je      short DoneLine
        cmp     al,26       ; ctrlz / eof
        je      short DoneLine

        cmp     al,'a'                  ; upper case
        jb      short DoStore
        cmp     al,'z'
        ja      short DoStore
        sub     al,' '

DoStore:
        stosb

        cmp     cx,si       ; check not off end of input buffer
        jc      short NoMore
        jmp     short NxtChar

DoneLine:
        mov     al,';'
        stosb

BadLine:
        cmp     cx,si       ; check not off end of input buffer
        jc      short NoMore

        lodsb
        cmp     al,10
        jne     short BadLine
        jmp     short NewLine

NoMore:

        RestoreReg <ax,edi,esi,es,ds>

        LeaveProc
        ret

EndProc ldrLieListAlloc





;***EP  _LDRInitOTE - initialize DosCalls OTE from arenainfo
;
;       _LDRInitOTE fills in the DosCalls OTE flags, selector, base and size
;       fields form the information provided in the arenainfo array.  It also
;       verifies that each 16 bit kernel code segment has a RETF and RETFD at
;       RETFOFFSET and RETFDOFFSET.
;
;       NOTE: The Object flags OBJSHARED and OBJRESIDENT are used in this
;             procedure to mean SWAPINSTDATA and MVDMINSTDATA.
;
;       ENTRY   lio_parenainfo - pointer to arenainfo array
;
;       EXIT    NONE
;
;       USES    EAX, ECX, EDX, Flags

MI_RETFD equ ((MI_LONG_RET shl 8) or MI_OPERANDSIZE)

Procedure _LDRInitOTE
    assume ds:FLAT, es:FLAT, fs:nothing, gs:nothing, ss:nothing
    ArgVar lio_parenainfo,dword
        EnterProc

        mov     eax,[lio_parenainfo]
        sub     eax,size arena_s
lio20:  add     eax,size arena_s
        mov     dx,[eax].a_aflags
        test    dx,AF_PEND
        jnz     liox
        mov     cx,dx
        and     cx,AF_SRCMASK
        cmp     cx,AF_OS2KRNL
        jnz     short lio20

;       Found a kernel memory object; point to the object table entry

        and     dx,AF_OBJMASK           ; (dx) = object table index
        shr     dx,AF_OBJSHIFT
        movzx   edx,dx
        cmp     edx,[_DosModMteSwappable.smte_objcnt]
        ja      lioie2
        dec     edx
        shl     edx,3
        lea     edx,[2*edx+edx+offset FLAT:dcm_ote_start]
        .errnz (size ldrote_s - ((2 + 1) * (1 shl 3)))

;       (EAX) = pointer to kernel arenainfo entry
;       (EDX) = pointer to matching DOSCALLS mte object table entry

        mov     ecx,[eax].a_size        ; set the size field
        mov     [edx].ote_size,ecx

        mov     ecx,[eax].a_oflags      ; set the flags field
        mov     [edx].ote_flags,ecx

        test    ecx,OBJSHARED           ; set the base field
        jnz     short lio45
        test    ecx,OBJRESIDENT
        jnz     short lio40
        mov     ecx,offset FLAT:_StartSWAPINSTDATA
        jmp     short lio50

lio40:  mov     ecx,offset FLAT:_StartMVDMINSTDATA
        jmp     short lio50             ; arena base is wrong for MVDM instance

lio45:  mov     ecx,[eax].a_laddr
lio50:  mov     [edx].ote_base,ecx

;       Verify that all 16 bit code segments have the proper thunk support

        test    [edx].ote_flags,OBJEXEC
        jz      short lio60
        test    [edx].ote_flags,OBJBIGDEF
        jnz     short lio60
        cmp     byte ptr [ecx+RETFOFFSET],MI_LONG_RET
        jnz     short lioie1
        cmp     word ptr [ecx+RETFDOFFSET],MI_RETFD
        jz      short lio60
lioie1: Panic   <'LDRInitOTE: missing thunk'>

;       (EAX) = pointer to kernel arenainfo entry
;       (EDX) = pointer to matching DOSCALLS mte object table entry

lio60:  test    [edx].ote_flags,OBJSHARED
        jnz     short lio70
        test    [edx].ote_flags,OBJRESIDENT
        jnz      short lio65
        cmp     [_cbKrnlInstSwap],0     ; set MVDM instance variables once
        jnz     short lio70
        mov     ecx,[eax].a_laddr
        mov     [_pKrnlInstSwap],ecx
        mov     ecx,[eax].a_size
        mov     [_cbKrnlInstSwap],ecx
        jmp     short lio70

lio65:  cmp     [_cbKrnlInstance],0     ; set MVDM instance variables once
        jnz     short lio70
        mov     ecx,[eax].a_laddr
        mov     [_pKrnlInstance],ecx
        mov     ecx,[eax].a_size
        mov     [_cbKrnlInstance],ecx

lio70:  mov     cx,GSEL GDT_PTDA                ; set the selector field
        cmp     [eax].a_sel,TASKAREA
        jz      short lio80
        mov     cx,[eax].a_sel
        test    [edx].ote_flags,OBJBIGDEF
        jz      short lio80
        mov     cx,seg FLAT:DOSHIGH32CODE       ; use32 code selector
        test    [edx].ote_flags,OBJEXEC
        jnz     short lio80
        mov     cx,seg FLAT:DGROUP              ; use32 data selector
lio80:  mov     [edx].ote_selector,cx           ; store selector
        jmp     lio20

lioie2: Panic   <'LDRInitOTE: 2 many kernel objs'>
liox:   mov     eax,offset FLAT:_DosModMte
        mov     [_mte_h],eax                    ; initialize mte list
        mov     [_global_h],eax                 ; and global list
        mov     [_global_l],eax
        CCALL32 _LDREnableCVK
        LeaveProc
        RETC
EndProc _LDRInitOTE



;***LP  InitMTEs - initializes kernel MTEs.
;
;       InitMTEs sets up for resident name table dynamic link entries to the
;       kernel MTEs (DOSCALLS, VDD, FSH) by initializing header fields that
;       can't be computed at assembly or link time, and sets up the handles
;       for each OTE in DOSCALLS.
;
;       ENTRY   NONE
;
;       EXIT    kernel MTEs initialized
;       USES    EAX, ECX, EDX, FS, GS, Flags

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

;       compute size of entry tables from:
;           offset xxx_entrytblend - (offset xxx_entrytblstart + 2)

        mov     eax,offset FLAT:_EndDOSMTEENTRY
        sub     eax,offset FLAT:dcm_entrytblstart+2     ; +2 for the length
        mov     [dcm_length],ax                 ; place size of table in table

        mov     eax,offset FLAT:_EndVDDMTEENTRY
        sub     eax,offset FLAT:vdd_entrytblstart+2     ; +2 for the length
        mov     [vdd_length],ax                 ; place size of table in table

;       Now loop through the OTEs, and store the handle for each object.

        mov     ecx,[_DosModMteSwappable].smte_objcnt   ; traverse each object
        mov     edx,offset FLAT:dcm_ote_start

imt10:  push    edx                             ; save OTE pointer
        push    ecx                             ; save loop count

        lea     eax,[edx].ote_handle

;       rc = VMGetHandle(p, hobptda, phob)

        CCALL32 _VMGetHandle, <[edx].ote_base, NA, eax> ; ignore errors

        pop     ecx                             ; restore loop count
        pop     edx                             ; restore OTE pointer
        add     edx,size ldrote_s
        loop    imt10                           ; and do 'em all...

        RETC
EndProc InitMTEs



;**     FreeDynLink - free DOS API dynamic link entry
;
;       ENTRY   (AX) = call gate selector
;
;       EXIT    CARRY Clear
;                 specified API deleted from DOSCALLS resident name table
;                 GDT descriptor invalidated
;               CARRY Set
;                 (AX) = error code
;       USES    AX, BX, CX, DX, SI, DI, Flags

Procedure FreeDynLink,far32
    assume ds:FLAT, es:FLAT, fs:nothing, gs:nothing, ss:nothing
;       PMONLY

        or      ax,RPL_RING3
        push    ax
        LDRGetSem                       ; get loader semaphore
        pop     bx                      ; (BX) = callgate selector
        or      eax,eax                 ; error?
        stc                             ; (assume error)
        jnz     short fdlx              ; yes - return it.

        xor     cx,cx                   ; indicate deletion

;       EditDynLink(bx=oldsel, cx=0 to delete entry from DOSCALLS)

        CCALL32 EditDynLink             ; ignore error!!

;       (BX) = callgate selector [preserved by EditDynLink]

        mov     ax,bx                   ; (AX) = selector to invalidate
        push    ds
        push    es
        CALL16 DescInvalidate
        pop     es
        pop     ds

        CCALL32 _LDRClearSem            ; release loader semaphore
        clc                             ; indicate Success

fdlx:   RETC
EndProc FreeDynLink




;***LP  EditDynLink - Edit DOS API in DOSCALLS mte.
;
;       ENTRY   (BX) = old call gate selector to match
;               (CX) = new call gate selector (or 0 to delete entry)
;       EXIT    Carry Flag clear on success
;                   DOS API callgate selector changed to new value.
;                   If new selector is 0, the API call is deleted from
;                   DOSCALLS mte.
;               Carry Flag set on failure
;                   Selector not found in entry table; could be valid
;                   dynlink with no entry table entry.
;       USES    AX, CX, DX, SI, DI, Flags

Procedure EditDynLink
    assume ds:FLAT, es:FLAT, fs:nothing, gs:nothing, ss:nothing
;       PMONLY

        push    bx
        and     bl,RPL_CLR              ; Clear RPLs
        and     cl,RPL_CLR

        mov     edi,offset FLAT:_DosModMteSwappable

;       search the entry table for the call gate selector.

edl20:  mov     esi,[edi].smte_enttab   ; (DS:SI) -> entry table
        inc     esi                     ; move over length of table
        inc     esi
        xor     dx,dx
edl40:  add     dx,(SIZE dce_s)

; Note: Need length of entry table in usecnt

        push    ebx                     ; save gate
        mov     ebx,[edi].smte_enttab
        cmp     [ebx],dx
        pop     ebx                     ; restore gate
        jb      short edlx              ; fail the request, not found

        lodsb                           ; get FLAG byte
        .errnz  (dce_flags)
        cmp     al,IS_GATE              ; is this a call gate entry?
        lodsw                           ; (AX) = call gate selector
        .errnz  (dce_gate - 1)
        jne     edl40                   ; not a call gate, try next entry
        push    ax                      ; Save selector
        and     al,RPL_CLR              ; Clear RPL bits
        cmp     ax,bx                   ; is it the selector we're looking for?
        pop     ax                      ; Restore selector
        jne     edl40                   ; no, check next selector in entry table

        dec     esi
        dec     esi                     ; (DS:SI) -> callgate selector
        and     al,RPL_MASK             ; Mask off all but RPL
        or      cl,al                   ; Set RPL in new selector
        mov     [esi],cx                ; set the new callgate selector word
        test    cx,RPL_CLR
        jnz     short edlx              ; jmp if all done

        dec     esi
        mov     [esi].dce_flags,IS_DOSCALL1 ; mark as invalid call gate

;       (DX) = (ordinal# * (SIZE dce_s))

        mov     cx,(SIZE dce_s)
        mov     ax,dx                   ; (AX) = ordinal#
        xor     dx,dx                   ; (DX) = 0
        div     cx                      ; (DX:AX) / (CX)
        xchg    ax,dx                   ; (DX) = ordinal#, (AX) = 0
        mov     esi,[edi].smte_restab   ; (DS:SI) -> resident name table

;       skip over module length,name & unused ordinal field

        xor     eax,eax                 ; clear high part for add
        lodsb                           ; (AL) = length of module name
        add     esi,eax                 ; skip over module name entry
        inc     esi
        inc     esi                     ; skip unused ordinal field

;       We stop searching for the name if we find a 0 length name.
;       This can happen when we get to the end of the table, or when
;       dynlinking by name has been disabled.  We cannot make the
;       distinction, so we always return success.  If we got this far,
;       we've already found (and zeroed) the call gate selector,
;       so this should be fine.

edl60:  xor     ecx,ecx                 ; (ECX) = 0, and clears carry for exit
        mov     cl,[esi]                ; (CL) = length of proc name
        jcxz    edlx                    ; error, invalid ordinal

        inc     esi                     ; skip over length byte
        push    esi                     ; we need it if ordinal# matches
        add     esi,ecx                 ; skip over the name
        lodsw                           ; (AX) = ordinal#
        cmp     dx,ax                   ; is this the matching entry?
        pop     esi                     ; (DS:SI) -> name
        je      short edl80

        add     esi,ecx                 ; skip over name
        inc     esi                     ; skip over...
        inc     esi                     ; ordinal# word...

        jmp     short edl60             ; try next entry in the name table

;       (BX) = callgate selector
;       (CX) = length of name string
;       (DS:SI) -> name string in resident nametable with matching selector
;
;       overwrite the apiname with a string of '*'
;       zero out ordinal# [may be overkill, but take no chances]

edl80:  mov     edi,esi
        mov     al,'*'
        rep     stosb                   ; replace API name with a string of '*'
        xor     ax,ax
        mov     word ptr [edi],ax       ; set ordinal = 0

edlx:   pop     bx
        RETC
EndProc EditDynLink


if 0


;***LP  Make_IOPL_list
;
;       Construct list of modules that can have I/O privilege
;
;       This routines take the module names that SYSINIT has parsed from
;       CONFIG.SYS and constructs a list containing these module names and
;       the module names from IOPL_Table
;
;       Go through IOPL packets sizing module names and calculating
;       the list size we will require. Allocate memory and then
;       rewalk lists copying module names to memory.
;
;       ENTRY   NONE
;
;       EXIT    NONE
;
;       USES    ALL except BP

VMFL    EQU     VMAC_ARENASYS or PG_R or PG_W or PG_ZEROFILL or PG_SWAPPABLE

        ASSUME DS:NOTHING,ES:NOTHING,SS:TASKAREA
Procedure Make_IOPL_list,near
LocalVar vmac,<SIZE vmac_s>
        EnterProc

;       Get segment of IOPL strings parsed from CONFIG.SYS by SYSINIT

        mov     ax,INITDATA
        mov     ds,ax
        ASSUME DS:INITDATA
        mov     ax,Config_Sys_Data      ; Get selector of IOPL strings segment
        mov     ds,ax
        mov     es,ax

        ASSUME DS:NOTHING, ES:NOTHING

;       We will require a list at least large enough to hold the static
;       names so initialise required list size to that size.

        xor     edx, edx                        ; Initial IOPL table size

;       Get pointer to first packet if there is one.

        movzx   ebx,ds:[0].CFG_IOPL.CFG_Head ; First packet for IOPL

        or      ebx,ebx                 ; Is there a packet?
        jz      short mil3              ; No, size OK, go allocate list.

;       Size queue items within packet.
;       Get pointer to first item in queue in this packet

mil1:   movzx   edi,[ebx].CFG_IOPL_Head ; Head of queue for 1st packet

;       Go through looking for terminating zero. When we find zero, calculate
;       length of name.

mil2:   push    edi                     ; save address of link field
        add     edi,CFG_IOPL_String     ; get pointer to name string
        mov     ecx,-1                  ; using cx as counter
        xor     al,al                   ; Looking for terminating zero
        repne   scasb

;       Here when we have found the terminating zero. Calculate size of name +
;       length byte, and add to required size.

        not     ecx
        add     edx,ecx                 ; adjust required list size

;       Is there a next item in the queue?

        pop     edi                     ; restore link field address
        movzx   edi,[edi].CFG_IOPL_Next ; get address of next item
        or      edi,edi                 ; Next item exists?
        jnz     mil2                    ; Yes, go size next item

;       Here finished sizing queue, see if there is another in next packet.

        movzx   ebx,[ebx].CFG_Next      ; Get next packet address
        or      ebx,ebx                 ; Is there one?
        jnz     mil1                    ; Yes, go size it.

;       Here we know the size of table we require, allocate it
;       DX = table size

mil3:   push    ds                      ; save Config_Sys_Data selector
        inc     edx                     ; allow for terminating zero

        FlatContext <ds,es>,ax
        CCALL32 DHF.DosHlpGetIOPLList   ; [ds:esi] = addr of default iopl table
        push    ds                      ; save it
        push    esi                     ; save it
        push    ecx                     ; [ecx] = size of iopl tabl
        add     edx, ecx
        FlatContext <ds>
        SSToDS  eax,vmac

;       rc = VMAllocMem(cb,cbcommit,fl,hptda,hobowner,hobmte,fselmgr,blkno,pac)

        xor     ecx,ecx
        CCALL32 _VMAllocMem, <edx,edx,VMFL,ecx,IOPLLISTOWNER,ecx,ecx,ecx,eax>
        or      eax,eax
        jz      short mil4              ; If allocated OK, skip internal error

        Panic   <'Make_IOPL_list'>

;       Save address returned in vmac for later (so loader can access when
;       checking for module having I/O privilege).

mil4:   mov     edi,vmac.ac_va
        mov     _pchIOPLlist,edi        ; save IOPL list pointer

;       Copy in static names

        pop     ecx                     ; default IOPL table size
        pop     esi
        pop     ds                      ; [ds:esi] = addr of default iopl table
        rep     movsb                   ; Copy in static names
        mov     esi,edi                 ; Keep updated destination in esi

;       Now walk the lists again, copying in the module names.

        pop     ds                      ; Restore Config_Sys_Data selector
        ASSUME DS:NOTHING               ; (Config_Sys_Data)

;       Get pointer to first packet (if there is one)

        movzx   ebx,ds:[0].CFG_IOPL.CFG_Head ; First packet for IOPL
        or      ebx,ebx                 ; Is there a packet?
        jz      short mil7              ; If not, done, skip to end

;       Get pointer to first item in queue in this packet

mil5:   movzx   edi,[ebx].CFG_IOPL_Head ; Head of queue for 1st packet

;       Go through looking for terminating zero. When we find zero, calculate
;       length of name.

mil6:   mov     edx,edi                 ; save address of link field
        add     edi,CFG_IOPL_String     ; get pointer to name string
        push    es
        push    ds
        pop     es                      ; Copy Config_Sys_Data selector to es
        ASSUME ES:NOTHING               ; (Config_Sys_Data)
        mov     ecx,-1                  ; using cx as count
        xor     al,al                   ; Looking for terminating zero
        repne   scasb

;       Here when we have found the terminating zero. Calculate size of name
;       + terminating zero.

        not     ecx

;       Here we copy the module name and its length byte into the list.

        sub     edi,ecx                 ; back up to start of name
        dec     ecx                     ; adjust to name length (without 0)
        xchg    esi,edi                 ; si -> name, di -> fill point
        pop     es
        ASSUME ES:FLAT
        mov     es:[edi],cl             ; put length in list
        inc     edi                     ; go forward to name field
        rep     movsb                   ; copy name in
        xchg    esi,edi                 ; keep updated destination in si

;       Finished dealing with this module name. Check for next item in queue.

        mov     edi,edx                 ; restore link field address
        movzx   edi,[edi].CFG_IOPL_Next ; get address of next item
        or      edi,edi                 ; Next item exists?
        jnz     mil6                    ; Yes, go check next module name

;       Here finished parsing queue in this packet, see if there is another.

        movzx   ebx,[ebx.CFG_Next]      ; Get next packet address
        or      ebx,ebx                 ; Is there one?
        jnz     mil5                    ; Yes, go parse it.

;       Here finished, return. (no errors possible).

mil7:   mov     ax,es
        mov     ds,ax                   ; Restore DS Flat
        LeaveProc
        RETC

EndProc Make_IOPL_list



;***LP  Make_Protect_list
;
;       Construct list of modules that can have protecttion enabled.
;
;       This routines take the module names that SYSINIT has parsed from
;       CONFIG.SYS and constructs a list containing these module names.
;
;
;       ENTRY   NONE
;
;       EXIT    NONE
;
;       USES    ALL except BP

        ASSUME DS:NOTHING,ES:NOTHING,SS:TASKAREA
Procedure Make_Protect_list,near
LocalVar vmac,<SIZE vmac_s>
        EnterProc

;       Get segment of PROTECT16 strings parsed from CONFIG.SYS by SYSINIT

        mov     ax,INITDATA
        mov     ds,ax
        ASSUME DS:INITDATA
        mov     ax,Config_Sys_Data      ; Get selector of prot16 strings segment
        mov     ds,ax
        mov     es,ax

        xor     edx,edx                 ; Initialise required list size

;       Get pointer to first packet if there is one.

        movzx   ebx,ds:[0].CFG_Protect16.CFG_Head ; First packet for PROT

        or      ebx,ebx                 ; Is there a packet?
        jz      short mpl3              ; No, size OK, go allocate list

;       Size queue items within packet.
;       Get pointer to first item in queue in this packet

mpl1:   movzx   edi,[ebx].CFG_Protect16_Head    ; Head of queue for 1st packet

;       Go through looking for terminating zero. When we find zero, calculate
;       length of name.

mpl2:   push    edi                     ; save address of link field
        add     edi,CFG_Protect16_String; step over link field
        mov     ecx,-1                  ; using cx as counter
        xor     al,al                   ; Looking for terminating zero
        repne   scasb

;       Here when we have found the terminating zero. Calculate size of name +
;       length byte, and add to required size.

        not     ecx
        add     edx,ecx                 ; adjust required segment size

;       Is there a next item in the queue?

        pop     edi                     ; restore link field address
        movzx   edi,[edi].CFG_Protect16_Next; get address of next item
        or      edi,edi                 ; Next item exists?
        jnz     mpl2                    ; Yes, go size next item

;       Here finished sizing queue, see if there is another in next packet.

        movzx   ebx,[ebx].CFG_Next      ; Get next packet address
        or      ebx,ebx                 ; Is there one?
        jnz     mpl1                    ; Yes, go size it.

;       Here we know the size of table we require, allocate it
;       DX = table size

mpl3:   push    ds                      ; save Config_Sys_Data selector
        inc     edx                     ; allow for terminating zero
        FlatContext <ds,es>,ax
        SSToDS  eax,vmac

;       rc = VMAllocMem(cb,cbcommit,fl,hptda,hobowner,hobmte,fselmgr,blkno,pac)

        xor     ecx,ecx
        CCALL32 _VMAllocMem, <edx,edx,VMFL,ecx,LDRPROT16OWNER,ecx,ecx,ecx,eax>
        or      eax,eax
        jz      short mpl4              ; If allocated OK, skip internal error

        Panic   <'Make_Protect_list'>

;       Save address returned in vmac for later (so loader can access when
;       checking for module having I/O privilege).

mpl4:   mov     esi,vmac.ac_va
        mov     _pchProt16List,esi      ; save PROT list pointer

;       Now walk the lists again, copying in the module names.

        pop     ds                      ; Restore Config_Sys_Data selector
        ASSUME DS:NOTHING               ; (Config_Sys_Data)

;       Get pointer to first packet (if there is one)

        movzx   ebx,ds:[0].CFG_Protect16.CFG_Head ; First packet for PROT
        or      ebx,ebx                 ; Is there a packet?
        jz      short mpl7              ; If not, done, skip to end

;       Get pointer to first item in queue in this packet

mpl5:   movzx   edi,[ebx].CFG_Protect16_Head; Head of queue for 1st packet

;       Go through looking for terminating zero. When we find zero, calculate
;       length of name.

mpl6:   mov     edx,edi                 ; save address of link field
        add     edi,size CFG_Protect16_Next; step over link field
        push    es
        push    ds
        pop     es                      ; Copy Config_Sys_Data selector to es
        ASSUME ES:NOTHING               ; (Config_Sys_Data)
        mov     ecx,-1                  ; using ecx as count
        xor     al,al                   ; Looking for terminating zero
        repne   scasb

;       Here when we have found the terminating zero. Calculate size of name
;       + terminating zero.

        not     ecx

;       Here we copy the module name and its length byte into the list segment.

        sub     edi,ecx                 ; back up to start of name
        dec     ecx                     ; adjust to name length (without 0)
        xchg    esi,edi                 ; si -> name, di -> fill point
        pop     es
        ASSUME ES:FLAT
        mov     es:[edi],cl             ; put length in list segment
        inc     edi                     ; go forward to name field
mpl6a:  lodsb                           ; get character from (DS:SI)
        CALL16  UCase
        stosb                           ; move character to (ES:DI)
        loop    mpl6a
        xchg    esi,edi                 ; keep updated destination in si

;       Finished dealing with this module name. Check for next item in queue.

        mov     edi,edx                 ; restore link field address
        movzx   edi,[edi].CFG_Protect16_Next; get address of next item
        or      edi,edi                 ; Next item exists?
        jnz     mpl6                    ; Yes, go check next module name

;       Here finished parsing queue in this packet, see if there is another.

        movzx   ebx,[ebx].CFG_Next      ; Get next packet address
        or      ebx,ebx                 ; Is there one?
        jnz     mpl5                    ; Yes, go parse it.

;       Here finished, return. (no errors possible). Clear out Config_sys_data
;       from ds since segment is temporary. Note bx contains zero.

mpl7:   mov     ds,bx                   ; and Config_sys_data
        LeaveProc
        RETC

EndProc Make_Protect_list

endif


;***EP  _LDRFreeInit - free init-only kernel objects
;
;       _LDRFreeInit frees init-only kernel objects,
;       and makes swappable kernel objects swappable.
;
;       ENTRY   NONE
;       EXIT    NONE
;       USES    EAX, ECX, EDX, Flags

Procedure _LDRFreeInit,far32
    assume ds:FLAT, es:FLAT, fs:nothing, gs:nothing, ss:nothing

        push    ebx

;       Free all of the pages in DOSINITR3CODE
;       leaving the GDT selector and VM data structures intact.

        mov     ax,DOSINITR3CODE
        CCALL32 _ldrFreeSegInit

;       Free all of the pages in DOSINITDATA,
;       leaving the GDT selector and VM data structures intact.

        mov     ax,DOSINITDATA
        CCALL32 _ldrFreeSegInit

;       Make the 32 bit swappable data swappable, being careful to not
;       accidentally make the resident data following init data swappable.

        mov     eax,offset FLAT:_StartCINIT_DATA
        shr     eax,PAGESHIFT           ; (eax) = start linear page

;       Be careful not to make anything past init data swappable

        mov     ebx,offset FLAT:_EndCSWAP_DATA
        shr     ebx,PAGESHIFT           ; (ebx) = end linear page
        sub     ebx,eax                 ; (ebx) = page count
        jz      short lfi10

;       err = PGSetType(page, pptda, npages, fl)

        CCALL32 _PGSetType <eax, NULL, ebx, PG_SWAPPABLE>

ifdef LDRSTRICT
        or      eax,eax
        jz      short lfi10
        jmp     lfrie
endif

;       Make the 32 bit swappable code swappable, skipping the first page
;       fragment, because resident code resides on the first part of the
;       first page of swappable code.

lfi10:  mov     eax,offset FLAT:_StartHIGH32SWAP_TEXT
        add     eax,PAGESIZE - 1        ; skip first page fragment
        shr     eax,PAGESHIFT           ; (eax) = start linear page

;       The last page will include some init code which should be freed (but
;       since we're making it swappable anyway, who cares).

        mov     ebx,offset FLAT:_StartCINIT_TEXT
        add     ebx,PAGESIZE - 1
        shr     ebx,PAGESHIFT           ; (ebx) = end linear page
        sub     ebx,eax                 ; (ebx) = page count
        jz      short lfi20

;       err = PGSetType(page, pptda, npages, fl)

        CCALL32 _PGSetType <eax, NULL, ebx, PG_SWAPPABLE>

ifdef LDRSTRICT
        or      eax,eax
        jz      short lfi20
        jmp     lfrie
endif

lfi20:

ifdef FSSTRICT
        mov     [fFSSwapStarted],-1
endif

;       Make the 16 bit HIGH4 swappable code swappable.  Also, if
;       tracing and logging weren't selected in config.sys, free
;       the corresponding code segments.

        mov     eax,offset FLAT:_StartHIGH4SWAPCODE
        add     eax,PAGEMASK
        shr     eax,PAGESHIFT
        mov     ebx,offset FLAT:_EndLOGSWAPCODE
        shr     ebx,PAGESHIFT
        sub     ebx,eax
        jz      short lfi25             ; jump if range is empty
        CCALL32 _PGSetType <eax, NULL, ebx, PG_SWAPPABLE>

ifdef LDRSTRICT
        or      eax,eax
        jz      short lfi25
        jmp     lfrie                   ; panic if call failed
endif

;       Start by assuming that we're going to free both RAS and LOG code

lfi25:  mov     eax,offset FLAT:_StartLOGLOCKCODE ; start of region to free
        mov     ebx,offset FLAT:_EndRASLOCKCODE   ; end of region to free

if RAS
;       if RAS is enabled, don't free RAS code

        cmp     [ras_stda_addr],0
        jz      short lfi26
        mov     ebx,offset FLAT:_EndLOGLOCKCODE
endif ;RAS

lfi26:

;       If there's anything to free, donate it to the kernel heap

lfi27:  sub     ebx,eax
        jz      short lfi28
        CCALL32 _VMDonateKHB <eax, ebx, PG_R>

ifdef LDRSTRICT
        or      eax,eax
        jz      short lfi28
        jmp     lfrie
endif

lfi28:  mov     ebx,offset DOSCODE:_StartINITCODE  ; set up to discard INITCODE
        test    ABIOS_present, -1                  ; are we on an abios machine
        jnz     short lfi30                        ; jmp if so
        mov     ebx,offset DOSCODE:_StartABIOSCODE ; also discard the abios code
lfi30:  xor     ax,ax
        mov     cx,ax
        mov     di,DOSCODE
; hack  CCALL32 SELSegRealloc
        xor     eax,eax ; hack
ifdef LDRSTRICT
        or      eax,eax
        jz      short lfi33
        jmp     lfrie
endif

lfi33:
        mov     eax,offset FLAT:_StartINITCODE  ; set up to donate page frag
        test    ABIOS_present, -1
        jnz     short lfi35
        mov     eax, offset FLAT:_StartABIOSCODE ; also includes abios code
lfi35:  mov     ebx, offset FLAT:_EndABIOSCODE
        add     ebx, PAGESIZE - 1                ; round up to next page
        and     ebx, NOT PAGEMASK
        sub     ebx, eax                         ; get size

        push    eax       ; need this address later - leave on stack

        push    eax       ; preserve over call
        push    ebx

        CCALL32 _VMDonateKHB <eax, ebx, PG_R>

        pop     ebx
ifdef LDRSTRICT
        or      eax, eax
        jz      short lfi50
        pop     ebx
        jmp     lfrie
endif

lfi50:  pop     eax
        add     ebx,eax           ; ebx holds address of 1st page to free

        mov     eax,offset FLAT:_DosModMteSwappable    ; pointer to kernel SMTE
        mov     ecx,[eax].smte_objcnt
        mov     eax,[eax].smte_objtab

lfi51:  mov     edx,[eax].ote_base    ; find the base addr in the object table
        cmp     ebx,edx
        jc      short lfi52
        add     edx,[eax].ote_size
        cmp     edx,ebx
        jnc     short lfi53
lfi52:  add     eax,size ldrote_s
        loop    lfi51

ifdef LDRSTRICT
        jmp     lfrie
endif

lfi53:  sub     edx,ebx          ; size in bytes of area to free

        pop     ecx                 ; restore donate base address
        sub     ecx,[eax].ote_base  ; update mte size
        mov     [eax].ote_size,ecx

        add     edx,PAGEMASK
        shr     edx,PAGESHIFT       ; size to blast in pages
        shr     ebx,PAGESHIFT       ; starting page
        CCALL32  _PGFree <ebx, NULL, edx, PG_SKIPFREE>

ifdef LDRSTRICT
        or      eax,eax
        jz      short lfi54
        jmp     lfrie
endif

;       Free the 32 bit init code.  Round the base boundary up, because the
;       first page fragment doesn't need to be freed (it shares the page with
;       swappable code).  Nothing resides past init code on the last page.

lfi54:  mov     eax,offset FLAT:_StartCINIT_TEXT
        add     eax,PAGESIZE - 1        ; skip first page fragment
        mov     ebx,offset FLAT:_StartENDHIGH32CODE
        sub     ebx,eax

        jmp     lfirest

EndProc _LDRFreeInit



;***EP  _ldrFreeSegInit - free one init-only kernel segment
;
;       _ldrFreeSegInit frees the pages for one init-only kernel segment.
;
;       ENTRY   (AX) = selector of segment whose pages are to be freed
;       EXIT    NONE
;       USES    EAX, EBX, ECX, EDX, Flags

Procedure _ldrFreeSegInit,,local
        mov     ebx,offset FLAT:_DosModMteSwappable    ; pointer to kernel SMTE
        mov     ecx,[ebx].smte_objcnt
        mov     ebx,[ebx].smte_objtab

lfs00:  cmp     [ebx].ote_selector,ax    ; find the selector in the object table
        jz      short lfs10
        add     ebx,size ldrote_s
        loop    lfs00

ifdef LDRSTRICT
lfsie:  Panic <'ldrFreeSegInit'>     ; panic if selector not found
endif

lfs10:  mov     edx,[ebx].ote_base    ; base of selector to blast

ifdef LDRSTRICT
        test    edx,PAGESIZE - 1      ; check page aligned
        jnz     short lfsie
endif

        shr     edx,PAGESHIFT
        mov     ecx,[ebx].ote_size
        add     ecx,PAGEMASK
        shr     ecx,PAGESHIFT

;       err = PGFree(page, pptda, npages, flags)

        CCALL32  _PGFree <edx, NULL, ecx, PG_SKIPFREE>

ifdef LDRSTRICT
        or      eax,eax
        jnz     short lfsie
endif

        RETC
EndProc _ldrFreeSegInit



;***EP  _CheckABIOS - Allow easy ABIOS check from paging at init time

Procedure  _CheckABIOS,near
        ASSUME DS:FLAT, ES:NOTHING, SS:NOTHING

        movzx    eax,ABIOS_present
        ret

EndProc    _CheckABIOS

CINIT_TEXT ends




        FARCODE HIGH32
LDRSWAP_TEXT segment



;***EP  lfirest - rest of _LDRFreeInit
;
;       ENTRY   (EAX) = base of CINIT_TEXT to free
;               (EBX) = count of CINIT_TEXT bytes to free
;       EXIT    NONE
;       USES    See _LDRFreeInit

Procedure lfirest,far32,local
    assume ds:FLAT, es:FLAT, fs:nothing, gs:nothing, ss:nothing
Entry lfirest,near,,nocheck

;       err = VMSetMem(p, cb, hobptda, fl, blkno)

        CCALL32 _VMSetMem <eax, ebx, HOBNULL, VMS_DECOMMIT, NA>

ifdef LDRSTRICT
        or      eax,eax
        jnz     short lfrie
endif

        pop     ebx
        RETC


ifdef LDRSTRICT
lfrie:  Panic <'LDRFreeInit'>
endif


EndProc lfirest

LDRSWAP_TEXT ends

        END

