
	.TITLE	REMOTE "Remote executioner"
	.IDENT /v1.0/

;+
; Facility:
;	REMOTE.MAR - Execute code in the context of another process.
;
; Abstract:
;
; Author:
;	Bruce R. Miller, MILLER@TGV.COM
;	TGV, Inc.
;	603 Mission St.
;	Santa Cruz, CA 95060
;	(408) 427-4366
;
; Date:
;	28-MAY-1991
;
; Functional Description:
;
; Notes:
;	Need to add support for an IOSB to hold the return status
;
; 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'
        .link		'sys$system:dcldef.stb'/SELECTIVE_SEARCH

	$ACBDEF
	$DYNDEF
	$IPLDEF
	$PCBDEF
	$PRIDEF
	$PSLDEF

	P1 =  4
	P2 =  8
	P3 = 12
	P4 = 16
	P5 = 20
	P6 = 24
	P7 = 28
	P8 = 32

;
; Extend the ACB structure
;
	$DEFINI	ACB
. = ACB$C_LENGTH
$DEF	ACB_L_FLAG		.BLKL 1
$DEF	ACB_L_STATUS		.BLKL 1
$DEF	ACB_L_HOME_IPID		.BLKL 1
$DEF	ACB_L_EFN		.BLKL 1
$DEF	ACB_L_ASTADR		.BLKL 1
$DEF	ACB_L_ASTPRM		.BLKL 1
$DEF	ACB_L_IOSB1		.BLKL 1
$DEF	ACB_L_IOSB2		.BLKL 1
$DEF	ACB_L_DATA_ADDR		.BLKL 1
$DEF	ACB_L_DATA_SIZE		.BLKL 1
$DEF	ACB_C_LENGTH
	$DEFEND


;++
; 	REMOTE_KRNL_S	- Execute code in another process
;
; FUNCTIONAL DESCRIPTION:
;
;
;
; CALLING SEQUENCE:
;	CALL
;
; INPUT PARAMETERS:
;	P1(AP)	- PID of target process
;	P2(AP)	- flags/acmode to use in target process context
;	P3(AP)	- ASTADR, begining of code to execute
;	P4(AP)	- length of code
;	P5(AP)	- ASTPRM, parameter to pass
;	P6(AP)	- arglist count
;
; IMPLICIT INPUTS:
;	Executing in kernel mode
;
; OUTPUT PARAMETERS:
;	R0	- status
;
; COMPLETION CODES:
;	SS$_NORMAL  -  NORMAL SUCCESSFUL COMPLETION STATUS
;	SS$_NONEXPR -  NON-EXISTENT PROCESS
;--


;++
;	Get_ACB - Allocate an ACB and initialize it.
;--
.ENTRY Get_ACB,^M<R2,R3>

	; Get internal PID
	MOVL	P2(AP),R0			; Get PID
	JSB	G^EXE$EPID_TO_IPID		; convert 
	BEQL	100$				; br on failure
	CMPW	R0,G^SCH$GL_SWPPID		; Swapper id is illegal
	BNEQ	10$				; br if not swapper
	MOVL	#SS$_NONEXPR,R0
	BRB	100$
10$:	MOVL	R0,R3				; Save IPID

	; Get an ACB
	MOVL	P1(AP),R1			; ACB size
	JSB	G^EXE$ALONONPAGED		; Get some on-paged pool
	BLBC	R0,100$				; br if error

	; Fill in the ACB
	CLRQ	ACB$L_ASTQFL(R2)		; Clear forward and back links
	MOVB	#DYN$C_ACB,ACB$B_TYPE(R2)	; Set VMS data type
	MOVW	R1,ACB$W_SIZE(R2)		; set size (for DEANONPAGED)
	MOVL	R3,ACB$L_PID(R2)		; Set target process
	CLRL	ACB$L_AST(R2)			; clear AST field
	CLRL	ACB$L_KAST(R2)			; clear KAST field

	CLRL	ACB_L_FLAG(R2)
	CLRL	ACB_L_STATUS(R2)

	; Get this process' PID  (is this synchronized?)
	MOVL	CTL$GL_PCB,R0
	MOVL	PCB$L_PID(R0),ACB_L_HOME_IPID(R2)

	; Return success
	MOVL	#SS$_NORMAL,R0			; Ok
	MOVL	R2,R1				; ACB in R1

100$:	RET

;++
;	Move_Code - Copy user's code into S0 space
;
; Abstract:
;	We allocate some non-paged pool.  Into it we copy the
;	header code, immediately followed by the user code.
;	Presumably the header code will do some setup and then
;	pass control to the user code.  The will probably also
;	do the cleanup afterwards.
;
; Input:
;	 4(AP)	- pointer to starting VA of user code segment
;	 8(AP)	- size of user code segment
;	12(AP)	- pointer to code header
;	16(AP)	- size of header code
;--
.ENTRY	Move_Code,^M<R2,R3,R4,R5>

	; Allocate some non-paged pool
	ADDL3	P2(AP),P4(AP),R1		; code size plus header length
	JSB	G^EXE$ALONONPAGED		; Get some on-paged pool
	BLBC	R0,100$				; br if error

	CLRQ	ACB$L_ASTQFL(R2)		; Clear forward and back links
	MOVL	R1,ACB$W_SIZE(R2)		; Set length field
	MOVL	#DYN$C_NON_PAGED,ACB$B_TYPE(R2); Set type field
	PUSHL	R2				; Save buffer address
	MOVC3	P4(AP),@P3(AP),12(R2)		; Copy Header.  R3->end of hdr
	MOVC3	P2(AP),@P1(AP),(R3)		; copy user code

	; Return success
	MOVL	#SS$_NORMAL,R0			; Ok
	POPL	R1				; buffer in R1

100$:	RET

;++
;	Queue_2_target - Send the ACB off to the other process
;
; Input:
;	4(AP)	- Initialized ACB
;--
.ENTRY	Queue_2_target,^M<R2,R3,R4,R5>
	; Queue AST to target process
	MOVL	#PRI$_TICOM,R2			; pass priority boost
	MOVL	P1(AP),R5			; pass ACB
	JSB	G^SCH$QAST

	RET



.ENTRY REMOTE_KRNL_S,^M<R2,R3,R4,R5,R10,R11>

	; Block ASTs for now
	DSBINT  IPL=#IPL$_ASTDEL

	; Set up the ACB
	PUSHL	P1(AP)				; pass PID
	ASHL	#2,P6(AP),-(SP)			; arglist byte length
	ADDL	#ACB$K_LENGTH+4,(SP)		; ACB length + user arglist
	CALLS	#2,Get_ACB			; alloc and init ACB
	BLBC	R0,100$				; br if error
	MOVL	R1,R10				; Save ACB in R10
	MOVB	P2(AP),ACB$B_RMOD(R10)		; set flags and acmode

	; Copy code to S0
	PUSHL	#hSlen				; Push header length
	PUSHAL	Header_S			; Push code header
	PUSHL	P4(AP)				; Push code size
	PUSHL	P3(AP)				; Push user code address
	CALLS	#4,Move_Code			; Copy code to S0
	BLBC	R0,110$				; br if error
	MOVL	R1,R11				; Save addr in R11
	MOVAB	12(R11),ACB$L_KAST(R10)		; link code to ACB

	; Tack arglist on end of ACB
	MOVAB	ACB_C_LENGTH(R10),R0		; point to "end" of ACB
	MOVL	P6(AP),R1			; Get arglist size
	MOVL	R1,(R0)+			; Add arglist length
	ASHL	#2,R1,R1			; arglist size in bytes
	MOVC3	R1,@P5(AP),(R0)			; copy arglist to ACB

	; Queue 
	PUSHL	R10				; Pass ACB
	CALLS	#1,Queue_2_target		; Send that puppy off
	BLBC	R0, 110$			; br if error

100$:	; Allow ASTs
	ENBINT
	RET

110$:	; Deallocate KAST buffer
	PUSHL	R0
	MOVL	R11,R0
	JSB	G^EXE$DEANONPAGED
	POPL	R0
	BRW	100$


;++
; 	REMOTE_KRNL_G	- Execute code in another process
;
; FUNCTIONAL DESCRIPTION:
;
;
;
; CALLING SEQUENCE:
;	CALL
;
; INPUT PARAMETERS:
;	P1(AP)	- PID of target process
;	P2(AP)	- User code address
;	P3(AP)	- User code size
;	P4(AP)	- User data address
;	P5(AP)	- User data size
;	P6(AP)	- AST address
;	P7(AP)	- AST parameter
;	P8(AP)	- EFN (Event Flag Number)
;
; IMPLICIT INPUTS:
;	Executing in kernel mode
;
; OUTPUT PARAMETERS:
;	R0	- status
;
; COMPLETION CODES:
;	SS$_NORMAL  -  NORMAL SUCCESSFUL COMPLETION STATUS
;	SS$_NONEXPR -  NON-EXISTENT PROCESS
;--

.ENTRY REMOTE_KRNL_G,^M<R2,R3,R4,R5,R10,R11>

	; Block ASTs for now
;	DSBINT  IPL=#IPL$_ASTDEL

	; Set up the ACB
	PUSHL	P1(AP)				; pass PID
	ADDL3	#ACB_C_LENGTH+4,P5(AP),-(SP)	; new ACB length
	ADDL2	#hGlen,(SP)			; add in header code size
	ADDL2	P3(AP),(SP)			; add in user code size
	CALLS	#2,Get_ACB			; alloc and init ACB
	BLBC	R0,100$				; br if error
	MOVL	R1,R10				; Save addr in R11
	CLRB	ACB$B_RMOD(R10)			; clear flags and acmode
	BISB2	#ACB$M_KAST,ACB$B_RMOD(R10)	; Mark as a special kmode

	; Tack data onto end of ACB
	MOVAB	ACB_C_LENGTH(R10),R0		; point to "end" of ACB
	MOVL	P4(AP),ACB_L_DATA_ADDR(R10)	; Store arglist addr
	MOVL	P5(AP),ACB_L_DATA_SIZE(R10)	; Store arglist length
	MOVC3	P5(AP),@P4(AP),(R0)		; copy arglist to ACB
	; note: MOVC3 leaves R3 = the VA of the byte after dst string

	; Copy code to S0
	MOVL	R3,ACB$L_KAST(R10)
	MOVC3	#hGlen,Header_G,(R3)		; Copy Header.  R3->end of hdr
	MOVC3	P3(AP),@P2(AP),(R3)		; copy user code

	; Save completion info
	MOVL	P6(AP),ACB_L_ASTADR(R10)	; Store AST address
	MOVL	P7(AP),ACB_L_ASTPRM(R10)	; Store AST parameter
	MOVL	P8(AP),ACB_L_EFN(R10)		; Store Event Flag Number

	; Send the AST off
	MOVL	#PRI$_TICOM,R2			; pass priority boost
	MOVL	R10,R5				; pass ACB
	JSB	G^SCH$QAST

100$:	; Allow ASTs
;	ENBINT
	RET


;++
;	Header_S - code to call user code, then deallocate self
;
; DESCRIPTION:
;	Save R5 (the ACB).  CALLS the user routine (in ACB$L_KAST)
;	passing the user supplied paramer (in ACB$L_ASTPRM).  Deallocate
;	the ACB.  We can't JSB to deallocate the code we're executing,
;	so we wait till last, then JMP to EXE$DEANONPAGED, letting it
;	do the RSB from Header.
;
; INPUTS:
;	R4 - Current PCB
;	R5 - ACB
;
; OUTPUTS:
;	Deallocates ACB and ACB$L_KAST(ACB)
;--
Header_S:
	PUSHL	R5				; save ACB
;	PUSHL	ACB$L_ASTPRM(R5)		; pass user's parameter
	ADDL3	#ACB_C_LENGTH,R5,R0		; end of standard ACB
	MOVAB	10$, R1				; User routine (after header)
	CALLG	(R0),(R1)
	POPL	R5
	PUSHL	ACB$L_KAST(R5)			; Save pointer to ourselves
	MOVL	R5,R0
	JSB	G^EXE$DEANONPAGED		; Deallocate ACB
	SUBL3	#12,(SP)+,R0			; real begining of code block
	JMP	G^EXE$DEANONPAGED		; Deallocate header
10$:
	hSlen = . - Header_S


;++
;	Header - code to call user code, then deallocate self
;
; DESCRIPTION:
;	Save R5 (the ACB).  CALLS the user routine (in ACB$L_KAST)
;	passing the user supplied paramer (in ACB$L_ASTPRM).  Deallocate
;	the ACB.  We can't JSB to deallocate the code we're executing,
;	so we wait till last, then JMP to EXE$DEANONPAGED, letting it
;	do the RSB from Header.
;
; INPUTS:
;	R0-R5 have been saved prior to entry
;	R4 - Current PCB
;	R5 - ACB
;
; OUTPUTS:
;	Deallocates ACB and ACB$L_KAST(ACB)
;--
Header_G:
	; Check whether this is stage I or II
	TSTL	ACB_L_FLAG(R5)			; Deja vu?
	BNEQ	100$				; br if so

	; Call the user code 
	PUSHL	R5				; save ACB
	PUSHL	ACB_L_DATA_SIZE(R5)		; data size
	PUSHAB	ACB_C_LENGTH(R5)		; data buffer
	CALLS	#2,B^200$
	POPL	R5

	; Save call status and send the ACB back to the original process
	MOVL	R0,ACB_L_STATUS(R5)		; Store the return value
	MOVL	#1,ACB_L_FLAG(R5)		; Mark phase I completed
	MOVL	ACB_L_HOME_IPID(R5),-		; Do the old switcheroo
		ACB$L_PID(R5)
	BISB2	#ACB$M_KAST,ACB$B_RMOD(R5)      ; Mark as a special kmode
	MOVL	#PRI$_TICOM,R2			; pass priority boost
	JMP	G^SCH$QAST			; Send ACB back out
	; note: SCH$QAST will do the RSB for us.

100$:	; Now we're back in the original process.
	PUSHL	R5				; Save ACB
	MOVAB	ACB_C_LENGTH(R5),R0		; end of standard ACB
	MOVC3	ACB_L_DATA_SIZE(R5),(R0),-	; Copy data back to user
		@ACB_L_DATA_ADDR(R5)
	POPL	R5				; Restore ACB pointer

	; Post the Event Flag
	MOVL	ACB_L_HOME_IPID(R5),R1
	CLRL	R2
	MOVZWL	ACB_L_EFN(R5),R3
	JSB	G^SCH$POSTEF

	; Declare the completion AST
	TSTL	ACB_L_ASTADR(R5)		; Is there an AST?
	BEQL	150$				; br if not
	MOVB	#PSL$C_USER,ACB$B_RMOD(R5)	; Do last one in user mode
	MOVL	ACB_L_ASTADR(R5),ACB$L_AST(R5)	; Call user's completion rtn.
	MOVL	ACB_L_ASTPRM(R5),ACB$L_ASTPRM(R5); Pass user's parameter
	CLRL	ACB$L_KAST(R5)			; Clear Kernel AST
	CLRL	R2				; No priority boost
	JMP	G^SCH$QAST			; Send ACB back out

150$:	; All done!  Now release the memory we've been using
	MOVL	R5,R0				; Move ACB into R0
	JMP	G^EXE$DEANONPAGED		; Deallocate ACB
	; DEANONPAGED is going to do the RSB for us, which is a lucky
	; thing since it deallocates the page we're executing off of.
200$:
	hGlen = . - Header_G


;++
;	CALL_REMOTE_S - execute a procedure in another process's address space
;
; FUNCTIONAL DESCRIPTION:
;
;	This code causes the supplied routine to be called in the
;	context of the target process.  The routine is specified
;	by the address in P2 and the size in P3.  The PID of the
;	target process is specified in P1.  The user code is called
;	with the CALLS instruction and passed the supplied arguments
;	in the AP vector, as if the target process had done:
;
;		Routine ( P4(AP), P5(AP), ... )
;
;	WARNING:  The code is called in KERNEL mode at IPL 2 (ASTDEL).
;	The AP vector is located in S0.  The SCHED lock is not held.
;	Even the user code has ben copied into non-paged pool.
;	So...  don't screw around unless you know what you're doing.
;
; INPUT:
;	P1(AP) - target PID
;	P2(AP) - begining of code to execute
;	P3(AP) - size of code block
;	P4(AP) - User P1
;	P5(AP) - User P2
;	Pn+3(AP) - User Pn
;
; OUTPUTS:
;	R0 - Status of the AST queueing process
;		Status of user routine is lost (in this version)
;--
.ENTRY	CALL_REMOTE_S,^M<>
	; Build ARG list
	SUBL3	#3,(AP),-(SP)			; arglist length
	PUSHAL	P4(AP)				; user supplied args
	PUSHL	P3(AP)				; user code size
	PUSHL	P2(AP)				; User code
	PUSHL	#ACB$M_KAST			; flags
	PUSHL	P1(AP)				; PID
	PUSHL	#6

	; Call the queueing code in kernel mode
	PUSHL	SP				; arg list
	PUSHAL	REMOTE_KRNL_S			; routine to call
	CALLS	#2,G^SYS$CMKRNL			; Change mode to kernel

	RET


;++
;	CALL_REMOTE_G - execute a procedure in another process's address space
;
; FUNCTIONAL DESCRIPTION:
;
;	Same as CALL_REMOTE, except for the passing of the parameters
;	to the user's code.  The user parameter is copied into S0 and
;	its address is passed to the routine in 4(AP) and its size in
;	8(AP).
;
; INPUT:
;	P1(AP) - target PID
;	P2(AP) - begining of code to execute
;	P3(AP) - size of code block
;	P4(AP) - User parameter
;	P5(AP) - parameter size
;	...
;
; OUTPUTS:
;	R0 - Status of the AST queueing process
;		Status of user routine is lost (in this version)
;--
.ENTRY	CALL_REMOTE_G,^M<>
	; Call the hairy code in kernel mode
	MOVL	AP,-(SP)			; arg list
	PUSHAB	REMOTE_KRNL_G			; routine to call
	CALLS	#2,G^SYS$CMKRNL			; Change mode to kernel

	RET

.END
