.title K_AST Deliver a "special KERNEL mode AST" ;++ ; K_AST ; ; Author: Nick de Smith ; Applied Telematics Services, 7 Vale Avenue ; Tunbridge Wells, Kent TN1 1DJ, England. ; +44 892 511000, PSI%234213300154::NICK ; Date : 05-May-87, 19-Dec-88 ; ; Copyright (c) 1987,1988,1989 by Applied Telematics Group Limited and Nick de Smith. ; This software is supplied for information only. No guarantee is supplied for ; this software, and no liability will be accepted for any action resulting from ; the use of this software or the information contained herein. Under no ; circumstances may this software be used for commercial gain, including its ; sale, lease or loan. This software may be copied only with the inclusion of ; this copyright notice. The author is prepared to enter into correspondance ; with interested parties, but will not necessarily maintain this software. ; Having said all that, enjoy it! ; ; Description: ; ; This routine runs in KERNEL mode to deliver a special KERNEL mode AST to ; the target process. ; ; *********************************** ; You should not need to modify this module - It is common to all programs ; that deliver special kernel mode ASTs. ; *********************************** ; ; The code to execute in the context of the target process is placed in ; a separate module (whose template is AST_TEMPLATE.MAR). ; The technique used is to place your code at the end of an extended ACB. ; The front of the ACB contains the delivery information and is set up by ; QUEUE_KERNEL_AST below. This routine also copies AST_LENGTH bytes from ; AST_CODE into the ACB, which it then queues as a special KERNEL mode AST. ; Note that this technique will only work for special KERNEL mode ASTs and ; for normal ASTs that have a "piggy-back" special KERNEL mode AST. In all ; other cases, the AST delivery code (in ASTDEL) deallocates the ACB before ; calling the AST routine. Therefore we would be executing code in a region ; of non-paged pool that was on the free list. Danger! Danger! See AST.TXT ; for a discussion on different types of ASTs. ; ; Edit Edit date By Why ; 02 01-Mar-89 NMdS Upgrade to use new page locking macros. ; 2.01 19-Dec-88 NMdS VMS V5, make into a template. ; 01 05-May-87 NMdS First attempt ; ;-- .ident /V02.02/ .library "SYS$LIBRARY:LIB" .link "SYS$SYSTEM:SYS.STB" /Selective_Search ; Save typing later $acbdef ; AST Control Block definitions $cpudef ; Define CPU block offsets $dyndef ; Data structure type codes $ipldef ; Synchronization IPL values $pcbdef ; Software PCB fields $pridef ; Priority boost classes $psldef ; Fields in PSL $sfdef ; Saved frame offsets $ssdef ; Define system statii $statedef ; Scheduler definitions ; ; The following symbols define the location of the user's AST code. ; See the user template, AST_TEMPLATE.MAR for an explanation. ; .global ACB_K_NEW_LENGTH ; Length of user's extended ACB .global ACB_T_AST_CODE ; Where to put user's code in ACB .global AST_CODE ; Address of user's AST code .global AST_CODE_LENGTH ; Length of user's AST code .subtitle QUEUE_KERNEL_AST Queue a KERNEL mode AST to another process ;++ ; QUEUE_KERNEL_AST Queue an AST to a target process ; ; Calling sequence: ; ; K_ARGS:.long 2 ; $CMKRNL argument list ; .address EPID ; => Target PID to deliver AST to ; .address PRCNAM_DESC ; => Process name descriptor ; ; $cmkrnl_s - ; Queue the AST ; routin = QUEUE_KERNEL_AST,- ; Routine to call ; arglst = K_ARGS ; Argument list ; ; Input Parameters: ; ; R4 => Caller's PCB (left there by the change mode dispatcher) ; 4(ap) => Target process ID ; 8(ap) => Target process name descriptor. Used if 4(ap) = 0. ; ; EXE$NAMPID converts a PID or process name into an EPID, IPID and PCB address: ; Requires: ; R4 => Caller's PCB (for privilege checks) ; @4(AP) = PID to look up (in any format) ; 8(AP) => Descriptor of process name to look up. Only used ; if 4(ap) = 0. ; Returns: ; R0 = Completion status ; R1 = IPID of target process, 0 if not found ; R4 => PCB of target process (if found) ; @4(AP) = EPID of target process (if found) ; spinlock SYNCH for success, ; IPL$_ASTDEL for failure ;-- .Entry QUEUE_KERNEL_AST, ^m movab g^EXE$SIGTORET, SF$A_HANDLER(fp) ; Set KERNEL mode exception handler pushr #^m ; Save our PCB address (popped as R6 later) ; ; Get process ID of target process. We use EXE$NAMPID as it provides ; access control checks (but don't call it at or above IPL$_SYNCH as ; it probes and modifies its argument list, which might incur a page fault). ; Because EXE$NAMPID can return at an elevated IPL (above ASTDEL) we must lock ; down the call to it because there is a chance (however slight) that we will ; page fault before we can unlock the SCHED spinlock. ; 5$: SETIPL 15$,- ; Lock down the following code environ = UNIPROCESSOR jsb g^EXE$NAMPID ; Returns with spinlock SCHED, or IPL$_ASTDEL blbs r0, 10$ ;; Branch if PID was valid and we have access SETIPL #0 ;; Drop IPL from ASTDEL ret ; Return to caller 10$: UNLOCK SCHED ,- ;; Remove SCHED spinlock newipl = #0 ,- ;; ...and drop elevated IPL preserve= NO ;; ...but don't save R0 cmpl r1, g^SCH$GL_SWPPID ; NULL and SWAPPER are illegal bgtru 20$ ; Branch if we have a valid IPID movzwl #SS$_NONEXPR, r0 ; Say "Non existant process" ret 15$: .long IPL$_SCHED ASSUME <.-5$> Le 512 ; Locked data area > 1 page ; ; Now allocate an AST control block and store the relevant parameters ; 20$: movl #ACB_K_NEW_LENGTH, r1 ; Set size of extended ACB jsb g^EXE$ALONONPAGED ; Allocate nonpaged pool space blbs r0, 30$ ; Continue if there was no error ret ; Error, so return to caller 30$: movl r2, r5 ; R5 => New ACB movb #DYN$C_ACB, ACB$B_TYPE(r5) ; Say block is an ACB movw r1, ACB$W_SIZE(r5) ; Store size of structure movl PCB$L_PID(r4), ACB$L_PID(r5) ; Store PID of target process movpsl r1 ; Get the current PSL extzv #PSL$V_PRVMOD, #PSL$S_PRVMOD, r1, r1 ; R1 = caller's access mode bisb3 #<1@ACB$V_KAST>, r1, ACB$B_RMOD(r5) ; and store it in ACB movab ACB_T_AST_CODE(r5), ACB$L_KAST(r5) ; Set the AST address popr #^m ; R6 => caller's PCB ; ; Fill in the extended ACB fields ; R4 => Target process's PCB ; R5 => Allocated ACB ; R6 => Caller's process PCB ; movl PCB$L_PID(r6), ACB_L_SPID(r5) ; Store PID of source process pushr #^m ; Save registers movc3 #AST_LENGTH, AST_CODE, ACB_T_AST_CODE(r5) ; Copy AST code into the ACB popr #^m ; Restore registers ; ; Ready to queue the AST to the target process. ; Note that the following code is rigorous. You don't need to check if the ; target PCB is that same as the NULL job PCB and that the PIDs are different ; - the latter test on its own would be sufficient. Also we carry out all ; possible checks (I think). A fair chunk of the code could be omitted, but ; "when in KERNEL mode, play safe". ; In theory we could deliver an AST to a process in MWAIT, but if the process ; is that far down the line, it has enough problems (if its in MWAIT, by the ; time it comes out, and can service the AST, most of the interesting data ; would be lost - use SDA in such situations). ; LOCKED_START: SETIPL LOCKED_END,- ; Stop page faults in following code environ = UNIPROCESSOR LOCK SCHED ,- ;; Lock the database preserve= NO ;; ...but don't save R0 cmpl PCB$L_PID(r4), ACB$L_PID(r5) ;; Are PIDs the same? bneq no_process ;; Error if not (process has gone away) bbs #PCB$V_DELPEN, PCB$L_STS(r4), no_process ;; Check if being deleted bbs #PCB$V_SUSPEN, PCB$L_STS(r4), suspended ;; Check if suspend pending cmpw #SCH$C_SUSP, PCB$W_STATE(r4) ;; Check if already suspended beql suspended ;; ...error if it is cmpw #SCH$C_SUSPO, PCB$W_STATE(r4) ;; ...or suspended, outswapped beql suspended ;; ...error if it is cmpw #SCH$C_MWAIT, PCB$W_STATE(r4) ;; ...or (possibly) long wait beql suspended ;; ...error if it is movl #PRI$_TICOM, r2 ;; SCH$QAST needs priority boost in R2 jsb g^SCH$QAST ;; Queue the AST ; ; If the target process is in COM, COMO, SUSP or SUSPO and at a lower priority ; than the current process, boost the target process's current priority to the ; current process's current priority. This is required because SCH$RSE (in RSE) ; will ignore EVT$_AST type events if the target process is in one of COM, COMO, ; SUSP or SUSPO, and therefore the target might not run (if we continue). Note ; also that we must not boost into the realtime priority band, as the scheduler ; will never subsequently lower the priority again. If the process was in any ; SUSPx state we would not have got this far anyway. ; cmpw #SCH$C_COM, PCB$W_STATE(r4) ;; Is process in COM state? beql 10$ ;; ...yes, so boost it cmpw #SCH$C_COMO, PCB$W_STATE(r4) ;; What about COMO? bneq 20$ ;; ...no, so ignore this one 10$: ; ; We can't use CTL$GL_PCB to get the current PCB because, although we have ; access to the process P1 space, we are now at IPL$_SYNCH, and a page fault ; here would crash the system. ; movb PCB$B_PRI(r6), r0 ;; R0 = our current priority ; ; As priorities are stored in the PCB in inverted format (eg. a value of 0 ; is a priority of 31), then the larger the value in the PCB, the lower the ; priority. This is why the following test appears to be the wrong way round. ; It is, in fact, correct. ; cmpb r0, PCB$B_PRI(r4) ;; Is target's priority higher? bgequ 20$ ;; ...yes, so quit cmpb #16, r0 ;; Will boost be into "realtime"? bgtru 20$ ;; ...yes, so don't boost jsb g^SCH$CHSEP ;; Boost target's priority 20$: UNLOCK SCHED ,- ;; Unlock the database newipl = #0 ,- ; ...force drop in IPL preserve= NO ; ...don't keep R0 movzwl #SS$_NORMAL, r0 ; Say we did everything ret ; Return to caller ; ; Process has gone away or is unavailable. Deallocate ACB and return through ; common exit path. ; .enable LSB NO_PROCESS: pushl #SS$_NONEXPR ;; This is error if process has gone away brb 10$ SUSPENDED: pushl #SS$_SUSPENDED ;; Error if process is suspended 10$: UNLOCK SCHED ,- ;; Remove spinlock newipl = #0 ,- ; ...and remove elevated IPL preserve= NO ; ...don't save R0 movl r5, r0 ; Get address of pool to be deallocated jsb g^EXE$DEANONPAGED ; Deallocate the ACB popl r0 ; Restore status ret ; ...and return LOCKED_END: ; End of locked pages .long IPL$_SCHED ASSUME <.-LOCKED_START> le 512 ; Locked data area > 1 page .end