.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 ; 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 ; 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 ; 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 ; 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 ; 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