; public_domain_notice ; ; Copyright (c) 1996 David P. Murphy for Datametrics Systems Corporation --- ; please send all comments and bug reports to murphy@connor.datametrics.com ; ; Permission is granted to any individual or institution to use, copy, or ; redistribute this software so long as all of the original files are included, ; that it is not sold for profit, and that this copyright notice is retained. ; Use at your own risk. Neither Murphy nor Datametrics assumes responsibility. ; Do not taunt Happy Fun Ball. .title MYCROFT - Obtain Privileged Process Information .ident 'V1.0' .library "sys$library:lib.mlb" $atrdef $ccbdef $ddbdef $dyndef $fcbdef $fibdef $ihadef $ihddef $ihidef $ihpdef $ihsdef $iodef $kfedef $pcbdef $phddef $sbdef $secdef $ssdef $tqedef $ucbdef $wcbdef ; are we assembling on a VAX or compiling on an AXP? digital does not ; provide a built-in __ALPHA macro (grrrr) so we use the same method ; as hunter goatley & many others: test a symbol which does not exist ; on both systems. i chose the "status" member of the Channel Control ; Block (a byte on a vax, a longword on an alpha) since i've already ; included that module. try to win style points by allowing it to be ; overridden, of course. ; .if not_defined __VAX .if not_defined __ALPHA .if defined ccb$l_sts __ALPHA == 1 .if_false __VAX == 1 .endc .endc .endc ; the new method of creating user-written system services ; renders this module unnecessary under AXP ; .if defined __VAX $plvdef .endc ; several data structures concerning image headers were changed ; between VAX and AXP . . . this is my way of dealing with it ; .if defined __ALPHA $eihadef $eihddef $eihidef $eihpdef $eihsdef .SYMBOL_ALIGNMENT QUAD $EQU iha$l_inishr eiha$l_inishr $EQU iha$l_tfradr1 eiha$l_tfradr1 $EQU iha$l_tfradr2 eiha$l_tfradr2 $EQU iha$l_tfradr3 eiha$l_tfradr3 $EQU ihd$b_imgtype eihd$l_imgtype $EQU ihd$s_ihddef eihd$s_eihddef $EQU ihd$w_patchoff eihd$l_patchoff $EQU ihd$v_lnkdebug eihd$v_lnkdebug $EQU ihd$l_lnkflags eihd$l_lnkflags $EQU ihi$t_imgid eihi$t_imgid $EQU ihi$t_imgnam eihi$t_imgnam $EQU ihi$t_linkid eihi$t_linkid $EQU ihi$q_linktime eihi$q_linktime $EQU ihp$l_eco1 eihp$l_eco1 $EQU ihs$l_dstvbn eihs$l_dstvbn .SYMBOL_ALIGNMENT NONE .endc ; need two home-grown modules ; .library "sys$disk:[]dpmdef.mlb" $dpmdef $icbdef ; ; a macro to declare a global entry point for a function ; .macro ENTRY_POINT, NAME, REGLIST, ARGC=0 .if defined __VAX .entry NAME, ^m .endc .if defined __ALPHA .if equal ARGC NAME:: .call_entry preserve= .if_false NAME:: .call_entry preserve=,max_args=ARGC,home_args=TRUE .endc .endc .endm ; convert an "external" pid to its "internal" equivalent ; .macro E2IPID, FROM, TO=0 movl FROM, r0 .if defined __ALPHA jsb G^exe$cvt_epid_to_ipid .if_false jsb G^exe$epid_to_ipid .endc .if equal TO movl r0, FROM .if_false movl r0, TO .endc .endm E2IPID .macro I2EPID, FROM, TO=0 movl FROM, r0 .if defined __ALPHA jsb G^exe$cvt_ipid_to_epid .if_false jsb G^exe$ipid_to_epid .endc .if equal TO movl r0, FROM .if_false movl r0, TO .endc .endm I2EPID ; ; this is cut&pasted straight from SYS$EXAMPLES:USSDISP.MAR --- ; please read the original if you have any questions ; .if defined __VAX .MACRO DEFINE_SERVICE,NAME,NARG=0,MODE=KERNEL .PSECT $$$TRANSFER_VECTOR,PAGE,NOWRT,EXE,PIC .ALIGN QUAD ; Align entry points for speed and style .TRANSFER NAME ; Define name as universal symbol for entry .MASK NAME ; Use entry mask defined in main routine .IF IDN MODE,KERNEL CHMK # ; Change to kernel mode and execute RET ; Return KERNEL_COUNTER=KERNEL_COUNTER+1 ; Advance counter .PSECT KERNEL_NARG,BYTE,NOWRT,EXE,PIC .BYTE NARG ; Define number of required arguments .PSECT USER_KERNEL_DISP1,BYTE,NOWRT,EXE,PIC .SIGNED_WORD 2+NAME-KCASE_BASE ; Make entry in kernel mode CASE table .IFF CHME # ; Change to executive mode and execute RET ; Return EXEC_COUNTER=EXEC_COUNTER+1 ; Advance counter .PSECT EXEC_NARG,BYTE,NOWRT,EXE,PIC .BYTE NARG ; Define number of required arguments .PSECT USER_EXEC_DISP1,BYTE,NOWRT,EXE,PIC .SIGNED_WORD 2+NAME-ECASE_BASE ; Make entry in exec mode CASE table .ENDC ; .ENDM DEFINE_SERVICE ; KERNEL_COUNTER=0 ; Kernel code counter EXEC_COUNTER=0 ; Exec code counter .PSECT KERNEL_NARG,BYTE,NOWRT,EXE,PIC KERNEL_NARG: ; Base of byte table containing the ; number of required arguments. .PSECT EXEC_NARG,BYTE,NOWRT,EXE,PIC EXEC_NARG: ; Base of byte table containing the ; number of required arguments. ; exec service to copy the unit #, device name, and nodename ; of all currently active channels into the caller's array ; DEFINE_SERVICE MH_GetChannelInfo, 6, EXEC ; kernel service to return infomation about one existing lock ; DEFINE_SERVICE MH_GetLockInfo, 5, KERNEL ; exec service to return infomation about one outstanding timer ; DEFINE_SERVICE MH_GetTimerInfo, 3, EXEC KCODE_BASE=-1024 ; Base CHMK code value for these services ECODE_BASE=-1024 ; Base CHME code value for these services ; Change Mode Dispatcher Vector Block .PSECT USER_SERVICES,PAGE,VEC,PIC,NOWRT,EXE .LONG PLV$C_TYP_CMOD ; Set type of vector to change mode dispatcher .LONG 0 ; Reserved .LONG KERNEL_DISPATCH-. ; Offset to kernel mode dispatcher .LONG EXEC_DISPATCH-. ; Offset to executive mode dispatcher .LONG 0 ; Offset to user rundown service .LONG 0 ; Reserved. .LONG 0 ; No RMS dispatcher .LONG 0 ; Address check - PIC image ; Kernel Mode Dispatcher .PSECT USER_KERNEL_DISP0,BYTE,NOWRT,EXE,PIC KACCVIO: ; Kernel access violation MOVZWL #SS$_ACCVIO,R0 ; Set access violation status code RET ; and return KINSFARG: ; Kernel insufficient arguments. MOVZWL #SS$_INSFARG,R0 ; Set status code and RET ; return KNOTME: RSB ; RSB to forward request KERNEL_DISPATCH:: ; Entry to dispatcher MOVAB W^-KCODE_BASE(R0),R1 ; Normalize dispatch code value BLSS KNOTME ; Branch if code value too low CMPW R1,#KERNEL_COUNTER ; Check high limit BGEQU KNOTME ; Branch if out of range ; ; The dispatch code has now been verified as being handled by this dispatcher, ; now the argument list will be probed and the required number of arguments ; verified. ; MOVZBL W^KERNEL_NARG[R1],R1 ; Get required argument count MOVAL @#4[R1],R1 ; Compute byte count including arg count IFNORD R1,(AP),KACCVIO ; Branch if arglist not readable CMPB (AP),W^[R0] ; Check for required number BLSSU KINSFARG ; of arguments MOVL FP,SP ; Reset stack for service routine CASEW R0,- ; Case on change mode - ; argument value #KCODE_BASE,- ; Base value # ; Limit value (number of entries) KCASE_BASE: ; Case table base address for DEFINE_SERVICE ; ; Case table entries are made in the PSECT USER_KERNEL_DISP1 by ; invocations of the DEFINE_SERVICE macro. The three PSECTS, ; USER_KERNEL_DISP0,1,2 will be abutted in lexical order at link-time. ; .PSECT USER_KERNEL_DISP2,BYTE,NOWRT,EXE,PIC BUG_CHECK IVSSRVRQST,FATAL ; Since the change mode code is validated ; above, we should never get here ; Executive Mode Dispatcher .PSECT USER_EXEC_DISP0,BYTE,NOWRT,EXE,PIC EACCVIO: ; Exec access violation MOVZWL #SS$_ACCVIO,R0 ; Set access violation status code RET ; and return EINSFARG: ; Exec insufficient arguments. MOVZWL #SS$_INSFARG,R0 ; Set status code and RET ; return ENOTME: RSB ; RSB to forward request EXEC_DISPATCH:: ; Entry to dispatcher MOVAB W^-ECODE_BASE(R0),R1 ; Normalize dispatch code value BLSS ENOTME ; Branch if code value too low CMPW R1,#EXEC_COUNTER ; Check high limit BGEQU ENOTME ; Branch if out of range ; ; The dispatch code has now been verified as being handled by this dispatcher, ; now the argument list will be probed and the required number of arguments ; verified. ; MOVZBL W^EXEC_NARG[R1],R1 ; Get required argument count MOVAL @#4[R1],R1 ; Compute byte count including arg count IFNORD R1,(AP),EACCVIO ; Branch if arglist not readable CMPB (AP),W^[R0] ; Check for required number BLSSU EINSFARG ; of arguments MOVL FP,SP ; Reset stack for service routine CASEW R0,- ; Case on change mode - ; argument value #ECODE_BASE,- ; Base value # ; Limit value (number of entries) ECASE_BASE: ; Case table base address for DEFINE_SERVICE ; ; Case table entries are made in the PSECT USER_EXEC_DISP1 by ; invocations of the DEFINE_SERVICE macro. The three PSECTS, ; USER_EXEC_DISP0,1,2 will be abutted in lexical order at link-time. ; .PSECT USER_EXEC_DISP2,BYTE,NOWRT,EXE,PIC BUG_CHECK IVSSRVRQST,FATAL ; Since the change mode code is validated ; above, we should never get here .endc ; .psect $data pic,con,rel,lcl,noshr,noexe,rd,wrt,quad .align quad faonumber: .ascid /!SL/ .align quad iosb: .blkq 1 tmpsd: .blkl 2 aa1: .blkl 1 ; this will be a pointer to the MYCROFT_CHEAP_CHANNEL_INFO structure aa2: .blkl 1 ; this will be a pointer to the MYCROFT_EXPENSIVE_CHANNEL_INFO structure aa3: .blkl 1 ; this will be a pointer to the MYCROFT_TIP_CHANNEL_INFO structure retlen: .blkw 1 TheChan: .blkw 1 IsFS: .blkb 1 TheFib: .blkb fib$k_length TheFilespec: .blkb atr$s_file_spec .align quad AtrList: .word atr$s_credate .word atr$c_credate .long 0 .word atr$s_revdate .word atr$c_revdate .long 0 .word atr$s_expdate .word atr$c_expdate .long 0 .word atr$s_bakdate .word atr$c_bakdate .long 0 .word atr$s_uic_ro .word atr$c_uic_ro .long 0 .word atr$s_fpro .word atr$c_fpro .long 0 .word atr$s_file_spec .word atr$c_file_spec .address TheFilespec .word atr$s_header .word atr$c_header .long 0 .long 0 ; end of list TheFibDesc: .long fib$k_length .address TheFib TheGrabDesc: .long 4 .address tmpsd .address TheChan TheQIOdesc: .long 12 .long 0, 0, 0 .address iosb .long 0, 0 .address TheFibDesc .long 0, 0, 0, 0, 0 TheReadDesc: .long 12 .long 0, 0, IO$_READVBLK .address iosb .long 0, 0, 0 .long dpm3$s_imageheader ; better be the same size as IHD$S_IHDDEF .long 1 ; the file header is always VBN #1 .long 0, 0, 0 TheFAOdesc: .long 4 .address faonumber .address retlen .address tmpsd .long 0 ; .psect $code pic,usr,con,rel,lcl,shr,exe,rd,nowrt,novec,long ; ; MH_GetChannelInfo : Get Active Channel Information ; ; Input Parameters: ; ; 04(AP) - context.ml - REQUIRED - address of unsigned longword which ; will serve as a marker so that we can tell which ; channel we are supposed to access . . . we start at ; the beginning if contains zero ; ; 08(AP) - thepid.rl - IGNORED - unsigned longword which is the identifier ; of the process to be examined (not yet implemented --- ; current process is assumed) ; ; 12(AP) - cheapptr.wl - REQUIRED - address of a CHEAP_CHANNEL_INFO ; structure into which various information is written ; ; 16(AP) - expenptr.wl - OPTIONAL - address of a EXPENSIVE_CHANNEL_INFO ; structure into which various information is written ; ; 20(AP) - tipptr.wl - OPTIONAL - address of a TIP_CHANNEL_INFO ; structure into which various information is written ; ; 24(AP) - unused.wl - MBZ - address reserved for future use ; ; Output Parameters: ; ; R0 - Completion Status Code ; SS$_NORMAL - an active channel was found and information passed back ; SS$_NOMOREFILES - no more active channels could be found ; SS$_BADPARAM - a non-zero value was passed for ; SS$_ACCVIO - a NULL pointer was passed ; any "failure" status from $FAO, $QIOW, $ASSIGN, $DASSGN ENTRY_POINT MH_GetChannelInfo, , 6 context = 4 thepid = 8 cheapptr = 12 expenptr = 16 tipptr = 20 unused = 24 ; ensure that is zero ; tstl thepid(ap) beql CI0 movzwl #SS$_BADPARAM, r0 ret ; ensure that is NULL, and are not, ; and that the "cheap", "expensive" and "tip" areas are entirely ; zeroed out if passed ; CI0: tstl unused(ap) bneq OUCH tstl context(ap) beql OUCH probew #0, #4, @context(ap) ; can we write to this area? bneq CIZ0 OUCH: movzwl #SS$_ACCVIO, r0 ret CIZ0: movl cheapptr(ap), aa1 beql OUCH movl aa1, r6 probew #0, #dpm1$k_length, (r6) beql OUCH movc5 #0, (r6), #0, #dpm1$k_length, (r6) CIZ1: movl expenptr(ap), aa2 beql CIZ2 movl aa2, r6 probew #0, #dpm2$k_length, (r6) beql OUCH movc5 #0, (r6), #0, #dpm2$k_length, (r6) CIZ2: movl tipptr(ap), aa3 beql CIZ3 movl aa3, r6 probew #0, #dpm3$k_length, (r6) beql OUCH movc5 #0, (r6), #0, #dpm3$k_length, (r6) ; ho ho ho --- under VAX/VMS, points to the *highest* entry ; in the array and contains the total number of *bytes* ; allocated to the array of ccb's. under AXP/VMS, points ; to the *lowest* entry in the array and contains the number ; of *entries*. in both cases, the global variable references the ; "first" channel (i.e., chan == 0x10). ; ; i can only assume that the dec engineers had a Darn Good Reason (tm) ; to play with my mind like this. ; CIZ3: .if defined __VAX movl @#CTL$GL_CCBBASE, r11 ; end of Channel Control Blocks table cvtwl @#CTL$GW_CHINDX, r2 ; total size of CCB table subl3 r2, r11, r9 ; r9 is now pointer to lowest CCB .endc .if defined __ALPHA movl @#CTL$GA_CCB_TABLE, r11 ; start of Channel Control Blocks table movl @#CTL$GL_CHINDX, r2 ; number of slots in the CCB table mull2 #ccb$k_length, r2 ; total size of CCB table addl3 r2, r11, r9 ; r9 is now pointer to lowest CCB .endc movl @context(ap), r6 ; pointer to last CCB reported bneq CISEEK ; if zero, we must initialize movl r11, r6 ; point to the first CCB .if defined __VAX addl2 #ccb$k_length, r6 ; so we can subtract within the loop .endc .if defined __ALPHA subl2 #ccb$k_length, r6 ; so we can add within the loop .endc ; at this point, we no longer need the value in R2, ; while R9 is a pointer to the lowest possible CCB structure ; and R11 is a pointer to the highest possible CCB structure ; (reverse those two descriptions if AXP, dammitdammitdammit) ; brb CISEEK ; we can enter the loop now NMC: clrl @context(ap) ; clear out the context field --- movzwl #SS$_NOMOREFILES, r0 ; we have checked all of the channels ret CISEEK: ; calculate pointers to the various structures for this channel . . . ; R6 will be a pointer to the CCB structure ; R7 will be a pointer to the UCB structure ; R8 will be a pointer to the DDB structure ; R9 will be a pointer to the WIND structure (if applicable) ; R10 will be a pointer to the ICB structure (if applicable) ; R11 will be a pointer to the FCB structure (if applicable) ; .if defined __VAX subl2 #ccb$k_length, r6 ; R6 is now (struct CCB *) cmpl r6, r9 ; check to see if we have gone past blss NMC ; the lowest CCB .endc .if defined __ALPHA addl2 #ccb$k_length, r6 ; R6 is now (struct CCB *) cmpl r6, r9 ; check to see if we have gone past bgtr NMC ; the highest CCB .endc tstb ccb$b_amod(r6) ; check if this channel is active beql CISEEK ; ignore if zero movl ccb$l_ucb(r6), r7 ; R7 is now (struct UCB *) beql CISEEK ; ignore if null UCB pointer cmpb ucb$b_type(r7), #dyn$c_ucb bneq CISEEK ; ignore if incorrect structure movl ucb$l_ddb(r7), r8 ; R8 is now (struct DDB *) beql CISEEK ; ignore if null DDB pointer cmpb ddb$b_type(r8), #dyn$c_ddb bneq CISEEK ; ignore if incorrect structure ; hooray, we found another active channel! ; update the argument with the address of this CCB . . . ; movl r6, @context(ap) ; . . . and calculate the channel number, storing it temporarily in R3 ; and permanently (so to speak) in the caller's structure, after which ; we no longer need the pointers in R9 and R11. ; .if defined __VAX subl3 r6, r11, r3 .endc .if defined __ALPHA movl ccb$l_chan(r6), r3 .endc movl aa1, r0 movl r3, dpm1$l_chan(r0) ; determine the remaining addresses ; (WCB into R9, ICB into R10, and FCB into R11) ; clrl r10 ; assume no ICB clrl r11 ; assume no FCB movl ccb$l_wind(r6), r9 ; R9 is now (struct WCB *) beql OWW ; skip if no window blbs r9, OWW ; skip if window is interlocked tstl r9 ; real window or PST offset? blss REALW cvtwl r9, r9 movl @#CTL$GL_PCB, r1 ; get address of our PCB and PHD movl @#CTL$GL_PHD, r2 addl3 pcb$l_phd(r1), phd$l_pstbasoff(r2), r0 moval sec$l_window(r0)[r9], r9 movl (r9), r9 ; is R9 pointing to a *real* Window Control Block? ; REALW: cmpb wcb$b_type(r9), #dyn$c_wcb bneq OWW ; is there a *real* File Control Block? ; movl wcb$l_fcb(r9), r11 beql OWW cmpb fcb$b_type(r11), #dyn$c_fcb beql HUH clrl r11 brb OWW ; obtain the ICB address by matching channel numbers ; among the entries in the list of activated images --- ; the matching address, if any, gets stored in R10 ; ; (you'll notice that we didn't bother doing this until ; we had a valid FCB . . . there's no point in looking ; for an ICB if the device is not file-structured) ; HUH: movl @#IAC$GL_IMAGE_LIST, r4 ; R4 is "firsticb" movl r4, r2 ; R2 is "icbptr" GILOOP: cmpl icb$l_flink(r2), r4 ; have we reached the end of the list? beql OWW ; then this is not really an image cmpw icb$w_chan(r2), r3 ; is this the channel we want? beql ISEXE ; hooray! movl icb$l_flink(r2), r2 ; make R2 point to next ICB brb GILOOP ; and keep looking ISEXE: movl r2, r10 ; now copy over the addresses & contents of various data structures ; OWW: calls #0, MH_I_CopyChannelInfo ; the test for whether the device is "file-structured" is ; whether it has a valid file identification, or just zeroes ; clrb IsFS tstl r11 beql CIDEV ffs #0, #fcb$s_fid, fcb$w_fid(r11), r0 beql CIDEV movb #1, IsFS ; construct a "full name" for this device ; CIDEV: calls #0, MH_I_BuildDeviceName blbc r0, CIOUT ; we now have a full device name and (if appropriate) a FID --- ; which means we are capable of obtaining the resultant filespec. ; tstl aa2 ; does the caller want "expensive" info? beql CIOK ; no tstb IsFS ; if the device is not file-structured, beql CIOK ; expensive info cannot be obtained calls #0, MH_I_GetFileInfo blbc r0, CIOUT ; we use two separate functions to gather image data ; for the MYCROFT_TIP_CHANNEL_INFO structure ; tstl aa3 ; does the caller want "tip" info? beql CIOK ; no tstl r10 ; does this channel reference an image? beql CIOK ; no calls #0, MH_I_FormatImageInfo ; copy data into the caller's buffer calls #0, MH_I_PrintECOs ; format the PATCH numbers into an asciz string CIOK: movzwl #SS$_NORMAL, r0 CIOUT: ret ; ; MH_I_CopyChannelInfo : Copy Channel Information ; ; Input Parameters: ; ; ; ; Output Parameters: ; ; ; ; Implicit Inputs: ; ; R6 - address of a valid Channel Control Block ; R7 - address of a valid Unit Control Block ; R8 - address of a valid Device Data Block ; R9 - address of a valid Window Block (or zero) ; R10 - address of a valid Image Control Block (or zero) ; R11 - address of a valid File Control Block (or zero) ; aa1 - address of a valid MYCROFT_CHEAP_CHANNEL_INFO structure ; ; Implicit Outputs: ; ; various fields within aa1 ENTRY_POINT MH_I_CopyChannelInfo, movl aa1, r4 ; now copy over the addresses of various data structures: ; movl r6, dpm1$l_ccb(r4) ; the Channel Control Block movl r7, dpm1$l_ucb(r4) ; the Unit Control Block movl r8, dpm1$l_ddb(r4) ; the Device Data Block movl r9, dpm1$l_wind(r4) ; the Window Block movl r10, dpm1$l_icb(r4) ; the Image Control Block movl r11, dpm1$l_fcb(r4) ; the File Control Block ; (the Known File Entry is assigned elsewhere) movl ucb$l_crb(r7), dpm1$l_crb(r4) ; the Channel Request Block movl ucb$l_orb(r7), dpm1$l_orb(r4) ; the Object's Rights Block movl ucb$l_vcb(r7), dpm1$l_vcb(r4) ; the Volume Control Block movl ddb$l_ddt(r8), dpm1$l_ddt(r4) ; the Driver Dispatch Table movl ddb$l_sb(r8), dpm1$l_sb(r4) ; the System Block which owns the device movl #SCS$AR_LOCALSB, dpm1$l_localsb(r4) ; the System Block for the current node ; copy values within the Channel Control Block ; .if defined __ALPHA movl ccb$l_ioc(r6), dpm1$l_ioc(r4) ; number of outstanding i/o requests on channel movl ccb$l_sts(r6), dpm1$l_csts(r4) ; channel status .if_false movzwl ccb$w_ioc(r6), dpm1$l_ioc(r4) movzbl ccb$b_sts(r6), dpm1$l_csts(r4) .endc movb ccb$b_amod(r6), dpm1$b_amod(r4) ; allocation access mode ; copy values within the Unit Control Block ; .if defined __ALPHA movl ucb$l_refc(r7), dpm1$l_refcnt(r4) ; reference count of processes movl ucb$l_errcnt(r7), dpm1$l_errcnt(r4) ; device error count movl ucb$l_devsts(r7), dpm1$l_devsts(r4) ; device dependent status .if_false movzwl ucb$w_refc(r7), dpm1$l_refcnt(r4) ; reference count of processes movzwl ucb$w_errcnt(r7), dpm1$l_errcnt(r4) ; device error count movzwl ucb$w_devsts(r7), dpm1$l_devsts(r4) ; device dependent status .endc movl ucb$l_opcnt(r7), dpm1$l_opcnt(r4) ; count of operations completed movl ucb$l_sts(r7), dpm1$l_usts(r4) ; device unit status movl ucb$l_devchar(r7), dpm1$l_devchar(r4) ; original device characteristic bits movl ucb$l_devchar2(r7), dpm1$l_devchar2(r4) ; extended " " " movl ucb$l_devdepend(r7), dpm1$l_devdepend(r4) ; first device dependent longword movl ucb$l_devdepnd2(r7), dpm1$l_devdepnd2(r4) ; second " " " movl ucb$l_devdepnd3(r7), dpm1$l_devdepnd3(r4) ; third " " " movl ucb$l_devdepnd4(r7), dpm1$l_devdepnd4(r4) ; fourth " " " movw ucb$w_unit(r7), dpm1$w_unit(r4) ; physical device unit number movw ucb$w_devbufsiz(r7), dpm1$w_devbufsiz(r4) ; buffer size, in bytes movw ucb$w_msgmax(r7), dpm1$w_msgmax(r4) ; maximum messages allowed (only if devclass == DC$_MAILBOX) movw ucb$w_msgcnt(r7), dpm1$w_msgcnt(r4) ; current number of messages (only if devclass == DC$_MAILBOX) movb ucb$b_devclass(r7), dpm1$b_devclass(r4) ; a DC$_XXXXX value from movb ucb$b_devtype(r7), dpm1$b_devtype(r4) ; a DT$_XXXXX value from ; copy values within the Device Data Block ; movl ddb$l_allocls(r8), dpm1$l_allocls(r4) ; device allocation class ; copy values within the Image Control Block, if it exists ; AOK1: tstl r10 beql AOK2 movl icb$l_startaddr(r10), dpm1$l_vastart(r4) movl icb$l_endaddr(r10), dpm1$l_vaend(r4) movzbl icb$b_majorid(r10), dpm1$l_majorid(r4) movzbl icb$b_minorid(r10), dpm1$l_minorid(r4) ; copy values within the Known File Entry, if it exists ; movl icb$l_kfe(r10), r1 beql AOK2 movl r1, dpm1$l_kfe(r4) movw kfe$w_flags(r1), dpm1$w_kfemask(r4) ; copy values within the File Control Block, if it exists ; AOK2: tstl r11 beql AOK3 movl fcb$l_hdlbn(r11), dpm1$l_hdlbn(r4) ; LBN of file header movl fcb$l_filesize(r11), dpm1$l_filesize(r4) ; file size in blocks .if defined __ALPHA movw fcb$l_revision(r11), dpm1$w_revision(r4) ; file revision movw fcb$l_status(r11), dpm1$w_status(r4) ; file status movw fcb$l_acnt(r11), dpm1$w_acnt(r4) ; file access count .endc .if defined __VAX movw fcb$w_status(r11), dpm1$w_status(r4) ; file status movw fcb$w_acnt(r11), dpm1$w_acnt(r4) ; file access count .endc movw fcb$w_fid_num(r11), dpm1$w_fid_num(r4) ; file identification movw fcb$w_fid_seq(r11), dpm1$w_fid_seq(r4) movw fcb$w_fid_rvn(r11), dpm1$w_fid_rvn(r4) ; now copy over ASCIC strings as ASCIZ: ; SCS nodename which "owns" the device ; generic path name of device ; image name string (for images only) ; ; use R6 as the address of the "cheap" area since R4 ; is gonna get whacked by the MOVC instructions ; AOK3: movl r4, r6 tstl r8 beql AOK5 movl ddb$l_sb(r8), r0 ; to what node is it allocated? beql AOK4 addl2 #sb$t_nodename, r0 movzbl (r0), r1 movc3 r1, 1(r0), dpm1$t_node(r6) clrb (r3) AOK4: addl3 #ddb$t_name, r8, r0 ; the generic device name movzbl (r0), r1 movc3 r1, 1(r0), dpm1$t_name(r6) clrb (r3) AOK5: tstl r10 ; the image name beql AOK7 .if defined __ALPHA addl3 #icb$t_section_name, r10, r0 ; try using the "section name" instead (AXP only) tstb (r0) bneq AOK6 .endc addl3 #icb$t_image_name, r10, r0 AOK6: movzbl (r0), r1 movc3 r1, 1(r0), dpm1$t_imgname(r6) clrb (r3) ; set the appropriate flags in the caller's "misc" bitmask; ; they are determined from information in the WCB and are ; whether the file is a "section file" or not, and whether ; the process has read and/or write access to the file ; AOK7: tstl r9 beql XX0 tstw wcb$l_pid(r9) ; is file a section? bneq RR0 bisb2 #dpm1$m_sectfile, dpm1$b_miscmask(r6) RR0: movzbl wcb$b_access(r9), r1 ; read access allowed? bbc #wcb$v_read, r1, WW0 bisb2 #dpm1$m_readacc, dpm1$b_miscmask(r6) WW0: bbc #wcb$v_write, r1, XX0 ; write access allowed? bisb2 #dpm1$m_writeacc, dpm1$b_miscmask(r6) XX0: ret ; ; MH_I_BuildDeviceName : Construct the Full Device Name ; ; Input Parameters: ; ; ; ; Output Parameters: ; ; R0 - Completion Status Code ; SS$_NORMAL - success ; any "failure" status from $FAO ; ; Implicit Inputs: ; ; R7 - address of a valid Unit Control Block ; R8 - address of a valid Device Data Block ; aa1 - address of a valid MYCROFT_CHEAP_CHANNEL_INFO structure ; ; Implicit Outputs: ; ; the "fulldevname" field within aa1 ENTRY_POINT MH_I_BuildDeviceName, ; a "device name" consists of the device driver's name (from the DDB) ; with the unit number (from the UCB) and a colon appended, as in ; NLA0: ; ; if the device is allocated to a particular node, then that node's ; name and a single dollar sign should precede the device name, as in ; VAXONE$DKA300: ; ; if the device is accessible through multiple paths, then the ; "allocation class" --- a number between 1 and 255 inclusive --- ; should be converted to ascii and placed in front of the device name ; with a dollar sign on either side, as in ; $1$DKB0: ; ; if it is a local device, leave the device name by itself; the same ; goes for non-file-structured devices such as terminals, mailboxes, ; and printers . . . ; TTA1: ; FTA5: ; MBA216: ; LPA0: ; OPA0: ; addl3 #dpm1$t_fulldevname, aa1, r9 tstb IsFS ; is the device *not* file-structured? beql CINAM ; don't bother with prefixes movzbl ddb$l_allocls(r8), r4 ; is this device accessible beql CIND ; through multiple paths? movb #^A/$/, (r9)+ ; prepend a dollar sign movl #15, tmpsd+0 ; prepare a string descriptor movl r9, tmpsd+4 movzwl r4, TheFAOdesc+16 ; the number to be converted $fao_g TheFAOdesc ; convert binary to ascii blbc r0, XX9 movzwl retlen, r4 addl2 r4, r9 movb #^A/$/, (r9)+ ; append a dollar sign brb CINAM CIND: movl ddb$l_sb(r8), r4 ; to what node is it allocated? beql CINAM ; none? cmpl r4, #SCS$AR_LOCALSB ; this node? beql CINAM ; don't bother addl2 #sb$t_nodename, r4 ; point to the node name . . . movzbl (r4), r0 ; which is an ASCIC string . . . movc3 r0, 1(r4), (r9) ; and copy it into our buffer movl r3, r9 ; move the pointer bytes movb #^A/$/, (r9)+ ; and append a dollar sign ; now copy the generic path name of the device driver . . . ; CINAM: cvtbl ddb$b_name_len(r8), r6 ; another ASCIC string movc3 r6, ddb$t_name_str(r8), (r9) movl r3, r9 ; move the pointer along ; . . . and append the unit number . . . ; movl #15, tmpsd+0 ; prepare a string descriptor movl r9, tmpsd+4 movzwl ucb$w_unit(r7), TheFAOdesc+16 ; the number to be converted $fao_g TheFAOdesc ; convert binary to ascii blbc r0, XX9 addw2 retlen, r9 ; move the pointer along ; . . . and a colon. voila! ; movb #^A/:/, (r9)+ clrb (r9) ; make "FULLDEVNAME" an asciz string movzwl #SS$_NORMAL, r0 XX9: ret ; ; MH_I_GetFileInfo : Obtain the Resultant Filespec and (maybe) File Header ; ; Input Parameters: ; ; ; ; Output Parameters: ; ; R0 - Completion Status Code ; SS$_NORMAL - success ; any "failure" status from $ASSIGN, $QIOW, $DASSGN ; ; Implicit Inputs: ; ; aa1->fulldevname - expected to contain the full device name to be used ; R10 - expected to contain a pointer to the ICB structure or NULL ; ; Implicit Outputs: ; ; various fields within aa2 ; aa3->imageheader ENTRY_POINT MH_I_GetFileInfo, ; convert a device-name/fid combination into a resultant filespec ; by actually opening the file and letting the ACP do it for us :-) ; ; if requested, also read in the image's header ; get a channel to the desired disk, open the desired file, ; read its header, close the file, and let go of the channel. ; perfectly simple. of course, anyone who assigns a channel ; while at EXEC mode surely knows that he must deassign it himself ; since image rundown will not do it for him . . . ; ASSIGN: addl3 #dpm1$t_fulldevname, aa1, r2 ; get the length of the locc #0, #dpm1$s_fulldevname, (r2) ; (asciz) full device name subl3 r2, r1, tmpsd+0 ; make a descriptor movl r2, tmpsd+4 ; for the device name $assign_g TheGrabDesc ; get a channel to the disk blbs r0, OPEN ret OPEN: ; assume we'll be successful ; movl #SS$_NORMAL, r6 ; we pass a Attribute Control Block to obtain the resultant filespec ; and other useful information ; addl3 #dpm2$q_credate, aa2, AtrList+4 ; creation date addl3 #dpm2$q_revdate, aa2, AtrList+12 ; revision date addl3 #dpm2$q_expdate, aa2, AtrList+20 ; expiration date addl3 #dpm2$q_bakdate, aa2, AtrList+28 ; backup date addl3 #dpm2$l_uic, aa2, AtrList+36 ; owner uic addl3 #dpm2$w_fpro, aa2, AtrList+44 ; file protection addl3 #dpm2$t_fileheader, aa2, AtrList+60 ; file header movab AtrList, TheQIOdesc+44 ; we need to be able to open a file for reading even if the ; protection on the file is only (E) instead of (R) or (RE) --- ; the flags used for opening are: ; access the file via EXECUTE protection ; preserve original attributes of the file ; override exclusive access ; inhibit recording of the file's expiration date ; movc5 #0, TheFib, #0, #fib$k_length, TheFib movl #FIB$M_EXECUTE!FIB$M_PRSRV_ATR!FIB$M_NOLOCK!FIB$M_NORECORD, - TheFib+fib$l_acctl ; copy the FileID from the CHEAP structure, into which it was copied ; earlier from the File Control Block ; movl aa1, r0 movw dpm1$w_fid_num(r0), TheFib+fib$w_fid_num movw dpm1$w_fid_seq(r0), TheFib+fib$w_fid_seq movw dpm1$w_fid_rvn(r0), TheFib+fib$w_fid_rvn movw TheChan, TheQIOdesc+8 movw #IO$_ACCESS!IO$M_ACCESS, TheQIOdesc+12 $qiow_g TheQIOdesc blbc r0, XZ1 movzwl iosb, r0 blbs r0, COPY XZ1: brw XZ4 ; copy the resultant filespec into the caller's buffer as ASCIZ --- ; remember that, although the filespec is an ASCIC string, ; the byte-count is a *word* instead of the usual *byte* ; COPY: movl aa2, r0 movc3 TheFilespec, TheFilespec+2, dpm2$t_resfs(r0) clrb (r3) ; read the image header (the first record in an .EXE file) ; READ: movl aa3, r0 ; but only if the caller supplied a non-NULL beql CLOSE ; MYCROFT_TIP_CHANNEL_INFO structure tstl r10 ; if ICB pointer is NULL, it's not an image beql CLOSE movw TheChan, TheReadDesc+8 addl3 r0, #dpm3$t_imageheader, TheReadDesc+28 $qiow_g TheReadDesc blbc r0, XZ2 movzwl iosb, r0 blbc r0, XZ2 brb CLOSE ; sometimes the "end of file" status will be returned --- ; i don't know why and i don't care, since the data is okay ; XZ2: cmpl r0, #SS$_ENDOFFILE beql CLOSE movl r0, r6 ; always close, 'cuz we're in EXEC mode ; CLOSE: movw #IO$_DEACCESS, TheQIOdesc+12 clrl TheQIOdesc+44 $qiow_g TheQIOdesc blbc r0, XZ3 movzwl iosb, r0 blbs r0, DROP ; an error occurred during the read and/or the close . . . ; if both, we want to return the read-condition ; XZ3: blbc r6, DROP XZ4: movl r0, r6 ; if an error occurs while deassigning the channel, ; we should return that status to the caller if and only if ; all other operations during this function were successful ; DROP: movzwl TheChan, -(sp) calls #1, G^sys$dassgn blbs r0, LEAVE blbc r6, LEAVE ret LEAVE: movl r6, r0 ret ; ; MH_I_FormatImageInfo : Format Image Information ; ; Input Parameters: ; ; ; ; Output Parameters: ; ; ; ; Implicit Inputs: ; ; aa3->dpm3$t_imageheader - expected to contain a valid image header ; ; Implicit Outputs: ; ; various fields within aa3 ENTRY_POINT MH_I_FormatImageInfo, ; calculate the address of the Image Header Information into R6 ; movl aa3, r10 addl3 r10, #dpm3$t_imageheader, r6 .if defined __VAX movzwl ihd$w_activoff(r6), r7 movzwl ihd$w_imgidoff(r6), r8 movzwl ihd$w_symdbgoff(r6), r9 .endc .if defined __ALPHA movl eihd$l_activoff(r6), r7 movl eihd$l_imgidoff(r6), r8 movl eihd$l_symdbgoff(r6), r9 .endc addl2 r6, r7 addl2 r6, r8 movl iha$l_tfradr1(r7), dpm3$l_tfradr1(r10) ; first transfer address movl iha$l_tfradr2(r7), dpm3$l_tfradr2(r10) ; second transfer address movl iha$l_tfradr3(r7), dpm3$l_tfradr3(r10) ; third transfer address movl iha$l_inishr(r7), dpm3$l_inishr(r10) ; shared image initialization movq ihi$q_linktime(r8), dpm3$q_linktime(r10) ; copy the LINK DATE/TIME ; copy the IDENT and LINKERID as asciz strings ; addl3 #ihi$t_imgid, r8, r0 movzbl (r0), r1 movc3 r1, 1(r0), dpm3$t_imgid(r10) clrb (r3) addl3 #ihi$t_linkid, r8, r0 movzbl (r0), r1 movc3 r1, 1(r0), dpm3$t_linkid(r10) clrb (r3) ; is this a "shareable image" instead of a "main executable"? ; cmpb ihd$b_imgtype(r6), #ihd$k_lim bneq ISTRC bisb2 #dpm3$m_shareable, dpm3$b_dpmmask(r10) ; the image was linked /TRACE if the debug symbol table VBN is non-zero ; (of course, there might not even be a symbol-table-offset!) ; ISTRC: tstl r9 beql ISDBG addl2 r6, r9 tstl ihs$l_dstvbn(r9) beql ISDBG bisb2 #dpm3$m_lnktrace, dpm3$b_dpmmask(r10) ; the image was linked /DEBUG if the appropriate flag is set ; in the image header's bitmask ; ISDBG: bbc #ihd$v_lnkdebug, ihd$l_lnkflags(r6), PFOK bisb2 #dpm3$m_lnkdebug, dpm3$b_dpmmask(r10) ; this is a "void" function ; PFOK: ret ; ; MH_I_PrintECOs : format ECO bitmask into asciz string of decimal numbers ; (for example, if ECOs 1 through 4 and 7 have been applied, ; the output will look like "1,2,3,4,7") ; ; Input Parameters: ; ; ; ; Output Parameters: ; ; R0 - the number of ECO bits set within the Patch Information Area ; ; Implicit Inputs: ; ; aa3->dpm3$t_imageheader - expected to contain a valid image header ; ; Implicit Outputs: ; ; aa3->dpm3$t_patches ENTRY_POINT MH_I_PrintECOs, ; i wasn't very excited about writing this in macro32, so i wrote it ; in C, cut&pasted the machine code generated by the compiler via a ; CC /NODEBUG /OPTIMIZE /LIST /MACHINE command, and edited it. ; does it show? will you forgive me? ; first thing to do is test the offset for the beginning of the image ; header to the Patch Information Area --- if it's clear, there are ; no patches recorded, and nothing for us to do (remember that few ; images are patched) ; addl3 #dpm3$t_imageheader, aa3, r3 movzwl ihd$w_patchoff(r3), r0 bneq PAD0 clrl r0 ret ; second thing to do is check the four longwords which comprise ; the ECO bitmask in the Patch Information Area in the image's header ; to see if this image has ever been patched. i would think that ; a non-zero patch-offset means there must be at least one patch, ; but i'd rather be safe than sorry. ; PAD0: addl3 r3, #dpm3$t_imageheader, r7 addl2 r0, r7 addl2 #ihp$l_eco1, r7 tstl (r7) bneq PAD1 tstl 4(r7) bneq PAD1 tstl 8(r7) bneq PAD1 tstl 12(r7) bneq PAD1 clrl r0 ret ; prepare the registers: ; R3 is already a pointer to a MYCROFT_TIP_CHANNEL_INFO structure ; R5 will be the bit offset within a longword (0 to 31) ; R6 will be the longword index (0 to 3) ; R7 is already a pointer to the Patch Information Area ; R8 will be the number of patches found ; R9 will be a pointer to the output buffer ; R10 will be the ECO number ; PAD1: clrl r6 clrl r8 addl3 r3, #dpm3$t_patches, r9 cvtwl #1, r10 ; top of the outer loop, which cycles four times ; to check each longword ; PAD2: clrl r5 ; clear the bit offset within a longword ; top of the inner loop, which cycles thirty-two times ; to check each bit of a single longword ; ; check to see if the 'n'th bit is set --- skip over if clear ; PAD3: ashl r5, #1, r0 mcoml r0, r0 bicl3 r0, (r7)[r6], r0 beql PAD7 ; okay, this bit is set, so we've found [another] ECO. ; ; we want to produce a comma-separated list of numbers . . . ; check whether this is the first number in the list or not ; to prevent the string from beginning or ending with a comma ; tstl r8 ; is this the first ECO bit found? beql PAD4 movb #^A/,/, (r9)+ ; no, add a comma ; increment the number of patches found ; and copy the ECO number into R4 so that it can be ; modified (makes dividing easier) ; PAD4: incl r8 movl r10, r4 ; handle the "hundreds" digit, if the ECO number is >= 100 ; movl r4, r2 cmpl r2, #99 bleq PAD5 divl3 #100, r2, r0 addl2 #^A/0/, r0 cvtlb r0, (r9)+ emul #0, #0, r2, r0 ediv #100, r0, r1, r0 movl r0, r4 ; handle the "tens" digit, if the ECO number is >= 10 ; PAD5: movl r4, r3 cmpl r3, #9 bleq PAD6 divl3 #10, r3, r2 addl2 #^A/0/, r2 cvtlb r2, (r9)+ emul #0, #0, r3, r0 ediv #10, r0, r1, r0 movl r0, r4 ; handle the "ones" digit, always ; PAD6: addl3 #^A/0/, r4, r2 cvtlb r2, (r9)+ ; go to the next bit, or the first bit in the next longword ; if we're already at the end of a longword, or quit if we're ; already at the end of the fourth longword ; PAD7: incl r10 ; increment the ECO counter aoblss #32, r5, PAD3 ; jump to top of inner loop? aoblss #4, r6, PAD2 ; jump to top of outer loop? ; all done --- place a null byte at the end to make it an asciz string ; and return the number of patches found ; clrb (r9) movl r8, r0 ret ; ; MH_GetLockInfo : Get Active Lock Information ; ; count = MH_GetLockInfo(unsigned long pid, int count, void *arrayptr); ; ; Input Parameters: ; ; 04(AP) - efn.rl - longword which is the event flag number to be specified in both $GETLKIW calls ; ; 08(AP) - lock1.rl - the address of a longword containing the lock-ID to be specified in the first $GETLKIW call ; ; 12(AP) - list1.rl - the address of an itemlist to be specified in the first $GETLKIW call ; ; 16(AP) - lock2.rl - the address of a longword containing the lock-ID to be specified in the second $GETLKIW call ; ; 20(AP) - list2.rl - the address of an itemlist to be specified in the second $GETLKIW call ; ; Output Parameters: ; ; R0 - condition value from R0 or the I/O Status Block, whichever is worse ; ; Implicit Inputs: ; ; ; ; Implicit Outputs: ; ; ENTRY_POINT MH_GetLockInfo, , 5 efn = 4 lock1 = 8 list1 = 12 lock2 = 16 list2 = 20 ; remember that $GETLKI() uses a *longword* condition value in the Status Block, ; not a *short* like the $QIO() calls. .GLOBL SYS$GETLKIW pushl #0 ; reserved pushl #0 ; astprm pushl #0 ; astadr pushab iosb pushl list1(ap) pushl lock1(ap) pushl efn(ap) calls #7,G^sys$getlkiw blbc r0, LOCKOUT movl iosb, r0 blbc r0, LOCKOUT pushl #0 ; reserved pushl #0 ; astprm pushl #0 ; astadr pushab iosb pushl list2(ap) pushl lock2(ap) pushl efn(ap) calls #7,G^sys$getlkiw blbc r0, LOCKOUT movl iosb, r0 LOCKOUT: ret ; ; MH_GetTimerInfo : Get Active Timer Information ; ; count = MH_GetTimerInfo(unsigned long pid, int count, void *arrayptr); ; ; Input Parameters: ; ; 04(AP) - pid.rl - OPTIONAL - unsigned longword which is the identifier ; of the process to be examined ; ; 08(AP) - count.rl - OPTIONAL - longword which contains the number of elements in ; ; 12(AP) - arrayptr.al - OPTIONAL - address of an array which will receive the data ; ; Output Parameters: ; ; R0 - number of timers copied (or existing, if is NULL) ; ; Implicit Inputs: ; ; ; ; Implicit Outputs: ; ; ENTRY_POINT MH_GetTimerInfo, , 3 pid = 4 count = 8 arrayptr = 12 clrl r6 ; number of entries counted so far movl count(ap), r7 ; number of array elements available movl arrayptr(ap), r8 ; pointer to first element of array movl @#EXE$GL_TQFL, r10 ; pointer to beginning of TQE list movl r10, r11 ; save a copy for later comparison ; (it's a circular queue) movl pid(ap), r9 ; if is zero, select timers bneq APID ; belonging to the current process movl @#CTL$GL_PCB, r9 ; pointer to our PCB movl pcb$l_pid(r9), r9 ; save the internal pid for comparison tstl r8 ; get the extended pid into AA1 beql GTLOOP ; if is non-NULL I2EPID r9, aa1 brb GTLOOP APID: movl r9, aa1 ; save the extended pid, and store the E2IPID r9 ; internal pid in R9 for comparison ; against pids owning timer entries GTLOOP: movl tqe$l_tqfl(r10), r10 ; jump to next timer entry beql GTDONE ; check for a NULL pointer cmpl tqe$l_tqfl(r10), r11 beql GTDONE ; check for the beginning of the list cmpb tqe$b_type(r10), #dyn$c_tqe bneq GTLOOP ; make sure it's a timer entry cmpl tqe$l_pid(r10), r9 ; select against the process id bneq GTLOOP tstl r8 ; if is NULL, bneq GTCOPY ; just count the timers incl r6 ; instead of copying the data brb GTLOOP GTCOPY: movc3 #tqe$k_length, (r10), (r8) movl aa1, tqe$l_pid(r8) ; send back the two pids in I2EPID tqe$l_rqpid(r8) ; "extended" format addl2 #tqe$k_length, r8 aoblss r7, r6, GTLOOP GTDONE: movl r6, r0 ret .end