.TITLE ZFMOUNT - Mount Image for ZFACP .IDENT /X01-000/ ;++ ; ZFMOUNT - Mount Image for ZFACP ; ; ABSTRACT: ; ; This program mounts the ZFACP on the device ZFA0. ; The user must have at least MOUNT, CMKRNL, DETACH, and SETPRV ; privileges. ;-- .PAGE .SBTTL External and local symbol definitions .LIBRARY \SYS$LIBRARY:LIB\ $AQBDEF ; ACP queue block $CCBDEF ; Channel control block $DEVDEF ; Device characteristics $DYNDEF ; Data structure ID codes $IODEF ; I/O function codes $PQLDEF ; Process quota list $PRDEF ; Processor register names $PRCDEF ; Process mode bits $PSLDEF ; Processor status longword $SSDEF ; System status codes $UCBDEF ; Unit control block $VCBDEF ; Volume control block ACP_K_WAITTIM = 60 ; half-second tics to wait for ACP to ; clear CREATING bit after awaken EF_K_TIMER = 1 ; event flag to use for $SETIMRs EF_K_MNTQIO = 2 ; ... for mount $QIO EF_M_TMRORMNT = <1@EF_K_TIMER> ! <1@EF_K_MNTQIO> ZF_K_ACPTYPE = 250 ZF_K_ACPCLASS = 251 ; redefine some VCB fields VCB_Q_CURIRP = VCB$L_CUR_FID VCB_Q_IOSB = VCB$L_IXHDR2LBN .PAGE .SBTTL Local storage .PSECT READONLY_DATA NOEXE, PIC, LONG, SHR, NOWRT UIC: .WORD ^O01 ; ACP UIC Group=001 .WORD ^O01 ; ACP UIC = [001,001] THIRTY_SEC: ; 30-sec delta time for .LONG -10*1000*1000*30,-1 ; MOUNTING check HALF_SEC: ; half-sec delta time for .LONG -10*1000*500,-1 ; CREATING check PLIST: .LONG -1,-1 ; give ACP all privileges QLIST: .BYTE PQL$_ASTLM ; quota list for ACP process .LONG 40 .BYTE PQL$_BIOLM .LONG 20 .BYTE PQL$_BYTLM .LONG 16384 .BYTE PQL$_CPULM .LONG 0 .BYTE PQL$_DIOLM .LONG 20 .BYTE PQL$_ENQLM .LONG 128 .BYTE PQL$_FILLM .LONG 40 .BYTE PQL$_PGFLQUOTA .LONG 8000 .BYTE PQL$_PRCLM .LONG 4 .BYTE PQL$_TQELM .LONG 20 .BYTE PQL$_WSDEFAULT .LONG 512 .BYTE PQL$_WSQUOTA .LONG 512 .BYTE PQL$_WSEXTENT .LONG 2000 .BYTE PQL$_LISTEND .PSECT LOCAL_DATA NOEXE, PIC, LONG SAVED_ERR_CODE: .LONG 0 ; place to store error code UNDO_FLAGS: .LONG 0 ; bit mask showing what to undo _VIELD UNDO,0,<- ; in case of failure ,- ; dassgn the chl to the dvc ,- ; delete the ACP process ,- ; delete the AQB ,- ; delete the VCB ,- ; delete the ACP process > ; cancel the MOUNT $QIO EPID: .BLKL 1 ; EPID returned by $CREPRC IPID: .BLKL 1 ; IPID derived from EPID UCB_ADDR: ; address of the mounted dvc's .BLKL 1 ; UCB, VCB_ADDR: .BLKL 1 ; VCB, AQB_ADDR: .BLKL 1 ; and AQB ZF_CHN: .BLKW 1 ; channel to the device IMAGE: .ASCID /ZFACP/ ; file name with ACP image PRCNAM: .ASCID /ZFA0ACP/ ; name for ACP ZF_DVC: .ASCID /ZFA0/ ; ZF device name .PAGE .SBTTL MNT_START, Main Program .PSECT CODE SHR, NOWRT, PIC, LONG .ENTRY MNT_START, ^M<> $CMKRNL_S B^BEGIN ; change mode to kernel RET ; all done .ENTRY BEGIN, ^M ; assign a channel to the device; if successful, ; get its UCB address $ASSIGN_S DEVNAM=ZF_DVC, CHAN=ZF_CHN,- ; assign channel to the device ACMODE=#PSL$C_USER ; at USER level BLBS R0, 10$ ; check for error RET ; return if n.g. 10$: BISL2 #UNDO_M_DASSGN, UNDO_FLAGS ; note cleanup task MOVZWL ZF_CHN, R0 ; obtain channel number JSB G^IOC$VERIFYCHAN ; get CCB addr in R1 ; (This routine destroys R0-R3; also, it checks channel ; accessibility for the previous mode--in this case, USER) BLBC_W R0, ERR_EXIT ; check for error MOVL CCB$L_UCB(R1), R5 ; get ZF device UCB address MOVL R5, UCB_ADDR ; and save it MOVL #SS$_DEVMOUNT, R0 ; assume device already mtd BBSSI_W #UCB$V_MOUNTING,UCB$L_STS(R5),- ; note device mounting ERR_EXIT ; br if already mounting BITL #DEV$M_MNT!DEV$M_DMT,- ; is unit already mounted UCB$L_DEVCHAR(R5) ; or marked for dismount? BNEQ_W ERR_EXIT ; br if yes to either ; allocate the Volume Control Block (VCB) MOVZBL #VCB$C_LENGTH, R1 ; set desired size JSB G^EXE$ALONONPAGED ; attempt alloc, addr in R2 BLBC_W R0, ERR_EXIT ; br if failed MOVL R2, R4 ; use R4 for the VCB MOVL R4, VCB_ADDR ; save VCB address BISL2 #UNDO_M_DEALVCB, UNDO_FLAGS ; note cleanup task ; Initialize VCB MOVB #DYN$C_VCB, VCB$B_TYPE(R4) ; record type MOVZBW #VCB$C_LENGTH, VCB$W_SIZE(R4) ; and size MOVW #1, VCB$W_TRANS(R4) ; initialize trans. count MOVL R4, UCB$L_VCB(R5) ; store VCB address in the UCB CLRQ VCB_Q_CURIRP(R4) ; init the "current IRP" qhdr ; look for an existing AQB with our class and acptype fields CALLS #0, LOCK_IODB ; lock the I/O database MOVAL G^IOC$GL_AQBLIST, R2 ; get addr of AQB pointer AQB_SRCH: MOVL (R2), R2 ; get addr of next AQB BEQL AQB_NOT_FOUND ; if zero pointer, end of list CMPB #ZF_K_ACPTYPE,AQB$B_ACPTYPE(R2) ; check acptype BNEQ AQB_SRCH ; if no match, go to next AQB CMPB #ZF_K_ACPCLASS, AQB$B_CLASS(R2) ; check acp class BNEQ AQB_SRCH ; if no match, go to next AQB BBS #AQB$V_UNIQUE,AQB$B_STATUS(R2),-; if unique ACP, AQB_SRCH ; go to next AQB ; here we have an existing AQB. Hook the new VCB to it and go do ; the mount $QIO MOVL R2, AQB_ADDR ; save the AQB address INCB AQB$B_MNTCNT(R2) ; bump the mount count MOVL R2, VCB$L_AQB(R4) ; store AQB address in VCB CALLS #0, UNLOCK_IODB ; release the I/O database BRW DO_MOUNT_QIO AQB_NOT_FOUND: CALLS #0, UNLOCK_IODB ; release the I/O database ; Allocate a new ACP Queue Block (AQB) MOVZBL #AQB$C_LENGTH, R1 ; set length JSB G^EXE$ALONONPAGED ; allocate AQB, addr in R2 BLBC_W R0, ERR_EXIT ; br if n.g. BISL2 #UNDO_M_DEALAQB, UNDO_FLAGS ; note cleanup task MOVL R2, AQB_ADDR ; save the address ; Initialize the AQB MOVZBW #AQB$C_LENGTH, AQB$W_SIZE(R2) ; record size MOVB #DYN$C_AQB, AQB$B_TYPE(R2) ; and type CLRQ AQB$L_ACPQFL(R2) ; init the IRP queue MOVB #1, AQB$B_MNTCNT(R2) ; and the mount count MOVB #ZF_K_ACPTYPE,- ; note that AQB$B_ACPTYPE(R2) ; it's ours MOVB #ZF_K_ACPCLASS,- ; ditto AQB$B_CLASS(R2) ; MOVB #AQB$M_CREATING,- ; start synchronization AQB$B_STATUS(R2) ; with ACP MOVL VCB_ADDR, R4 ; recover VCB address MOVL R2, VCB$L_AQB(R4) ; put AQB address in VCB ; Start the ACP process $CREPRC_S PIDADR=EPID,- ; PID returned IMAGE=IMAGE,- ; ACP image file PRVADR=PLIST,- ; privilege mask QUOTA=QLIST,- ; give large quotas PRCNAM=PRCNAM,- ; process name BASPRI=#4,- ; as with most ACPs UIC=UIC,- ; want detached process STSFLG=#PRC$M_HIBER ; start in HIB. state BLBC_W R0, ERR_EXIT ; br if failure BISL2 #UNDO_M_STOPACP, UNDO_FLAGS ; note cleanup task ; we create the ACP in the hibernate state because as soon as it ; runs the ACP will look for an AQB containing its own (internal) ; PID... and we haven't put it there yet; nor have we put the AQB ; in the AQB list, where the ACP can find it. We'll awaken the ACP ; in a moment. MOVL EPID, R0 ; get the EPID JSB G^EXE$EPID_TO_IPID ; convert to IPID MOVL R0, IPID ; save the result MOVL R0, AQB$L_ACPPID(R2) ; and put it in the AQB ; Link AQB into AQB list CALLS #0, LOCK_IODB ; lock the I/O database MOVAB G^IOC$GL_AQBLIST, R1 ; get AQB listhead MOVL (R1),AQB$L_LINK(R2) ; forward link MOVL R2,(R1) ; put AQB at head CALLS #0, UNLOCK_IODB ; release the database ; now we can let the ACP run. Awaken it so it can find its AQB, ; clear the CREATING bit therein, and generally get itself ready ; to do enter its main loop. Meanwhile we sit in a timeout loop, ; waiting for the CREATING bit to go away. If we time out, the ; ACP has died or is otherwise in trouble, so we'll have to clean up. $WAKE_S PIDADR=EPID ; kick it off MOVL #ACP_K_WAITTIM, R3 ; init wait loop counter WAIT_FOR_ACP: $SETIMR_S EFN=#EF_K_TIMER,- ; start a timer DAYTIM=HALF_SEC ; for half a second $WAITFR_S EFN=#EF_K_TIMER ; wait for done BBC #AQB$V_CREATING,- ; if the bit is clear, AQB$B_STATUS(R2), 10$ ; all is well SOBGTR R3, WAIT_FOR_ACP ; else try again ; if we get here, the ACP has taken more than the allotted time ; to clear the CREATING bit. Clearly it's in trouble, so ; get rid of it and exit. BRW ERR_EXIT 10$: ; if we get here, all is well; the ACP should be hibernating, ; waiting for requests from the driver. DO_MOUNT_QIO: ; issue the $MOUNT $QIO and wait for the MOUNTING bit to clear $QIO_S EFN=#EF_K_MNTQIO, CHAN=ZF_CHN,- ; start the mount FUNC=#IO$_MOUNT ; $QIO BLBC_W R0, ERR_EXIT ; br if error BISL2 #UNDO_M_CNCLMNT, UNDO_FLAGS ; note cleanup task ; wait up to thirty seconds for the mount $QIO to complete $SETIMR_S EFN=#EF_K_TIMER,- ; start a timer DAYTIM=THIRTY_SEC ; request $WFLOR_S EFN=#EF_K_TIMER,- ; wait for MASK=#EF_M_TMRORMNT ; either event flag ; Timeout or mount $QIO finished; check synchronization MOVL UCB_ADDR, R5 ; recover UCB address BBS_W #UCB$V_MOUNTING,UCB$W_STS(R5),- ; error if MOUNTING ERR_EXIT ; still set BBC_W #DEV$V_MNT,UCB$L_DEVCHAR(R5),- ; error if MNT ERR_EXIT ; not set $DASSGN_S CHAN=ZF_CHN ; deassign channel to pdvc MOVZWL #SS$_NORMAL,R0 ; return success code RET ; to caller ; ERR_EXIT -- come here if any of the above code detects a mistake. ; Look at the UNDO_FLAGS to see what we have to undo, if anything, ; then return to caller. ERR_EXIT: MOVL R0, SAVED_ERR_CODE ; remember why we're here BBCC #UNDO_V_CNCLMNT,UNDO_FLAGS,10$ ; need to cancel MOUNT $QIO? $CANCEL_S CHAN=ZF_CHN 10$: BBCC #UNDO_V_STOPACP,UNDO_FLAGS,20$ ; need to stop ACP? $DELPRC_S PIDADR=EPID ; yes CALLS #0, LOCK_IODB PUSHL AQB_ADDR ; we also need to remove our CALLS #1, UNLINK_AQB ; AQB from the system's list CALLS #0, UNLOCK_IODB 20$: BBCC #UNDO_V_DEALAQB,UNDO_FLAGS,30$ ; need to delete AQB? MOVL VCB_ADDR, R4 ; yes. Ensure that VCB CLRL VCB$L_AQB(R4) ; no longer points to it MOVL AQB_ADDR, R0 ; delete JSB G^EXE$DEANONPAGED ; it 30$: BBCC #UNDO_V_DEALVCB,UNDO_FLAGS,40$ ; need to delete VCB? MOVL UCB_ADDR, R5 ; yes. Get UCB addr BICL #UCB$M_MOUNTING,UCB$W_STS(R5) ; note no longer mounting CLRL UCB$L_VCB(R5) ; no longer have VCB MOVL VCB_ADDR, R0 ; delete JSB G^EXE$DEANONPAGED ; the VCB 40$: BBCC #UNDO_V_DASSGN,UNDO_FLAGS,50$ ; need to deassign channel? $DASSGN_S CHAN=ZF_CHN ; yes 50$: MOVL SAVED_ERR_CODE, R0 ; remember why we're here RET ; and return to caller .END MNT_START