
	.TITLE	PAGE "Page management related routines"
	.IDENT /v1.0/
;+
; Facility:
;	PAGE.MAR - Fool with memory
;
; Abstract:
;	General utilities for manipulating VA
;
; Author:
;	Bruce R. Miller, MILLER@TGV.COM
;	TGV, Inc.
;	603 Mission St.
;	Santa Cruz, CA 95060
;	(408) 427-4366
;
; Date:
;	3-JUN-1991, late afternoon, on a beautiful sunny day, when I
;	should be outside playing.
;
; Copyright (c) 1991 Bruce R. Miller
; All rights reserved.
;
;	Redistribution and use in source and binary forms are permitted
;	provided that the above copyright notice and this paragraph are
;	duplicated in all such forms and that any documentation,
;	advertising materials, and other materials related to such
;	distribution and use acknowledge that the software was developed
;	by Bruce R. Miller.
;	THIS SOFTWARE IS PROVIDED AS IS'' AND WITHOUT ANY EXPRESS OR
;	IMPLIED WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED
;	WARRANTIES OF MERCHANTIBILITY AND FITNESS FOR A PARTICULAR PURPOSE.
;
; Modifications:
;
;-

	.link		"sys$system:sys.stb"/SELECTIVE_SEARCH
	.library	"sys$Library:lib.mlb"

	$DYNDEF
	$IPLDEF
	$IRPDEF
	$PCBDEF
	$PSLDEF
	$PTEDEF
	$SSDEF

.entry MM_Krnl_AloNonPaged,^m<R2,R3,R4,R5>

	; Get a chunk of npagdyn
	MOVL	@8(AP),R1			; Get size
	JSB	G^EXE$ALONONPAGED		; Get some non-paged pool
	BLBC	R0,100$				; If failed exit error

	; Initialize it
	CLRQ	(R2)				; Clear forward and back links
	MOVL	R1,IRP$W_SIZE(R2)		; Set length field
	MOVL	#DYN$C_NON_PAGED,IRP$B_TYPE(R2)	; Set type field

	; leave
	MOVL	R2,@4(AP)			; return address
	MOVL	R1,@8(AP)			; return size
	MOVL	#SS$_NORMAL,R0			; success!

100$:	RET

.entry MM_AloNonPaged,^m<R2>
	MOVL	AP,-(SP)
	MOVAB	MM_Krnl_AloNonPaged,-(SP)
	CALLS	#2,G^SYS$CMKRNL
	RET



.entry MM_Krnl_DeaNonPaged,^m<R2,R3,R4,R5>

	; Check if current size is ok
	TSTL	@8(AP)				; Size specified?
	BEQL	10$				; br if not
	MOVL	@8(AP),IRP$W_SIZE(R2)		; Set length field
10$:	MOVZWL	IRP$W_SIZE(R2),@8(AP)		; return size

	; Throw it away
	MOVL	4(AP),R0			
	JSB	G^EXE$DEANONPAGED		; Deallocate

	; leave
	MOVL	#SS$_NORMAL,R0			; success!

100$:	RET


.entry MM_DeaNonPaged,^m<R2>
	MOVL	AP,-(SP)
	MOVAB	MM_Krnl_DeaNonPaged,-(SP)
	CALLS	#2,G^SYS$CMKRNL
	RET



;++
;	Deposit_Krnl
;
; Input:
;	4(AP) - buffer address (in S0)
;	8(AP) - buffer size
;
; Note:
;	First longword of buffer is target VA.
;	Second longword of buffer # of bytes to transfer.
;	The rest of the buffer is for the data transfer.
;
; Calling Context:
;	IPL = 2 if special AST, otherwise 0
;	mode = kernel
;	JSB	Deposit_Krnl
;--

ADDR =  4
SIZE =  8
PID  = 12
BUFF = 16

.entry Deposit_Krnl,^m<R3,R4,R5>
; Hey, how 'bout we add some flags, like /PID and /SPINLOCK?
; Check if IPL > 3?

	; Get buffer address
	MOVL	4(AP),R3

	; set previous access mode to kernel
;	MOVPSL	-(SP)					; PSL
;	PUSHAB	101$					; PC
;	MOVPSL	-(SP)					; PSL
;	PUSHAB	10$					; PC
;	INSV	#0,#PSL$V_PRVMOD,#PSL$S_PRVMOD,4(SP)	; previous mode=kernel
;	REI
10$:
	; Read old value, if we can
	MOVL	4(AP),R3
	PROBER	#0,4(R3),@(R3)
	BEQL	110$
	; Save old value on the stack
	SUBL	4(R3),SP
	MOVL	SP,R4
	PUSHR	#^m<r0,r1,r2,r3,r4,r5>
	MOVC	4(R3),@(R3),(R4)
	POPR	#^m<r0,r1,r2,r3,r4,r5>

	; Write new value, if we can (with SMP synchronization)
	PROBEW	#0,4(R3),@(R3)
	BEQL	110$
	INVALIDATE_TB ADDR=4(AP) ENVIRON=VMS -
	    INST1=<PUSHR	#^M<r0,r1,r2,r3,r4,r5>> -
	    INST2=<MOVC3	4(R3),8(R3),@(R3)> -
	    INST3=<POPR		#^m<r0,r1,r2,r3,r4,r5>>
	MOVL	#SS$_NORMAL,R0

	; Move old value into the caller's buffer
	PUSHR	#^m<r0,r1,r2,r3,r4,r5>
	MOVC	4(R3),(R4),8(R3)
	POPR	#^m<r0,r1,r2,r3,r4,r5>
	ADDL	4(R3),SP

100$:;	REI
101$:	RET

110$:	MOVL	#SS$_ACCVIO,R0
	BRB	100$
DEPOSIT_LENGTH = . - Deposit_Krnl

; We have to CMKRNL twice to setup the PSL prev acc field correctly for PROBE.
; It looks ugly, but it's the simplest way to check protection.
; Note: We could also do the old fake REI trick
.entry Deposit2,^m<>
	MOVL	AP,-(SP)
	MOVAB	Deposit_Krnl,-(SP)
	CALLS	#2,G^SYS$CMKRNL
	RET

;++
;	Deposit a value into a memory location (POKE) -
;
; Input:
;	4(AP) - Virtual address to modify
;	8(AP) - number of bytes to poke
;	12(AP) - PID of process to use as context (0 means current process)
;	16(AP) - address of buffer to hold values / store values in
;
; Output:
;	R0 - status
;	@16(AP) - bytes at location
;--
.entry Deposit,^m<R2,R3,R4,R5>
	; Set up buffer
	SUBL	8(AP),SP			; space for data
	MOVC	8(AP),@16(AP),(SP)		; copy the data
	PUSHL	8(AP)				; byte size
	PUSHL	4(AP)				; target VA
	MOVL	SP,R2				; buffer in R2
	ADDL3	#8,SIZE(AP),R3			; buffer size in R3
;.BYTE	^xFD
;	CLRL	0

	; Clear the event flag
	$CLREF_S	EFN=#0

	; was a remote PID specified?
	TSTL	12(AP)
	BEQL	50$

	; Call Deposit_Krnl() in the context of process PID ()
	PUSHL	#0				; EFN
	CLRQ	-(SP)				; ASTADR and ASTPRM
	PUSHL	R3				; Data size
	PUSHL	R2				; Data ptr
	PUSHL	#DEPOSIT_LENGTH			; Size of code
	PUSHAB	Deposit_Krnl			; Code to execute
	PUSHL	PID(AP)				; Our PID
	CALLS	#8,CALL_REMOTE_G
	BLBC	R0,100$

	$WAITFR_S	EFN=#0				; wait for event flag.
	BRB	80$

50$:	; do a local deposit
	PUSHL	R3
	PUSHL	R2
	PUSHL	#2
	PUSHL	SP
	MOVAB	Deposit2,-(SP)
	CALLS	#2,G^SYS$CMKRNL

80$:	; Copy data into caller-provided buffer
	PUSHR	#^m<r0,r1,r2,r3,r4,r5>
	MOVC3	SIZE(AP),8(R2),@16(AP)
	POPR	#^m<r0,r1,r2,r3,r4,r5>

100$:	RET



;++
;	Examine_Krnl
;
; Input:
;	4(AP) - buffer address (in S0)
;	8(AP) - buffer size
;
; Note:
;	First longword of buffer is target VA.
;	Second longword of buffer # of bytes to transfer.
;	The rest of the buffer is for the data transfer.
;
; Calling Context:
;	IPL = 2 if special AST, otherwise 0
;	mode = kernel
;	JSB	Examine_Krnl
;--

.entry Examine_Krnl,^m<r2,r3,r4,r5>
; Check if IPL > 3?

	; Get buffer address
	MOVL	4(AP),R0

	; Read value, if we can
; HACK!!!  PROBER is no good!  Use $PROBER macro.
	PROBER	#0,4(R0),@(R0)
	BEQL	110$
	PUSHR	#^m<r0,r1,r2,r3,r4,r5>
	MOVC3	4(R0),@(R0),8(R0)
	POPR	#^m<r0,r1,r2,r3,r4,r5>

	MOVL	#SS$_NORMAL,R0
100$:	RET

110$:	MOVL	#SS$_ACCVIO,R0
	BRB	100$
EXAMINE_LENGTH = . - Examine_Krnl

; We have to CMKRNL twice to setup the PSL prev acc field correctly for PROBE.
; It looks ugly, but it's the simplest way to check protection.
; Note: We could also do the old fake REI trick
.entry Examine2,^m<>
	MOVL	AP,-(SP)
	MOVAB	Examine_Krnl,-(SP)
	CALLS	#2,G^SYS$CMKRNL
	RET



ADDR =  4
SIZE =  8
PID  = 12
BUFF = 16
;++
;	Examine a memory location (PEEK) -
;
; Input:
;	4(AP) - Virtual address to examine
;	8(AP) - number of bytes to get
;	12(AP) - PID of process to use as context
;	16(AP) - address of buffer to store values in
;
; Output:
;	R0 - status
;	@16(AP) - bytes at location
;--
.ENTRY	Examine,^M<R2,R3,R4,R5>

	; Set up buffer
	SUBL	8(AP),SP			; space for data
	PUSHL	8(AP)				; byte size
	PUSHL	4(AP)				; target VA
	MOVL	SP,R2				; buffer in R2
	ADDL3	#8,SIZE(AP),R3			; buffer size in R3

	; Clear the event flag
	$CLREF_S	EFN=#0

	; was a remote PID specified?
	TSTL	12(AP)
	BEQL	50$

	; Call Examine_Krnl() in the context of process PID ()
	PUSHL	#0				; EFN
	CLRQ	-(SP)				; ASTADR and ASTPRM
	PUSHL	R3				; Data size
	PUSHL	R2				; Data ptr
	PUSHL	#EXAMINE_LENGTH			; Size of code
	PUSHAB	Examine_Krnl			; Code to execute
	PUSHL	PID(AP)				; Our PID
	CALLS	#8,CALL_REMOTE_G
	BLBC	R0,100$

	$WAITFR_S	EFN=#0				; wait for event flag.
	BRB	80$

50$:	; do a local examine
	PUSHL	R3
	PUSHL	R2
	PUSHL	#2
	PUSHL	SP
	MOVAB	Examine2,-(SP)
	CALLS	#2,G^SYS$CMKRNL

80$:	; Copy data into caller-provided buffer
	PUSHR	#^m<r0,r1,r2,r3,r4,r5>
	MOVC3	SIZE(AP),8(R2),@16(AP)
	POPR	#^m<r0,r1,r2,r3,r4,r5>

100$:	RET


;++
;
; Calling sequence:
;		CALL	#4,Set_Protection	; from kernel mode
;
; Input:
;		4(AP) - Starting VA
;		8(AP) - Length
;		12(AP) - Protection
;		16(AP) - Previous protection
;
; Output:
;		R0 - Status
;		R1 - Old protection, if R0 == sucess
;--

Locked_Pages:
	.LONG	First_Locked_Page
	.LONG	Last_Locked_Page
First_Locked_Page:

.entry Set_Protection,^m<R2,R3,R4,R5,R6>

	BBS	#31,4(AP),100$			; br if system VA

	; Modify process page protection
	ADDL3	8(AP),4(AP),-(SP)		; Ending VA
	PUSHL	4(AP)				; Starting VA
	CLRL	-(SP)
	PUSHL	SP				; Previous protection addr
	PUSHL	12(AP)				; New prot:
	PUSHL	#PSL$C_KERNEL			; access mode = kernel
	CLRL	-(SP)				; no RETADR
	PUSHAL	-8(FP)				; VA to change
	CALLS	#5,G^SYS$SETPRT
	POPL	R1				; Put old prot in R1
	RET

100$:
	; This is high IPL code.  Make sure we don't pagefault.
        $LKWSET_S       Locked_Pages 		; Bolt those suckers down
	BLBC	R0,900$

	MOVL	4(AP),R2
	MOVL	MMG$AR_SYSPCB,R4
	MOVL	MMG$GL_SYSPHD,R5
	MFPR	#PR$_IPL,R6
	JSB	MMG$PTEREF
	BLBC	R0,900$
	EXTZV	#PTE$V_PROT,#PTE$S_PROT,-	; Get old prot from PTE
		(R3),R1
	INSV	12(AP),#PTE$V_PROT,-		; Set prot in PTE
		#PTE$S_PROT,(R3)
;HACK!!! Replace next instruction with INVALIDATE_TB macro for SMP synchr.
	MTPR	R2,#PR$_TBIS			; Invalidate translation buffer
	UNLOCK	LOCKNAME=MMG,-			; Unlock the MMG database
		NEWIPL=R6,-
		PRESERVE=YES
900$:	
	RET
Last_Locked_Page:

.entry Set_Page,^m<>
	MOVL	AP,-(SP)
	MOVAB	Set_Protection,-(SP)
	CALLS	#2,G^SYS$CMKRNL
	TSTL	16(AP)
	BEQL	110$
	MOVL	R1,@16(AP)
110$:	RET


;++
;
; Calling sequence:
;		CALL	#2,Show_Protection		; from kernel mode
;
; Input:
;		4(AP) - Starting VA
;		8(AP) - Previous protection
;
; Output:
;		R0 - Status
;		R1 - Old protection, if R0 == sucess
;--

Locked_Pages2:
	.LONG	First_Locked_Page2
	.LONG	Last_Locked_Page2
First_Locked_Page2:

.entry Show_Protection,^m<R2,R3,R4,R5,R6>

	; This is high IPL code.  Make sure we don't pagefault.
        $LKWSET_S       Locked_Pages2 		; Bolt those suckers down
	BLBC	R0,100$

	MOVL	4(AP),R2
	BBS	#31,R2,10$			; br if system VA

	MOVL	G^CTL$GL_PCB,R4
	MOVL	PCB$L_PHD(R4),R5
	BRB	20$

10$:	MOVL	G^MMG$AR_SYSPCB,R4
	MOVL	G^MMG$GL_SYSPHD,R5

20$:
	MFPR	#PR$_IPL,R6
	JSB	G^MMG$PTEREF
	BLBC	R0,100$
	EXTZV	#PTE$V_PROT,#PTE$S_PROT,-	; Get old prot from PTE
		(R3),R1
	UNLOCK	LOCKNAME=MMG,-			; Unlock the MMG database
		NEWIPL=R6,-
		PRESERVE=YES
100$:	
	RET
Last_Locked_Page2:

.entry Show_Page,^m<>
	MOVL	AP,-(SP)
	MOVAB	Set_Protection,-(SP)
	CALLS	#2,G^SYS$CMKRNL
	TSTL	8(AP)
	BEQL	110$
	MOVL	R1,@8(AP)
110$:	RET

.end
