SCCSID  equ     "@(#)ldrrun.asm 6.18 92/03/25"
        PAGE    ,132
TITLE   LDRRUN - OS/2 2.x runtime-library load routines
NAME    LDRRUN

;***    OS/2 runtime-library load routines
;
;       IBM/Microsoft Confidential
;
;       Copyright (c) IBM Corporation 1987, 1991
;       Copyright (c) Microsoft Corporation 1987, 1991
;
;       All Rights Reserved
;
;   137076  12/8/95  Change Team - change clean-up of nonresident names table
;                                  to be cleaned-up during garbage collection.
;
;   155770  06/18/96 AJR - Set mtehdl befor call to ldrFindDetach in the
;                          function w_loadmodule.
;

.XLIST
        include fnomem.inc
        include dosseg.inc
        include basemaca.inc
        include osmaca.inc
        include osconst.inc
        include mi.inc
        include error.inc
        include protmode.inc
        include seldesc.inc
        include dossymc.inc
        include sigdef.inc
        include except.inc
        include pgexport.inc
        include vmexport.inc
        include selxport.inc
        include vmowner.inc
        include tkexport.inc
        include infoseg.inc
        include curdir.inc
        include dirent.inc
        include find.inc
        include screen.inc
        include sem.inc
        include tkptda.inc
        include ldrvars.inc
        include ldrxport.inc
        include dynxport.inc
        include virtsym.inc
        include sft.inc
.LIST

        CpuMode 386
        ModName ldrrun,FNO_LDRRUN_ASM

        ReferFlat       _mte_h,DWORD
        ReferFlat       _DosModHdl,WORD
        ReferFlat       _fTKUsesR2,BYTE
        ReferFlat       _pTCBCur,DWORD
        ReferFlat       _pPTDACur,DWORD
        ReferFlat       _hMteDoscall1,WORD
        ReferFlat       _pmteDoscall1,DWORD

if RAS
        ReferFlat       _RASselSTDA,WORD
        ReferFlat       _fDYNActive,WORD
endif

        ReferFlat       _FSHModMte,DWORD
        ReferFlat       _BasicHWConfig,WORD
        ReferFlat       _ldrpFileNameBuf,DWORD
        ReferFlat       _ldr16pFileNameBuf,DWORD
        ReferFlat       _ldrDoGCollect,BYTE
        ReferFlat       _ldrfromLdrRun,BYTE

        ReferGlobal     _IsMiniIFS,BYTE

ifdef LDRSTRICT
        ReferFlat       _TaskNumber,WORD
        ReferFlat       _ldrDebugFlags,DWORD
endif

        EXTRN           is_path:near
        EXTRN           CheckModName:near
        EXTRN           Bmp_Get:near

        EXTRN16         StrLen
        EXTRN16         StrLen32
        EXTRN16         is_path
        EXTRN16         CheckModName
        EXTRN16         FreeTempBuffer
        EXTRN16         GetTempBuffer
        EXTRN16         SuBuffz

        EXTRN32         ldrGDTPreload

if RAS
        EXTRN32         _DYNAttach
        EXTRN32         _DYNAttachError
endif

        EXTRN32         _ldrCheckDLLTerm
        EXTRN32         _ldrClearGraph
        EXTRN32         _LDRClearSem
        EXTRN32         _ldrFindDetach
        EXTRN32         _ldrFindModule
;;137076        EXTRN32         _LDRFreeNonResNamSegs
        EXTRN32         _ldrGetModule
        EXTRN32         _LDRGetProcAddr
        EXTRN32         _ldrLibDetach
        EXTRN32         _ldrLinkMTE
        EXTRN32         _LDRQAppType
        EXTRN32         _ldrIncDLDRecord
        EXTRN32         _ldrDecDLDRecord
        EXTRN32         _ldrWasLoadModuled
        EXTRN32         _ldrSetLoaded
        EXTRN32         _ldrTransPath
        EXTRN32         _ldrUCaseString
        EXTRN32         _ldrUnlinkMTE
        ReferFlat       _pldrClose,dword
;;137076        EXTRN32         _LDRUseNonResNamSegs
        EXTRN32         _ldrASMpMTEFromHandle
        EXTRN32         _ldrWriteErrTxt
        EXTRN32         _SELClearPreload
        EXTRN32         _SELFreeGDT
        EXTRN32         _SELFreeLDT
        EXTRN32         _SELGetDescInfo
        EXTRN32         _SELLdrDesc
        EXTRN32         _SELVirtToLin
        EXTRN32         _TKAllocR2Stacks
        EXTRN32         _TKCheckExitListRef
        EXTRN32         _TKLibiError
        EXTRN32         _TKLibiStartDispatch
        EXTRN32         _TKTstnClrKernelSem
        EXTRN32         _VMGetPTDAAddr
        EXTRN32         _VMAllocKHB


ifdef LDRSTRICT
        EXTRN32         _printf:NEAR
endif

        EXTRNF32        _LDRClearSem

        EXTRNFAR        StrLen32
        EXTRNFAR        DStrLen
        EXTRNFAR        SuByte
        EXTRNFAR        SuBuff
        EXTRNFAR        FuByteNF


CONSTANTS SEGMENT

; Moved to sdata.asm for System Anchor Block - f122427
        extrn   _DOSCALL1:byte

        PUBLIC  LoadDevDrv              ; BUGBUG - should be in _DATA ????
LoadDevDrv      db      0

CONSTANTS ENDS


ifdef LDRSTRICT

DATA SEGMENT

ldrDLM1   db   'ldrDLM entry - slot %x ptda %lx'
          db   0Dh, 0Ah, 00h
ldrDLM2   db   'ldrDLM name  - slot %x name %s'
          db   0Dh, 0Ah, 00h
ldrDLM3   db   'ldrDLM free  - slot %x'
          db   0Dh, 0Ah, 00h
ldrDLM4   db   'ldrDLM exit  - slot %x'
          db   0Dh, 0Ah, 00h

DATA ENDS

endif


        FARCODE HIGH32

SWAP32_13      segment

;***EP  w_loadmodule - Load dynamic link library module.
;
;       w_loadmodule loads a dynamic link library module and returns a
;       handle for the library module.
;
;       We need to load the module requested by the caller. Set up the ENTRY
;       conditions for get_module and call get_module. For a quick reference,
;       here is what the get_module will do:
;       + call find_module to check if the module was already loaded
;       + if the module is not already loaded, create a module table entry
;         for it by calling create_mte
;       + call load_module so that the module may be loaded.
;         load_module performs the following:
;         - allocate memory for each segment in the module..
;         - call ldrLoadSegment to load each segment in the module. ldrLoadSegment
;           does the following:
;               + read in the segment
;               + do relocation fixups
;               + call get_module to initiate loading modules
;                 containing functions imported by the segment
;
;       Once get_module returns [which will happen after the requested module
;       and all the related module table entries are created and the
;       corresponding modules are loaded if they require to be preloaded],
;       generate a handle for the module loaded. This handle will be returned
;       to the caller.
;
;         call Find_Rmp for the ptda, mte pair.
;         if (entry is found)
;           increment the cnt field
;         else
;           call Rmp_Add with ptda, mte, cnt (= 1) as params.
;
;       ENTRY   (ES:DI) -> ModuleName
;               ModuleName is the address of the ASCIIZ name string containing
;               the pathname for the library module file.
;
;               (DS:SI) = ObjNameBuf
;               where ObjNameBuf is the address of a buffer in which the name
;               of an object which contributed to the failure of DOSLOADMODULE
;               call is to be placed.
;
;               (AX) = ObjNameBufL
;               where ObjNameBufL is the length, in bytes, of the buffer
;               described by ObjNameBuf.
;
;       EXIT    Carry Clear
;                   (AX) = 0 (Success)
;                   (BX) = Library Module Handle
;               Carry Set
;                   (AX) = Error Code
;
;       COMMENTS
;               If the file is a new format executable for a library module,
;               then the module will be loaded and a handle will be returned.
;               The returned handle is used for freeing the library module,
;               getting procedure addresses and getting full module pathname.
;

        ASSUME DS:NOTHING, ES:NOTHING, FS:NOTHING, GS:NOTHING, SS:NOTHING
Procedure w_loadmodule,far32

LocalVar  p16ModName,DWORD        ; 16:16 pointer to modname
LocalVar  pchName,DWORD           ; flat pointer to modname
LocalVar  pobjnamebuf,DWORD
LocalVar  pmte,DWORD
LocalVar  cnest,WORD
LocalVar  mtehdl,WORD
LocalVar  ONBufLen,WORD
LocalVar  class,BYTE
LocalVar  modtype,BYTE
LocalVar  fDOSCALLS,BYTE
LocalVar  fAttached,BYTE          ; Attachment flag
LocalVar  fFSDAttached,BYTE       ; FSD Attachment flag

        TaskTime

        EnterProc

        .errnz  NO_ERROR

;       Save module name and switch to flat model.
        mov     p16ModName.Segmt,es    ; save modname
        mov     p16ModName.Offst,di
        mov     pobjnamebuf.Segmt,ds   ; save object name buffer
        mov     pobjnamebuf.Offst,si

        mov     ONBufLen,ax

        FlatContext <ds,es>,eax

;       We take the DLMsem semaphore in the PTDA to ensure that no other
;       thread is running DLL initialization.  This makes it impossible for
;       legal code to access an entry point in a DLL before the DLL
;       initialization has completed.  There is another KSEMRequestMutex()
;       call in the TKStartLibiDispatch() that will INCREMENT the reference
;       count iff there is init. code to run for something we are loading.
;       At the end of w_loadmodule() there is a KSEMReleaseMutex() call
;       that either releases the semaphore if this is a PDD/IFS/VDD (or
;       there simply was no init. code to run) or decrements the reference
;       count by one.  After all init. code completes (either success or
;       error), we release the semaphore in TKLibiInitNextDLL().

        mov     ebx,[_pPTDACur]        ; use EBX to point to the current PTDA
                                       ; as it is preserved across KSEM calls

ifdef LDRSTRICT

        mov     eax,_ldrDebugFlags
        and     eax,LDR_PRINT_DLM_SEM
        jz      short DLMNoDebug1
        movzx   eax,_TaskNumber
        CCall   _printf, < FLAToffset ldrDLM1, eax, ebx >, cdecl
DLMNoDebug1:

endif

        ; serialise dosloadmodules on a per process basis
        lea     eax,[ebx].ptda_DLMsem   ; the per process dosloadmodule semaphore
        CCALL32 _KSEMRequestMutex,<eax,-1>   ; -1 is a wait for ever
        or      eax,eax                 ;  get out on error, incl ERROR_INTERRUPT
        jnz     wldm99d

        ; Check for DLL termination in progress.
        ; Do NOT do this while holding the PTDA sem.
        CCALL32 _ldrCheckDLLTerm        ; wait for DLL termination to complete
        or      eax,eax                 ; exit on error
        jnz     wldm99c

        ; take the kernel semaphore to protect the ptda fields we modify
        lea     eax,[ebx].ptda_ptdasem  ; ebx still holds the pPTDACur
        CCALL32 _KSEMRequestMutex,<eax,-1>
        or      eax,eax                 ;  exit on error, incl ERROR_INTERRUPT
        jnz     wldm99c

        mov     fAttached,al            ; No attachment yet (al=0)
        mov     fFSDAttached,al         ; No FSD attachment yet
        mov     mtehdl,ax               ; clear handle field

        ; laddr|-1 = SELVirtToLin(off, sel)
        movzx   esi,pobjnamebuf.Offst
        movzx   edx,pobjnamebuf.Segmt
        CCALL32 _SELVirtToLin,<esi,edx>
        cmp     eax,-1                  ; User buffer valid ?
        jne     short wldm0c            ; Yes, proceed
        xor     eax,eax                 ; Invalid buffer pointer and length
        xor     ebx,ebx


wldm0c:
        ; Init global error message variables
        mov     edx,_pTCBCur
        mov     [edx].TCB_ObjNameBuf,eax        ; Save pointer and length of
        mov     bx,ONBufLen
        mov     [edx].TCB_ObjNameBufL,bx        ; user buffer

        ldrInvTgtErrTxt edx

        movzx   eax,p16ModName.Segmt            ; Convert modname ptr to flat
        movzx   ecx,di                          ; DI=p16ModName.Offst

        ; laddr|-1 = SELVirtToLin(off, sel)
        CCALL32 _SELVirtToLin,<ecx,eax>
        mov     pchName,eax                     ; Save flat pointer to name
        cmp     eax,-1
        je      wldm99b

ifdef LDRSTRICT

        push    ecx
        mov     ecx,_ldrDebugFlags
        and     ecx,LDR_PRINT_DLM_SEM
        jz      short DLMNoDebug2
        movzx   ecx,_TaskNumber
        CCall   _printf, < FLAToffset ldrDLM2, ecx, eax >, cdecl
DLMNoDebug2:
        pop     ecx

endif

        ; Get the loader semaphore
        LDRGetSem
        or      eax,eax
        jnz     wldm99b                           ; Error from _LDRGetSem()

        ; Determine the type of module being loaded.
        ;
        ; If loading a dynlink library,
        ;         Validate the given ascii string as a module name.
        ;
        ; If loading a device driver,
        ;         Reset the special flag that indicates this (LoadDevDrv)
        ;         Treat the given ascii string as an absolute path.
        ;
        ; Note: fDOSCALLS is made FALSE here. This fact is used later to
        ; indicate that DOSCALLS has not been specified.

        mov     _fTKUsesR2,al                   ; Assume ring 2 not used (ax=0)
        mov     fDOSCALLS,al                    ; reset flag DOSCALLS is loaded
        xor     edx,edx                         ; we want both dl and dh zero
        xchg    LoadDevDrv,dl                   ; Get/Clear device/IFS flag

        mov     modtype,EXT_LIBRARY             ; Assume DLL Load
        mov     class,CLASS_GLOBAL              ; Assume GLOBAL class
        or      dl,dl                           ; Device or IFS load?
        jz      short wldm1                     ; no - proceed with DLL load

;       At this point, we are loading either a device or IFS driver.

        mov     class,CLASS_SPECIFIC
        mov     modtype,dl                      ; Assume DD
        cmp     dl,EXT_DEVICE                   ; DD?
        je      short wldm1                     ; yes - continue

;       We are loading an IFS driver.

        lea     eax,_FSHModMte
        or      [eax].mte_flags1,CLASS_GLOBAL

;       void ldrLinkMTE(pmte)

        CCALL32 _ldrLinkMTE,eax                 ; link MTE to list

wldm1:  mov     edi,pchName                     ; (ES:EDI) = pointer to modname
        CALL16  StrLen32                        ; (ECX) = module name strlen
        dec     cx                              ; Ignore terminating NULL

        lds     si,p16ModName                   ; (DS:SI) -> ModName
        cmp     modtype,EXT_DEVICE
        je      short wldm2

;       We are loading a DLL.
;
;       We determine which class this module falls into (global or specific)
;       by checking for the presence of path characters in the module name.
;       If there are, we want to call ldrTransPath to expand the pathname,
;       and proceed with specific.  Otherwise, it's global. We also want to
;       check the module name to make certain it's valid.

        CALL16  is_path                         ; are "path" characters present?

        jnz     short wldm3                     ; no, its a global class module
        mov     class,CLASS_SPECIFIC            ; yes,its a specific class module

;       We are loading a specific class module. We expand the path here so
;       that we can see if the library is already in our MTE list. If so,
;       we can avoid the Open. First check if module name is valid.

wldm2:  mov     eax,CHECK_WILDCARDS
        CALL16  CheckModName                    ; is name a legal module name ?

        FlatContext <ds>,edx
        movzx   eax,ax
        jc      wldmD                          ; name illegal - return (AX)

;       rc = ldrTransPath(pchName)

        CCALL32 _ldrTransPath,[pchName]         ; expand and validate path
        or      eax,eax
        jnz     wldmF                           ; error - cleanup & return

;       Because we already have a valid path, and we don't want ldrTransPath
;       to be called again in LdrOpen, we set the pchName to point to
;       _ldrFileNameBuf, and always use the canonical path from now on.
;       LdrOpen is written to be able to accept _ldrFileNameBuf as input.

        mov     edi,_ldrpFileNameBuf
        mov     pchName,edi

        CALL16  StrLen32                ; (ECX) = StrLen32 (ModName)(FULL PATH)

        clc                             ; no error
        jmp     short wldm4

;       Loading a DLL, global class. (No path specified)
;       Validate the name, and get the ModName length.
;       DS:SI - ModName

wldm3:  mov     eax,CHECK_ALL
        CALL16  CheckModName            ; is name a legal module name ?

        FlatContext <ds>,edx

        movzx   eax,ax
        jc      wldmD                   ; name illegal - return (AX)

;       ------- End of w_LoadModule Load Prelude -------
;
;       Note that the old address to p16ModName has possibly been
;       destroyed, and it may now point to ldrFileNameBuf.
;
;       (CX)      = module name length
;       modtype   = EXT_DEVICE, EXT_FSD or EXT_LIBRARY
;       fDOSCALLS = 0 if previous load was not for DOSCALLS.
;                 = 1 if previous load was for DOSCALLS.
;       -------
;       Load the module and set up the addresses by calling ldrGetModule:
;
;       rc = ldrGetModule(pachModname,cchModname,chLdrtype,class,ppmte)

wldm4:  movzx   ecx,cx
        push    ecx                     ; save module name length.

        xor     eax,eax
        mov     [pmte],eax
        SSToDS  eax,pmte
        PushP   eax                     ; ppmte

        mov     eax,ecx
        shl     eax,16                  ; length in high word

        mov     al,modtype
        shl     ax,8                    ; type in ah

        mov     al,class                ; class in al

        mov     [_ldrfromLdrRun],1
        CCALL32 _ldrGetModule,<[pchName],eax>,,PushP
        mov     [_ldrfromLdrRun],0

        cmp     modtype,EXT_FSD         ; If this is not a file system driver,
        jne     short wldm6             ; then continue with load.

        or      eax,eax                 ; If the load failed,
        jnz     short wldm5             ; then skip GDTPreload.

        mov     ebx,[pmte]
        mov     cx,word ptr [ebx]      ; don't preload for 32-bit FSDs
        and     cx,MTEFORMATMASK
        cmp     cx,MTEFORMATLX
        je      short wldm5

        CCALL32 ldrGDTPreload           ; preload GDT segments.

wldm5:  push    eax                     ; save error

        mov     [_ldrDoGCollect],1

        mov     eax,offset FLAT:_FSHModMte
        CCALL32 _ldrUnlinkMTE,eax       ; unlink the FSHELPER mte
        pop     eax                     ; restore error

wldm6:  mov     ebx,eax                 ; EBX = error code
        CCALL32 _ldrSetLoaded           ; Traverse the list
        or      ebx,ebx                 ; Any error from above?
        jz      short wldm7             ; no - continue load

        mov     eax,_pTCBCur
        mov     [eax].TCBCheckedSig,0   ; clear TCBCheckedSig byte
        mov     eax,ebx                 ; (EAX) = error code
        jmp     wldmC                   ; load failed, handle error

;       If this is the DOSCALLS handle, then point at "DOSCALL1" and get
;       that module.
;       (pushed) ECX - module name length

wldm7:  mov     ebx,[pmte]
        mov     dx,[ebx].mte_handle
        mov     [mtehdl],dx             ; Get handle to the loaded module
        test    [ebx].mte_flags1,DOSMOD ; is this the DOSCALLS module?
        jz      short wldm8             ;  no, continue

;       This is the DOSCALLS module.  We now point p16ModName at
;       the string "DOSCALL1", and loop back to load that module.
;       We set fDOSCALLS to TRUE to indicate that the previous
;       load was for DOSCALLS.

        mov     pchName,OFFSET FLAT:_DOSCALL1
        DosContext ,ax,NOASSUME
        shl     eax,16
        mov     ax,offset DOSGROUP:_DOSCALL1
        mov     p16ModName,eax          ; p16ModName = (16:16) "DOSCALL1";
        mov     fDOSCALLS,TRUE

        pop     ecx                     ; remove saved ecx on stack

        mov     ecx,SIZEDOSCALL1STR     ; (ECX) = StrLen ("DOSCALL1")
        jmp     wldm4                   ; _ldrGetModule DOSCALL1.

;       ------- _ldrGetModule complete -------
;       (pushed) module name length

wldm8:  cmp     modtype,EXT_DEVICE      ; are we loading a device driver
        jne     short wldm9

        mov     ebx,[pmte]
        mov     dx,word ptr [ebx]      ; don't preload for 32-bit FSDs
        and     dx,MTEFORMATMASK
        cmp     dx,MTEFORMATLX
        je      short wldm9
        CCALL32 ldrGDTPreload           ; preload GDT segments
        or      eax,eax
        jnz     wldmC                   ; (AX) = err if preload error
                                        ; BUGBUG - no close file on error ?!

wldm9:
        ; Close the file as we have finished with it in the DD or IFS cases.
        ; Null out the SFN so we can detect that the file is closed.
        cmp     modtype,EXT_DEVICE
        jne     short wldm9b    ; 400
; 400   je      short wldm9a
; 400   cmp     modtype,EXT_FSD
; 400   jne     short wldm9b
; 400   cmp     [_IsMiniIFS],0
; 400   jnz     short wldm9b
wldm9a: mov     ebx,[pmte]
        movzx   edx,[ebx].mte_sfn
        mov     [ebx].mte_sfn,SFN_NULL
        CCALL32 <[_pldrClose]>,<edx> ; Note - need to call pldrClose and not
                                     ;   ldrClose as we have a funny SFN from a
                                     ;   mini-IFS - ldrClose gets clever and
                                     ;   bypasses the close.

wldm9b:
;       Allocate ring 2 stacks if needed.

        cmp     _fTKUsesR2,0            ; Does the module use ring 2?
        jz      short wldmA             ;  no, skip ahead
        CCALL32 _TKAllocR2Stacks        ; Go allocate ring 2 stacks
        or      eax,eax                 ; Successful?
        jne     short wldmC             ;  no, skip ahead

wldmA:  cmp     fDOSCALLS,FALSE         ; Was load of DOSCALLS requested?
        jz      short wldmB             ; no - continue normally

;       Here DOSCALLS was requested, but we last loaded DOSCALL1.
;       Convert back to the DOSCALLS handle, to save it in the RMP
;       segment, and return _DosModHdl to the caller.

        mov     bx,_DosModHdl           ; get the DOSCALLS handle.
        mov     WORD PTR [mtehdl],bx    ; save it away.

;       Create a runtime attachment from the current PTDA to the newly
;       loaded module.
;
;       rc = ldrIncDLDRecord(hobmte, pptda);

wldmB:  movzx   eax,[mtehdl]
        CCALL32 _ldrIncDLDRecord,<eax,[_pPTDACur]>
        pop     ecx                     ; (ECX) = module name length
        or      eax,eax
        jnz     short wldmD             ; ERROR from _ldrIncDLDRecord.
        inc     fAttached               ; Increment attachment count

IF RAS
        cmp     _RASselSTDA,0           ; Is RAS enabled?
        jz      wldmF                   ; NO_ERROR from _ldrIncDLDRecord.
        cmp     _fDYNActive,0           ; Is DynTrc active?
        je      short wldmB0            ; no - skip call to _DYNAttach

        push    ecx
        CCALL32 _DYNAttach              ; Attempt DynamicTrace reattachment
        pop     ecx                     ; (ECX) = module name length
        or      eax,eax
        jnz     short wldmD             ; reattachment failed

wldmB0: cmp     modtype,EXT_DEVICE      ; are we loading a device driver?
        je      short wldmB1            ; yes - attach MTE to kernel

        cmp     modtype,EXT_FSD         ; Are we loading an FSD driver?
        jne     wldmF                   ; no - don't attach MTE to kernel

;       RAS is enabled, and we have just loaded either an FSD or a
;       device driver.  We must keep the MTE around to support
;       profiling and DynamicTrace on these modules.   To do this, we
;       simply create a runtime attachment to the NULL ptda (the kernel).

wldmB1: push    ecx                     ; save module name length...
        movzx   eax,[mtehdl]

;       rc = ldrIncDLDRecord(hobmte, pptda);

        CCALL32 _ldrIncDLDRecord,<eax,0>
        pop     ecx                     ; (ECX) = module name length.
        or      eax,eax
        jnz     short wldmD             ; ERROR from _ldrIncDLDRecord.
        inc     fFSDAttached            ; Increment attachment count
ENDIF

        jmp     wldmF                  ; NO_ERROR from _ldrIncDLDRecord.


;       ------- Error From Above. -------
;       Cleanup:
;       if (this module is loaded (done via find_module))
;               ldrLibDetach
;
;       ldrLibDetach will check if the program has links to this module
;       before Detaching the module, and will call _ldrGarbageCollect if
;       needed.
;
;       At this point, the ObjNameBuf should be filled in.
;
;       (pushed) module name length
;       (EAX) = Error Code

wldmC:  pop     ecx                     ; (ECX) = module name length
wldmD:  mov     ebx,eax                 ; (EBX) = error code
        movzx   esi,cx                  ; (ESI) = module name length

;       rc = ldrWriteErrTxt(errcode, pmte)

        CCALL32 _ldrWriteErrTxt,<ebx,0>

if RAS
        cmp     _fDYNActive,0           ; If DynTrc not active,
        jz      short wldmD0            ; skip call to _DYNAttachError.

        CCALL32 _DYNAttachError         ; Clean up DynamicTrace structures
endif

;       _ldrWriteErrTxt could have GP faulted just now, and might be
;       returning ERROR_PROTECTION_VIOLATION.  However, we are already
;       on an error path, so we will return the first error discovered,
;       ignoring (EAX) at this point.

;       (EBX) = Error Code
;       (ESI) = module name length

wldmD0: movzx   ecx,p16ModName.Segmt    ; Convert modname ptr to flat
        movzx   eax,p16ModName.Offst

        ; laddr|-1 = SELVirtToLin(off, sel)
        CCALL32 _SELVirtToLin,<eax,ecx>
        cmp     eax,-1
        je      wldmF

        mov     edi,eax                 ; EDI = pointer to module name
        cmp     class,CLASS_GLOBAL      ; Is it a global module ?
        je      short wldmD1            ; Yes, no need for TransPath

        ; rc = ldrTransPath(pchName)
        CCALL32 _ldrTransPath,eax
        or      eax,eax
        jnz     wldmF

        mov     edi,_ldrpFileNameBuf

wldmD1: ; UpperCase name passed to ldrFindModule
        CCALL32 _ldrUCaseString,<edi,esi>
        SSToDS  ecx,[pmte]
        movzx   eax,class

        ; ldrFindModule(pachModname, cchModname, class, ppmte)
        CCALL32 _ldrFindModule,<edi,esi,eax,ecx>
        or      eax,eax
        mov     eax,ebx                 ; EAX = error code
        jnz     wldmF
        mov     edx,[pmte]
        or      edx,edx                 ; Is the module here?
        jnz     short wldmD2
        jmp     wldmF                   ; no - ldrLibDetach not required

;       Identify the modules which need to be detached if [mtehdl]
;       is freed.
;
;       155770  
;       When an error occured loading a dll and this error path was taken
;       the call to ldrFindDetach returned ERROR_INVALID_HANDLE because
;       the value of mtehdl was not setup prior to the call.  This also 
;       caused the system to IPE in VMFreeAll() by leaving uncleaned
;       context records.
;
;       rc = ldrFindDetach(hobmte, pptda)

wldmD2: 
        mov     eax,[pmte]              ; 155770   Copy pmte to eax
        movzx   eax,[eax].mte_handle    ; 155770   Copy mte handle to e(ax)
        mov     [mtehdl],ax             ; 155770   Save ax to mtehdl
        CCALL32 _ldrFindDetach,<eax,[_pPTDACur],0>

        ; Detach the modules which need to be detached.
        cmp     fAttached,0
        je      short wldmE1
        movzx   eax,[mtehdl]
        CCALL32 _ldrDecDLDRecord,<eax,_pPTDACur,1>
wldmE1: cmp     fFSDAttached,0
        je      short wldmE2
        movzx   eax,[mtehdl]
        CCALL32 _ldrDecDLDRecord,<eax,0,1>

wldmE2: CCALL32 _ldrLibDetach
        CCALL32 _ldrClearGraph

;       w_loadmodule was not successful. Hence, library
;       initialization should be cancelled by calling TKLibiError

        CCALL32 _TKLibiError            ; Cancel Library Initialization Setup
        mov     [_ldrDoGCollect],1

        ; incremented the sem ref count in start dispatch, so now decrement
        ; it if count > 1
ldmnf0: SSToDS  ecx,cnest
        mov     eax,[_pPTDACur]
        lea     eax,[eax].ptda_DLMsem
        push    eax
        CCALL32 _KSEMQueryMutex,<eax,ecx>
        or      eax,eax
        pop     eax
        jz      short ldmnf1
        movzx   ecx,cnest
        or      ecx,ecx       ; BUGBUG  cmp ecx,2   jc ??
        jz      short ldmnf1
        CCALL32 _KSEMReleaseMutex,<eax>
        jmp     short ldmnf0
ldmnf1:

        mov     eax,ebx

;       If loadmodule was successful, set up library initialization.
;       (EAX) = error code

wldmF:  or      eax,eax                 ; error code in eax on jump to here
        jnz     short wldm10

        cmp     modtype,EXT_DEVICE      ; are we loading a device driver?
        jnz     short wldmF2            ; jmp if no!

        mov     ebx,[pmte]              ; Check if the DD imported a DLL
        mov     ecx,[ebx].mte_impmodcnt ; if so start libinit.
        or      ecx,ecx
        jz      short wldm10
        add     ebx,size ldrmte_s       ; modptrs table
wldmF1: mov     edx,[ebx]
        mov     edx,[edx].mte_flags1
        test    edx,LIBRARYMOD
        jnz     short wldmF3
        add     ebx,4
        loop    wldmF1
        jmp     short wldm10

wldmF2: cmp     modtype,EXT_LIBRARY
        jne     short wldm10

wldmF3: CCALL32 _TKLibiStartDispatch,<eax>
        mov     ebx,eax                 ; (EAX) = error code
        or      eax,eax                 ; Failed to set up for dispatch?
        jnz     wldmD2                  ;  yes, go clean up

;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;       error code will be in eax at all entry points below here when
;       jumping in on an error condition ... preserve error code and
;       do not overwrite it with a bad cleanup condition


;       Invalidate user buffer pointer copy by setting length to 0.  This
;       prevents error handling from subsequent demand loads from using
;       the pointer when no longer valid
wldm10: mov     edx,_pTCBCur
        xor     ecx,ecx
        mov     [edx].TCB_ObjNameBufL,cx        ; mark glbl error text invalid

wldm99a:
        mov     ebx,eax                         ; clear the loader semaphore,
        CCALL32 _LDRClearSem                    ; but preserve the error code
        mov     eax,ebx                         ; we already have in eax

wldm99b:
        mov     ebx,eax                         ; save RC
        mov     edx,[_pPTDACur]                 ; address current PTDA
        CCALL32 _TKTstnClrKernelSem             ; Free kernel semaphore, if owned
        mov     eax,ebx                         ; retrieve RC

;    Clear the DLM init semaphore or decrement ref count.
;    If there are init. routines to run, the ref count will be > 1.
;    The second RequestMutex is in TKStartLibiDispatch(), iff there
;    is init code to run.  When it finishes, the TKLibiInitNextDLL()
;    code will call KSEMReleaseMutex() allowing the next DLM to start.
wldm99c:
        mov     ebx,eax                         ; save RC

        SSToDS  ecx,cnest
        mov     eax,[_pPTDACur]
        lea     eax,[eax].ptda_DLMsem
        push    eax
        CCALL32 _KSEMQueryMutex,<eax,ecx>
        or      eax,eax
        pop     eax
        jz      short ldmnf2
        movzx   ecx,cnest
        or      ecx,ecx
        jz      short ldmnf2
        CCALL32 _KSEMReleaseMutex,<eax>
ldmnf2:

ifdef LDRSTRICT
        mov     eax,_ldrDebugFlags
        and     eax,LDR_PRINT_DLM_SEM
        jz      short DLMNoDebug3
        movzx   eax,_TaskNumber
        CCall   _printf, < FLAToffset ldrDLM3, eax >, cdecl
DLMNoDebug3:
endif

        mov     eax,ebx                         ; retrieve RC

wldm99d:

ifdef LDRSTRICT
        push    eax
        mov     eax,_ldrDebugFlags
        and     eax,LDR_PRINT_DLM_SEM
        jz      short DLMNoDebug4
        movzx   eax,_TaskNumber
        CCall   _printf, < FLAToffset ldrDLM4, eax >, cdecl
DLMNoDebug4:
        pop     eax
endif

        movzx   ebx,[mtehdl]                    ; return hMTE in ebx

        cmp     eax,1                           ; return carry set on error
        cmc

        LeaveProc
        RETC

EndProc w_loadmodule

SWAP32_13      ENDS
LDRSWAP_TEXT SEGMENT



;**     _LDRGetLibNameToHeap - GetLibName, but place result on swap. heap.
;
;       _LDRGetLibNameToHeap returns the fully qualified pathname
;       associated with the referenced module handle, but places the
;       result on the kernel swappable heap.  The memory allocated from
;       the heap is trimmed to the size of the module name rather than
;       allocating MAXPATHLEN every time, to save space.
;
;       The caller must free this swappable heap block when finished.
;
;       _LDRGetLibNameToHeap is C callable.
;
;       _LDRGetLibNameToHeap (VMHOB hMTE, ushort_t *pcchLibName,
;                                     char_t **ppLibName);
;
;       ENTRY   hMTE            - module MTE handle
;               pcchLibName     - place to return library name length (SHORT!)
;               ppLibName       - place to return library name pointer
;
;       EXIT    (EAX) = 0  No error
;                       (ushort_t *) *pcchLibName filled in with length
;                       *ppLibName contains pointer to swappable heap block,
;                       **ppLibName contains the library path name.
;
;               (EAX) != 0  Error, error code in EAX
;
;       USES:   EAX, ECX, EDX

Procedure _LDRGetLibNameToHeap,near
        ASSUME DS:NOTHING, ES:NOTHING, FS:NOTHING, GS:NOTHING, SS:NOTHING

        ArgVar  MTE,DWORD
        ArgVar  pcchLibName,DWORD
        ArgVar  ppLibName,DWORD

        EnterProc
        SaveReg <ebx,edi,esi,ds,es>
        CCALL32 ldrGetLibNameSub
        or      eax,eax
        jnz     short lglnh9            ; Error - Exit

;       (ES:DI) -> LibName
;       Find the length of the path name.

        movzx   edi,di                  ; (ES:EDI)-> temp buffer
        mov     esi,edi                 ; (ES:EDI)-> temp buffer
        xor     eax,eax                 ; (AL) = Null byte to search for.
        mov     ecx,MAXPATHLEN          ; (ECX) = Scan length
        mov     ebx,ecx                 ; (EBX) = MAXPATHLEN
        repne   scasb                   ; find the null byte
        sub     ebx,ecx                 ; (EBX) = (MAXPATHLEN - ECX) = strlen+1
        push    es
        FlatContext     <ds,es>,ax

;       Allocate swappable heap buffer:
;
;       rc = VMAllocKHB(hkh, cb, hobOwner, fl, ppbData)

        CCALL32 _VMAllocKHB <VM_HKH_PUB_SWAPRW, ebx, LDRMTEPATHOWNER, \
                NA, ppLibName>          ; Allocate swappable heap buffer
        pop     ds                      ; (DS:ESI)-> TempBuffer path
        or      eax,eax                 ; Error allcating buffer?
        jnz     short lglnh8            ; yes - FreeTempBuffer and exit

;       (ES) = FLAT
;       (EBX) = strlen(path) + 1 (one to account for MULL byte)

        push    ds
        push    esi                     ; Save TempBuffer pointer
        mov     edi,ppLibName
        mov     edi,es:[edi]            ; (ES:EDI)-> heap buffer (from salloc)
        mov     ecx,ebx                 ; (ECX) = Path length.
        rep     movsb                   ; Put module name onto the heap
        mov     edi,pcchLibName
        dec     ebx                     ; (EBX) = strlen(path)
        mov     es:[edi],bx             ; store SHORT module name length.
        pop     edi
        pop     es                      ; (ES:EDI)-> temp buffer to free.
        xor     eax,eax
lglnh8: CALL16  FreeTempBuffer
lglnh9: movzx   eax,ax
        RestoreReg <es,ds,esi,edi,ebx>
        LeaveProc
        RETC
EndProc _LDRGetLibNameToHeap

LDRSWAP_TEXT ENDS
SWAP32_11 segment

;***LP  ldrSkipEnts - skip the given number of entries in the entry table
;*
;*      For a given number, return the number of bytes to skip to the
;*      next bundle in the entry table.
;*
;*      ENTRY   fNE  - set to 1 if NE, reset to 0 if LX
;*              type - the type of bundle to skip
;*              count - the number of entries in this bundle
;*
;*      EXIT    count of byte to skip to get to desired entry
;*
;*   This procedure performs the following steps:
;*
;*      - determines the type of module
;*      - returns the number of bytes to skip based on entry type
;*
;
;     ulong_t ldrSkipEnts( uchar_t  fNE,
;                          uchar_t  type,
;                          uchar_t  count )
;
Procedure  _ldrSkipEnts,near
        ArgVar  fNE,        DWORD       ;       fNE = 8
        ArgVar  typeBundle, DWORD       ;       type = 12
        ArgVar  count,      DWORD       ;       count = 16

        EnterProc

        xor     eax, eax
        cmp     BYTE PTR [fNE],al       ; Zero for LX
        jne     SHORT lse_NE
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
lse_LX:
      ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
      ;  switch (type & ~TYPEINFO)
      ;     case EMPTY:    /* unused bundle */
      ;       return(sizeof(ldrempty_t));
      ;     case ENTRY16:  /* 16-bit offset entry */
      ;       return(sizeof(ldret_t) + sizeof(ldret16_t) * count);
      ;     case GATE16:   /* 286 call gate */
      ;       return(sizeof(ldret_t) + sizeof(ldret16iopl_t) * count);
      ;     case ENTRY32:  /* 32-bit offset entry */
      ;       return(sizeof(ldret_t) + sizeof(ldret32_t) * count);
      ;     case ENTRYFWD: /* forwarder entry */
      ;       return(sizeof(ldret_t) + sizeof(ldrfwd_t) * count);
      ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;

        movzx   edx,BYTE PTR [count]
        movzx   ecx,BYTE PTR [typeBundle]
        and     ecx,NOT 80h

        jcxz    SHORT lse_LX_EmptyBundle        ;case EMPTY:    /* unused bundle */
        dec     ecx                    ;case ENTRY16:  /* 16-bit offset entry */
        jz      SHORT lse_LX_Entry16
        dec     ecx                    ;case GATE16:   /* 286 call gate */
        jz      SHORT lse_LX_Gate16
        dec     ecx                    ;case ENTRY32:  /* 32-bit offset entry */
        jz      SHORT lse_LX_Entry32
        dec     ecx                    ;case ENTRYFWD: /* forwarder entry */
        jnz     SHORT lse_x

lse_LX_Forwarder: ;return(sizeof(ldret_t) + sizeof(ldrfwd_t) * count);
        lea     eax,[edx*8]            ; eax=count*8
        sub     eax,edx                ; count*7 (sizeof ldrfwd_t)
        add     eax, 4                ; size ldret_s = 4
        LeaveProc NoCheck
        RETC

lse_LX_Entry16:   ;return(sizeof(ldret_t) + sizeof(ldret16_t) * count);
        lea     eax,[edx][edx*2]       ; count*3 (sizeof ldret16_t )
        add     eax, 4                ; size ldret_s = 4
        LeaveProc NoCheck
        RETC

lse_LX_Gate16:    ;return(sizeof(ldret_t) + sizeof(ldret16iopl_t) * count);
lse_LX_Entry32:   ;return(sizeof(ldret_t) + sizeof(ldret32_t) * count);
        lea     eax,[edx][edx*4]       ; count*5 (sizeof ldret16iopl_t )
                                       ; count*5 (sizeof ldret32_t )
        add     eax, 4                ; size ldret_s = 4
        LeaveProc NoCheck
        RETC
lse_LX_EmptyBundle:
        mov     eax,size ldrempty_s        ;size ldrempty_s = 2
        LeaveProc NoCheck
        RETC

;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
lse_NE:
      ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
      ;  switch (type)
      ;     case 0:                           /* unused bundle */
      ;         return(sizeof(ldrempty_t));
      ;
      ;     case 0xff:                                /* movable object */
      ;         return(sizeof(ldrempty_t) + sizeof(ldrcte_t) * count);
      ;
      ;     default:                          /* fixed object */
      ;          return(sizeof(ldrempty_t) + sizeof(ldrent_t) * count);
      ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
        xor     eax,eax                    ;clear in case of empty bundle
        movsx   ecx, BYTE PTR [typeBundle] ; Yes, it should be sign-extend
        movzx   edx, BYTE PTR [count]
        jcxz    SHORT lse_EmptyBundle      ;case 0:    /* unused bundle  */
        inc     ecx
        jnz     SHORT lse_NE_fixed         ;case 0xff: /* movable object */
                                           ;default    /* fixed object   */

lse_NE_movable:
        shl     edx,1                      ;count=count*2 (so we return *6)
lse_NE_fixed:
        lea     eax,[edx][edx*2]           ; count*3 (sizeof ldrent_t)

lse_EmptyBundle:
        add     eax,size ldrempty_s        ;size ldrempty_s = 2
        ; Fall through to return

;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
lse_x:
        LeaveProc NoCheck
        RETC
_ldrSkipEnts    ENDP

SWAP32_11 ENDS
LOAD_TUNE4_SWP segment

;/***LP  ldrBlastResource
; *
; *      ENTRY   pMTE
; *              Sel
; *
; *      EXIT    void
; *
; */
;
;     void ldrBlastResource( ldrmte_t *pMTE,
;                            ulong_t LAddr )

Procedure _ldrBlastResource,near
        ArgVar  pMTE ,DWORD          ;       pMTE = 8
        ArgVar  LAddr,DWORD          ;       LAddr = 12
        EnterProc

        SaveReg <ebx,ecx,edi,esi>

;       register ebx = pSMTE
;       register esi = pote
;       register edi = pste
;       register ecx = Objnum

        xor     ecx,ecx      ; Objnum = 0


        mov     eax, DWORD PTR [pMTE]
        mov     ebx, DWORD PTR [eax].mte_swapmte ; ebx = pMTE->mte_swapmte;

; 32-bit(LX) or 16-bit(NE) ?
        mov     al, BYTE PTR [eax].mte_flags2
        and     al, MTEFORMATMASK
        cmp     al, MTEFORMATLX
        jne     SHORT lbr_neloop

;  // 32-bit module
;    while ( ObjNum < pSMTE->smte_objcnt)

lbr_lxloop:
        cmp     DWORD PTR [ebx].smte_objcnt, ecx
        jbe     lbr_xloop
;|***             pote = &((ldrote_t *)pSMTE->smte_objtab)[ObjNum];
;|***             if ( LAddr ==  pote->ote_base )
        ;; ESI = [ebx].smte_objtab + (ObjNum * sizeof(ldrote_t))
        ;;                                     sizeof(ldrote_t) == 24

        lea     eax,[ecx*8]      ; eax = ObjNum*8
        lea     esi,[eax][eax*2] ; esi = ObjNum*24
        add     esi, DWORD PTR [ebx].smte_objtab


        mov     eax, DWORD PTR [LAddr]
        cmp     DWORD PTR [esi].ote_base, eax
;;        jne     SHORT lbr_lx_notfound
;;        jmp     SHORT lbr_xloop_fromwithin
        je      lbr_xloop_fromwithin

lbr_lx_notfound:
        inc     ecx
        jmp     SHORT lbr_lxloop


; 16-bit module
;      while (ObjNum < pSMTE->smte_objcnt)


lbr_neloop:
        cmp     DWORD PTR [ebx+32], ecx
        jbe     SHORT lbr_xloop

;    pste = &((ldrste_t *)pSMTE->smte_objtab)[ObjNum];
;    // don't look up linear for a zero selector; ok to use tiled call
;    // as can not be packed
;    if (pste->ste_selector)

        ;; EDI = [ebx].smte_objtab + (ObjNum * sizeof(ldrste_t))
        ;;                                     sizeof(ldrste_t) == 16

        mov     edi, ecx
        shl     edi, 4
        add     edi, DWORD PTR [ebx+28]
        mov     ax, WORD PTR [edi].ste_selector
        or      ax,ax
        jz      SHORT lbr_ne_notfound

;|***                 if ( LAddr == SelToLaTiled(pste->ste_selector) )

        SelToLaTiled  ax
        cmp     eax,[LAddr]
        jne     SHORT lbr_ne_notfound

        jmp     SHORT lbr_xloop_fromwithin

lbr_ne_notfound:
        inc     ecx
        jmp     SHORT lbr_neloop


; // If not found after checking all objects, return.
; // This implies that VM and Ldr are inconsistent ?

;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
; If we got here, we found the resource
lbr_xloop_fromwithin:
       ; If exiting from within loop, we set error code to 0.
       ; No need to go through check above.

        mov     eax, DWORD PTR [pMTE]
        mov     al, BYTE PTR [eax].mte_flags2
        and     al, MTEFORMATMASK
        cmp     al, MTEFORMATLX
        jne     SHORT lbr_blast_ne

; // 32-bit module
lbr_blast_lx:
        sub     eax, eax
        mov     DWORD PTR [esi].ote_base, eax
        mov     WORD PTR [esi].ote_selector, ax
        mov     WORD PTR [esi].ote_handle,   ax

        and     DWORD PTR [esi].ote_flags, (NOT OBJALLOC)

        jmp     SHORT lbr_x

;  // 16-bit module
lbr_blast_ne:
        sub     ax, ax
        mov     WORD PTR [edi].ste_selector, ax
        mov     WORD PTR [edi].ste_seghdl, ax

lbr_xloop:
lbr_x:

        RestoreReg <esi,edi,ecx,ebx>
        LeaveProc
        RETC
EndProc _ldrBlastResource

LOAD_TUNE4_SWP ENDS


        FARCODE HIGH32
HIGH32CODE SEGMENT



;**     _ldrDirtyWorkerThunk - Get from 16:16 call gate to flat worker.

Procedure _ldrDirtyWorkerThunk

       extrn _Ldr32DirtyWorker:near

       push   ecx                     ; command
       push   eax                     ; pointer to data
       call   _Ldr32DirtyWorker       ; near 32-bit call
       add    esp,8                   ; clean up stack
       db     0CBh                    ; force 48-bit return

EndProc _ldrDirtyWorkerThunk




;**     _LDRGetLibName - Get dynamic link libray module pathname (user space)
;
;       _LDRGetLibName returns the fully qualified pathname associated
;       with the referenced module handle.  It is a 32-bit C callable
;       stub to w_getmodname.
;
;       ENTRY   LDRGetLibName(VMHOB MTE, char *pBuff, ulong BuffLen)
;               MTE - module MTE handle
;               pBuff - flat pointer to module name buffer (USER SPACE ONLY)
;               BuffLen - length of module name buffer
;
;       EXIT    (EAX) = 0  No error
;               (EAX) != 0  Error, error code in EAX
;
;       USES:   EAX, ECX, EDX

Procedure _LDRGetLibName,near
        ASSUME DS:NOTHING, ES:NOTHING, FS:NOTHING, GS:NOTHING, SS:NOTHING

ArgVar  MTE,DWORD
ArgVar  pBuff,DWORD
ArgVar  BuffLen,DWORD

        EnterProc
        SaveReg <ebx,edi,esi,ds,es>

        mov     eax,[MTE]
        CCALL32 ldrGetLibNameSub
        or      eax,eax
        jnz     short lgln3             ; Error - Exit

;       (ES:DI) -> LibName
;       Move name to user space via SuBuffz

        mov     ecx,[BuffLen]
        cmp     ecx,MAXPATHLEN          ; if (user buff len > temp buff len)
        jb      short lgln1

        mov     ecx,MAXPATHLEN          ; user buff len = temp buff len
lgln1:  mov     ax,es
        mov     ds,ax
        mov     si,di                   ; (DS:SI) = temp buff
        push    es
        push    edi
        mov     edi,[pBuff]
        ; ok to use Tiled version as r/w data can not be segment packed
        LaToSelTiled edi
        mov     bx,ax                   ; (BX:DI) = user buff
        CALL16  SuBuffz
        pop     edi                     ; (ES:DI) = offset of temp buff
        pop     es
        jc      short lgln2

        xor     eax,eax
lgln2:  CALL16  FreeTempBuffer
lgln3:  movzx   eax,ax
        RestoreReg <es,ds,esi,edi,ebx>
        LeaveProc
        RETC
EndProc _LDRGetLibName




;**     ldrGetLibNameSub - Get Module pathname into a kernel address.
;
;       ldrGetLibNameSub returns the address of a temporary kernel
;       buffer (allocated via GetTempBuffer) which is filled with the
;       module's fully qualified pathname.  When the caller is finished
;       with that temporary buffer, the caller must call FreeTempBuffer
;       to free the buffer.
;
;       ldrGetLibNameSub is not C callable.
;
;       If an error is indicated, the temporary buffer will already
;       be free.
;
;       ENTRY   (EAX) = hMTE    - mte handle to
;
;       EXIT    (EAX) = 0       No error
;                       (ES:DI) -> temporary BMP buffer
;               (EAX) != 0      Error, error code in EAX
;
;       USES:   EAX, EBX, ECX, EDX, EDI, ESI, ES, DS, EFlags

Procedure ldrGetLibNameSub,near
        ASSUME DS:NOTHING, ES:NOTHING, FS:NOTHING, GS:NOTHING, SS:NOTHING

        push    eax
        CALL16  GetTempBuffer           ; (ES:DI) -> temp buff (can't fail)
        pop     eax
        mov     ebx,MAXPATHLEN
        push    es
        push    edi
        CALL16  w_getmodname            ; (ES:DI) -> Module name, in buffer.
        pop     edi
        pop     es
        jnc     short lglns1

        CALL16  FreeTempBuffer          ; Error - call FreeTempBuffer
lglns1: movzx   eax,ax                  ; (EAX) = Error code, if any.
        RETC
EndProc ldrGetLibNameSub

HIGH32CODE      ENDS



        FARCODE HIGH3
HIGH3CODE SEGMENT


Procedure w_loadmodule,faronly
        CALLF32 w_loadmodule,,nocheck
        ret
EndProc w_loadmodule



;***    w_QAppType - Get application type
;
;       ENTRY   (DS:DX) = pointer to module name
;
;       EXIT    Carry set
;                   (AX) = error code
;               Carry clear
;                   (AX) = application type
;
;       USES    All except BP
;
        ASSUME DS:NOTHING, ES:NOTHING, FS:NOTHING, GS:NOTHING, SS:NOTHING

Procedure w_QAppType,faronly

LocalVar  qat_ulType,DWORD

        EnterProc
        mov     ax,ds
        movzx   eax,ax
        movzx   edx,dx
        FlatContext <ds,es>,ecx

;       laddr|-1 = SELVirtToLin(off, sel)

        CCALL32 _SELVirtToLin, <edx,eax>
        cmp     eax,-1
        stc
        je      short qatx             ; Invalid address
        SSToDS  ebx,[qat_ulType]

;       rc = LDRQAppType(pszModName, pulType)

        CCALL32 _LDRQAppType, <eax,ebx>
        cmp     eax,1                   ; was there an error?
        cmc
        jc      short qatx              ; jump if error

qat10:  mov     eax,[qat_ulType]        ; (AX) = type

qatx:   LeaveProc
        ret
EndProc w_QAppType



;***EP  w_getprocaddr - Get dynamic link procedure address.
;
;       returns a far address to the desired procedure
;       within a dynamic link library module.
;
;       (ES:DI) must be a valid kernel-space address.
;
;       ENTRY   (AX) = ModuleHandle
;               (ES:DI) -> ASCIIZ ProcName
;
;               ModuleHandle is the handle to the library module to
;               find the procedure in.
;
;               ProcName is the address of an ASCIIZ name string
;               containing the procedure name that is being referenced
;               in the library module file.  Alternately if the selector
;               portion of the pointer is null, then the offset portion
;               of the pointer is an explicit entry number within the
;               library module.
;
;       EXIT    Carry Clear
;                   (DX:AX) = procedure address
;               Carry Set
;                   (AX) = Error Code
;
;       USES    ALL except BP


        ASSUME DS:NOTHING, ES:NOTHING, SS:NOTHING
Procedure w_getprocaddr,<faronly>
LocalVar pAddress, DWORD
        EnterProc

        movzx   esi,ax                  ; ESI=module handle

;       if (selector part of ProcName == NULL)
;         ordinal# = offset part of ProcName

        mov     ax,es
        movzx   eax,ax
        movzx   edi,di
        FlatContext <ds,es>,dx
        or      ax,ax
        jnz     short wgpa1
        movzx   ebx,di                  ; EBX=ordinal, EAX=NULL
        jmp     short wgpa2

;       laddr|-1 = SELVirtToLin(off, sel)

wgpa1:  CCALL32 _SELVirtToLin, <edi,eax>
        xor     ebx,ebx                 ; EBX=ordinal (0)
        cmp     eax,-1                  ; Invalid address
        jnz     short wgpa2
        stc
        jmp     short wgpax

;       rc = LDRGetProcAddr(hmte, ulord, pchname, paddress, fflat, penttype)

wgpa2:  SSToDS  edx,pAddress
        CCALL32 _LDRGetProcAddr, <esi,ebx,eax,edx,0,0>
        xor     edx,edx                 ; clear reg for error path
        or      eax,eax
        stc
        jnz     short wgpax
        mov     edx,pAddress
        mov     ax,dx
        shr     edx,16
        clc
wgpax:  LeaveProc
        ret
wgpae:  mov     eax,ERROR_PROC_NOT_FOUND
        stc
        jmp     short wgpax
EndProc w_getprocaddr




;**     w_getmodhandle - Get dynamic link library module handle.
;
;       Returns the handle to a library module that was previously loaded.
;       An explicit call to DOSLOADMODULE need not have been made.
;
;       If path characters are not present, CLASS_GLOBAL will be
;       searched before anything else.  If that fails, then all classes
;       are searched using the full pathname, just as if path characters
;       were specified.
;
;       ENTRY   (ES:DI) -> ModuleName
;
;               ModuleName is the address of an ASCIIZ name string
;               containing the library module name.
;
;       EXIT    Carry Clear
;                   (AX) = 0 (Success)
;                   (BX) = Library Module Handle
;               Carry Set
;                   (AX) = Error Code
;
;       USES    ALL, except EBP
;
;       COMMENTS
;               The module name must match the name of the module
;               defined by the LIBRARY statement of the library's
;               corresponding .DEF file.  An error code is returned
;               if the library module is not loaded.  This handle
;               may only be used for calls to the w_getmodname
;               function.

        ASSUME DS:NOTHING, ES:NOTHING, FS:NOTHING, GS:NOTHING, SS:NOTHING

Procedure w_getmodhandle,<hybrid, far16>

LocalVar pmte,DWORD
LocalVar p16ModName,DWORD
LocalVar pchName,DWORD
LocalVar cchName,DWORD
LocalVar class,BYTE

;       PMONLY

        EnterProc
        mov     [p16ModName.Segmt],es
        mov     [p16ModName.Offst],di

        FlatContext <ds,es>,ax

;       rc = LDRGetSem()

        LDRGetSem
        or      eax,eax
        jnz     wgmhx                  ; Can't get loader sem - return error.

        mov     ecx,[_pPTDACur]

        movzx   eax,p16ModName.Segmt
        movzx   ecx,di                  ; DI=p16ModName.Offst

;       laddr|-1 = SELVirtToLin(off, sel)

        CCALL32 _SELVirtToLin,<ecx,eax>
        mov     pchName,eax
        cmp     eax,-1
        je      wgmh7                   ; Error getting address...

        lds     si,[p16ModName]         ; (DS:SI) -> Path name
        ASSUME  DS:NOTHING

        CALLFAR DStrLen
        dec     cx
        movzx   ecx,cx
        mov     [cchName],ecx

;       If input name a pathname?

        call    is_path                 ; ZR -> path chars present
        mov     class,CLASS_ALL + SEARCH_BY_PATH
        mov     ax,CHECK_WILDCARDS      ; No wildcards allowed in path.
        jz      short wgmh1             ; It's a path.

        mov     class,CLASS_GLOBAL
        mov     ax,CHECK_ALL            ; no path chars or wildcards allowed

;       At this point:
;       If name contains path characters:
;               class: Search all modules, using the path name.
;               (AX) = CHECK_WILDCARDS (input to CheckModName)
;       If name contained no path characters,
;               class: Search global class only, not using the path name.
;               (AX) = CHECK_ALL (input to CheckModName)

wgmh1:  call    CheckModName            ; Check for any illegal characters.
        FlatContext <ds>
        jc      wgmh9                   ; Name illegal - exit.

        cmp     [class],CLASS_GLOBAL    ; Search global class?
        jne     short wgmh3             ; no - prepare to search all modules.

;       ldrFindModule requires that the module name is uppercased

wgmh2:  CCALL32 _ldrUCaseString,<[pchName],[cchName]>

;       Call _ldrFindModule to search either CLASS_CLOBAL or CLASS_ALL.
;       Note that there is a jump from below back up to this point.
;       class = class to search.
;       (CX) = Name length.
;       [pchName] = Flat address of module's mod/path name

        SSToDS  eax,[pmte]              ; place to return pointer to mte
        movzx   edx,[class]

;       rc = ldrFindModule(pachModname, cchModname, class, ppmte)

        CCALL32 _ldrFindModule,<[pchName],[cchName],edx,eax>
        or      eax,eax
        jnz     short wgmh9             ; error - return

        mov     eax,[pmte]
        or      eax,eax                 ; [pmte] = 0 if module not present
        jnz     short wgmh8             ; Module found - return handle.

        cmp     class,CLASS_ALL + SEARCH_BY_PATH
        je      short wgmh7             ; Module can not exist.  Exit.

;       Either _ldrFindModule failed on CLASS_GLOBAL, or we have a module
;       name with path characters.   We now search all the modules using
;       the full path name.
;
;       rc = ldrTransPath(pchName)

wgmh3:  CCALL32 _ldrTransPath,<[pchName]>
        or      eax,eax
        jnz     short wgmh7

;       _ldrTransPath has now placed the fully qualified path in ldrFileNameBuf
;       Adjust the pointers to reflect this.

        mov     class,CLASS_ALL + SEARCH_BY_PATH
        mov     edi,_ldr16pFileNameBuf
        mov     [p16ModName.Offst],di
        ror     edi,16
        mov     [p16ModName.Segmt],di
        mov     edi,_ldrpFileNameBuf
        mov     pchName,edi

;       Get new (Full path) string length.

        CALLFAR StrLen32                ; (ECX) = ModuleName/FullPathName len

        mov     [cchName],ecx

;       Call _ldrFindModule again to search all modules using the path.

        jmp     short wgmh2

;       Error.

wgmh7:  mov     eax,ERROR_MOD_NOT_FOUND ; (EAX) = errorcode module not loaded
        jmp     short wgmh9

;       Success.  (EBX) gets the module handle.

wgmh8:  mov     eax,[pmte]
        movzx   ebx,[eax].mte_handle    ; (EBX) = mte handle
        xor     eax,eax                 ; (EAX) = NO_ERROR

;       Exit.
;       (EAX) = Error code

wgmh9:  push    eax

;       LDRClearSem()

        CALLF32 _LDRClearSem
        pop     eax
wgmhx:  LeaveProc                       ; Error from _LDRGetSem.
        cmp     eax,1
        cmc                             ; Set carry if (EAX) nonzero.
        ret
EndProc w_getmodhandle




;**     w_getmodname - Get dynamic link library module pathname.
;
;       w_getmodname returns the fully qualified pathname associated
;       with the referenced module handle.
;
;       If ModuleHandle is the handle to DOSCALLS, we return the fully
;       qualified path to DOSCALL1, but with the '1' changed to an 'S'.
;       Even though there is no file at this path, the pathname may be
;       used by the program or utility to construct the name of a
;       different but related file by altering the file extension.  The
;       user should NOT expect this path to point to a valid file.
;
;       ENTRY   (AX) = ModuleHandle
;               (BX) = BufferLength
;               (ES:DI) -> ASCIIZ ModuleName
;
;               ModuleHandle is the handle to the library module to
;               return the pathname to.
;
;               BufferLength is the maximum length of the pathname
;               buffer.
;
;               ModuleName is the address of the buffer where the null
;               terminated module pathname will be returned.
;
;       EXIT    Carry Clear
;                   (AX) = 0 (Success)
;               Carry Set
;                   (AX) = Error Code
;
;               If Success, the ModuleName buffer is filled in.
;
;       USES    ALL except BP
;
;       COMMENTS
;               If the buffer is not large enough an error will be
;               returned.

        ASSUME DS:NOTHING, ES:NOTHING, FS:NOTHING, GS:NOTHING, SS:NOTHING

Procedure w_getmodname,<hybrid, far16>
;       PMONLY

        LocalVar        <wgmn_modhndl>,WORD
        LocalVar        <wgmn_buflen>,WORD
        LocalVar        <wgmn_nameptr>,DWORD
        LocalVar        SelTemp,WORD
        EnterProc       <ax,bx,es,di>

        FlatContext <ds,es>,cx

;       rc = LDRGetSem()

        LDRGetSem                       ; Get loader semaphore.
        or      eax,eax
        jnz     wgmn9                   ; any error propogated.

;       Check that the given handle is an MTE handle.

        mov     dx,wgmn_modhndl         ; (DX) = passed-in handle
        call    CheckModHandle          ; check the handle.
        jc      wgmn6                   ; ERROR_INVALID_HANDLE if invalid

;       If the handle is to DOSCALLS, transform it to the DOSCALL1 handle.
;       (DX) = Valid passed-in handle

        cmp     dx,_DosModHdl           ; Is it the DOSCALLS handle?
        jne     short wgmn0             ; No, skip handle adjustment

        movzx   edx,_hMteDoscall1       ; (DX) = DOSCALL1 MTE handle.
        mov     eax,_pmteDoscall1       ; (EAX)= pointer to DOSCALL1 MTE
        jmp     short wgmn1

;       Map to the MTE so that we can retrieve the module's full path.
;       (EDX) = target MTE handle - different than wgmn_modhndl if DOSCALLS.

wgmn0:  movzx   edx,dx
        push    edx
        CCALL32 _ldrASMpMTEFromHandle,<edx>
        pop     edx                     ; (EDX) = target mte handle

wgmn1:  mov     eax,[eax].mte_swapmte   ; (EAX) = swappable mte pointer
        mov     esi,[eax].smte_path     ; (ESI) -> module path name in MTE
        mov     edi,esi                 ; (EDI) -> module path name in MTE
        movzx   ecx,wgmn_buflen         ; (ECX) = buffer length
        or      ecx,ecx
        jz      wgmn7                   ; ERROR_BAD_LENGTH if no buffer

        xor     eax,eax
        repne   scas byte ptr es:[edi]  ; Find NULL byte
        jne     wgmn7                   ; ERROR_BAD_LENGTH if buffer too small

        sub     edi,esi
        mov     ecx,edi                 ; (ECX) = strlen(path) + 1

;       Copy the module's full path name from the mte to caller's buffer
;       (ECX) = length of null-terminated module name.
;       (DX) = target MTE handle.
;       (DS:SI) -> module path name in MTE

        push    ecx                     ; save length

;       NOTE: need to convert linear addr to (sel:off) -> (DS:SI)

        mov     [SelTemp],0
        SSToDS  ebx,SelTemp

;       rc = SELLdrDesc(laddr, cb, psel)

        CCALL32 _SELLdrDesc,<esi,ecx,ebx>
        pop     ecx                     ; restore length
        or      eax,eax
        jz      short wgmn4             ; no error.  Continue

        Panic   <'w_getmodname: SELLdrDesc'>

wgmn4:  mov     ds,[SelTemp]
        xor     si,si                   ; (DS:SI) -> pathname in mte
        mov     bx,wgmn_nameptr.Segmt
        mov     di,wgmn_nameptr.Offst   ; (BX:DI) -> user's buffer
        CALLFAR SuBuff                  ; copy the modname (DS:SI) -> (BX:DI)
        FlatContext <ds,es>,dx
        jc      short wgmn5             ; any error propogated.

        xor     eax,eax                 ; (EAX) = 0, assuming no error.
        mov     dx,wgmn_modhndl         ; get pass mte handle
        cmp     dx,_DosModHdl           ; is it DOSCALLS ?
        jne     short wgmn5             ; no - return success.

;       The user wants the DOSCALLS path, and the SuBuff sucessfully copied
;       out the path to DOSCALL1.  So, we change the '1' in DOSCALL1 to
;       an 'S', resulting in a 'path' to DOSCALLS.  (DI) is the offset
;       to the byte after the null byte, so (DI-6) is the '1'.

        mov     bx,wgmn_nameptr.Segmt
        sub     di,6                    ; (BX:DI) -> '1' in "DOSCALL1.DLL"
        mov     al,'S'                  ; explicitly change '1' to 'S'.
        CALLFAR SuByte                  ; set the byte at (BX:DI) to 'S'.
        FlatContext <ds,es>,dx
        jc      short wgmn5             ; any error propogated.

        xor     eax,eax                 ; (EAX) = NO_ERROR
wgmn5:  push    eax

        movzx   eax,[SelTemp]           ; selector to return

;       rc = SELFreeGDT(sel)

        CCALL32 _SELFreeGDT,<eax>
        pop     eax
        jmp     short wgmn8             ; return error, if any

wgmn6:  mov     eax,ERROR_INVALID_HANDLE; CheckModHandle failed.
        jmp     short wgmn8             ; return error

wgmn7:  mov     eax,ERROR_BAD_LENGTH    ; caller's buffer too small.

;       release loader semaphore
;       (EAX) = 0 if no error, else (EAX) = error code.

wgmn8:  push    eax

;       LDRClearSem()

        CALLF32 _LDRClearSem
        pop     eax                     ; (EAX) = error code or zero
wgmn9:  cmp     eax,1                   ; set carry according to (AX) value
        cmc
        LeaveProc
        ret
EndProc w_getmodname




;***    CheckModHandle - Check that handle is an mte handle
;
;       This routine checks that a given handle is the handle of some
;       module table entry.  The table of loaded modules is traversed;
;       this is a linked list of module table entries, each link being
;       the pointer to the next entry.
;
;       ENTRY   DX = handle to be verified
;
;       EXIT    Carry clear :
;                       handle is an mte handle
;                       (SI) = (DX) = MTE handle that was verified.
;               Carry set if not an MTE handle
;
;       USES    DS, ESI

    ASSUME DS:NOTHING,ES:NOTHING,SS:NOTHING

; BUGBUG - duplicate function to ldrValidateMteHandle in ldrsubr.c

Procedure       CheckModHandle,near
;       PMONLY

        FlatContext ds,ax
        mov     esi,_mte_h              ; get head of module table list

cmh1:   or      esi,esi                 ; end of module list ?
        jz      short cmh2              ; jump if end of list reached
        cmp     dx,[esi].mte_handle     ; compare mte handle with one given
        jz      short cmh3              ; jump if given handle found
        mov     esi,[esi].mte_link      ; save next mte's handle
        jmp     short cmh1              ; loop to check this one out
cmh2:   stc                             ; handle not an mte handle
        ret
cmh3:   mov     si,dx                   ; save handle in si
        ret                             ; dx still has input handle

EndProc CheckModHandle



;**     w_QueryABIOSSupport - return flags to define ABIOS status
;
;       ENTRY   no parms (dword reserved 0)
;
;       EXIT    (AX) = flags
;

        ASSUME DS:NOTHING, ES:NOTHING, FS:NOTHING, GS:NOTHING, SS:NOTHING

Procedure w_QueryABIOSSupport,<hybrid, far16>
;       PMONLY

        EnterProc

        FlatContext <ds,es>,ax

        movzx   eax,_BasicHWConfig
        stc

        LeaveProc
        ret
EndProc w_QueryABIOSSupport




;***EP  w_ldrDirtyWorker
;
;       ENTRY
;
Procedure w_ldrDirtyWorker,faronly
    assume ds:nothing, es:nothing, ss:nothing, fs:nothing, gs:nothing

        push    ds
        push    es

        movzx   eax,ax       ; command code

        xor     ecx,ecx      ; bugbug - lost pointer parm in bx:cx

        FlatContext <ds,es>, dx

        call far ptr FLAT:_ldrDirtyWorkerThunk

        pop     es
        pop     ds

        or      ax,ax         ; set C if error
        jnz     short lDWerr
        clc
        ret

lDWerr: stc
        ret

EndProc w_ldrDirtyWorker




HIGH3CODE ENDS

        END
