;	SCCSID = @(#)vmmisc.asm 6.5 92/03/05

;	 IBM/Microsoft Confidential
;
;	 Copyright (c) IBM Corporation 1987, 1989
;	 Copyright (c) Microsoft Corporation 1987, 1989
;
;	 All Rights Reserved
;
; Modification:
;********************************************************************
;82818 ChangeTeam 02/07/95				      ;82818
;82818	   Moving r206 debug .mk command to R207	      ;82818
;      Khoa Huynh 03/15/96
;	   Fix Defect 148507
;

name vmmisc

.xlist
include dosseg.inc
include basemaca.inc
include fnomem.inc
include osmaca.inc
include mi.inc
include osconst.inc
include seldesc.inc
include vmowner.inc
include pgexport.inc
include vmexport.inc
include selxport.inc
include virtsym.inc
include error.inc
include tkexport.inc
.list

	CpuMode 386
	ModName vmmisc,FNO_VMMISC_ASM

	ReferGlobal _InitCodeInvalid,BYTE
	ReferGlobal PtoV_Selectors
	ReferGlobal	CurrIntLevel,word
	ReferFlat	_pTCBCur,dword
	ReferFlat	_pPTDACur,dword
	ReferFlat	_SELGDTLaddr,dword

	EXTRN		_dhw_VMLock:NEAR
	EXTRN		_dhw_VMUnlock:NEAR
	EXTRN		_dhw_VMProcessToGlobal:NEAR
	EXTRN		_dhw_VMGlobalToProcess:NEAR
	EXTRN		_dhw_VMAlloc:NEAR
	EXTRN		_dhw_VMInitAlloc:NEAR
	EXTRN		_dhw_VMFree:NEAR
	EXTRN		_dhw_VMSetMem:NEAR
	EXTRN		_SELVirtToLin:NEAR
	EXTRN		_PGLinToPageList:NEAR
	EXTRN		_PGPageListToLin:NEAR
	EXTRN		_SELCheckAccess:NEAR
	EXTRN		_vmdh_AllocRemoveList:NEAR
	EXTRNFAR	ProbeBuff
	EXTRN		_VMGetOwner:NEAR
	EXTRN		dhFrameCS:abs
	EXTRN		dhFrameIP:abs
	EXTRN32 	_VMReserve					;novell

ThunkFar32 _LDRClearSem,,_TEXT
ThunkFar32 _LDRDisableDirtyHooks,,CINIT_TEXT
ThunkFar32 _PGInitBuf,,CINIT_TEXT
ThunkFar32 _PGInitInfoSegs,,CINIT_TEXT
ThunkFar32 _PGPhysAvail,,_TEXT
ThunkFar32 _VMInitProtect,,CINIT_TEXT


	FARCODE HIGH32
_TEXT	segment

;***EP	ReserveVM - Reserve virtual memory
;
;	ENTRY		eax		- address to deallocate
;			ecx		- Number of 4Meg to allocate
;					  0 = deallocate
;
;	EXIT		eax		- virtual address allocated
;

Procedure ReserveVM,near
    assume ds:FLAT, es:FLAT, ss:nothing

    LocalVar		ppar,DWORD
    EnterProc

    sal      ecx,10		  ; convert # directories => # pages
    SSToDS   edx,ss:ppar	  ; Loc to arena header pointer


    CCALL32 _VMReserve,<ecx,VMAC_LOCANY+VMAC_ALIGNDIR+VMAC_ARENASYS,  \
			0,0,HOBNULL,edx>
    mov      ebx,ppar
    mov      eax,[ebx].ar_union0.ar_struct0._ar_ul ; flags and address
    and      eax,_ar_ipg

    LeaveProc
    ret
EndProc ReserveVM

;***EP	_vmbmScan - Bitmap scan routine
;
;	G/ven a bit location within a bitmap, this function scans the
;	bitmap backwards from that point to find the highest bit (if any)
;	that is set at or preceding the given location in the bitmap.
;
;	This function is implemented in assembly language to make use
;	for the fast SCASD and BSR instructions.
;
;	ulong_t vmbmScan(VMBM **papbm,
;			 ulong_t *pbmNumb,
;			 ulong_t *pbmeNumb,
;			 ulong_t *pbmeBitNumb)
;
;	ENTRY		papbm		- Pointer to bitmap directory
;			pbmNumb 	- Pointer to bitmap number
;			pbmeNumb	- Pointer to bitmap entry number
;					  indicating bitmap entry to start
;					  scan at.
;			pbmeBitNumb	- Pointer to bit number indicating
;					  bit to start at.
;	EXIT		*pbmNumb	- Bitmap number for base address of
;					  of nearest reservation at or
;					  below given starting point
;			*pbmeNumb	- Bitmap entry for...
;			*pbmeBitNumb	- Bit number for ...
;
;	RETURN		ulong_t 	TRUE if bit found
;					False otherwise
;	USES
;

Procedure _vmbmScan,near
    assume ds:FLAT, es:FLAT, ss:nothing
    ArgVar		papbmBase,DWORD
    ArgVar		pbmNumb,DWORD
    ArgVar		pbmeNumb,DWORD
    ArgVar		pbmeBitNumb,DWORD

    LocalVar		lbmeBitMask,DWORD
    LocalVar		papbmStart,DWORD
    LocalVar		papbm,DWORD
    LocalVar		pbmeStart,DWORD
    LocalVar		pbme,DWORD

	EnterProc

	push	ebx
	push	edi
	push	esi
	push	ecx
	push	edx

;
;	Create a mask containing the bit specified by the given bit number
;	and all lower bits set.  This mask is used if the first bitmap
;	entry that has non-zero bits set is the starting bitmap entry.
;	Bits set corresponding to linear addresses higher than the
;	starting address must be excluded.
;

	mov	ebx,papbmBase		; EBX points to bitmap directory
	mov	eax,1
	mov	esi,pbmeBitNumb 	; ESI points to starting bitnumb
	mov	ecx,[esi]		; ECX = starting bit numb
	shl	eax,cl			; EAX has corresp. bit set
	mov	ebx,eax 		; EBX has corresp. bit set
	dec	eax			; EAX has all lower bits set
	or	eax,ebx 		; EAX has starting bit set also
	mov	lbmeBitMask,eax 	; Save eligible bit mask


;
;	Scan the array of bitmap pointers looking for the highest bitmap
;	that exists at or below the given starting point in the array

	mov	ebx,papbmBase		; EBX points to bitmap directory
	mov	edi,ebx 		; EDI points to bitmap directory
	mov	ecx,pbmNumb		; ECX points to start bitmap #
	mov	ecx,[ecx]		; ECX = start bitmap number
	shl	ecx,2			; ECX = offset to bitmap dir entry
	add	edi,ecx 		; EDI points to start bitmap dir ent
	mov	papbmStart,edi		; Save starting bitmap dir entry ptr
	shr	ecx,2			; ECX = start bitmap number
	inc	ecx			; ECX = max count to scan
bms10:	std				; Do backwards scan
	xor	eax,eax 		; EAX = 0 for scasd - KDH D148507
	repe scasd
	cld				; Restore forwards scan
	jz	bmse1			; Jump if no bitmap found

;	A bitmap has been found.  Set up to scan the bitmap entries working
;	backwards from the appropriate starting point.	This point is
;	the minimum of the last entry and (in the case of the starting
;	bitmap) the starting bitmap entry number.  First get bme number
;	for last bme on bitmap.

	add	edi,4			; Point at found non-zero bm dir entry
	mov	papbm,edi		; Save bm dir entry pointer
	mov	edx,[edi]		; EDX points to bitmap
	mov	ecx,[edx-4]		; ECX = bitmap heap block header
	and	ecx,00007ffch		; ECX = heap block size (reg. heap blk)
	sub	ecx,8			; ECX = size of bitmap entries
	shr	ecx,2			; ECX = count of bitmap entries
	dec	ecx			; ECX = default starting bme (= last)

;	Set starting bme for search to the last bme on the bitmap by
;	default.  If the bitmap is the starting bitmap and the given
;	bitmap entry is less than the highest on the bitmap, clamp
;	starting bme to the given one; if the given bitmap entry
;	is beyond the end of the bitmap, reset the relevance mask
;	to indicate that all bits in the entry are relevant.

	cmp	edi,papbmStart		; Is bitmap  starting one ?
	jne	bms20			; Jump if not
	mov	eax,pbmeNumb		; EAX points to given bme
	cmp	ecx,[eax]		; Is last bme < given bme
	jae	bms18			; J if last bme >= given one

;	Given bme lies beyond end of starting bitmap.  Search will start at
;	a lower bme, so all bits represent addresses lower than given
;	address.  Reset relevance mask to all ones.

	mov	lbmeBitMask,-1		; Reset mask to all 1's
	jmp	short bms20

bms18:	je	bms20			; J if last bme = given one

;	Last bme on bitmap > given bme.  Set starting bme = given bme

	mov	ecx,[eax]		; Clamp bme to starting one
bms20:	mov	edi,ecx 		; EDI = start bme #
	inc	ecx			; ECX = count of bme's to scan
	shl	edi,2			; EDI = byte disp from bme 0
	add	edi,4			; EDI = byte disp from bitmap start
	add	edi,edx 		; EDI = bme to start scan
	mov	pbmeStart,edi		; Save scan start
bms30:	std				; Do backwards scan
	xor	eax,eax 		; EAX = 0 for scasd
	repe scasd			; Scan backwards
	cld				; Restore forwards scan
	jz	bms50			; Jump if no non-zero bme found
	add	edi,4			; Point at non-zero bme
	mov	pbme,edi		; Save pointer to bme

;	A non-zero bitmap entry has been found.  Check if this bitmap entry
;	is the starting one on the starting bitmap.

	mov	esi,[edi]		; ESI = bme
	mov	eax,papbm		; EAX = ptr to bitmap dir entry
	cmp	eax,papbmStart		; Starting one ?
	jne	bms40			; J if not
	cmp	edi,pbmeStart		; Starting bme ?
	jne	bms40			; J if not

;	Bitmap entry is the starting one.  This entry may have bits set
;	that correspond to linear addresses above that corresponding to the
;	given bitmap, entry and bit number.  Mask out those bits and test if
;	any remaining bits are non-zero.  Note that if the given bitmap entry
;	number is above the highest in the bitmap, the mask was reset to 1's.

	and	esi,lbmeBitMask 	; And out with mask
bms40:	and	esi,esi 		; Any non-zero eligible bits ?
	jnz	bms60			; J if so - eligible bit found

;	Only higher bits set in starting bme.  Continue scan

	jecxz	bms50			; J if no more bme's in bitmap
	mov	edi,pbme		; Point at bme
	sub	edi,4			; Move down one bme
	mov	pbme,edi		; Update bme pointer
	jmp	bms30

;	No more bme's.  Move down to next bm (if any)

bms50:	mov	ecx,papbm		; ECX points to curr bitmap dir entry
	mov	edi,papbm		; EDI points to curr bitmap dir entry
	sub	ecx,papbmBase		; ECX = offset of curr dir entry
	jecxz	bmse1			; J if no more bitmaps left
	sub	edi,4			; EDI points to next lower bde
	shr	ecx,2			; ECX = count of remaining bitmaps
	jmp	bms10			; Look for next bitmap

;	An eligible bit has been found.  Compute and return bitmap number,
;	bitmap entry number and bit number

bms60:	bsr	eax,esi 		; EAX = bit n
	mov	esi,pbmeBitNumb
	mov	[esi],eax		; Store bit numb
	mov	ecx,papbm
	mov	ebx,[ecx]		; EBX points to bitmap
	sub	ecx,papbmBase
	shr	ecx,2
	mov	esi,pbmNumb
	mov	[esi],ecx		; Store bm numb
	mov	ecx,pbme
	sub	ecx,ebx
	sub	ecx,4			; ECX = bme offset from bme 0
	shr	ecx,2			; ECX = bme number
	mov	esi,pbmeNumb
	mov	[esi],ecx		; Store bme numb

	mov	eax,1			; Return true
bms80:	pop	edx
	pop	ecx
	pop	esi
	pop	edi
	pop	ebx

	LeaveProc
	RETC

;	Error - nothing below given point in arena

bmse1:	xor	eax,eax
	jmp	bms80

EndProc _vmbmScan

;IFDEF	VMSTRICT    ;82818 removed ifdef vmstrict
;***EP	_vmRetAddr - Get Routine Return Address
;
;	This function will provide the return address of the routine
;	that called the routine that called this function. The assumption
;	is made that at the start of routine that called this function,
;	the routine saved EBP and set EBP to ESP i.e.
;		    push ebp
;		    move ebp, esp
;
;	ulong_t vmRetAddr(VOID)
;
;	ENTRY		None
;
;	EXIT		None
;
;	RETURN		ulong_t 	return address
;
;	USES		EAX
;


Procedure _vmRetAddr,near
    assume ds:FLAT, es:FLAT, ss:nothing

	mov	eax, [ebp+4]
	RETC

EndProc _vmRetAddr

;ENDIF		    ;82818 removed Endif

_TEXT	ends


	FARCODE LOW
CODE segment

;	This section contains the new DevHlp functions interface which must
;	reside in FARCODE LOW segment


;***EP	dh_VMLock - Lock memory range in physical memory.
;
;	dh_VMLock locks a memory range in physical memory.
;	Contiguous locks will not be allowed to exceed 64K in size. Contiguous
;	locks may require byte by byte copy of the pages in physical memory,
;	and are considerably slower than locks that do not require contiguity.
;
;	ENTRY	(EAX) = flags, action to be taken
;		  1:VMDHL_NOBLOCK Return immediately if pages not available.
;		  2:VMDHL_CONTIGUOUS Make memory range physically contiguous.
;		  4:VMDHL_16M Lock memory below 16Meg physical address.
;		  8:VMDHL_WRITE Lock is for a write request.
;		 16:VMDHL_LONG Long term lock (short term if not set)
;		 32:VMDHL_VERIFY verify accessibility
;		(EBX) = laddr, linear address of the region to be locked
;		(ECX) = size, size of lock  (<= 64Kb)
;		(ESI) = phlock, pointer to location to write 64 bit lockhandle.
;		(EDI) = pointer to location to write an array of pagelist_s
;			strucutres that describe the physical pages underlying
;			the linear address range.
;			If this value is set to -1 no array is returned.
;			For Verify locks, this parameter must be -1.
;	EXIT:	Carry clear if success
;		   (EAX) = number of element in PageList array (if requested)
;		Carry set if failed
;		   (EAX) = error code
;	USES:	EAX, EFlags
;
;		FS and GS must be preserved (DevHelp requirement)
;
;	PseudoCode:
;
;	Call dhw_VMLock(ulong flags,ulong laddr,ulong size,
;		struct lockhandle_s *phlock, ulong (*pphysaddr)[])
;
Procedure   dh_VMLock,near
    assume ds:nothing, es:nothing, fs:nothing, gs:nothing, ss:nothing
	LocalVar	vml_nelements,dword  ; return value from lock

	EnterProc <0>,,alignesp 			      ;147369

	; Save user registers
	SaveReg <ds, es, fs, gs, ecx, edx>

vml10:	FlatContext <ds, es>, dx
	NullContext <fs, gs>,dx     ; zero FS and GS in case we block

;   _dhw_VMLock(fl, laddr, cb, phlock, ppaddr)

	SSToDS	edx,ss:vml_nelements ; Loc to return # of elements in below
	CCALL32  _dhw_VMLock <eax, ebx, ecx, esi, edi, edx>
	or	eax, eax	; error?
	.errnz	NO_ERROR
	stc
	jnz	vml80		; if error, jmp
	mov	eax,ss:[vml_nelements]	; if no error, put number of pagelist
					; array elements in eax and exit
	clc
;   Restore saved registers

vml80:	RestoreReg <edx, ecx, gs, fs, es, ds>
	LeaveProc
	ret

EndProc dh_VMLock


;***EP	dh_VMUnlock - Unlock memory range locked by dh_VMLock.
;
;	dh_VMUnlock unlock previously locked memory range.
;
;	ENTRY	(DS:ESI) = Address to read lock handle returned by VMLock.
;	EXIT	Carry clear if success
;		Carry set if error
;		    (EAX) = Error code
;	USES	EAX, EFlags
;
;		FS and GS must be preserved (DevHelp requirement)
;
;	PseudoCode:
;
;	Call dhw_VMUnlock (struct lockhandle_s hlock)
;	return
;
Procedure   dh_VMUnlock, near
    assume ds:nothing, es:nothing, fs:nothing, gs:nothing, ss:nothing

	; Save user registers
	SaveReg <ds, es, fs, gs, ecx, edx>
	NullContext <fs, gs>, dx
	FlatContext <ds, es>, dx

;	_dhw_VMUnlock(phlock)

	CCALL32 _dhw_VMUnlock <esi>
	or	eax, eax			; clears carry
	.errnz	NO_ERROR
	jz	short vmu90

	stc

;   Restore saved registers

vmu90:	RestoreReg <edx, ecx, gs, fs, es, ds>
	ret
EndProc dh_VMUnlock


;***EP	dh_VMGlobalToProcess - Create user accessible mapping to global
;	    address range.
;
;	dh_VMGlobalToProcess creates a mapping to global address range in
;	the user address range. The address range can not cross memory
;	objects.
;	If selector map is requested, selectors will be allocated as
;	ring 3 data.
;	WARNING: Existing hardware may not support read only mapping.
;
;	ENTRY	(EAX) = Action flags
;		      1:VMDHGP_WRITE Read/write map requested (r/o otherwise)
;		      2:VMDHGP_SELMAP Selector map requested
;		      4:VMDHGP_SGSCONTROL Put mapping under sgs control
;		      8:VMDHGP_4MEG Allocate mapping on 4 Meg boundary
;		(EBX) = laddr, linear address of region to be aliased
;		(ECX) = size, size of region to be aliased
;	EXIT	Carry clear upon success
;		    (EAX) = Linear address
;		Carry set if error
;		    (EAX) = error code
;	USES	EAX, EFlags
;
;		FS and GS must be preserved (DevHelp requirement)
;
;	PseudoCode:
;
;	Call dhw_VMGlobalToProcess(ulong flags, ulong laddr, ulong size,
;	    ulong_t *pladdr)
;
Procedure   dh_VMGlobalToProcess, near
    assume ds:nothing, es:nothing, fs:nothing, gs:nothing, ss:nothing
	LocalVar    gtp_laddr,DWORD	; location to return address

	EnterProc

	; Save user registers

	SaveReg <ds, es, fs, gs, ecx, edx>
	NullContext <fs, gs>, dx	; zero FS and GS in case we block
	FlatContext <ds, es>, dx

;	_dhw_VMGlobalToProcess(fl, laddr, cb, pladdr)

	SSToDS	edx, ss:gtp_laddr	; Flat address of gtp_laddr
	CCALL32 _dhw_VMGlobalToProcess <eax, ebx, ecx, edx>
	or	eax, eax
	.errnz	NO_ERROR
	stc
	jnz	short gtp90

gtp80:	clc
	mov	eax, gtp_laddr

;   Restore saved registers

gtp90:	RestoreReg <edx, ecx, gs, fs, es, ds>

	LeaveProc
	ret
EndProc dh_VMGlobalToProcess


;***EP	dh_VMProcessToGlobal - Create global mapping to user's address range.
;
;	dh_VMProcessToGlobal creates a mapping in global address range to
;	the user address range. The address range can not cross memory
;	objects.
;	WARNING: Existing hardware may not support read only mapping.
;
;	ENTRY	(EAX) = flags, action flags
;		    VMDHGP_WRITE Read/write map requested (r only otherwise)
;		(EBX) = laddr, linear address of region to be aliased
;		(ECX) = size, size of region to be aliased
;	EXIT	Carry clear if success
;		    (EAX) = Alias linear address
;		Carry set if error
;		    (EAX) = error code
;	USES	EAX, EFlags
;
;		FS and GS must be preserved (DevHelp requirement)
;
;	PseudoCode:
;
;	Call dhw_VMProcessToGlobal(ulong flags, ulong laddr, ulong size,
;	    ulong_t *pladdr)
;	return
;
Procedure   dh_VMProcessToGlobal, near
    assume ds:nothing, es:nothing, fs:nothing, gs:nothing, ss:nothing
	LocalVar    ptg_laddr,DWORD	; location to return address

	EnterProc

	; Save user registers

	SaveReg <ds, es, fs, gs, ecx, edx>
	NullContext <fs, gs>, dx	; zero FS and GS in case we block
	FlatContext <ds, es>, dx

;	_dhw_VMProcessToGlobal(fl, laddr, cb, pladdr)

	SSToDS	edx, ss:ptg_laddr	; Flat address of ptg_laddr
	CCALL32 _dhw_VMProcessToGlobal <eax, ebx, ecx, edx>
	or	eax, eax
	.errnz	NO_ERROR
	stc
	jnz	short ptg90

ptg80:	clc
	mov	eax, ptg_laddr

;   Restore saved registers

ptg90:	RestoreReg <edx, ecx, gs, fs, es, ds>
	LeaveProc
	ret
EndProc dh_VMProcessToGlobal


;***EP	dh_VMAlloc - Allocate virtual memory, map or commit storage.
;
;	dh_VMAlloc allocates virtual memory, and depending on flags,
;	it either commits physical storage or maps virtual memory to
;	given physical address.
;	Virtual memory is allocated in global (system) address space, unless
;	private process space is requested. Memory requested in process space
;	can only be swappable. If VMA_PHYS flag is set the, virtual memory
;	is mapped to given physical address. The physical memory must be
;	fixed or locked in this case.
;	If requested, memory allocated in process space can be registered
;	under screen group switch control. In that case, task will be denied
;	write access to this memory unless it is in foreground.
;
;	ENTRY	(EAX) = Action flags
;		  1:VMDHA_16M Allocate below 16Meg physical address.
;		    (valid only if VMDHA_FIXED)
;		  2:VMDHA_FIXED Allocate fixed memory
;		    (not valid if VMA_PROCESS)
;		  4:VMDHA_SWAP Allocate swappable memory.
;		  8:VMDHA_CONTIG Allocate, physically contiguous memory
;		    (valid only with VMDHA_FIXED, not valid if VMA_PROCESS)
;		 16:VMDHA_PHYS Map given physical address.
;		 32:VMA_PROCESS Allocate virtual memory in process space.
;		 64:VMDHA_SGSCONT Register memory under screen group switch
;		    control. (valid only if VMA_PROCESS)
;		128:VMDHA_SELMAP Map selectors and return 16:16 address in ECX
;		256:VMDHA_RESERVE Reserve (do not commit) memory
; dcr 32500
;	      0x800:VMDHA_USEHIGHMEM Use memory above 16 meg at init time???
;				       if it exists
; end
;		(ECX) = size of memory to be allocated.
;		(EDI) = physical address to be mapped(not used if -1)
;		    (used only if VMDHA_PHYS or VMDHA_CONTIG)
;	EXIT	Carry clear if success
;		    (EAX) = Linear address of allocated region
;		    (ECX) = 16:16 address only if VMDHA_SELMAP
;		Carry set if failed
;		    (EAX) = Error code
;	USES	EAX, EFlags
;
;		FS and GS must be preserved (DevHelp requirement)
;
;	PseudoCode:
;
;	Call dhw_VMAlloc(ulong flags, ulong size, ulong_t *pladdr,
;	    ulong physaddr)
;	return
;
Procedure   dh_VMAlloc, near
    assume ds:nothing, es:nothing, fs:nothing, gs:nothing, ss:nothing
	LocalVar    vma_ecx,DWORD
	LocalVar    vma_edx,DWORD
	LocalVar    vma_laddr,DWORD	; location to return address
	LocalVar    vma_falloc,DWORD	; Alloc function to call
	LocalVar    vma_callerBP,BP	; callers BP (used for ownership)

	EnterProc <ecx, edx>		; save registers

	; Make sure VMDHIA_USER flag is not specified

	test	eax, VMDHIA_USER
	jnz	vma92

	; Save user registers

	SaveReg <ds, es, fs, gs, ebx, esi>
	NullContext <fs, gs>, dx	; zero FS and GS in case we block

	FlatContext ds, dx
	mov	[vma_falloc], offset FLAT:_dhw_VMAlloc

	; See if we are still at init time

	DosContext es, dx
	test	es:[_InitCodeInvalid], 1
	jnz	short vma20		; jmp if past init time

	; Init time, replace with init routine

	mov	[vma_falloc], offset FLAT: _dhw_VMInitAlloc

;   _VMGetOwner(segment, offset)

vma20:	FlatContext es, dx
	SaveReg <eax,ecx>		; save around GetOwner call
	movzx	esi,[vma_callerBP]
	SSToDS	esi
    assume DS:nothing		; BUGBUG - donh 4/3/90: avoid a linker bug
	movzx	eax,word ptr [esi+dhFrameCS] ;ok to blast eax
	movzx	ecx,word ptr [esi+dhFrameIP] ;we'll restore ecx anyway...
	CCALL32 _VMGetOwner <eax, ecx>
	mov	esi,eax 		; (esi) = owner or HOBNULL

;   _dhw_VMAlloc(fl, cb, pladdr, pphysaddr, pvaddr, owner)

vma25:	RestoreReg <ecx,eax>
	SSToDS	edx, vma_laddr		; Flat address of vma_laddr
	SSToDS	ebx, vma_ecx		; Flat address of vma_ecx

	CCALL32 vma_falloc <eax, ecx, edx, edi, ebx, esi>,nosegcheck
vma30:	or	eax, eax
	stc
	jnz	short vma90

	clc
	mov	eax, [vma_laddr]

;   Restore saved registers

vma90:	mov	edx, [vma_edx]		; user register
	mov	ecx, [vma_ecx]		; user reg. or 16:16 address
	RestoreReg <esi, ebx, gs, fs, es, ds>
vmax:	LeaveProc
	ret

vma92:	mov	eax, ERROR_INVALID_PARAMETER
	stc
	jmp	short vmax

EndProc dh_VMAlloc


;***EP	dh_VMFree - Free memory allocated via dh_VMAlloc, or release the
;	    mapping.
;
;	dh_VMFree frees the fixed memory allocated via dh_VMAlloc, or
;	the mappings created via GlobalToProcess and ProcessToGlobal.
;
;	ENTRY	(EAX) = linear address of memory object
;	EXIT	Carry clear if success
;		Carry set if failed
;		    (EAX) = Error code
;	USES	EAX, EFlags
;
;		FS and GS must be preserved (DevHelp requirement)
;
;	PseudoCode:
;
;	Call dhw_VMFree(ulong laddr)
;	return
;
Procedure   dh_VMFree, near
    assume ds:nothing, es:nothing, fs:nothing, gs:nothing, ss:nothing

	; Save user registers

	SaveReg <ds, es, fs, gs, ecx, edx>
	NullContext <fs, gs>, dx	    ; zero FS and GS in case we block

	FlatContext es, dx

;   If we are still at init time, we must remove the entry from DevHelp
;   access list.

	push	eax			; save laddr

	DosContext  ds, cx
	test	[_InitCodeInvalid], 1
	FlatContext ds, dx
	jnz	short vmf10		; OK if past init time

;   (EAX) = laddr

	CCALL32 _vmdh_AllocRemoveList <eax>
vmf10:

	pop	eax			; restore laddr

;   _dhw_VMFree(laddr)

	CCALL32 _dhw_VMFree <eax>
	or	eax, eax
	stc
	.errnz	NO_ERROR
	jnz	short vmfx
	clc

vmfx:
;   Restore saved registers

	RestoreReg <edx, ecx, gs, fs, es, ds>
	ret

EndProc dh_VMFree


;***EP	dh_VMSetMem - Commit/ decommit/change type of a range of memory
;
;	dh_VMAlloc commits, decommits or changes type (swappable to resident
;	or back) of a range of memory based on input flags.
;	The entire memory region to be acted upon must be of uniform type
;	(all swappable, all resident, or all free)
;	The address and size passed must be page aligned.
;
;	ENTRY	(EAX) = Action flags
;		    1:VMDHS_DECOMMIT	Decommit (free) address range
;		    2:VMDHS_RESIDENT	Make memory resident
;		    4:VMDHS_SWAP	Make memory swappable
;		(EBX) = Starting address of the memory range
;					(must be page aligned)
;		(ECX) = Size of the memory range (must be page aligned)
;	EXIT	Carry clear if success
;		Carry set if failed
;		    (EAX) = Error code
;	USES	EAX, Flags
;
;		FS and GS must be preserved (DevHelp requirement)
;
;	PseudoCode:
;
;	Call dhw_VMSetMem
;	return
Procedure   dh_VMSetMem, near
    assume ds:nothing, es:nothing, fs:nothing, gs:nothing, ss:nothing

	SaveReg     <ds, es, fs, gs, ecx, edx>
	NullContext <fs, gs>, dx	; zero FS and GS in case we block

	FlatContext <ds, es>, dx

;   _dhw_VMSetMem(fl, va, cb>

	CCALL32     _dhw_VMSetMem <eax, ebx, ecx>
	or	    eax, eax		; Clears carry
	jz	    short vms80

	stc
vms80:	RestoreReg <edx, ecx, gs, fs, es, ds>
	ret
EndProc dh_VMSetMem


;********************** START OF SPECIFICATIONS **********************
;*								     *
;* SUBROUTINE NAME:  dh_VerifyAccess				     *
;*								     *
;* DESCRIPTIVE NAME: Verify access to memory			     *
;*								     *
;* FUNCTION:							     *
;*								     *
;*   This routine is used to verify that the user process has the    *
;*   correct access rights for the memory that is passed to the      *
;*   device driver.  If the process does not have the needed access  *
;*   rights to the memory, then it will be terminated.		     *
;*								     *
;*   Verification may only take place in protect mode.	If	     *
;*   VerifyAccess is called in real mode, it will return that the    *
;*   memory is accessible.  dh_VerifyAccess is callable at kernel    *
;*   mode time only.						     *
;*								     *
;*   If the target selector is a stack selector is will be treated   *
;*   as if it is a data selector.				     *
;*								     *
;* NOTES:							     *
;*								     *
;* ENTRY POINT: dh_VerifyAccess 				     *
;*    LINKAGE:	NEAR						     *
;*								     *
;* INPUT:	AX = Target Selector or Segment 		     *
;*		CX = Memlength					     *
;*			0 => 64K				     *
;*		DI = Mem_Offset 				     *
;*		DH = Type of Access				     *
;*			0 => Read				     *
;*			1 => Read / Write			     *
;*								     *
;* EXIT-NORMAL: CF = 0	 Access Verified (or in real mode)	     *
;*								     *
;* EXIT-ERROR:	CF = 1	 Access attempt failed			     *
;*								     *
;* EFFECTS:	All registers preserved 			     *
;*								     *
;*		FS and GS must be preserved (DevHelp requirement)    *
;*								     *
;* INTERNAL REFERENCES: 					     *
;*	    ROUTINES:						     *
;*								     *
;* EXTERNAL REFERENCES: 					     *
;*	    ROUTINES: ProbeBuff 				     *
;*								     *
;*********************** END OF SPECIFICATIONS ***********************

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

Procedure dh_VerifyAccess

    SaveReg	    <si,bx,ecx,ds,eax,es,fs,gs>
    NullContext <fs,gs>,bx		; zero FS and GS in case we block
    mov     bx, ax			; (BX) = selector
    mov     si, di			; (SI) = mem offset
    CALLFAR ProbeBuff			; uses AX, BX, CX, DS, ES, flags
    RestoreReg	    <gs,fs,es,eax,ds,ecx,bx,si>
    ret

EndProc dh_VerifyAccess


;***EP	dh_LinToPageList - build PageList array describing a given linear range
;
;	Given a linear address and count of bytes, this routine will
;	build an array of pagelist_s structures which will describe the
;	physical pages mapped by the linear range.
;
;	This routine may be called at interrupt time.
;
;	FS and GS must be preserved (DevHelp requirement)
;
;	Warning:  The caller is trusted to pass in linear addresses that
;		  are valid and locked or fixed.  If the linear range is
;		  not present, we could get a fatal fault in the kernel.
;
;		  The caller is also trusted to pass in a big enough
;		  PageList buffer.
;
;	ENTRY:	EAX = base linear address
;		ECX = count of bytes
;		EDI = pointer to return buffer (array of pagelist_s structs)
;
;	EXIT:	If success:
;			carry clear
;			array pointed to by EDI is filled in
;			EAX = number of elements in array
;		If failure:
;			carry set
;			EAX = error code
;
;	Pseudocode:
;	    Call PGLinToPageList
;
Procedure dh_LinToPageList, near
    assume ds:nothing, es:nothing, fs:nothing, gs:nothing, ss:nothing

;	save the registers C might use

	push	ecx
	push	edx
	push	ds
	push	es

;	_PGLinToPageList does all the work for us

	FlatContext <ds,es>,dx
	CCALL32 _PGLinToPageList <eax, [_pPTDACur], ecx, edi>
					; (eax) = number of elements in array
					; or -1 if error
	mov	edx,1
	add	edx,eax 		; set carry if failure

	pop	es
	pop	ds
	pop	edx
	pop	ecx
	ret
EndProc dh_LinToPageList



;***EP	dh_PageListToLin - build a linear mapping of a page list
;
;	This routine maps the physical pages described in the page list array
;	pointed to by EDI to a reserved range of linear addresses, allocated
;	by PhysToVirtInit.  The mapping is only valid until the next
;	PageListToLin or PhysToVirt call, or until the caller yields --
;	whichever is first.
;
;	An error is returned if the resulting mapping would be greater than
;	17 linear pages long.
;
;	This routine may be called at interrupt time.
;
;	FS and GS must be preserved (DevHelp requirement)
;
;	ENTRY:	ECX = sum of size fields in PageList array
;		EDI = pointer to array
;
;	EXIT:	If success:
;			Carry is clear
;			EAX = linear address the pagelist was mapped to
;		If error:
;			Carry is set
;			EAX = error code
;
;	Pseudocode:
;		Compute linear address range for this interrupt level
;		Call _PGPageListToLin
;
Procedure dh_PageListToLin, near
    assume ds:nothing, es:nothing, fs:nothing, gs:nothing, ss:nothing
	LocalVar pltl_cb,dword	; parameter to _PGPageListToLin
	LocalVar pltl_laddr,dword ; place to save linear address we map to

	EnterProc

;	save the registers C might use

	push	ecx
	push	edx
	push	ds
	push	es

;	Find the linear address range reserved for this interrupt level.
;	It is stored in the base field of a selector in the array
;	PToV_Selectors.

	DosContext es,dx
	DosContext ds,dx
	movzx	edx, CurrIntLevel	; get active hardware interrupt level
	inc	dx			; get the H/W int level +1
	shl	dx, 2			; (edx) = index into sel table
	movzx	edx, word ptr PtoV_Selectors[edx] ; (edx) = selector

	FlatContext  <ds,es>,ax
	and	edx, SEL_RPLCLR 	; clear RPL bits (if any)
	add	edx, [_SELGDTLaddr]  ; (edx) = flat address of descriptor

	mov	ah, [edx].d_extaddr
	mov	al, [edx].d_hiaddr  ; (ax) = high word of linear address
	shl	eax, WORDSHIFT
	mov	ax, [edx].d_loaddr  ; (eax) = linear address in descriptor
	mov	[pltl_laddr],eax    ; save base linear address
	shr	eax, PAGESHIFT	    ; (eax) = page no.


;	call PGPageListToLin(ppl, cb, page, flags, pnpages)

	mov	ss:[pltl_cb],64*1024	; on entry: max bytes allowed in map
	SSToDS	edx,ss:pltl_cb		; on exit: actual bytes in map
	CCALL32 _PGPageListToLin <edi, ecx, eax, dword ptr -1,\
		    edx>
	or	eax,eax 		; error?
	stc
	jnz	short pltlx		; if error, jmp to exit

;	return first bytes mapped in eax

	mov	eax,[pltl_laddr]	; restore linear address mapping to
	and	eax,not PAGEMASK	; (eax) =address mapped rounded to page
	mov	ecx,[edi].pl_paddr	; (ecx) = starting physical address
	and	ecx,PAGEMASK		; (ecx) = starting physical page offset
	add	eax,ecx 		; (eax) = linear starting BYTE
					; clears carry!

pltlx:	pop	es
	pop	ds
	pop	edx
	pop	ecx
	LeaveProc
	ret
EndProc dh_PageListToLin


CODE	ends
end
