;       SCCSID = @(#)vmname.asm 6.2 91/08/14
SCCSID equ "@(#)vmname.asm      6.2 91/08/14"
page    ,132

title   vmname - Named Shared Memory Routines

;***    OS2 Shared Memory Routines
;
;       IBM/Microsoft Confidential
;
;       Copyright (c) IBM Corporation 1987, 1989
;       Copyright (c) Microsoft Corporation 1987, 1989
;
;       All Rights Reserved
;
;       This module implements the Shared Memory system call which
;       maintains memory regions for private use between two or
;       more tasks.
;
;       Shared memory areas have no underlying structure.  It is up to
;       the cooperating tasks to define their own format and protocols
;       for shared access to the memory area.
;
;       The names and locations of shared memory segments are maintained
;       in a separate movable segment.  This separate segment is managed
;       by a Record Management Package.  This package provides a compact
;       method of storing the data required to maintain the shared memory
;       segments.

;
;       Change History
;
;       34695   92/09/14        BJ Hargrave  OS/2 Change Team
;                               Added check to _vmGetNamedShrMem to check
;                               for overflow when incrementing the local
;                               reference count. ERROR_GEN_FAILURE is returned
;                               in overflow condition. Also a short jump was
;                               modified because it went out of range.
;                               (See defect 53432 for 1.3 fix.)
;

.xlist
include dosseg.inc
.xcref
include fnomem.inc
include mi.inc
include basemaca.inc
include osmaca.inc
include defdbug.inc
include error.inc
include seldesc.inc
include vmowner.inc
include virtsym.inc
include osconst.inc
include kmode.inc
include pgexport.inc
include vmexport.inc
include selxport.inc
include semxport.inc
include dossymc.inc          ; included for tkptda.inc
include except.inc           ; included for tkptda.inc
include sigdef.inc           ; included for tkptda.inc
include tkexport.inc         ; included for tkptda.inc
include infoseg.inc          ; included for tkptda.inc
include sem.inc              ; included for tkptda.inc
include screen.inc           ; included for tkptda.inc
include curdir.inc           ; included for tkptda.inc
include dirent.inc           ; included for tkptda.inc
include find.inc             ; included for tkptda.inc
include tkptda.inc
.cref
.list

CpuMode 386

        ModName vmname,FNO_VMNAME_ASM

        ReferFlat       _pPTDACur,DWORD
        ReferFlat       _pTCBCur,DWORD
        ReferFlat       _TKSSBase,DWORD
        ReferFlat       _pobvmZero,DWORD

        EXTRN32         _KSEMAlloc
        EXTRN32         _KSEMRequestExclusive
        EXTRN32         _KSEMRelease
        EXTRN32         _SELAllocKHB
        EXTRN32         _SELGetDescInfo
        EXTRN32         _VMGetOd
        EXTRN32         _VMAllocMem
        EXTRN32         _VMAttach
        EXTRN32         _VMGetOwner
        EXTRN32         _VMDonateKHB
        EXTRN32         _vmCopyKHB
        EXTRN32         _VMFreeMem
        EXTRN32         _VMClearSem

        EXTRNFAR        CanonBuf
        EXTRNFAR        Rmp_Find
        EXTRNFAR        Rmp_Add
        EXTRNFAR        Rmp_Rem
        EXTRNFAR        FreeTempBuffer


        EXTRN16         CanonBuf
        EXTRN16         Rmp_Alloc
        EXTRN16         Rmp_Add
        EXTRN16         Rmp_Find
        EXTRN16         Rmp_Rem
        EXTRN16         FreeTempBuffer

; Error Equates  -  local versions from error.inc

NAME_NOT_FOUND  EQU  ERROR_FILE_NOT_FOUND

;   Allocation flags, used by w_AllocShrSeg

WAS_VM1 equ VMAC_LOCANY or VMAC_ALIGNSEL or VMAC_SELALLOC or VMAC_OBAPI
WAS_VM2 equ VMAC_ARENASHR
WAS_PG equ PG_SWAPPABLE or PG_U or PG_R or PG_W or VMAC_SHARED


DATA segment

; Moved to sdata.asm for System Anchor Block - f122427
        extrn  ShareRmpStruc:dword

DATA ends


_DATA SEGMENT
; Moved to sdata.asm for System Anchor Block - f122427
        extrn  _vmNamShrSem:dword

_vmNamShrPrfx   db  _vmNamShrPrfxLen,'\SHAREMEM\'  ; shared segment name prefix
_vmNamShrPrfxLen    EQU     $-_vmNamShrPrfx-1
_DATA ENDS


        FARCODE HIGH3
HIGH3CODE segment

;***EP  w_AllocShrSeg - Create a Named Shared Memory Segment, 0 - 64K
;***    w_AllocShrProtSeg - Create a Protected Named Memory Segment, 0 - 64K
;
;       w_AllocShrSeg implements the DOSALLOCSHRSEG system function,
;       issued by tasks to create a shared memory area with the specified
;       name and size.  w_AllocShrProtSeg allocates the memory segment from
;       protected memory.
;
;       ENTRY   w_AllocShrSeg:
;               BX = size of shared area, in bytes (0 means 65536)
;               DS:DX = address of name string - must start with \SHAREMEM\
;       EXIT    'C' set if error
;                 AX = ERROR_INVALID_NAME
;                 AX = ERROR_ALREADY_EXISTS
;                 AX = ERROR_NOT_ENOUGH_MEMORY
;                 AX = ERROR_INTERRUPT
;               'C' clear if OK
;                 AX = selector to shared memory segment
;       USES    EAX, EBX, ECX, EDX, ESI, EDI, DS, ES, EFlags

Procedure w_AllocShrProtSeg,hybrid
    assume ds:nothing,es:nothing,fs:nothing,gs:nothing,ss:nothing
    LocalVar was_name,DWORD     ; address to the name of segment
    LocalVar was_ac,<size vmac_s>

        mov     edi,VMAC_PROT           ; allocate protected memory
        jmp     short was1

Entry   w_AllocShrSeg,hybrid
        xor     edi,edi                 ; allocate unprotected memory

was1:   movzx   ebx,bx
        or      bx,bx
        jnz     short was2
        mov     ebx,SEL_64K             ; make 65536 mean 65536

was2:   EnterProc <ds, dx>              ; address of name string

        or      edi,WAS_VM1 or WAS_VM2 or WAS_PG        ; VMAlloc flags

;       MMIOPH Performance Hook Removed
;       Maj/Min = 2462H, Data = name, bx (string at ds:dx, size)

;   _vmAllocNamedShrMem(cb, cbCom, pBuf, flsel, flvm, pac, owner)
;   (EBX) = size to commit
;   (EDI) = VM flags

        FlatContext <ds,es>, si
        mov     esi,[_pTCBCur]
        mov     esi,[esi].TCB_pFrameBase
        movzx   eax,[esi].ssf_cs
        CCALL32 _VMGetOwner <eax, [esi].ssf_eip>

        SSToDS  esi, ss:was_ac

        CCALL32  _vmAllocNamedShrMem <SEL_64K, ebx, [was_name], \
                  dword ptr (SELAL_SELMAP or SELAL_W or D_DPL3), edi, esi, eax>

        or      ax, ax
        stc
        jnz     short wasx

        mov     ax, [was_ac].ac_sel
        clc

;       MMIOPH Performance Hook Removed
;       Maj/Min = 24E2H, Data = ax (selector)

wasx:   LeaveProc                       ; Clean up the stack
        ret
EndProc w_AllocShrProtSeg


BREAK <Assign an existing shared memory area to the current task>

;***EP  w_GetShrSeg - Assign access to an existing Shared Memory segment
;
;
;       ENTRY   w_GetShrSeg:
;               DS:DX = name string
;       EXIT    'C' set if error
;                 AX = ERROR_INVALID_NAME  (if badly formed name)
;                 AX = NAME_NOT_FOUND
;                 AX = ERROR_NOT_ENOUGH_MEMORY
;                 AX = ERROR_ACCESS_DENIED
;               'C' clear if OK
;                 AX = selector to shared memory segment
;       USES    EAX, EBX, ECX, EDX, ESI, EDI, DS, ES, EFlags

Procedure w_GetShrSeg,hybrid
    assume ds:nothing,es:nothing,fs:nothing,gs:nothing,ss:TASKAREA
    LocalVar wgs_pBuf,DWORD
    LocalVar wgs_ac,<size vmac_s>  ; VMAC return buffer

        EnterProc <ds, dx>          ; Address of buffer

;       MMIOPH Performance Hook Removed
;       Maj/Min = 2463H, Data = name (string at ds:dx)

;       _vmGetNamedSharedMem(pBuf, fl, pac)

        FlatContext <ds, es>, si
        SSToDS  eax, wgs_ac
        CCALL32 _vmGetNamedShrMem <[wgs_pBuf], \
                    (PG_R or PG_U or PG_W or PG_NOINCR), eax>
        or      eax, eax
        stc
        jnz     short wgsx

        mov     ax, [wgs_ac].ac_sel
        clc

;       MMIOPH Performance Hook Removed
;       Maj/Min = 24E3H, Data = ax (selector)

wgsx:   LeaveProc                       ; Clean up the stack
        ret
EndProc w_GetShrSeg


HIGH3CODE ends


        FARCODE HIGH32
SWAP32_13 segment

;***LP  _vmAllocNamedShrMem - Allocate named shared memory
;
;       _vmAllocNamedShrMem allocates named shared memory. This procedure
;       is c callable.
;       WARNING: This procedure is in swappable code.
;
;       1.  Validate the name & determine if the named segment exists.
;       2.  Add 2 new records tp the rmp segment, one global and one local.
;       3.  Allocate a segment of memory from the system pool.
;       4.  Initialize the handles and ref. counts in rmp records.
;       5.  Return the selector to the segment, or the error code
;
;       ENTRY   cb   - Size of memory to be allocated
;               cbCom- size to be committed (<= cb)
;               pBuf - 16:16 address of the name string
;               flsel- Selector flags (see SELAL_ in selxport.h)
;               flvm - VM flags (see VMAC_ in vmexport.h, PG_ in
;                           pgexport.h)
;               pac  - Pointer to VMAC buffer to return address and selector
;               hmte - mte of calling module (for ownership tracking)
;       RETURN  EAX  - Error code (NO_ERROR if no error)
;                       If no error, pac->ac_sel and pac->ac_va filled with
;                       selector and address of the new memory
;
;       USES    EAX, ECX, EDX, Eflags
;       int  vmAllocNamedShrMem(cb, cbCommit, pBuf, flsel, flvm, pac)
;
Procedure _vmAllocNamedShrMem, near
    assume ds:FLAT, es:FLAT, fs:nothing, gs:nothing, ss:nothing
    ArgVar ans_cb,DWORD             ; size of memory
    ArgVar ans_cbCom,DWORD          ; size to be committed
    ArgVar ans_pBuf,DWORD           ; 16:16 address of name string
    ArgVar ans_flsel,DWORD          ; Selector flags
    ArgVar ans_flvm,DWORD           ; VM flags
    ArgVar ans_pac,DWORD            ; Pointer to return info
    ArgVar ans_hmte,DWORD           ; user's hmte (for ownership)

    LocalVar    ans_name,DWORD      ; 16:16 pointer to temporary buffer
    LocalVar    ans_record,DWORD    ; 16:16 address of global record

        EnterProc

        ; Save registers for C calling conventions

        SaveReg <ebx, esi, edi>

;       es:di=address of temp.buffer = CanonBuf(ds:si=address of namestring,
;               bx = offset in temp.buffer)

        lds     si, [ans_pBuf]
        xor     bx, bx
        CALL16  CanonBuf            ; check name for syntax, alloc temp buf.
        mov     [ans_nameh],es
        mov     [ans_namel],di          ; must set these up before exiting
        jc      ansx

        ; Obtain named shared semaphore
        ; KSEMRequestExclusive(sem handle, timeout)

        FlatContext <ds, es>, cx
        CCALL32 _KSEMRequestExclusive,<[_vmNamShrSem],-1>
        or      eax, eax
        stc
        jnz     ansx                    ; return if error

;       Validate the name & see if it is already claimed
;       ((DS:ESI)= addr. name string excluding prefix, ecx=length, ax=error)
;        = ValidateName(ds:esi=address of name string inc. prefix,
;          es:edi = address of prefix)

        xor     esi, esi
        lds     si,[ans_name]
        mov     edi,offset FLAT:_vmNamShrPrfx
        CCALL32 ValidateName,,nocheck
        jc      ans90

;       Check to see if this region already exists, if so, error.
;       (CX) = Size of name (bytes - \SHAREMEM\ + null)
;       (DS:SI) =  points to the match pattern
;
;   (es:di=addr of match, cx=size) = Rmp_Find(ds:si=addr of pattern to match,
;   cx=length,es:di=address of handle to managed segment,bx=offset into record)

        mov     bx,offset sm_name       ; BX = offset in record to start match
                                        ; CX = length of match pattern
        DosContext es, di
        mov     di,offset DOSGROUP:ShareRmpStruc
        CALL16  Rmp_Find                ; find a record with a matching name
        mov     ax,ERROR_ALREADY_EXISTS
        jnc     ans90

;       Add new global record for shared memory segment data
;       (CX) = Size of name (bytes - \SHAREMEM\ + null)
;
;   (es:di=addr of new record) = Rmp_Add(ds:si=addr of structure to add,
;   cx=legth, es:di=address of handle to managed segment)

        lds     si,[ans_name]
        add     si,_vmNamShrPrfxLen-sm_name ; remove prfx and add space for data
        add     cx,offset sm_name       ; CX = record size
        DosContext es,di
        mov     di,offset DOSGROUP:ShareRmpStruc
        CALL16  Rmp_Add                 ; add new global record data
    assume es:nothing
        mov     ax,ERROR_NOT_ENOUGH_MEMORY
        jc      ans90

        xor     eax,eax
        mov     es:[di].sm_handle,ax
        inc     ax                      ; set global reference count to 1
        mov     es:[di].sm_refcount,ax
        mov     [ans_recordh],es        ; save address of global record
        mov     [ans_recordl],di

;       Global record added, now add local record

        mov     cx, size lsm_s              ; length of local record
        DosContext es, di
        mov     di, offset DOSGROUP:ShareRmpStruc
        lds     si,[ans_name]           ; fill in garbage for now
        CALL16  Rmp_Add                 ; add local record
  assume es:nothing
        jnc     short ans30

        push    dword ptr ERROR_NOT_ENOUGH_MEMORY     ; push error code
        jmp     short ans50

ans30:  xor     eax,eax                 ; set zero bytes in local record
        mov     es:[di].lsm_seghandle,ax ; set segment handle to zero for now
        inc     ax                      ; set local ref.count = 1
        mov     es:[di].lsm_refcount,ax
        FlatContext ds, si
        mov     edx, [_pPTDACur]
        movzx   edx,[edx].ptda_handle   ; edx = ptda
        mov     es:[di].lsm_ptda,dx     ; set local record's ptda

;       rmp records created, now allocate shared segment
;       (EDX) = ptda
;       (ES:DI) = Address of local record
;       (DS) = FLAT

        push    es                      ; save rmp local record address
        push    di

        FlatContext es, si

;       (EDX) = PTDA

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


        CCALL32  _VMAllocMem <[ans_cb], [ans_cbCom], [ans_flvm], \
                edx, MSHAREOWNER, [ans_hmte], [ans_flsel], 0, [ans_pac]>
        pop     di                      ; restore local rmp record address
        pop     es
        assume es:nothing
        or      eax, eax
        jz      short was80             ; no error

;   Error, release rmp records

        push    eax
        DosContext ds,ax
        mov     bx,offset DOSGROUP:ShareRmpStruc
        CALL16  Rmp_Rem                 ; remove the new local record

;   (SS:SP) = error code

ans50:  les     di,[ans_record]         ; address of global record
        DosContext ds,ax
        mov     bx,offset DOSGROUP:ShareRmpStruc
        CALL16  Rmp_Rem                 ; remove the new global record
        pop     eax                     ; eax= error code
        jmp     short ans90

;       Memory allocated, initialize shared memory records.
;       (ES:DI) = Address of local record.
;       (DS) = FLAT

was80:  mov     ebx, [ans_pac]
        mov     ax,[ebx].ac_hob
        mov     es:[di].lsm_seghandle,ax        ; set handle in local record
        mov     di,[ans_recordl]                ; es:di=addr of global record
        mov     es:[di].sm_handle,ax    ; set handle in global record

        ; BUGBUG: VMAllocMem doesn't call SELMGR for reserve only requests
        ;mov     ax, [was_ac].ac_sel
        mov     eax, [ebx].ac_va
        LaToSelTiled eax
        or      ax, SEL_RPL3
        mov     es:[di].sm_selector,ax  ; set selector in global record
        mov     [ebx].ac_sel, ax
        xor     eax, eax                ; return NO_ERROR

ans90:  push    eax
        FlatContext <ds, es>, ax
        CCALL32 _KSEMRelease,<[_vmNamShrSem]>
                                        ; release named shared semaphore
        pop     eax

        ; (ax) = error code

ansx:   movzx   eax,ax
        les     di,[ans_name]
        CALL16  FreeTempBuffer      ; (es:di) = Address of buffer to be freed

        ; Restore registers for c calling convetions

        FlatContext <ds, es>, bx
        RestoreReg <edi, esi, ebx>
        LeaveProc
        RETC
EndProc _vmAllocNamedShrMem

;***LP  _vmGetNamedShrMem - Get access to an existing named shared memory
;
;       _vmGetNamedShrMem obtains access to an existing named shared memory.
;       This procedure is c callable.
;       WARNING: This procedure is in swappable code.
;
;       1.  Validate the name & determine if the named segment exists.
;       2.  Search for local record. If found increment ref. count. If not
;               add the local record and attach the segment.
;       3.  Increment the global reference count.
;       4.  Return the selector and address to the segment, or the error code
;
;       ENTRY   pBuf - 16:16 address of the name string
;               fl   - VMAttach flags (PG_* flags in pgexport.h)
;                       PG_NOINCR means call originated from w_GetShrSeg
;               pac  - Pointer to VMAC buffer to return address and selector
;                       (Address not returned in GDT selector case)
;       RETURN  EAX  - Error code (NO_ERROR if no error)
;                       If no error, pac->ac_sel and pac->ac_va filled with
;                       selector and address of the new memory
;       USES    EAX, ECX, EDX, Eflags
;
;       int  vmGetNamedShrMem(pBuf, fl, pac)
;
Procedure _vmGetNamedShrMem, near
    assume ds:FLAT, es:FLAT, fs:nothing, gs:nothing, ss:nothing
    ArgVar gns_pBuf,DWORD           ; 16:16 address of name string
    ArgVar gns_fl,DWORD             ; VMAttach flags
    ArgVar gns_pac,DWORD            ; Pointer to return info

    LocalVar    gns_name,DWORD      ; 16:16 pointer to temporary buffer
    LocalVar    gns_record,DWORD    ; 16:16 pointer to global record
    LocalVar    gns_od,<size vmod_s>

        EnterProc

        ; Save registers for C calling conventions

        SaveReg <ebx, esi, edi>

;       Copy and canonicalize shared segment name to temporary buffer
;       Returns with temporary buffer allocated even in case of failure.
;       es:di=address of temp.buffer = CanonBuf(ds:si=address of namestring,
;               bx = offset in temp.buffer)

        xor     bx,bx
        lds     si, [gns_pBuf]
        CALL16  CanonBuf
        mov     [gns_nameh],es
        mov     [gns_namel],di           ; must set these up before exiting
        jc      gnsx

        ; Obtain named shared semaphore
        ; KSEMRequestExclusive(sem handle, timeout)

        FlatContext <ds, es>, cx
        CCALL32 _KSEMRequestExclusive,<[_vmNamShrSem],-1>
        or      eax, eax
        stc
        jnz     gnsx                    ; return if error

;       Validate the name & see if it is already claimed
;       ((DS:ESI)= addr. name string excluding prefix, ecx=length, ax=error)
;        = ValidateName(ds:esi=address of name string inc. prefix,
;          es:edi = address of prefix)

        xor     esi, esi
        lds     si,[gns_name]
        mov     edi,offset FLAT:_vmNamShrPrfx
        CCALL32 ValidateName,,nocheck
        jc      gns85

;       Check to see if shared segment already exists
;       (DS:SI) =  points to the match pattern
;       (CX) = Length of match pattern.
;
;   (es:di=addr of match, cx=size) = Rmp_Find(ds:si=addr of pattern to match,
;   cx=length,es:di=address of handle to managed segment,bx=offset into record)

        mov     bx,offset sm_name       ; BX = offset in record to start match
                                        ; CX = length of match pattern
        DosContext es, di
        mov     di,offset DOSGROUP:ShareRmpStruc
        CALL16  Rmp_Find                ; find a record with a matching name
        assume es:nothing
        mov     ax, NAME_NOT_FOUND
        jc      gns85                   ; error if record not found

;       If the segment has a GDT selector, there is no need to change rmp
;       records.
;       (ES:DI) = Address of global record

        mov     cx,es:[di].sm_selector  ; (AX) = selector
        FlatContext ds, bx
        mov     ebx, [gns_pac]
        mov     [ebx].ac_sel, cx
        xor     eax, eax                ; set eax=NO_ERROR
        test    cx,SEL_LDTBIT           ;check for global selector
        jz      gns85                   ; return if GDT sel

        SelToLaTiled cx
        mov     [ebx].ac_va,eax         ; save flat address
        mov     [gns_od].od_laddr,eax   ; also save it in object info
        mov     [gns_recordh],es        ; save address of global record
        mov     [gns_recordl],di

        mov     ax, NAME_NOT_FOUND
        cmp     es:[di].sm_handle, 0    ; is handle valid?
        jz      gns85                   ; if invalid - return error not found

;       Find a context attached to the object, simultaneously taking the
;       object handle semaphore to prevent the context from being freed.
;       Note that to prevent undue serialization, VMFreeMemOd releases
;       the named shared memory semaphore after updating named shared memory
;       data structures and taking the handle semaphore, so the target
;       context could disconnect even though we hold the named shared s4.
;
        FlatContext es, ax
        mov     eax,[_pPTDACur]
        mov     gns_od.od_pPTDA,eax
        SSToDS  eax, gns_od
        CCALL32 _VMGetOd <VMGS_TAKESEM+VMOD_ANY_CTXT+VMOD_BASE, eax>
        or      eax, eax
        jnz     gns85

;       If the object is > 64K and w_GetShrSeg was called, return error.
;       (ES:DI) = global record's address

        test    [gns_fl], PG_NOINCR     ; called from w_GetShrSeg
        jz      short  gns30            ; no skip
        mov     eax,gns_od.od_par
        mov     eax, dword ptr [eax].ar_union5
        and     eax, _ar_cpg
        cmp     eax, SEL_64K SHR PAGESHIFT ; > 64Kb
;;;;;;; cmp     [eax].ar_cpg, SEL_64K SHR PAGESHIFT; > 64Kb
        mov     eax, ERROR_ACCESS_DENIED
        ja      short gns35                   ; yes, return error       ;34695

        and     [gns_fl], not PG_NOINCR    ; don't pass this flag to VMAttach

;       See if local rmp record already exists

gns30:  lds     si,[gns_record]
        push    word ptr [si].sm_handle  ; set up search pattern in stack
        FlatContext ds, bx
        mov     ebx, _pPTDACur
        mov     ax, [ebx].ptda_handle
        push    ax
        push    ss
        pop     ds
        assume ds:nothing
        mov     si,sp                   ; (DS:SI) points to match pattern
        mov     cx,4                    ; cx=no. of bytes to be matched
        mov     bx,offset lsm_ptda      ; bx=offset of match pattern
        DosContext es,ax
        mov     di,offset DOSGROUP:ShareRmpStruc

        CALL16  Rmp_Find                ; returns carry if no local record
assume  es:nothing
        jc      short gns40

;       Local rmp exists, increment local reference count
;       (ES:DI) = Address of local record

        pop     dx                      ; dx = ptda
        pop     si                      ; si = handle
        inc     es:[di].lsm_refcount
        jnz     short gns60             ; go to calling VMAttach        ;34695
        dec     es:[di].lsm_refcount    ; restore refcount              ;34695
        mov     eax, ERROR_GEN_FAILURE  ; set return code               ;34695
gns35:  jmp     short gns90                                             ;34695

;       No local rmp exists, add local rmp

gns40:  lds     si, [gns_name]          ; Fill in garbage for now
        DosContext es,ax
        mov     di, offset DOSGROUP:ShareRmpStruc
        mov     cx, size lsm_s
        CALL16  Rmp_Add                 ; returns es:di = addr. of record
        pop     dx                      ; dx = ptda
        pop     si                      ; si = handle
        jnc     short gns50

        mov     ax,ERROR_NOT_ENOUGH_MEMORY
        jmp     short gns90             ; Jump if Rmp_Add failed        ;34695

;       (ES:DI) = Addr. of local record
;       (DX) = ptda
;       (SI) = handle

gns50:  assume  es:nothing
        mov     es:[di].lsm_seghandle,si ; set local record
        mov     es:[di].lsm_ptda,dx
        xor     eax,eax
        inc     eax
        mov     es:[di].lsm_refcount,ax

; Local record set, now increment global record ref.count since it is a        ; B724765
; new attachment                                                               ; B724765

        mov     di,[gns_recordl]        ; es:di = addr of global record        ; B724765
        inc     es:[di].sm_refcount     ; Increment # of tasks accessing       ; B724765

;       Attach the ptda to shared segment, or modify permissions


;   _VMAttach(hob, hptda,fl)
;   (DX) = hptda
;   (SI) = handle
;   (DI) = offset of local record

gns60:  FlatContext     <ds,es>,ax
        movzx   edx, dx                 ; hptda
        movzx   esi,si                  ; handle
        CCALL32 _VMAttach <esi, edx, [gns_fl]>

;   (AX) = error code

; B724765
; Note: if _VMAttach fails the codepath is as follows:
;
;              _VMAttach               calls
;              _VMFreeMem              calls
;              _VMFreeMemNotifyOd      calls
;              _VMFreeNamedSharedMem
;
;   If we've added the local record VMFreeNamedSharedMem will delete
;   it since it's ref.count which was set to 1 by us will go down to 0,
;   and the global ref.count will be decremented to it's original value.
;   On the other hand, if the local record existed it's ref.count will
;   be > 1 (since we've incremented it) _VMFreeNamedSharedMem will
;   decrement it, but since it's value will be > 0 the global ref.count
;   will remain unchanged.

; *** Commented out for B724765
;       mov     es,[gns_recordh]        ; addr of rmp managed shared segment
;       assume es:nothing
;       or      eax,eax
;       jz      short gns80             ; jmp if success
;       _VMAttach failed, update local record
;       (ES:DI) = address of local record
;       dec     es:[di].lsm_refcount
;       jnz     short gns90         ; skip Rmp_Rem if other references exist
;       DosContext ds,bx
;       mov     bx,offset DOSGROUP:ShareRmpStruc
;       CALL16  Rmp_Rem
;       jmp     short gns90
; Local record set, now increment global record ref.count if necessary
;gns80: cmp     es:[di].lsm_refcount, 1 ; new attachment?
;       jnz     short gns90             ; skip if not
;       mov     di,[gns_recordl]        ; es:di = addr of global record
;       inc     es:[di].sm_refcount     ; Increment # of tasks accessing
; ***  Commented out for B724765

gns90:  FlatContext <ds, es>, dx
        push    eax
        mov     eax,gns_od.od_par
        mov     eax,[eax].ar_hob
        CCALL32 _VMClearSem,<eax>
gns95:  CCALL32 _KSEMRelease,<[_vmNamShrSem]>   ; release named shared semaphore
        pop     eax

;   (AX) = error code

gnsx:   movzx   eax,ax
        les     di,[gns_name]
        CALL16  FreeTempBuffer      ; (es:di) = Address of buffer to be freed

        ; Restore registers for c calling convetions

        FlatContext <ds, es>, bx
        RestoreReg <edi, esi, ebx>
        LeaveProc
        RETC

;   (AX) = error code

gns85:  FlatContext <ds, es>, dx
        push    eax
        jmp     short gns95

EndProc _vmGetNamedShrMem
SWAP32_13      ENDS
SWAP32_13      segment


;***LP  ValidateName - Validate string prefix
;
;       ValidateName verifies that the pathname starts with the
;       specified prefix.
;       WARNING: This code is in swappable code
;
;       ENTRY:  DS:ESI = address name string
;               ES:EDI = address of prefix, count followed by string
;
;       EXIT:   'C' clear if name valid
;                 DS:ESI = address name string excluding prefix
;                 ECX = # of chars in name string
;                    (excluding prefix, including 00 terminator)
;               'C' set if name format invalid
;                 AX = ERROR_INVALID_NAME  (error code)
;
;       USES:   EAX,ECX,ESI,EDI,ES,EFlags

Procedure ValidateName,<near,far32>
    assume ds:nothing,es:nothing,fs:nothing,gs:nothing,ss:nothing

;       Make sure the string starts with the prefix

        xor     ecx,ecx
        mov     cl,es:[edi]              ; ECX = prefix len
        inc     edi
        repe cmpsb                      ; is the prefix valid?
        jne     short vln1              ;  no, it's not the same prefix

        mov     ax, ds
        mov     es, ax
        mov     edi, esi

;    (ES:EDI) = (DS:ESI) = address of name string excluding prefix

        xor     al, al
        xor     ecx, ecx
        dec     ecx
        repne   scasb
        not     ecx

        cmp     ecx, 1                  ; is name invalid?
        jz      short vln1              ;  yes, the name is invalid
        clc                             ; return with 'C' clear
        jmp     short vlnx

vln1:   mov     ax,ERROR_INVALID_NAME   ; Invalid Name string - exit w/ error
        stc
vlnx:   RETC
EndProc ValidateName

;***LP  _vmFreeNamedSharedMem - Update Shared Memory Records for a free
;
;       _vmFreeNamedSharedMem decrements the reference count in the named
;       shared memory records, and frees them if necessary. Then it returns
;       NO_ERROR if the last reference to the shared memory is removed from
;       the given task. It returns ERROR_BUSY if the memory is not yet to
;       be freed.
;       WARNING: This procedure assumes that Shared Memory and the object
;       handle semaphores are taken!
;       WARNING: This procedure is in swappable code
;
;       ENTRY   [ESP+4] pointer to object information buffer
;               [ESP]   flags (Task termination or not)
;       EXIT    [EAX]    0 if memory is to be freed
;                       ERROR_BUSY if there are outstanding references
;       USES    EAX, ECX, EDX, Eflags

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

    ArgVar  fsm_fl, DWORD       ; flags VMFM_TASK_TERM or not
;;; ArgVar  fsm_poi,DWORD       ; pointer to object info
    ArgVar  fsm_pod,DWORD       ; pointer to object descriptor

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

;;;;;;; mov     ebx, [fsm_poi]
;;;;;;; cmp     [ebx].oi_va, SEL_512MEG
        mov     ebx, [fsm_pod]
        cmp     [ebx].od_laddr, SEL_512MEG
        mov     ax, ERROR_BUSY      ; Don't free if GDT selector!
        jae     fsmx

; (es:di)=Address of record,(cx)=size = Rmp_Find((ds:si)=Address of match
;       pattern, cx=length of match pattern, bx=offset into the record,
;       (es:di) = address of handle to managed seg.)

;;;;;;; mov     ax, [ebx].oi_hob
;;;;;;; push    ax                          ; object handle
;;;;;;; mov     ax, [ebx].oi_hobptda        ; ptda handle
;;;;;;; push    ax                          ; set up match pattern in stack

        mov     eax, [ebx].od_pob
        vmPobToHob eax
        push    ax                          ; object handle
        mov     eax, [ebx].od_pPTDA         ; ptda handle
        mov     ax, [eax].ptda_handle
        push    ax                          ; set up match pattern in stack
        push    ss
        pop     ds
        assume ds:nothing
        mov     si, sp                      ; ds:si points to ptda handle
        mov     cx, 4                       ; length of match pattern
        mov     bx, offset lsm_ptda         ; offset of match pattern
        DosContext es, di
        mov     di, offset DOSGROUP:ShareRmpStruc
        CALL16  Rmp_Find                    ; Find the local record
        pop     eax                         ; Clear stack 2 words!
        jc      short fsmie1

        test    [fsm_fl], VMFM_TASK_TERM    ; task termination ?
        jnz     short fsm20                 ; jmp if so

        dec     es:[di].lsm_refcount        ; remove one reference
        jmp     short fsm30                 ; Preserve Zero Flag!

fsmie1: Panic <'_vmFreeNamedSharedMem: Name not found'>

fsm20:  mov     bx, es:[di].lsm_refcount
        sub     es:[di].lsm_refcount, bx    ; remove all references, set ZF
fsm30:  mov     eax, ERROR_BUSY
        jnz     short fsmx          ; skip if not last reference being removed

;   Last reference of the task is being removed. Remove the local record,
;   and decrement the global reference count
;   Rmp_Rem((es:di)= Address of record to remove, (ds:bx)=address of handle
;       to managed segment)
;   (es:di) = Address of local record

        DosContext ds, ax
        mov     bx, offset DOSGROUP:ShareRmpStruc
        CALL16  Rmp_Rem                     ; remove local record

;   Now we have to find the global record

        FlatContext ds, bx
;;;;;;; mov     ebx, [fsm_poi]
;;;;;;; mov     ax, [ebx].oi_hob

        mov     ebx, [fsm_pod]
        mov     eax, [ebx].od_pob
        vmPobToHob eax
        push    ax                          ; setup match pattern in stack
        push    ss
        pop     ds
        assume ds:nothing
        mov     si, sp
        mov     cx, 2                       ; length of match pattern
        mov     bx, offset sm_handle
        DosContext es, di
        mov     di, offset DOSGROUP:ShareRmpStruc
        CALL16  Rmp_Find
        pop     ax                          ; clear stack
        jc      short fsmie1                ; Panic if not found!

        dec     es:[di].sm_refcount         ; decrement global ref count
        jnz     short   fsm90               ; jmp if not 0

;   Global reference count is zero, we have to remove the global record
;   (es:di) = address of record

        DosContext ds, ax
        mov     bx, offset DOSGROUP:ShareRmpStruc
        CALL16  Rmp_Rem                     ; remove local record
fsm90:  xor     eax, eax                    ; return NO_ERROR, indicate free
fsmx:   RestoreReg <es,ds,edi,esi,ebx>
        LeaveProc
        RETC
EndProc   _vmFreeNamedSharedMem

SWAP32_13      ENDS


        FARCODE INIT
INITCODE segment


;***EP   w_GlobalSeg - Make a local segment global and shared
;
;
; INPUT:
;             AX      Current (Local) Selector
;             DS:DX   Address of Name String
;
; EXIT-NORMAL:
;             AX = New (Global) Selector
;             CLC
;
; EXIT-ERROR:
;             AX != 0
;                     Invalid Name
;                     Name Exists
;                     Invalid Selector
;             STC
;
; EFFECTS:
;             Convert given segment to Read-Only, Global,
;             Shared Data Segment
;
;
;       Procedure w_GLOBALSEG
;
;       check name syntax via CanonBuf
;       obtain shared memory semaphore via GetShrSem
;       if (no error) then
;         validate name via ValidateName
;         if (no error) then
;           insure name does not exist via Rmp_Find
;           if (no error) then
;             set error
;           else
;             add shared record via Rmp_Add
;             if (no error) then
;               obtain info from local via GetDescInfo
;               if (no error) then
;                 Call _SELAllocKHB to reserve memory and GDT selector
;                 Call _VMCopy to copy over the segment
;                   (Read Only, Shared, Data)
;                 Update handle & selector in shared memory record
;                 VMFreeMem(free the local LDT image of the segment)
;               else
;                 set error code = ERROR_ACCESS_DENIED
;                 Free shared rmp record via Rmp_Free
;               endif
;             endif
;           endif
;         endif
;         release shared memory semaphore via ClearShrSem
;       endif
;       free temporary storage via FreeTempBuffer
;       return
;
;       End Procedure w_GLOBALSEG

Procedure w_GlobalSeg,hybrid
    assume ds:nothing,es:nothing,fs:nothing,gs:nothing,ss:TASKAREA
    LocalVar wgl_oldsel,WORD            ; LDT selector
    LocalVar wgl_desc,<size seldesc_s>
    LocalVar wgl_ac,<size vmac_s>       ; Return info from VMAlloc
    LocalVar wgl_name,DWORD             ; Address of temporary buffer
    LocalVar wgl_srs,DWORD              ; Address of global rmp record

        EnterProc <ax>                  ; LDT selector

;       check name syntax, allocate temp buffer via CanonBuf

        mov     si,dx                   ;source offset
        xor     bx,bx                   ;start offset in temp for RMP rec
        CALLFAR CanonBuf
        mov     [wgl_nameh],es          ;save temp address
        mov     [wgl_namel],di          ;  of name
        jc      wglx

;       obtain named shared mem sem
;       KSEMRequestExclusive(sem handle, timeout)

        FlatContext <ds, es>, cx
        CCALL32 _KSEMRequestExclusive,<[_vmNamShrSem],-1>
        or      eax, eax
        stc
        jnz     wglx                    ;quit if error

;       validate name
;       ((DS:ESI)= addr. name string excluding prefix, ecx=length, ax=error)
;        = ValidateName(ds:esi=address of name string inc. prefix,
;          es:edi = address of prefix)

        xor     esi, esi
        lds     si,[wgl_name]           ;load source address
        mov     edi,offset FLAT:_vmNamShrPrfx ;offset of shared prefix string
        CALLF32 ValidateName,,nocheck   ;Validation Routine
        jc      short wgl25             ; quit if error

;       insure that region does not exist

        push    cx                      ;save name size (-\SHAREMEM\)
        mov     bx,offset sm_name       ;offset in record to start  match
        DosContext es, di
        mov     di,offset DOSGROUP:ShareRmpStruc
        CALLFAR Rmp_Find                ;see if this name already exists
        pop     cx                      ;restore name size
        jc      short wgl20
        stc                             ;set error flag
        mov     ax,ERROR_ALREADY_EXISTS ;set error code
        jmp     short wgl25             ;quit

;       add shared record

wgl20:  lds     si,[wgl_name]
        add     si,_vmNamShrPrfxLen-sm_name ;remove prefix, add data space
        add     cx,offset sm_name       ;cx = record size
        DosContext es           ;set es:di =
        mov     di,offset DOSGROUP:ShareRmpStruc
        CALLFAR Rmp_Add                 ;add record
        assume  es:nothing
        jnc     short wgl30
        mov     ax,ERROR_NOT_ENOUGH_MEMORY ;set error code
wgl25:  jmp     wglx1                   ;quit

wgl30:  mov     [wgl_srsh],es           ;save segment of shared record
        mov     [wgl_srsl],di           ;save offset of shared record

;       update shared mem structure

        mov     es:[di].sm_handle,0      ;clear handle
        mov     es:[di].sm_refcount,1    ;set refcount to 1

;       obtain segment information
;   _SELGetDescInfo(sel, hptda, pdesc)

        FlatContext <ds, es>, cx
        SSToDS  ecx, wgl_desc
        xor     edi, edi
        mov     ax, ss:[wgl_oldsel]
        CCALL32 _SELGetDescInfo <eax, edi, ecx>
        cmp     eax, NO_ERROR
        mov     eax, ERROR_ACCESS_DENIED
        jnz     short wgle

;       reserve system memory and selector from the read only heap
;   _SELAllocKHB(hkh, cb, fl, flsel, hobowner, pac)

        SSToDS  ecx, wgl_ac             ; Flat address of wgl_ac
        mov     esi, ss:[wgl_desc].desc_cb
        CCALL32 _SELAllocKHB <VM_HKH_PUB_SWAPRO, esi, PG_U, \
                    D_DPL3, MSHAREOWNER, ecx>
        cmp     eax, NO_ERROR
        jnz     short wglie1                  ; Internal Error if failed

;       copy over the local segment to system address and memory
;       _vmCopyKHB(pbDest, pbSrc,cb,fl)
;       (esi) = size of segment

        CCALL32 _vmCopyKHB <[wgl_ac].ac_va, [wgl_desc].desc_laddr, esi, PG_U>

;       add selector & handle to shared memory structure

        les     di,ss:[wgl_srs]         ;address shared mem. record
        assume es:nothing
        or      ss:[wgl_ac].ac_sel, SEL_RPL3   ;mark the selector as ring3
        mov     ax,ss:[wgl_ac].ac_sel   ;get new GDT selector
        mov     word ptr es:[di].sm_selector,ax ; selector

;       no errors occurred, so free the local segment, clear the
;       carry flag, and load the new selector into ax
;       _VMFreeMem(va, hptda, fl)

        FlatContext es, cx
        xor     eax, eax
        CCALL32  _VMFreeMem <[wgl_desc].desc_laddr, eax, eax>
        or      eax, eax
        jnz     short wglie3

        assume ds:nothing, es:nothing

        mov     ax,ss:[wgl_ac].ac_sel   ;load selector
        clc                             ;carry flag set accordingly
        jmp     short wglx1


wgle:

;       if an error occurred, remove the rmp record added, set the carry flag,
;       and load the error code into ax

        push    ax                      ; save error code
        DosContext ds,bx
        mov     bx,offset DOSGROUP:ShareRmpStruc
        les     di,[wgl_srs]            ; address of rmp record
        CALLFAR Rmp_Rem
        stc                             ;set carry flag
        pop     ax                      ;load error code

;       clear shared memory semaphore

wglx1:  pushf
        push    eax                     ; save error code/selector
        FlatContext <ds, es>, dx
        CCALL32 _KSEMRelease,<[_vmNamShrSem]>   ; release named shared semaphore
        pop     eax
        popf

;       free temporary buffer

wglx:   pushf                           ;save flags
        mov     di, [wgl_nameh]
        mov     es, di
        mov     di,[wgl_namel]          ;free
        CALLFAR FreeTempBuffer          ;  temporary storage
        popf                            ;restore flags

;       exit

        LeaveProc                       ;clean up the stack
        ret

wglie1: Panic <'w_GlobalSeg: SELAllocKHB'>
wglie3: Panic <'w_GlobalSeg: VMFreeMem'>
EndProc w_GlobalSeg

INITCODE ends


    FARCODE HIGH32

CINIT_TEXT segment

;***LP  _vmNameShareInit - Initialize the name shared RMP segment and sem4.
;
;       _vmNameShareInit calls Rmp_Alloc to initialize named shared
;       RMP records segment and KSEMAlloc to initialize named shared
;       semaphores
;
;       ENTRY   NONE
;       EXIT    NONE
;       USES    EAX, ECX, EDX, Flags

Procedure _vmNameShareInit
    assume ds:nothing,es:nothing,fs:nothing,gs:nothing,ss:TASKAREA
        ; Save registers for C calling conventions

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

        ;   Initialize named shared RMP records segment

        mov     ax,MSHARERMPOWNER               ; (ax) = hobowner
        xor     ebx,ebx                         ; (bx) = hobmte
        mov     esi,PG_SWAPPABLE                ; (esi) = PG alloc flags
        DosContext es                   ; (es:di) -> rmp handle
        mov     di,offset ShareRmpStruc
        CALL16  Rmp_Alloc
        jc      short nsiie

        ;   Initialize named shared memory semaphore
        ;   eax = KSEMAlloc(&sem handle, sem type, flags)

        FlatContext <ds, es> ax

        CCALL32 _KSEMAlloc, <<offset FLAT:_vmNamShrSem>, KRNL_SHARESEM, 0>
        or      eax, eax
        .errnz  NO_ERROR
        jnz     short nsiie

        ; Restore saved registers

        RestoreReg <edi,esi,ebx,es,ds>
        RETC
nsiie:  Panic <'_vmNameShareInit'>
EndProc _vmNameShareInit


;***LP  _vmDonateHighCode - Initialize the name shared RMP segment and sem4.
;
;       _vmDonateHighCode donates the unused portion of the last page
;       of the HIGH2CODE and HIGH3CODE segments to the kernel resident
;       heap
;
;       ENTRY   NONE
;       EXIT    NONE
;       USES    DS, ES, EAX, EBX, ECX, EDX, Flags

Procedure _vmDonateHighCode
    assume ds:nothing,es:nothing,fs:nothing,gs:nothing,ss:TASKAREA
        ; Save registers for C calling conventions
        LocalVar vms_desc,<size seldesc_s>

        SaveReg <ds,es,ebx,ecx,edx>

        SSToDS  ecx, vms_desc
        xor     edx, edx
        mov     ax,DOSHIGH3CODE         ; (ax) = DOSHIGH3CODE selector

        CCALL32 _SELGetDescInfo <eax, edx, ecx>

        cmp     eax, NO_ERROR
        jnz     short cant_donate1                      ; error

        mov     ebx, ss:[vms_desc].desc_cb
        mov     eax, ss:[vms_desc].desc_laddr

        add     eax, ebx             ; add limit to base addr

        mov     ebx, PAGESIZE        ; ebx = 1000 - (addr & 0xfff)
        mov     dx,  ax
        and     dh,  15
        movzx   edx, dx
        sub     ebx, edx

        CCALL32 _VMDonateKHB <eax, ebx, PG_R>
cant_donate1:

        SSToDS  ecx, vms_desc
        xor     edx, edx
        mov     ax,DOSHIGH2CODE         ; (ax) = DOSHIGH2CODE selector
        movzx   eax, ax

        CCALL32 _SELGetDescInfo <eax, edx, ecx>

        cmp     eax, NO_ERROR
        jnz     short cant_donate2                      ; error

        mov     ebx, ss:[vms_desc].desc_cb
        mov     eax, ss:[vms_desc].desc_laddr

        add     eax, ebx             ; add limit to base addr

        mov     ebx, PAGESIZE        ; ebx = 1000 - (addr & 0xfff)
        mov     dx,  ax
        and     dh,  15
        movzx   edx, dx
        sub     ebx, edx

        CCALL32 _VMDonateKHB <eax, ebx, PG_R>
cant_donate2:

        ; Restore saved registers

        RestoreReg <edx,ecx,ebx,es,ds>
        RETC
EndProc _vmDonateHighCode

CINIT_TEXT ends
        end
