.TITLE LDDRIVER, VAX/VMS Logical Disk driver .IDENT 'V6.3' ;**************************************************************************** ;* * ;* COPYRIGHT (c) 1991, 2000 BY * ;* DIGITAL EQUIPMENT CORPORATION, MAYNARD, MASSACHUSETTS. * ;* ALL RIGHTS RESERVED. * ;* * ;* THIS SOFTWARE IS FURNISHED UNDER A LICENSE AND MAY BE USED AND COPIED * ;* ONLY IN ACCORDANCE WITH THE TERMS OF SUCH LICENSE AND WITH THE * ;* INCLUSION OF THE ABOVE COPYRIGHT NOTICE. THIS SOFTWARE OR ANY OTHER * ;* COPIES THEREOF MAY NOT BE PROVIDED OR OTHERWISE MADE AVAILABLE TO ANY * ;* OTHER PERSON. NO TITLE TO AND OWNERSHIP OF THE SOFTWARE IS HEREBY * ;* TRANSFERRED. * ;* * ;* THE INFORMATION IN THIS SOFTWARE IS SUBJECT TO CHANGE WITHOUT NOTICE * ;* AND SHOULD NOT BE CONSTRUED AS A COMMITMENT BY DIGITAL EQUIPMENT * ;* CORPORATION. * ;* * ;* DIGITAL ASSUMES NO RESPONSIBILITY FOR THE USE OR RELIABILITY OF ITS * ;* SOFTWARE ON EQUIPMENT WHICH IS NOT SUPPLIED BY DIGITAL. * ;* * ;* * ;**************************************************************************** ;+++ ; ; FACILITY: ; ; VAX/VMS Logical Disk driver ; ; ABSTRACT: ; ; This device driver behaves identically like a physical diskdrive, ; but all I/O goes via a physical disk driver to a file on disk,the ; so called LOGICAL DISK. ; Another possibility is to replace an existing drive completely with ; a logical disk. That makes is possible to trace every I/O for the drive. ; ; Compile/link instructions ; ; $ MACRO LDDRIVER ; $ LINK LDDRIVER,SYS$INPUT/OPTIONS ; BASE=0 ; ; AUTHOR: ; ; A. Sweep 16-OCT-1985 Version 01.00 ; ; Jur van der Burg vdburg@utrtsc.uto.dec.com ; ; REVISION HISTORY: ; ; Jur van der Burg 25-OCT-1988 Version 01.01 ; Modified for VMS V5 ; ; Jur van der Burg 13-OCT-1992 Version 02.00 ; Add Host based shadowing support. ; Corrected qlen bug (was not decremented on physical device). ; Setup as 'not last track device' so that INIT won't get confused. ; Add check to avoid usage of one logical disk file more than once. ; Add check NOT to return info in sensemode if not connected. ; Modify sizeing algorithm to make better use of allocated blocks. ; Make disk as big as specified (via P4 at connect time), instead ; of blindly using allocated size. If the EOF pointer was not at ; the end of the file, the blocks in between would not be backed-up. ; Add hack to workaround DUdriver hang problem. ; ; Jur van der Burg 17-FEB-1993 Version 03.00 ; ; Added trace capabilities ; Added 'replace' function ; ; Jur van der Burg 25-FEB-1993 Version 03.01 ; ; Add support for DECram ; Expand logging to include IOSB ; Check for a disk-device when we replace another device ; Correct possible synchronization problem with tracebuffer ; ; Jur van der Burg 14-APR-1993 Version 04.00 ; ; Add support for waiting for tracedata to become available ; Add lost tracedata detection ; ; Jur van der Burg 8-JUL-1993 Version 04.01 ; ; Promote bytecount field in tracerecord to longword. ; ; Jur van der Burg 29-SEP-1993 Version 05.00 ; ; Add support for 'watch' commands ; Reworked control interface ; Corrected quota handling: if process A allocates a tracebuffer ; and process B deallocates this, then process B would be credited ; for the quota! Tracebuffer quota is now process specific. ; Add snapshot support ; Add [no]protect options ; Removed devicelock stuff to prevent MSCP serving. We do this now ; by setting the allocation class to 256 in the DDB. ; ; Jur van der Burg 28-OCT-1994 Version V5.1 ; ; Re-worked avoidance of MSCP-serving. This is needed as VMS V6.2 ; needs an allocation class to be specified when mounting a shadowed ; disk. We do it now by setting DEV$V_CDP in UCB$L_DEVCHAR2. ; Allow container files and 'replaced' devices to be shared by different ; nodes in a cluster by checking the devicename clusterwide. ; Corrected bug when a tracebuffer was allocated if an I/O request was ; active but not yet completed. This generated an invalid trace entry. ; Added capabaility to set the device geometry (number of sectors, ; number of tracks, number of cylinders, maximum blocknumber). ; Added capability to set the allocation class. ; ; Jur van der Burg 15-NOV-1996 Version V6.0 ; ; Allow non-contiguous containerfiles. ; Added entrypoints to global symboltable to make life easier in SDA. ; Moved data items in code to separate psects. ; Added proper synchronization in case a drive is disconnected while ; there was still I/O in progress on the physical device. ; Correct WCB mapping for VMS V6 version of driver for FILE watchpoints ; Removed restriction of nesting of LD devices. Any combination is ; possible now, like a containerfile on a shadowset on a containerfile. ; Add check to disallow transfers of more than 512 bytes to go past ; the end of the container file. ; Switch Unit 0 online to prevent DECAmds from complaining. ; Check if container device supports IO$_DSE, flag it if not (DPdriver). ; Allow trap for all I/O functions in watchpoints ; ; Jur van der Burg 24-DEC-1996 Version V6.1 ; ; Corrected systemcrash from LD_START_CONNECT when we attempted to replace ; a disk which was accessed by another thread. The first attempt returned ; an error as it should, but the device lock was not dequeued. The second ; attempt caused the blocking ast routine to attempt to convert a lock which ; was not granted. Dequeue all $LOGDISK locks in that case. ; Correct invalid returned pid when a process was suspended on a watchpoint ; on a shadowset member. ; ; Jur van der Burg 19-AUG-1998 Version V6.2 ; ; Corrected possible hang on SMP systems. When inserting an i/o request ; back into the systemwide postprocessing queue (IOC$GQ_POSTIQ) a software ; interrupt was generated via the SOFTINT macro. If we had a thread doing ; this on a non-primary processor and we were the first one doing this on ; an empty queue the resulting interrupt was taken on the non-primary ; processor, where it is simply dismissed as these interrupts must be ; serviced on the primary processor. In that case the postprocessing queue ; is not processesd anymore resulting in a system hang. Replaced the code ; to insert the packet with a call to CALL_POST_IRP, a system routine ; which checks on which cpu we do the action. If needed it will tell the ; primary processor to do the job. ; ; Jur van der Burg 5-JUL-2000 Version V6.3 ; ; Allow containerfile to reside on an NFS mounted volume ; Fake IO$_DSE support if physical device does not support that ; Add check for special XQP I/O when tracing, PID is invalid in that case ; Rework geometry calculation to allow for bigger containerfiles up ; to the disk size. ; Drop requirement for inputfilespec on disconnect. This allows deleted ; containerfiles to be disconnected without /ABORT. ; ;--- ; MDDRIVER_WORKAROUND=1 ; Work around MDdriver bug DUDRIVER_WORKAROUND=1 ; Work around DUdriver bug V6=1 ; VMS V6.x support .SBTTL External and local symbol definitions ;+++ ; Libraries and link files ;--- .LIBRARY 'SYS$LIBRARY:LIB.MLB' .LINK 'SYS$SYSTEM:SYS.STB'/SELECTIVE_SEARCH ;+++ ; External symbols ;--- $ACBDEF ; Define ACB offsets $AQBDEF ; Define AQB offsets $ARBDEF ; Define ARB offsets $CANDEF ; Cancel I/O definitions $CCBDEF ; Channel control block $CRBDEF ; Channel request block $CDDBDEF ; Class Driver Data Block $DALDEF ; Device access lockvalue block $DCDEF ; Device classes and types $DDBDEF ; Device data block $DDTDEF ; Driver Dispatch Table $DPTDEF ; Driver Prologue Table $DEVDEF ; Device characteristics $DYNDEF ; Dynamic pool block names $FCBDEF ; File control block $FKBDEF ; Fork block $FIDDEF ; File id block $IDBDEF ; Interrupt data block $IHDDEF ; Image header definitions $IOCDEF ; IO coding bit values $IODEF ; I/O function codes $IPLDEF ; Hardware IPL definitions $IRPDEF ; I/O request packet $JIBDEF ; Job Information Block definitions $LCKDEF ; Lock definitions $MSCPDEF ; MSCP definitions $OPCDEF ; Opcom definitions $PCBDEF ; Process control block $PHDDEF ; Process header definitions $PRIDEF ; Priority definitions $PRVDEF ; Privilege definitions $PSLDEF ; Processor status longword $PTEDEF ; Page table entries definitions $RSNDEF ; Process wait states $RVTDEF ; Relative Volume Table $SBKDEF ; Statistics block $SHADDEF ; Shadow block definitions $SPLCODDEF ; Spinlock code definitions $SSDEF ; System status codes $UCBDEF ; Unit control block $VCBDEF ; Volume Control Block $VECDEF ; Interrupt vector block $WCBDEF ; Window Control Block ;+++ ; Local symbols ;--- LD_MAX_UNITS = 1 ; Max. nr of units on one controller ; P1 = 0 ; First function dependent parameter P2 = 4 ; Second P3 = 8 ; Third P4 = 12 ; Fourth P5 = 16 ; Fifth P6 = 20 ; Sixth ;+++ ; Macro definitions ;--- ; ; Macro to lock Tracebuffer ; ; Input: R5 = UCB ; ; Output: R0 contains status for system context ; ; All registers will be preserved for process context ; .MACRO LOCK_TRACE ACCESS=READ,CONTEXT=PROCESS .IF IDN , .IF IDN , PUSHL R0 MOVAB UCB$L_LD_TRCMUTEX(R5),R0 JSB G^SCH$LOCKR MOVL (SP)+,R0 .IFF MOVAB UCB$L_LD_TRCMUTEX(R5),R0 JSB G^SCH$LOCKREXEC .ENDC .ENDC .IF IDN , .IF IDN , PUSHL R0 MOVAB UCB$L_LD_TRCMUTEX(R5),R0 JSB G^SCH$LOCKW MOVL (SP)+,R0 .IFF MOVAB UCB$L_LD_TRCMUTEX(R5),R0 JSB G^SCH$LOCKWEXEC .ENDC .ENDC .ENDM ; ; Macro to unlock Tracebuffer ; ; Input: R5 = UCB ; ; All registers will be preserved ; .MACRO UNLOCK_TRACE CONTEXT=PROCESS,NEWDATA=NO,?L10,?L20,?L30,?L40 PUSHR #^M MOVAB UCB$L_LD_TRCMUTEX(R5),R0 .IF IDN , JSB G^SCH$UNLOCK .IFF JSB G^SCH$UNLOCKEXEC .ENDC FORKLOCK LOCK=UCB$B_FLCK(R5),- SAVIPL=-(SP),- PRESERVE=NO REMQUE @UCB$L_LD_TRCMUTEXQFL(R5),R3 ; Get eventual waiting IRP BVS L10 ; Nothing there PUSHL R5 ; Save UCB MOVL R3,R5 ; Save pointer to Fork block JSB G^EXE$QUEUE_FORK ; Queue forkthread MOVL (SP)+,R5 ; Restore UCB L10: .IF IDN , L20: REMQUE @UCB$L_LD_TRCWAITQFL(R5),R3 ; Get IRP waiting for data BVS L40 ; Nothing there PUSHL R5 ; Save UCB BSBW LD_ALLO_LDIOB ; Get ACB BLBS R0,L30 ; Check for errors BUG_CHECK INCONSTATE,FATAL L30: MOVL R2,R5 MOVL R3,ACB$L_ASTPRM(R5) ; Save IRP address MOVL IRP$L_PID(R3),- ; Save PID ACB$L_PID(R5) MOVB #,- ACB$B_RMOD(R5) ; Special kernel mode ast MOVAB LD_TRACE_AST,ACB$L_KAST(R5) MOVZBL #PRI$_IOCOM,R2 ; Set up priority increment JSB G^SCH$QAST ; Queue the AST MOVL (SP)+,R5 ; Restore UCB BRB L20 L40: .ENDC FORKUNLOCK LOCK=UCB$B_FLCK(R5),- NEWIPL=(SP)+,- PRESERVE=NO,- CONDITION=RESTORE POPR #^M .ENDM ; ; Macro to enqueue a lock ; ; Input: R5 = UCB ; .MACRO ENQ_LOCK MODE,LKSBL,RESNAME=0,CMPLADR=0,BLKADR=0,EFLAGS=0,ERROR,?L10,?L20 .IF IDN ,<0> $LCK_ENQ LKMODE=MODE,- LKSB=LKSBL,- RESNAM=RESNAME,- BLK_ADR=BLKADR,- CMPL_ADR=L20,- CTX_PRM=R5,- FLAGS=<<#LCK$M_SYSTEM!- LCK$M_NODLCKWT!- LCK$M_NODLCKBLK!- EFLAGS>> .IFF $LCK_ENQ LKMODE=MODE,- LKSB=LKSBL,- RESNAM=RESNAME,- BLK_ADR=BLKADR,- CMPL_ADR=CMPLADR,- CTX_PRM=R5,- FLAGS=<<#LCK$M_SYSTEM!- LCK$M_NODLCKWT!- LCK$M_NODLCKBLK!- EFLAGS>> .ENDC CMPW R0,#SS$_NORMAL ; Queued thread BNEQ L10 RSB L10: .IF NB BRW ERROR .IFF .IF IDN ,<0> .ERROR ; CMPLADR must be specified when ERROR absent .ENDC .ENDC L20: .ENDM ;+++ ; Definitions for local structures ; ; I/O block ;--- $DEFINI LDIOB,GLOBAL ; LOGICAL DISK I/O BLOCK $DEF LDIOB_L_QFL .BLKL 1 ; Forward link $DEF LDIOB_L_QBL .BLKL 1 ; Backward link $DEF LDIOB_W_SIZE .BLKW 1 ; Size field $DEF LDIOB_B_TYPE .BLKB 1 ; Type field $DEF LDIOB_B_RSVD .BLKB 1 ; Reserved field $DEF LDIOB_L_IRP .BLKL 1 ; IRP $DEF LDIOB_L_PID .BLKL 1 ; PID $DEF LDIOB_L_MEDIA .BLKL 1 ; Media address $DEF LDIOB_L_BCNT .BLKL 1 ; Bytecount $DEF LDIOB_W_FUNC .BLKW 1 ; Function $DEF LDIOB_W_IOST .BLKW 1 ; Final iosb contents $DEF LDIOB_Q_STAT .BLKQ 1 ; IOSB contents $DEF LDIOB_Q_ST_TIME .BLKQ 1 ; Start time $DEF LDIOB_Q_EN_TIME .BLKQ 1 ; End time $DEF LDIOB_L_ELAPSED .BLKL 1 ; Elapsed time $DEF LDIOB_L_ABCNT .BLKL 1 ; Accumulated bytecount $DEF LDIOB_L_FWDQFL .BLKL 1 ; Forwarded IRP queue FL $DEF LDIOB_L_FWDQBL .BLKL 1 ; Forwarded IRP queue BL $DEF LDIOB_W_IRPCNT .BLKW 1 ; IRP count $DEF LDIOB_W_SPARE .BLKW 1 ; Spare for alignment LDIOB_K_LENGTH = . ; Length of LDIOB $DEFEND LDIOB ; ; Trace block ; $DEFINI LDTRCENT,GLOBAL ; LOGICAL DISK Trace entry $DEF LDTRC_L_PID .BLKL 1 ; Pid $DEF LDTRC_L_ADDR .BLKL 1 ; Logical block number $DEF LDTRC_L_BCNT .BLKL 1 ; Bytecount $DEF LDTRC_W_FUNC .BLKW 1 ; Functioncode $DEF LDTRC_W_RSVD .BLKW 1 ; Reserved $DEF LDTRC_Q_IOSB .BLKQ 1 ; IOSB $DEF LDTRC_Q_ST_TIME .BLKQ 1 ; Start time $DEF LDTRC_Q_EN_TIME .BLKQ 1 ; End time $DEF LDTRC_L_ELAPSED .BLKL 1 ; Elapsed time LDTRCENT_K_LENGTH = . ; Length of LDTRCENT $DEFEND LDTRCENT ; ; Watch block ; $DEFINI LDWATCHENT,GLOBAL ; LOGICAL DISK Watch entry $DEF LDWATCH_L_FLINK .BLKL 1 ; Forward link $DEF LDWATCH_L_BLINK .BLKL 1 ; Backward link $DEF LDWATCH_W_SIZE .BLKW 1 ; Size field $DEF LDWATCH_B_TYPE .BLKB 1 ; Type field $DEF LDWATCH_B_SPARE .BLKB 1 ; Spare $DEF LDWATCH_L_LBN .BLKL 1 ; Logical block number $DEF LDWATCH_W_FLAGS .BLKW 1 ; Flags $DEF LDWATCH_W_ACTION .BLKW 1 ; Action to perform $DEF LDWATCH_W_FUNC .BLKW 1 ; Functioncode $DEF LDWATCH_W_RETCODE .BLKW 1 ; Return code $DEF LDWATCH_L_PID .BLKL 1 ; Pid of process owning this block $DEF LDWATCH_L_SUSPCNT .BLKL 1 ; Count of suspended processes $DEF LDWATCH_L_SUSPFL .BLKL 1 ; Suspend forward link $DEF LDWATCH_L_SUSPBL .BLKL 1 ; Suspend backward link $DEF LDWATCH_L_VBN .BLKL 1 ; Virtual block number $DEF LDWATCH_L_FCB .BLKL 1 ; FCB of watched file $DEF LDWATCH_L_UCB .BLKL 1 ; Device where file resides $DEF LDWATCH_W_FID_NUM .BLKW 1 ; File ID Num $DEF LDWATCH_W_FID_SEQ .BLKW 1 ; Seq $DEF LDWATCH_W_FID_RVN .BLKW 1 ; Rvn LDWATCHENT_K_LENGTH = . ; Length of LDWATCHENT ; ; Watch block block flags ; _VIELD LDWATCH,0,< - ,- ; Function characteristics ,- ; Remove all entries ,- ; Extent on volumeset > ; ; Subfields in CHARS field ; _VIELD LDWATCH,0,< - ,- ; Function without lbn ,- ; File access > $DEFEND LDWATCHENT ; ; Watchpt parameter block ; $DEFINI LDWATCHPT,GLOBAL ; LOGICAL DISK Watch parameter entry $DEF LDWATCHPT_L_LBN .BLKL 1 ; Logical block number $DEF LDWATCHPT_W_FLAGS .BLKW 1 ; Flags $DEF LDWATCHPT_W_ACTION .BLKW 1 ; Action to perform $DEF LDWATCHPT_W_FUNC .BLKW 1 ; Functioncode $DEF LDWATCHPT_W_RETCODE .BLKW 1 ; Return code $DEF LDWATCHPT_L_SBK .BLKL 1 ; Statistics block $DEF LDWATCHPT_W_FID_NUM .BLKW 1 ; File ID Num $DEF LDWATCHPT_W_FID_SEQ .BLKW 1 ; Seq $DEF LDWATCHPT_W_FID_RVN .BLKW 1 ; Rvn LDWATCHPT_K_LENGTH = . ; Length of LDWATCHPT ; ; Watch parameter block flags ; _VIELD LDWATCHPT,0,< - ,- ; Function characteristics ,- ; Remove all entries > ; ; Subfields in CHARS field ; _VIELD LDWATCHPT,0,< - ,- ; Function without lbn ,- ; File access > $DEFEND LDWATCHPT ; ; Suspended process list ; $DEFINI LDSUSPLST,GLOBAL ; LOGICAL DISK suspended process list $DEF LDSUSPLST_L_PID .BLKL 1 ; Process id $DEF LDSUSPLST_L_LBN .BLKL 1 ; Logical block number $DEF LDSUSPLST_W_FLAGS .BLKW 1 ; Flags $DEF LDSUSPLST_W_ACTION .BLKW 1 ; Action $DEF LDSUSPLST_W_FUNC .BLKW 1 ; Function $DEF LDSUSPLST_W_RETCODE .BLKW 1 ; Return code LDSUSPLST_K_LENGTH = . ; Length of LDSUSPLST $DEFEND LDSUSPLST ; ; $SNDOPR parameter list ; $DEFINI LDSNDOPRLST,GLOBAL ; $SNDOPR parameter list $DEF LDSNDOPRLST_L_ASTQFL .BLKL 1 ; AST queue flink $DEF LDSNDOPRLST_L_ASTQBL .BLKL 1 ; AST queue blink $DEF LDSNDOPRLST_W_SIZE .BLKW 1 ; Size $DEF LDSNDOPRLST_B_TYPE .BLKB 1 ; Type $DEF LDSNDOPRLST_B_RMOD .BLKB 1 ; Ast flags $DEF LDSNDOPRLST_L_PID .BLKL 1 ; Process id $DEF LDSNDOPRLST_L_AST .BLKL 1 ; AST address $DEF LDSNDOPRLST_L_ASTPRM .BLKL 1 ; AST parameter $DEF LDSNDOPRLST_L_KAST .BLKL 1 ; KAST address $DEF LDSNDOPRLST_L_LBN .BLKL 1 ; Logical block number $DEF LDSNDOPRLST_W_FLAGS .BLKW 1 ; Flags $DEF LDSNDOPRLST_W_ACTION .BLKW 1 ; Action $DEF LDSNDOPRLST_W_FUNC .BLKW 1 ; Function $DEF LDSNDOPRLST_W_RETCODE .BLKW 1 ; Return code $DEF LDSNDOPRLST_W_FID_NUM .BLKW 1 ; File ID Num $DEF LDSNDOPRLST_W_FID_SEQ .BLKW 1 ; Seq $DEF LDSNDOPRLST_W_FID_RVN .BLKW 1 ; Rvn $DEF LDSNDOPRLST_T_DEVNAM .BLKB 64 ; Device name LDSNDOPRLST_K_DEVNAM = 64 ; Device name length LDSNDOPRLST_K_LENGTH = . ; Length of LDSNDOPRLST $DEFEND LDSNDOPRLST ; ; LD file lockvalueblock definitions ; $DEFINI LDFLVB,GLOBAL ; LD file lockvalue block $DEF LDFLVB_W_CYLINDERS .BLKW 1 ; Cylinders $DEF LDFLVB_B_TRACKS .BLKB 1 ; Tracks $DEF LDFLVB_B_SECTORS .BLKB 1 ; Sectors $DEF LDFLVB_L_MAXBLOCK .BLKL 1 ; Maximum blocknumber $DEF LDFLVB_L_ALLOCLS .BLKL 1 ; Allocation class $DEF LDFLVB_W_UNIT .BLKW 1 ; Unit number $DEF LDFLVB_B_FLAGS .BLKB 1 ; Flags ASSUME . LE 16 ; Length must be <= 16 ; ; Subfields in FLAGS field ; _VIELD LDFLVB,0,< - ,- ; Shared accessable > $DEFEND LDFLVB ; ; LD device lockvalueblock definitions ; $DEFINI LDDLVB,GLOBAL ; LD file lockvalue block $DEF LDDLVB_W_FID .BLKW 1 ; File ID Num $DEF LDDLVB_W_SEQ .BLKW 1 ; File Seq $DEF LDDLVB_W_RVN .BLKW 1 ; File Rvn $DEF LDDLVB_B_ALLOCLS .BLKB 1 ; Allocation class $DEF LDDLVB_W_UNIT .BLKW 1 ; Unit number $DEF LDDLVB_T_DEVNAM .BLKB 7 ; Devicename ASSUME . LE 16 ; Length must be <= 16 ; ; Definitions for our functioncodes ; IO$_LD_CONTROL = 20 ; Main control function ; (Physical I/O function) LDIO_CONNECT = 0 ; Connect drive/file LDIO_DISCONNECT = 1 ; Disconnect drive/file LDIO_ENABLE_TRACE = 2 ; Enable trace LDIO_DISABLE_TRACE = 3 ; Disable trace LDIO_GET_TRACE = 4 ; Get tracedata LDIO_RESET_TRACE = 5 ; Reset tracebuffer LDIO_GET_CONNECTION = 6 ; Get connected file/devicename LDIO_SET_SEED = 7 ; Set unit seed for cloned device LDIO_ENABLE_WATCH = 8 ; Enable watch LDIO_DISABLE_WATCH = 9 ; Disable watch LDIO_GET_WATCH = 10 ; Get watchpoints LDIO_RESUME_WATCH = 11 ; Resume watchpoints LDIO_GET_SUSPEND_LIST = 12 ; Get list of suspended processes LDIO_ENABLE_PROTECT = 13 ; Enable write-protect LDIO_DISABLE_PROTECT = 14 ; Disable write-protect LDIO_SET_ALLOCLASS = 15 ; Set allocation class LDIO_V_FUNC = 0 LDIO_S_FUNC = 8 ; ; Function modifiers ; $DEFINI LDIO,GLOBAL _VIELD LDIO,8,<- ,- ; Replace drive ,- ; Abort disconnect ,- ; Get buffer size ,- ; Don't wait for tracedata ,- ; Reset after retrieving tracedata ,- ; Shared access > $DEFEND LDIO ; ; Watchpoint actions ; WATCH_ACTION_SUSPEND = 0 ; Suspend thread WATCH_ACTION_CRASH = 1 ; Crash system WATCH_ACTION_ERROR = 2 ; Return error WATCH_ACTION_OPCOM = 3 ; Send message to OPCOM WATCH_ACTION_MAX = 3 ; Maximum action ; ;+++ ; Definitions for IRP fields ;--- $DEFINI IRP,GLOBAL ; IRP .=IRP$K_LENGTH $DEF IRP$L_LD_LDUCB .BLKL 1 ; Logical Disk UCB $DEF IRP$L_LD_LDIOB .BLKL 1 ; Logical disk IOB $DEF IRP$L_LD_FWDQFL .BLKL 1 ; Forwarded IRP queue FL $DEF IRP$L_LD_FWDQBL .BLKL 1 ; Forwarded IRP queue BL $DEF IRP$K_LD_IRPLEN ; Length of new IRP $DEFEND IRP ;+++ ; Definitions that follow the standard UCB fields ;--- $DEFINI UCB,GLOBAL ; Start of UCB definitions .=UCB$K_MSCP_DISK_LENGTH ; Position at end of UCB $DEF UCB$L_LD_PDUCB .BLKL 1 ; UCB of Phys. disk $DEF UCB$L_LD_AIOFL .BLKL 1 ; Active I/O list forward link $DEF UCB$L_LD_AIOBL .BLKL 1 ; Active I/O list backward link ASSUME UCB$L_LD_AIOBL EQ UCB$L_LD_AIOFL+4 $DEF UCB$L_LD_IOBFL .BLKL 1 ; LDIOB free queue FL $DEF UCB$L_LD_IOBBL .BLKL 1 ; LDIOB free queue BL $DEF UCB$L_LD_IRPFL .BLKL 1 ; Forward IRP free queue FL $DEF UCB$L_LD_IRPBL .BLKL 1 ; Forward IRP free queue BL $DEF UCB$L_LD_FCB .BLKL 1 ; Save for FCB pointer $DEF UCB$L_LD_WCB .BLKL 1 ; Window control block $DEF UCB$L_LD_ORBSAV .BLKL 1 ; Saved ORB $DEF UCB$L_LD_SAVEPC .BLKL 1 ; Caller's address $DEF UCB$L_LD_SAVST .BLKL 1 ; Safe place for status $DEF UCB$L_LD_FR3 .BLKL 1 ; Safe place for R3 $DEF UCB$L_LD_FR4 .BLKL 1 ; Safe place for R4 ASSUME UCB$L_LD_FR4 EQ UCB$L_LD_FR3+4 $DEF UCB$Q_LD_PD_LKSB .BLKB 8 ; Phys. dev. lock status block $DEF UCB$A_LD_PD_LVB .BLKB 16 ; + lock value block $DEF UCB$T_LD_PD_RESNAM .BLKB 28 ; Phys. dev. lock resource name $DEF UCB$Q_LD_FILE_LKSB .BLKB 8 ; Lock status block $DEF UCB$A_LD_FILE_LVB .BLKB 16 ; + lock value block $DEF UCB$T_LD_FILE_RESNAM .BLKB 40 ; File resource name 32 bytes + descriptor $DEF UCB$Q_LD_DEV_LKSB .BLKB 8 ; Lock status block $DEF UCB$A_LD_DEV_LVB .BLKB 16 ; + lock value block $DEF UCB$T_LD_DEV_RESNAM .BLKB 40 ; Device resource name 32 bytes + descriptor $DEF UCB$L_LD_TRCWAITQFL .BLKL 1 ; Trace data wait queue forward link $DEF UCB$L_LD_TRCWAITQBL .BLKL 1 ; Trace data wait queue backward link $DEF UCB$L_LD_TRCMUTEXQFL .BLKL 1 ; Trace mutex wait queue forward link $DEF UCB$L_LD_TRCMUTEXQBL .BLKL 1 ; Trace mutex wait queue backward link $DEF UCB$L_LD_TRCPC .BLKL 1 ; Fork trace PC save $DEF UCB$L_LD_TRCMUTEX .BLKL 1 ; Trace buffer mutex $DEF UCB$L_LD_TRCBUFOWN .BLKL 1 ; Owner of tracebuffer $DEF UCB$L_LD_TRCBUF .BLKL 1 ; Pointer to tracebuffer $DEF UCB$L_LD_TRCBUFSIZ .BLKL 1 ; Tracebuffer size $DEF UCB$L_LD_TRCBUFALLOCSIZ .BLKL 1 ; Allocated tracebuffer size $DEF UCB$L_LD_TRCBUFPTR .BLKL 1 ; Tracebuffer pointer $DEF UCB$L_LD_TRCWRAP .BLKL 1 ; Flag if buffer wrapped $DEF UCB$L_LD_TRCLOST .BLKL 1 ; Number of lost trace packets $DEF UCB$L_LD_WATCHQFL .BLKL 1 ; Watch queue forward link $DEF UCB$L_LD_WATCHQBL .BLKL 1 ; Watch queue backward link $DEF UCB$L_LD_WATCHCNT .BLKL 1 ; Watch queue entry count $DEF UCB$L_LD_WATCHPNDQFL .BLKL 1 ; Watch pending queue forward link $DEF UCB$L_LD_WATCHPNDQBL .BLKL 1 ; Watch pending queue backward link $DEF UCB$W_LD_FID_NUM .BLKW 1 ; File ID Num $DEF UCB$W_LD_FID_SEQ .BLKW 1 ; Seq $DEF UCB$W_LD_FID_RVN .BLKW 1 ; Rvn $DEF UCB$L_LD_CPID .BLKL 1 ; Charge PID of cloning process $DEF UCB$W_LD_CHARGE .BLKW 1 ; Charge amount $DEF UCB$W_LD_FLAGS .BLKW 1 ; Flags byte $VIELD UCB,0,< - , - ; Conn./Disconn. status bit ,- ; Replace mode status bit ,- ; Connected to DECRAM disk ,- ; Write protect ,- ; Shared accessable ,- ; Containerfile on volumeset ,- ; DSE not supported ,- ; Virtual I/O to container file ,- ; Forkblock busy (internal only) ,- ; Disc. pending (internal only) > ; ; Filename string buffer $DEF UCB$K_LD_UCBLEN ; Length of new UCB $DEFEND UCB ; End of UCB definitions .SBTTL Standard tables ;+++ ; Driver prologue table ;--- .IF DF V6 DPT_FLAGS= ; SMP safe ; Driver supports snapshots ; if not in a cluster .IFF DPT_FLAGS= ; SMP safe .ENDC DPTAB - ; DPT-creation macro END=LD_END,- ; End of driver label ADAPTER=NULL,- ; Adapter type UCBSIZE=,- ; Length of UCB MAXUNITS=LD_MAX_UNITS,- ; Max nr of units UNLOAD=LD_DRV_UNLOAD,- ; Unload routine NAME=LDDRIVER,- ; Driver name FLAGS=DPT_FLAGS ; Default flags DPT_STORE INIT ; Start of load ; initialization table DPT_STORE DDB,DDB$L_ACPD,L,<^A\F11\> ; Default ACP name DPT_STORE DDB,DDB$L_ACPD+3,B,DDB$K_PACK ; ACP class DPT_STORE UCB,UCB$B_FLCK,B,SPL$C_IOLOCK8; FORK IPL DPT_STORE UCB,UCB$B_DIPL,B,8 ; Device IPL DPT_STORE UCB,UCB$L_DEVCHAR,L,<- ; Device characteristics DEV$M_IDV!- ; input device DEV$M_ODV!- ; output device DEV$M_FOD!- ; files oriented DEV$M_DIR!- ; directory structured DEV$M_AVL!- ; available DEV$M_SHR!- ; sharable DEV$M_RND> ; random access DPT_STORE UCB,UCB$L_DEVCHAR2,L,<- DEV$M_NNM!- ; Prefix name with NODE$ DEV$M_NLT!- ; Not-last-track device (no bad block data) DEV$M_CDP!- ; To prevent MSCP serving DEV$M_MSCP> ; Fake to get shadowing to work DPT_STORE UCB,UCB$W_STS,W,UCB$M_TEMPLATE; Template device DPT_STORE UCB,UCB$L_LD_TRCBUF,L,0 ; Pointer to tracebuffer DPT_STORE UCB,UCB$B_DEVCLASS,B,DC$_DISK ; Device class DPT_STORE UCB,UCB$B_DEVTYPE,B,DT$_FD1 ; Dev. type = foreign disk DPT_STORE UCB,UCB$W_DEVBUFSIZ,W,512 ; Default buffer size DPT_STORE UCB,UCB$W_DEVSTS,W,- ; Inhibit logical to ; physical xlation. DPT_STORE REINIT ; Start of reload ; initialization table DPT_STORE DDB,DDB$L_DDT,D,LD$DDT ; Address of DDT DPT_STORE CRB,- ; Address of controller CRB$L_INTD+VEC$L_INITIAL,- ; initialization routine D,LD_CONTROL_INIT DPT_STORE CRB,- ; Address of device CRB$L_INTD+VEC$L_UNITINIT,- ; unit initialization D,LD_UNIT_INIT ; routine DPT_STORE END ; End of initialization ;+++ ; Driver dispatch table ;--- DDTAB - ; DDT-creation macro DEVNAM=LD,- ; Name of device START=LD_START,- ; Start I/O routine FUNCTB=LD_FUNCTABLE,- ; FDT address CANCEL=LD_CANCEL,- ; Cancel I/O routine UNITINIT=LD_UNIT_INIT,- ; Unit init. routine CLONEDUCB=LD_CLONED_UCB ; Cloned UCB routine ;+++ ; Function decision table ;--- UNIVERSAL_SYMBOL LD_FUNCTABLE ;LD_FUNCTABLE: ; FDT for driver FUNCTAB ,- ; Valid I/O functions ; LD control functions FUNCTAB ,- ; Buffered I/O functions ; LD control functions FUNCTAB LD_FDT_SHAD_WCHECK,<- ; Check write to shadow set mbr WRITELBLK,- ; Write LOGICAL Block WRITEPBLK,- ; Write Physical Block WRITEVBLK> ; Write VIRTUAL Block FUNCTAB +ACP$READBLK,- ; Read functions ; Read virtual block FUNCTAB +ACP$WRITEBLK,- ; Write functions ; Write virtual block FUNCTAB LD_FDT_DSE,<- ; DSE function DSE> ; Data Secutiry Erase FUNCTAB +ACP$ACCESS,- ; Access functions ; Create file/dir FUNCTAB +ACP$DEACCESS,- ; Deaccess functions ; Deaccess file FUNCTAB +ACP$MODIFY,- ; Modify functions ; Modify file attributes FUNCTAB +ACP$MOUNT,- ; Mount functions ; Mount volume FUNCTAB +EXE$LCLDSKVALID,- ; Local disk valid functions ; Pack acknowledge FUNCTAB +EXE$ZEROPARM,- ; Zero parameter functions ; Available FUNCTAB +EXE$ONEPARM,- ; One parameter functions ; Seek FUNCTAB +EXE$SENSEMODE,- ; Sense functions ; Sense mode FUNCTAB +EXE$SETCHAR,- ; Set functions ; Set mode FUNCTAB LD_FDT_CRESHAD,- ; Create shadowset virtual unit FUNCTAB LD_FDT_REMSHAD,- ; Remove shadowset member FUNCTAB LD_FDT_CONTROL,- ; General LDdriver control ; ; Local data ; .SAVE_PSECT .PSECT $$$110_LD_DATA,LONG,WRT ; UNIVERSAL_SYMBOL LD_REFCNT ;LD_REFCNT: .LONG 0 ; Number of active devices ; LBN_WP: .ASCII \***** LDdriver detected LBN watchpoint access *****!/\ .ASCII \PID: !XL!/\ .ASCII \Image: !AC!/\ .ASCII \Device: !AC!/\ .ASCII \Function: !XW!/\ .ASCII \LBN: !UL\ LBN_WP_LEN=.-LBN_WP ; VBN_WP: .ASCII \***** LDdriver detected VBN watchpoint access *****!/\ .ASCII \PID: !XL!/\ .ASCII \Image: !AC!/\ .ASCII \Device: !AC!/\ .ASCII \Function: !XW!/\ .ASCII \VBN: !UL!/\ .ASCII \File id: (!UW,!UW,!UW)\ VBN_WP_LEN=.-VBN_WP ; NONESTR: .ASCIC /None/ ; If no imagename available OPCOM_NAME: .ASCIC /OPCOM/ ; Process name of OPCOM process .RESTORE_PSECT ; ; End of driver data ; .SBTTL LD_FDT_CONTROL, general control and dispatch FDT routine ;+++ ; LD_FDT_CONTROL, general control and dispatch FDT routine ; ; Functional description: ; ; This routine is invoked via an IO$_LD_CONTROL function. ; We will dispatch to the various other routines according to ; the P6 parameter. ; ; Inputs: ; ; R0-R2 - scratch registers ; R3 - address of the IRP (I/O request packet) ; R4 - address of the PCB (process control block) ; R5 - address of the UCB (unit control block) ; R6 - address of the CCB (channel control block) ; R7 - bit number of the I/O function code ; R8 - address of the FDT table entry for this routine ; R9-R11 - scratch registers ; AP - address of the 1st function dependent QIO parameter ; P6(AP) - Address of longword with functioncode and modifiers ; ; Outputs: ; ; The routine must preserve all registers except R0-R2, and ; R9-R11. ; ;-- UNIVERSAL_SYMBOL LD_FDT_CONTROL ;LD_FDT_CONTROL: ; General Control FDT routine MOVZWL P6(AP),R0 ; Get parameter MOVL R0,IRP$L_EXTEND(R3) ; Save function in IRP ASSUME LDIO_S_FUNC EQ 8 DISPATCH R0,TYPE=B,<- ; Dispatch according to function ,- ,- ,- ,- ,- ,- ,- ,- ,- ,- ,- ,- ,- ,- ,- > MOVZWL #SS$_ILLIOFUNC,R0 ; Set illegal I/O function code JMP G^EXE$ABORTIO ; Abort the I/O .SBTTL LD_GET_CONNECTION, Get connection characteristics FDT routine ;+++ ; LD_GET_CONNECTION, Get connection characteristics FDT routine ; ; Functional description: ; ; This routine returns the full filename string to the callers buffer, ; specified via the descriptor address in P1. ; The IOSB returned, contains; ; - Longword 0, The return status code ; and in word 1,The nr of characters transferred ; - Longword 1, The Connected status flag, where ; bit 0 = 1 : Connected, and ; 0 : Disconnected ; bit 1 = 0 : Normal and ; 1 : Replaced ; bit 2 = 0 : Normal disk ; 1 : DECRAM disk ; bit 3 = 0 : Normal access ; 1 : Write protected ; ; Inputs: ; ; R0-R2 - scratch registers ; R3 - address of the IRP (I/O request packet) ; R4 - address of the PCB (process control block) ; R5 - address of the UCB (unit control block) ; R6 - address of the CCB (channel control block) ; R7 - bit number of the I/O function code ; R8 - address of the FDT table entry for this routine ; R9-R11 - scratch registers ; AP - address of the 1st function dependent QIO parameter ; P1(AP) - Address of buffer to receive the devicename string ; P2(AP) - Size of buffer to receive the devicename string ; P3(AP) - Address of buffer to receive file-id of connected file ; ; Outputs: ; ; The routine must preserve all registers except R0-R2, and ; R9-R11. ; ;-- UNIVERSAL_SYMBOL LD_GET_CONNECTION ;LD_GET_CONNECTION: ; Get Connection FDT routine MOVL P1(AP),R0 ; Address of buffer MOVL P2(AP),R1 ; Get buffer length BEQL 10$ ; Length 0, just return flags JSB G^EXE$READCHK ; Check buffer for write access MOVZWL #SS$_ACCVIO,R0 ; Assume trouble IFNOWRT #6,@P3(AP),20$ ; Check if fid buffer writeable MOVZWL #SS$_DEVINACT,R0 ; Assume already inactive BBC #UCB$V_LD_CONSTS,- ; Active? UCB$W_LD_FLAGS(R5),20$ MOVL UCB$L_LD_PDUCB(R5),R2 ; Get physical device ucb JSB G^SCH$IOLOCKR ; Lock the IO database PUSHR #^M MOVL R2,R5 ; Copy ucb address MOVZBL #1,R4 ; DVI$_ALLDEVNAM MOVL P1(AP),R1 ; Address of buffer MOVZWL P2(AP),R0 ; Buffer size JSB G^IOC$CVT_DEVNAM ; Get alloclass devicename POPR #^M PUSHR #^M ; Save across unlock JSB G^SCH$IOUNLOCK ; Unlock the IO database POPR #^M ; Restore IRP and status BLBC R0,20$ ; Trouble MOVL P3(AP),R2 ; Get fid buffer address ASSUME FID$W_NUM+2 EQ FID$W_SEQ ASSUME FID$W_SEQ+2 EQ FID$W_RVN ASSUME UCB$W_LD_FID_NUM+2 EQ UCB$W_LD_FID_SEQ MOVL UCB$W_LD_FID_NUM(R5),(R2)+ ; Insert in buffer MOVW UCB$W_LD_FID_RVN(R5),(R2) 10$: MOVZWL #SS$_NORMAL,R0 INSV R1,#16,#16,R0 ; Place high word in R0 MOVZWL UCB$W_LD_FLAGS(R5),R1 ; Copy connected status flags JMP G^EXE$FINISHIO ; Done 20$: JMP G^EXE$ABORTIO ; Abort the I/O .SBTTL LD_GET_TRACE, Get trace data FDT routine ;+++ ; LD_GET_TRACE, Get trace data FDT routine ; ; Functional description: ; ; This routine retrieves trace data from the trace buffer. ; The IOSB returned, contains; ; - Longword 0, The return status code ; - Longword 1, The number of returned trace buffers ; ; Inputs: ; ; R0-R2 - scratch registers ; R3 - address of the IRP (I/O request packet) ; R4 - address of the PCB (process control block) ; R5 - address of the UCB (unit control block) ; R6 - address of the CCB (channel control block) ; R7 - bit number of the I/O function code ; R8 - address of the FDT table entry for this routine ; R9-R11 - scratch registers ; AP - address of the 1st function dependent QIO parameter ; P1(AP) - address of buffer to receive the trace data ; P2(AP) - size of buffer to receive the trace data ; ; Outputs: ; ; R0 - I/O status ; R1 - Number of returned bytes ; ; The routine must preserve all registers except R0-R2, and ; R9-R11. ; ;-- UNIVERSAL_SYMBOL LD_GET_TRACE ;LD_GET_TRACE: TSTL UCB$L_LD_TRCBUF(R5) ; Check for tracebuffer BEQL 10$ ; Not set BBC #LDIO_V_INQUIRE,- ; Return tracebuffer IRP$L_EXTEND(R3),30$ MOVL UCB$L_LD_TRCBUFSIZ(R5),R2 ; Get tracebuffer size MOVZWL #SS$_NORMAL,R0 ; Success BRW 50$ ; Finish the I/O 10$: MOVZWL #SS$_NODATA,R0 ; Trace not active 20$: JMP G^EXE$ABORTIO ; Abort the I/O 30$: MOVZWL #SS$_IVBUFLEN,R0 ; Assume buffer too small MOVL P2(AP),R1 ; Get buffer length CMPL R1,UCB$L_LD_TRCBUFSIZ(R5) ; Check size BLSS 20$ ; Too small MOVL P1(AP),R0 ; Get address of buffer JSB G^EXE$READCHK ; Check userbuffer accessability MOVL R0,IRP$L_SVAPTE(R3) ; Save buffer address BSBW LD_MOVE_TRACE ; Move data to user BLBC R0,60$ ; Stop on overrun error ; The count will always be the ; number of packets in the buffer 50$: DIVL3 #LDTRCENT_K_LENGTH,R2,R1 ; Convert size to number of entries BNEQ 60$ ; Something there BBS #LDIO_V_NOWAIT,- ; We don't want to wait IRP$L_EXTEND(R3),60$ JMP G^EXE$QIODRVPKT ; Finish in start I/O 60$: CLRL IRP$L_SVAPTE(R3) ; No more SVAPTE JMP G^EXE$FINISHIO ; Done .SBTTL LD_MOVE_TRACE, Move trace data to user ;+++ ; LD_MOVE_TRACE, move trace data to user ; ; Functional description: ; ; This routine moves the tracedata to the user's buffer. ; If the modifier LDIO_RESET was specified for the transfer ; then the tracebuffer will be reset afterwards. ; ; Inputs: ; ; R3 - address of the IRP (I/O request packet) ; R5 - address of the UCB (unit control block) ; ; Outputs: ; ; R0 - Status, either SS$_NORMAL or SS$_DATAOVERUN (buffer overflow) ; R1 - 0 or on failure number of lost packets ; R2 - number of bytes moved ; ; The routine must preserve all registers except R0-R2. ; ;-- UNIVERSAL_SYMBOL LD_MOVE_TRACE ;LD_MOVE_TRACE: PUSHR #^M MOVL IRP$L_SVAPTE(R3),R0 ; Get address MOVL IRP$L_BCNT(R3),R1 ; Get length BBC #LDIO_V_RESET,- ; Reset requested? IRP$L_EXTEND(R3),40$ ; No LOCK_TRACE ACCESS=WRITE ; Lock the tracebuffer for write BRW 50$ 40$: LOCK_TRACE ; Lock the tracebuffer for read 50$: CLRL R10 ; Total number of bytes moved MOVL UCB$L_LD_TRCBUFPTR(R5),R2 ; Get tracebuffer pointer MOVL UCB$L_LD_TRCWRAP(R5),R9 ; Did the buffer wrap? BEQL 60$ ; No SUBL3 R2,R9,R1 ; Bytecount MOVL R1,R10 ; Save count BSBW MOVE_TRACE ; Move the data (first part) 60$: MOVL UCB$L_LD_TRCBUF(R5),R2 ; Point to start of buffer SUBL3 R2,UCB$L_LD_TRCBUFPTR(R5),R1 ADDL2 R1,R10 ; Accumulate count BSBW MOVE_TRACE ; Move the data (second part) MOVL UCB$L_LD_TRCLOST(R5),R1 ; Get count MOVZWL #SS$_NORMAL,R0 ; Assume no overflow TSTL R9 ; Buffer wrapped? BEQL 70$ ; No ; ; Calculate number of lost packets. This will be done as follows: ; ; Count = (((TRCBUFPTR - TRCBUF) / LDTRCENT_K_LENGTH) + ; ((TRCLOST - 1) * ((TRCWRAP - TRCBUF) / LDTRCENT_K_LENGTH))) ; DECL R1 ; TRCLOST - 1 SUBL3 UCB$L_LD_TRCBUF(R5),- ; TRCWRAP - TRCBUF UCB$L_LD_TRCWRAP(R5),R0 DIVL2 #LDTRCENT_K_LENGTH,R0 ; / LDTRCENT_K_LENGTH MULL2 R1,R0 SUBL3 UCB$L_LD_TRCBUF(R5),- ; TRCBUFPTR - TRCBUF UCB$L_LD_TRCBUFPTR(R5),R1 DIVL2 #LDTRCENT_K_LENGTH,R1 ; / LDTRCENT_K_LENGTH ADDL2 R0,R1 ; Result MOVZWL #SS$_DATAOVERUN,R0 ; We were overrun by a truck 70$: BBC #LDIO_V_RESET,- ; Reset requested? IRP$L_EXTEND(R3),80$ ; No CLRL UCB$L_LD_TRCWRAP(R5) ; Not yet wrapped CLRL UCB$L_LD_TRCLOST(R5) ; Nothing lost yet MOVL R2,UCB$L_LD_TRCBUFPTR(R5) ; Init pointer 80$: UNLOCK_TRACE ; Unlock trace mutex MOVL R10,R2 ; Return amount moved POPR #^M RSB ; MOVE_TRACE: PUSHR #^M MOVL R1,R6 ; Length MOVL R2,R1 ; Source MOVL R0,R3 ; Destination DIVL3 #65535,R6,R7 ; Number of times we must loop BEQL 20$ ; Total count is less than 65535 10$: MOVC3 #65535,(R1),(R3) ; Move data (max MOVC can handle) SUBL2 #65535,R6 ; Accumulate bytes moved SOBGTR R7,10$ ; Until done 20$: MOVC3 R6,(R1),(R3) ; Move the remainder MOVL R3,R0 ; Return last byte+1 POPR #^M RSB .SBTTL LD_DISCONNECT, Disconnect FDT routine ;+++ ; LD_DISCONNECT, Disconnect FDT routine ; ; Functional description: ; ; This routine will disconnect the Logical disk from the Phys. Disk. ; If LDIO_M_ABORT is specified we simply skip all checks and disconnect ; the LD device as it is. ; ; A prereqisite to call these routines, is that the file is opened, ; except when we replace a whole drive. ; ; Inputs: ; ; R0-R2 - scratch registers ; R3 - address of the IRP (I/O request packet) ; R4 - address of the PCB (process control block) ; R5 - address of the UCB (unit control block) ; R6 - address of the CCB (channel control block) ; R7 - bit number of the I/O function code ; R8 - address of the FDT table entry for this routine ; R9-R11 - scratch registers ; AP - address of the 1st function dependent QIO parameter ; P1(AP) - Address of the SBK block (Ignored with abort or ; replaced drive) ; ; Outputs: ; ; The routine must preserve all registers except R0-R2, and ; R9-R11. ; ;-- UNIVERSAL_SYMBOL LD_DISCONNECT ;LD_DISCONNECT: ; Disconnect FDT routine MOVZWL #SS$_DEVINACT,R0 ; Assume inactive BBC #UCB$V_LD_CONSTS,- ; Check if in DISCONNECT state UCB$W_LD_FLAGS(R5),10$ MOVZWL #SS$_DEVFOREIGN,R0 ; Assume foreign mounted status BBS #DEV$V_FOR,UCB$L_DEVCHAR(R5),10$; Device foreign mounted? MOVZWL #SS$_DEVMOUNT,R0 ; Assume mounted status BBS #DEV$V_MNT,UCB$L_DEVCHAR(R5),10$; Device mounted? MOVZWL #SS$_DEVASSIGN,R0 ; Assume channels assigned CMPW UCB$W_REFC(R5),#1 ; Are we the only one? BNEQ 10$ ; No, get out BSBW LD_DEALLOC_TRCBUF ; Get rid of trace buffer BSBW LD_DEALLOC_WATCHBUF ; Get rid of watch buffers CVTWL IRP$W_CHAN(R3),R0 ; Get channel number MOVL G^CTL$GL_CCBBASE,R1 ; Get base of channels MOVAB (R1)[R0],R0 ; Get CCB MOVL UCB$L_LD_WCB(R5),CCB$L_WIND(R0) ; Save WCB address for cleanup JMP G^EXE$QIODRVPKT ; Finish in start I/O 10$: JMP G^EXE$ABORTIO ; And abort the I/O .SBTTL LD_CONNECT, Connect FDT routine ;+++ ; LD_CONNECT, Connect FDT routine ; ; Functional description: ; ; This routine will connect a Logical disk to the Physical Disk. ; ; A prereqisite to call these routines, is that the file is opened, ; except when we replace a whole drive. ; ; Inputs: ; ; R0-R2 - scratch registers ; R3 - address of the IRP (I/O request packet) ; R4 - address of the PCB (process control block) ; R5 - address of the UCB (unit control block) ; R6 - address of the CCB (channel control block) ; R7 - bit number of the I/O function code ; R8 - address of the FDT table entry for this routine ; R9-R11 - scratch registers ; AP - address of the 1st function dependent QIO parameter ; P1(AP) - Address of the SBK block (Normal connect) ; P1(AP) - Address of phys. disk device name desc. (Replace drive) ; P2(AP) - Size of disk, if 0 allocated size of FCB will be used ; P3(AP) - Number of Tracks ; P4(AP) - Number of Sectors ; P5(AP) - Number of Cylinders ; ; Outputs: ; ; The routine must preserve all registers except R0-R2, and ; R9-R11. ; ;-- UNIVERSAL_SYMBOL LD_CONNECT ;LD_CONNECT: ; Connect FDT routine BBC #UCB$V_LD_CONSTS,- ; Check if disconnected UCB$W_LD_FLAGS(R5),10$ MOVZWL #SS$_DEVACTIVE,R0 ; Set Device Active BRW 140$ 10$: BBC #LDIO_V_SHARE,- ; Shared access requested? IRP$L_EXTEND(R3),20$ TSTL @#CLU$GL_CLUB ; Cluster code loaded? BNEQ 20$ ; Yes MOVZWL #SS$_UNSUPPORTED,R0 ; Not supported BRW 140$ 20$: BBS #LDIO_V_REPLACE,- ; Replace a drive? IRP$L_EXTEND(R3),40$ MOVL #SBK$K_LENGTH,R1 ; Get the SBK length MOVL P1(AP),R0 ; Get the SBK address JSB G^EXE$WRITECHK ; Check if SBK is readable PUSHL R0 JSB G^SCH$IOLOCKR ; Lock the IO database for read MOVL (SP)+,R0 MOVL SBK$L_FCB(R0),R1 ; Get the FCB address BGEQ 30$ ; Must be system address CMPB FCB$B_TYPE(R1),#DYN$C_FCB ; Check if it is a FCB BNEQ 30$ ; No, return error TSTW FCB$W_REFCNT(R1) ; File open? BEQL 30$ ; No, error MOVL R1,UCB$L_LD_FCB(R5) ; Save FCB address for later MOVL FCB$L_WLFL(R1),R1 ; Get a WCB address MOVL #SS$_FILNOTACC,R0 ; Assume completely mapped BBC #WCB$V_COMPLETE,- ; Check if complete map WCB$B_ACCESS(R1),35$ MOVL R1,UCB$L_LD_WCB(R5) ; Save WCB address MOVL WCB$L_ORGUCB(R1),R1 ; Get UCB of physical device MOVL UCB$L_VCB(R1),R2 ; Get VCB MOVL VCB$L_AQB(R2),R0 ; Get AQB CMPB AQB$B_ACPTYPE(R0),#AQB$K_F11V2 ; Serviced by F11BXQP? BNEQ 27$ ; No TSTW VCB$W_RVN(R2) ; Relative volume number BEQL 25$ ; Branch if not a volume set MOVL VCB$L_RVT(R2),R0 ; Fetch RVT address BEQL 25$ ; Not there MOVL RVT$L_UCBLST(R0),R1 ; Get root volume UCB 25$: MOVL R1,UCB$L_LD_PDUCB(R5) ; Save it BRB 50$ 27$: MOVZWL #SS$_WRONGACP,R0 ; Bad ACP type BRW 90$ 30$: MOVZWL #SS$_FORMAT,R0 ; FCB invalid 35$: BRW 90$ ; Abort the I/O and unlock mtx 40$: MOVL P1(AP),R1 ; Get devicename descriptor address MOVL 4(R1),R0 ; Get devicename string address MOVZWL (R1),R1 ; Get devicename string length JSB G^EXE$WRITECHK ; Check if string is readable PUSHR #^M ; Save some registers JSB G^SCH$IOLOCKR ; Lock the IO database for read MOVL P1(AP),R1 ; Get devicename descr. addr. MOVL #IOC$M_ANY,R2 ; Set the SEARCHALL bit CLRL R3 ; No SB specified (local only) JSB G^IOC$SEARCH ; Search the IO database POPR #^M ; Restore the registers BLBC R0,90$ ; Return on error MOVL R1,UCB$L_LD_PDUCB(R5) ; Save the UCB of the phys. disk 50$: MOVZWL #SS$_IVDEVNAM,R0 ; Assume invalid device CMPB UCB$B_DEVCLASS(R1),#DC$_DISK ; Connect to a disk? BNEQ 90$ ; No, not allowed PUSHR #^M ; Save across unlock JSB G^SCH$IOUNLOCK ; Unlock the IO database POPR #^M ; Restore IRP BRB 100$ ; Passed the test... 90$: PUSHR #^M ; Save across unlock JSB G^SCH$IOUNLOCK ; Unlock the IO database POPR #^M ; Restore the return status BRW 140$ 100$: BBC #LDIO_V_REPLACE,- ; Replace drive? IRP$L_EXTEND(R3),110$ MOVL UCB$L_LD_PDUCB(R5),R2 ; Get physical UCB MOVZWL #SS$_DEVFOREIGN,R0 ; Assume foreign mounted status BBS #DEV$V_FOR,- ; Check if foreign mounted UCB$L_DEVCHAR(R2),140$ MOVZWL #SS$_DEVMOUNT,R0 ; Assume mounted status BBS #DEV$V_MNT,- ; Check if device is mounted UCB$L_DEVCHAR(R2),140$ MOVZWL #SS$_DEVALLOC,R0 ; Assume allocated TSTL UCB$L_LOCKID(R2) ; Any lock for device? BNEQ 140$ ; Yes, error exit BRW 130$ ; Continue 110$: IFRD #4,P2(AP),120$ ; Check if size readable MOVZWL #SS$_ACCVIO,R0 ; Access violation BRB 140$ ; Abort 120$: BSBW LD_SET_GEOMETRY ; Fill in geometry information BLBC R0,140$ ; Quit on error MOVL P1(AP),R9 ; Get the SBK address MOVL SBK$L_FCB(R9),R9 ; Get the FCB address 130$: BSBW LD_MAKE_FILE_RESNAM ; Create file resourcename BSBW LD_MAKE_DEV_RESNAM ; Create device resourcename JMP G^EXE$QIODRVPKT ; Do the rest in the start I/O 140$: JMP G^EXE$ABORTIO ; And abort the I/O .SBTTL LD_SET_GEOMETRY, Setup pseudo device geometry ;+++ ; LD_SET_GEOMETRY, Setup pseudo device geometry ; ; Functional description: ; ; This routine will set the geometry for the pseudo device. ; ; Inputs: ; ; R0-R2 - scratch registers ; R3 - address of the IRP (I/O request packet) ; R4 - address of the PCB (process control block) ; R5 - address of the UCB (unit control block) ; R9-R11 - scratch registers ; AP - address of the 1st function dependent QIO parameter ; P1(AP) - Address of the SBK block (Normal connect) ; P2(AP) - Size of disk, if 0 allocated size of FCB will be used ; P3(AP) - Number of Tracks ; P4(AP) - Number of Sectors ; P5(AP) - Number of Cylinders ; ; Outputs: ; ; R0 - status ; ; The routine must preserve all registers except R0-R2, and ; R9-R11. ; ;-- UNIVERSAL_SYMBOL LD_SET_GEOMETRY ;LD_SET_GEOMETRY: MOVL P1(AP),R2 ; Get SBK address MOVL SBK$L_FCB(R2),R0 ; Get the FCB address MOVL FCB$L_EFBLK(R0),R0 ; Get maximum number of blocks MOVL R0,R2 ; Also in R2 MOVL P2(AP),R1 ; Get the size BNEQ 10$ ; Available, check it MOVL R0,R1 ; Use allocated size BRB 20$ 10$: CMPL R1,R0 ; Not too big? BLEQU 20$ ; Valid, use it MOVZWL #SS$_ILLBLKNUM,R0 ; Illegal lbn BRW 120$ ; ; At this point R1 is the maximum logical blocknumber ; 20$: MOVL P3(AP),R9 ; Get number of tracks MOVL P4(AP),R10 ; Get number of sectors MOVL P5(AP),R11 ; Get number of cylinders BISL3 R9,R10,R0 ; Check for all zeroes BISL2 R11,R0 BEQL 80$ ; All zero, use own algorithm ; ; At least one of (TRACKS,SECTORS,CYLINDERS) was specified ; Make sure they are all at least 1 and that they fit in the UCB. ; TSTL R9 ; Tracks zero? BNEQ 30$ ; No INCL R9 ; Make 1 30$: CMPL R9,#256 ; Within bounds? BGEQU 70$ ; No TSTL R10 ; Sectors zero? BNEQ 40$ ; No INCL R10 ; Make 1 40$: CMPL R10,#256 ; Within bounds? BGEQU 70$ ; No TSTL R11 ; Cylinders zero? BNEQ 50$ ; No INCL R11 ; Make one 50$: CMPL R11,#65536 ; Within bounds? BGEQU 70$ ; No MULL3 R9,R10,R0 ; Calculate total size needed MULL2 R11,R0 CMPL R0,R2 ; Not past allocated blocks? BGTRU 70$ ; Not valid MOVB R9,UCB$B_TRACKS(R5) ; Setup tracks MOVB R10,UCB$B_SECTORS(R5) ; Setup sectors MOVW R11,UCB$W_CYLINDERS(R5) ; Setup cylinders MOVL P2(AP),R1 ; Get the size BNEQ 60$ ; Something specified MOVL R0,R1 ; Setup new size as dictated ; by T/S/C ; ; Final check: see if the maximum block specified either by the user or by ; the filesize is less or equal to the size specified by the number of ; tracks/sectors/cylinders. ; 60$: CMPL R1,R0 BLEQU 110$ 70$: MOVZWL #SS$_BADPARAM,R0 ; Bad geometry parameter BRW 145$ 80$: MOVB #1,UCB$B_TRACKS(R5) ; Setup for 1 track CMPL R1,#256 ; Smaller then 256 blocks ? BGEQ 90$ ; No, next check MOVB R1,UCB$B_SECTORS(R5) ; File size sectors MOVW #1,UCB$W_CYLINDERS(R5) ; 1 cylinder BRB 110$ ; Common code path 90$: CMPL R1,#65536 ; Smaller then 65536 blocks ? BGEQ 120$ ; No, next check MOVB #2,UCB$B_SECTORS(R5) ; 2 sectors ASHL #-1,R1,R1 ; Divide file size by 2 MOVW R1,UCB$W_CYLINDERS(R5) ; And enter as cylinders ASHL #1,R1,R1 ; Round to a 2 block boundary 110$: MOVL R1,UCB$L_MAXBLOCK(R5) ; Set max. nr of blocks BRW 140$ 120$: MOVL R1,UCB$L_MAXBLOCK(R5) ; Save number of blocks ASSUME UCB$B_SECTORS+1 EQ UCB$B_TRACKS MOVW #^X604,UCB$B_SECTORS(R5) ; Fill in dummy values for sector ; and track fields ; ; Calculate a new value for cylinders to ensure that the product is greater ; than maxblock. Use the same algorithm as DUDRIVER so that disks served ; in a cluster appear to have the same geometry on every node. ; 130$: MOVZBL UCB$B_TRACKS(R5),R1 ; Get number of tracks MOVZBL UCB$B_SECTORS(R5),R0 ; Get number of sectors MULL R1,R0 ; Multiply MOVL UCB$L_MAXBLOCK(R5),R1 ; Get number of blocks CLRL R2 ; Prepare for extended divide EDIV R0,R1,R0,R1 ; Calculate number of cylinders, remainder CMPL R0,#65534 ; Is cylinder number legal? BGTR 150$ ; If so go try a crude fix MOVW R0,UCB$W_CYLINDERS(R5) ; Save number of cylinders TSTL R1 ; Zero remainder? BEQL 140$ ; Branch if so INCW UCB$W_CYLINDERS(R5) ; Otherwise, increment cylinders ; (tracks * sectors * cylinders must ; be >= maxblock) 140$: MOVZWL #SS$_NORMAL,R0 145$: RSB ; ; Out of line code to handle really large geometries to the limits of what ; VMS can currently do. ; ; First method can waste 1024 blocks. ; Second method can waste 9216 blocks. Third can waste 65025. This is rather ; crude, but will at least allow the structure to be used. ; ; Note that we test against 65534 cylinders because cyl count may be ; incremented if maxblock field is more than trk*sect*cyl. This guarantees ; the cylinder field will be legal and thus the device will be usable, other ; things being equal. Note that because we accept devices with zeroes ; in trk or sect and will have filled in ^X604 above, we have four fake ; geometries in all: ; 6 x 4 x n ; 32 x 32 x n ; 96 x 96 x n ; 255 x255 x n ; ; This means geometry based loss of up to 23, 1023, 9215, or 65024 ; blocks as the device gets bigger, but the device will be usable over ; most of its surface. ; 150$: MOVB #32,UCB$B_TRACKS(R5) MOVB #32,UCB$B_SECTORS(R5) CMPL UCB$L_MAXBLOCK(R5),- ; Does 32 by 32 by n work? #<65534*32*32> BLSSU 130$ MOVB #96,UCB$B_TRACKS(R5) ; Set 96 by 96 by n geom MOVB #96,UCB$B_SECTORS(R5) CMPL UCB$L_MAXBLOCK(R5),- ; Be sure disk not too big #<65534*96*96> BLSSU 130$ ; Redo computation if ok ; ; If disk is over 300Gb, try to allow 2TB ; ; This should be adequate for any currently supported disk size (i.e., ; with a 32-bit block number a la SCSI-2), losing at most a small bit of ; capacity but allowing disk access for most of the surface. ; MOVB #255,UCB$B_TRACKS(R5) ; Go for broke MOVB #255,UCB$B_SECTORS(R5) BRW 130$ ; This is as big as we can go. .SBTTL LD_SET_SEED, Set seed unit number FDT routine ;+++ ; LD_SET_SEED, Set seed unit number FDT routine ; ; Functional description: ; ; This routine sets the seed number to the specified value ; ; Inputs: ; ; R0-R2 - scratch registers ; R3 - address of the IRP (I/O request packet) ; R4 - address of the PCB (process control block) ; R5 - address of the UCB (unit control block) ; R6 - address of the CCB (channel control block) ; R7 - bit number of the I/O function code ; R8 - address of the FDT table entry for this routine ; R9-R11 - scratch registers ; P1(AP) - seed value ; ; Outputs: ; ; The routine must preserve all registers except R0-R2, and ; R9-R11. ; ;-- UNIVERSAL_SYMBOL LD_SET_SEED ;LD_SET_SEED: MOVL P1(AP),R0 ; Get value CMPL R0,#9999 ; Limit range BGTRU 10$ MOVL UCB$L_DDB(R5),R1 ; Get address of port DDB MOVL DDB$L_UCB(R1),R1 ; Get address of UNIT 0 UCB MOVW R0,UCB$W_UNIT_SEED(R1) ; Setup seed MOVL #SS$_NORMAL,R0 ; Success JMP G^EXE$FINISHIOC ; Finish the I/O 10$: MOVL #SS$_BADPARAM,R0 ; Trouble JMP G^EXE$ABORTIO ; Abort the I/O .SBTTL LD_SET_ALLOCLASS, Set allocation class FDT routine ;+++ ; LD_SET_ALLOCLASS, Set allocation class FDT routine ; ; Functional description: ; ; This routine is the FDT routine to set the allocation class for ; the LD devices. This can only be done when no other LD devices ; are active. If that's not the case and the specified allocation ; class is the same as the one already set we will ignore it and ; return success. ; ; Inputs: ; ; R0-R2 - scratch registers ; R3 - address of the IRP (I/O request packet) ; R4 - address of the PCB (process control block) ; R5 - address of the UCB (unit control block) ; R6 - address of the CCB (channel control block) ; R7 - bit number of the I/O function code ; R8 - address of the FDT table entry for this routine ; R9-R11 - scratch registers ; P1(AP) - allocation class value ; ; Outputs: ; ; The routine must preserve all registers except R0-R2, and ; R9-R11. ; ;-- UNIVERSAL_SYMBOL LD_SET_ALLOCLASS ;LD_SET_ALLOCLASS: MOVL P1(AP),R2 ; Get value CMPL R2,#255 ; Limit range BGTRU 20$ ; Out of range MOVL UCB$L_DDB(R5),R1 ; Get address of port DDB CMPL R2,DDB$L_ALLOCLS(R1) ; Same number? BEQL 10$ ; Ok, accept it TSTW LD_REFCNT ; Already someone connected? BNEQ 30$ ; Yes, not allowed FORKLOCK LOCK=UCB$B_FLCK(R5),- ; Synchronize SAVIPL=-(SP),- PRESERVE=NO MOVL R2,DDB$L_ALLOCLS(R1) ; Setup allocation class in DDB MOVL UCB$L_CDDB(R5),R1 ; Get address CDDB MOVL R2,CDDB$L_ALLOCLS(R1) ; Setup allocation class in CDDB FORKUNLOCK LOCK=UCB$B_FLCK(R5),- ; Release lock NEWIPL=(SP)+,- ; Return to old ipl PRESERVE=NO 10$: MOVL #SS$_NORMAL,R0 ; Success JMP G^EXE$FINISHIOC ; Finish the I/O 20$: MOVL #SS$_BADPARAM,R0 ; Trouble BRB 40$ 30$: MOVZWL #SS$_UNSAFE,R0 ; No devices may be connected 40$: JMP G^EXE$ABORTIO ; Abort the I/O .SBTTL LD_DISABLE_TRACE, Disable tracing FDT routine ;+++ ; LD_DISABLE_TRACE, Disable tracing FDT routine ; ; Functional description: ; ; This routine disables tracing and deallocates the tracebuffer ; ; Inputs: ; ; R0-R2 - scratch registers ; R3 - address of the IRP (I/O request packet) ; R4 - address of the PCB (process control block) ; R5 - address of the UCB (unit control block) ; R6 - address of the CCB (channel control block) ; R7 - bit number of the I/O function code ; R8 - address of the FDT table entry for this routine ; R9-R11 - scratch registers ; ; Outputs: ; ; The routine must preserve all registers except R0-R2, and ; R9-R11. ; ;-- UNIVERSAL_SYMBOL LD_DISABLE_TRACE ;LD_DISABLE_TRACE: MOVL UCB$L_LD_TRCBUF(R5),R0 ; Get buffer pointer BNEQ 10$ ; Set MOVZWL #SS$_NODATA,R0 ; Trace not set BRW 30$ 10$: LOCK_TRACE ACCESS=WRITE ; Lock trace mutex CLRL UCB$L_LD_TRCBUF(R5) ; Zero pointer MOVL UCB$L_LD_TRCBUFALLOCSIZ(R5),R1 ; Get allocated size SUBL2 #12,R0 ; Point to real start JSB G^EXE$DEANONPGDSIZ ; Release memory MOVL UCB$L_LD_TRCBUFOWN(R5),R2 ; Get buffer owner ADDL3 #12,UCB$L_LD_TRCBUFSIZ(R5),R0 ; Calculate size we requested CMPL R2,IRP$L_PID(R3) ; Did we own it ourselves? BNEQ 20$ ; Yes JSB G^EXE$CREDIT_BYTCNT_BYTLM ; Return quota BRB 25$ 20$: PUSHL R4 MOVL R0,R1 ; Amount to return MOVL R2,R4 ; Process to credit BSBW LD_RETURN_QUOTA ; Credit correct process ; Ignore errors (Proc. may have gone away) MOVL (SP)+,R4 25$: UNLOCK_TRACE ; Unlock trace mutex MOVZWL #SS$_NORMAL,R0 ; Success JMP G^EXE$FINISHIOC ; Finish the I/O 30$: JMP G^EXE$ABORTIO ; And abort the I/O .SBTTL LD_ENABLE_TRACE, Enable tracing FDT routine ;+++ ; LD_ENABLE_TRACE, Enable tracing FDT routine ; ; Functional description: ; ; This routine allocates a tracebuffer and enables tracing ; ; Inputs: ; ; R0-R2 - scratch registers ; R3 - address of the IRP (I/O request packet) ; R4 - address of the PCB (process control block) ; R5 - address of the UCB (unit control block) ; R6 - address of the CCB (channel control block) ; R7 - bit number of the I/O function code ; R8 - address of the FDT table entry for this routine ; R9-R11 - scratch registers ; ; Outputs: ; ; The routine must preserve all registers except R0-R2, and ; R9-R11. ; ;-- UNIVERSAL_SYMBOL LD_ENABLE_TRACE ;LD_ENABLE_TRACE: MOVZWL #SS$_TOOMUCHDATA,R0 ; Assume trace already set MOVL UCB$L_LD_TRCBUF(R5),R2 ; Get buffer pointer BNEQ 10$ ; Already set MOVZWL #SS$_BADPARAM,R0 ; Assume bad parameter MOVL P1(AP),R1 ; Get size (in entries) BEQL 10$ ; Nothing?? MULL2 #LDTRCENT_K_LENGTH,R1 ; Length of LDTRCENT * #of entries MOVL R1,UCB$L_LD_TRCBUFSIZ(R5) ; Save used size ; ; Add packet overhead (we need a packet of minimal FKB$K_LENGTH bytes to be ; able to deallocate the packet as a forkblock when the driver is reloaded ; ADDL2 #12,R1 ; Add packet overhead BISL2 #^X80000000,R1 ; Avoid check against MAXBUF JSB G^EXE$DEBIT_BYTCNT_BYTLM_NW ; Check quota BLBS R0,20$ ; Enuf left MOVZWL #SS$_EXBYTLM,R0 ; Out of bytlim quota 10$: BRW 30$ ; Get out 20$: JSB G^EXE$ALONONPAGED ; Get memory BLBC R0,10$ ; Error ADDL2 #12,R2 ; Leave some room ; LOCK_TRACE ACCESS=WRITE ; Lock trace mutex MOVL IRP$L_PID(R3),- ; Register owner UCB$L_LD_TRCBUFOWN(R5) CLRL UCB$L_LD_TRCWRAP(R5) ; Not yet wrapped CLRL UCB$L_LD_TRCLOST(R5) ; Nothing lost yet MOVL R1,UCB$L_LD_TRCBUFALLOCSIZ(R5) ; Setup buffer size MOVL R2,UCB$L_LD_TRCBUF(R5) ; Setup buffer MOVL R2,UCB$L_LD_TRCBUFPTR(R5) ; Init pointer UNLOCK_TRACE ; Unlock trace mutex MOVZWL #SS$_NORMAL,R0 JMP G^EXE$FINISHIOC ; Finish the I/O 30$: JMP G^EXE$ABORTIO ; And abort the I/O .SBTTL LD_RESET_TRACE, Reset tracebuffer FDT routine ;+++ ; LD_RESET_TRACE, Reset tracebuffer FDT routine ; ; Functional description: ; ; This routine resets the tracebuffer pointer ; ; Inputs: ; ; R0-R2 - scratch registers ; R3 - address of the IRP (I/O request packet) ; R4 - address of the PCB (process control block) ; R5 - address of the UCB (unit control block) ; R6 - address of the CCB (channel control block) ; R7 - bit number of the I/O function code ; R8 - address of the FDT table entry for this routine ; R9-R11 - scratch registers ; ; Outputs: ; ; The routine must preserve all registers except R0-R2, and ; R9-R11. ; ;-- UNIVERSAL_SYMBOL LD_RESET_TRACE ;LD_RESET_TRACE: MOVZWL #SS$_NODATA,R0 ; Assume not yet set MOVL UCB$L_LD_TRCBUF(R5),R2 ; Get buffer pointer BEQL 10$ ; Not there LOCK_TRACE ACCESS=WRITE ; Lock trace mutex CLRL UCB$L_LD_TRCWRAP(R5) ; Not yet wrapped CLRL UCB$L_LD_TRCLOST(R5) ; Nothing lost yet MOVL R2,UCB$L_LD_TRCBUFPTR(R5) ; Init pointer UNLOCK_TRACE ; Unlock trace mutex MOVZWL #SS$_NORMAL,R0 JMP G^EXE$FINISHIOC ; Finish the I/O 10$: JMP G^EXE$ABORTIO ; And abort the I/O .SBTTL LD_ENABLE_PROTECT, Enable write protect ;+++ ; LD_ENABLE_PROTECT, Enable write protect ; ; Functional description: ; ; This routine enables write-protection on the logical disk. ; ; Inputs: ; ; R0-R2 - scratch registers ; R3 - address of the IRP (I/O request packet) ; R4 - address of the PCB (process control block) ; R5 - address of the UCB (unit control block) ; R6 - address of the CCB (channel control block) ; R7 - bit number of the I/O function code ; R8 - address of the FDT table entry for this routine ; R9-R11 - scratch registers ; AP - address of the 1st function dependent QIO parameter ; ; Outputs: ; ; The routine must preserve all registers except R0-R2, and ; R9-R11. ; ;-- UNIVERSAL_SYMBOL LD_ENABLE_PROTECT ;LD_ENABLE_PROTECT: ; Enable protect FDT routine BISL2 #DEV$M_SWL,UCB$L_DEVCHAR(R5) ; General bit BISW2 #UCB$M_LD_PROTECT,- ; Specific bit UCB$W_LD_FLAGS(R5) MOVZWL #SS$_NORMAL,R0 JMP G^EXE$FINISHIOC .SBTTL LD_DISABLE_PROTECT, Disable write protect ;+++ ; LD_DISABLE_PROTECT, Disable write protect ; ; Functional description: ; ; This routine disables write-protection on the logical disk. ; ; Inputs: ; ; R0-R2 - scratch registers ; R3 - address of the IRP (I/O request packet) ; R4 - address of the PCB (process control block) ; R5 - address of the UCB (unit control block) ; R6 - address of the CCB (channel control block) ; R7 - bit number of the I/O function code ; R8 - address of the FDT table entry for this routine ; R9-R11 - scratch registers ; AP - address of the 1st function dependent QIO parameter ; ; Outputs: ; ; The routine must preserve all registers except R0-R2, and ; R9-R11. ; ;-- UNIVERSAL_SYMBOL LD_DISABLE_PROTECT ;LD_DISABLE_PROTECT: ; Disable protect FDT routine BICL2 #DEV$M_SWL,UCB$L_DEVCHAR(R5) ; General bit BICW2 #UCB$M_LD_PROTECT,- ; Specific bit UCB$W_LD_FLAGS(R5) MOVZWL #SS$_NORMAL,R0 JMP G^EXE$FINISHIOC .SBTTL LD_DEALLOC_TRCBUF, Trace buffer release ;+++ ; LD_DEALLOC_TRCBUF, Trace buffer release ; ; Functional description: ; ; This routine deallocates the tracebuffer if we disconnect ; from the physical device (in case of cloned device) or in ; case of driver reload. ; ; This routine may be called from any IPL (needed in case we're called ; from LD_DRV_UNLOAD which runs at IPL$_POWER). ; ; Inputs: ; ; R4 - address of the PCB (process control block) ; - 0 if called from LD_DRV_UNLOAD ; R5 - address of the UCB (unit control block) ; ; Outputs: ; ; The routine must preserve all registers except R0-R2, and ; R9-R11. ; ;-- UNIVERSAL_SYMBOL LD_DEALLOC_TRCBUF ;LD_DEALLOC_TRCBUF: MOVL UCB$L_LD_TRCBUF(R5),R0 ; Get address BNEQ 10$ ; Buffer available BRW 60$ 10$: TSTL R4 ; PCB available? BEQL 20$ ; No, no need to synchronize ; (Called by LD_DRV_UNLOAD) LOCK_TRACE ACCESS=WRITE ; Lock trace buffer CLRL UCB$L_LD_TRCBUF(R5) ; Zero pointers MOVL UCB$L_LD_TRCBUFOWN(R5),R1 ; Get owner UNLOCK_TRACE BRB 30$ 20$: CLRL UCB$L_LD_TRCBUF(R5) ; Zero pointers MOVL UCB$L_LD_TRCBUFOWN(R5),R1 ; Get owner 30$: MOVQ R3,-(SP) ; Save R3 + R4 MOVL UCB$L_LD_TRCBUFALLOCSIZ(R5),R3 ; Get size SUBL2 #12,R0 ; Account for overhead ASSUME FKB$B_FLCK EQ FKB$B_TYPE+1 MOVW #>,- ; and proper spinlock FKB$B_TYPE(R0) ; for this to be a fork block PUSHL R5 ; Save UCB MOVL R0,R5 ; Copy address PUSHAB 40$ ; Set up return address FORK ; Create fork MOVL R5,R0 ; Deallocate the block MOVL FKB$L_FR3(R5),R1 ; Get size JMP G^EXE$DEANONPGDSIZ ; Get out 40$: MOVL (SP)+,R5 ; Restore UCB pointer MOVQ (SP)+,R3 ; Restore R3 + R4 TSTL R4 ; PCB available? BEQL 60$ ; No, no one to credit ADDL3 #12,UCB$L_LD_TRCBUFSIZ(R5),R0 ; Calculate size we requested CMPL PCB$L_PID(R4),R1 ; Are we the owner? BNEQ 50$ ; No JSB G^EXE$CREDIT_BYTCNT_BYTLM ; Return quota BRB 60$ 50$: PUSHL R4 MOVL R1,R4 ; Owner pid MOVL R0,R1 ; Amount to return BSBW LD_RETURN_QUOTA ; Credit user MOVL (SP)+,R4 60$: RSB .SBTTL LD_DEALLOC_WATCHBUF, Watch buffer release ;+++ ; LD_DEALLOC_WATCHBUF, Watch buffer release ; ; Functional description: ; ; This routine deallocates all watchbuffers if we disconnect ; from the physical device (in case of cloned device) or in ; case of driver reload. ; ; This routine may be called from any IPL (needed in case we're called ; from LD_DRV_UNLOAD which runs at IPL$_POWER). ; ; Inputs: ; ; R4 - address of the PCB (process control block) ; - 0 if called from LD_DRV_UNLOAD ; R5 - address of the UCB (unit control block) ; ; Outputs: ; ; The routine must preserve all registers except R0-R2, and ; R9-R11. ; ;-- UNIVERSAL_SYMBOL LD_DEALLOC_WATCHBUF ;LD_DEALLOC_WATCHBUF: TSTL R4 ; PCB available? BEQL 50$ ; No, no need to synchronize FORKLOCK LOCK=UCB$B_FLCK(R5),- ; Raise to Fork level SAVIPL=-(SP) 10$: REMQUE @UCB$L_LD_WATCHQFL(R5),R0 ; Get entry BVS 40$ ; None there MOVL LDWATCH_L_PID(R0),R1 ; Get owner PUSHL R0 ; Save buffer address CMPL R1,PCB$L_PID(R4) ; Ours? BNEQ 20$ ; No MOVL #LDWATCHENT_K_LENGTH,R0 ; Amount to return JSB G^EXE$CREDIT_BYTCNT_BYTLM ; Return quota BRB 30$ 20$: PUSHL R4 MOVL R1,R4 ; Get pid MOVL #LDWATCHPT_K_LENGTH,R1 ; Amount to return BSBW LD_RETURN_QUOTA ; Return to correct process MOVL (SP)+,R4 30$: MOVL (SP)+,R0 ; Restore bufferpointer JSB G^COM$DRVDEALMEM ; Dealloc memory BRB 10$ 40$: FORKUNLOCK LOCK=UCB$B_FLCK(R5),- ; Release lock NEWIPL=(SP)+,- ; Return to old ipl PRESERVE=NO BRB 60$ 50$: REMQUE @UCB$L_LD_WATCHQFL(R5),R0 ; Get entry BVS 60$ ; None there JSB G^COM$DRVDEALMEM ; Dealloc memory BRB 50$ 60$: RSB .SBTTL LD_ENABLE_WATCH, Enable watchpoints FDT routine ;+++ ; LD_ENABLE_WATCH, Enable watchpoints FDT routine ; ; Functional description: ; ; This is the FDT routine to enable watchpoints ; ; Inputs: ; ; R0-R2 - scratch registers ; R3 - address of the IRP (I/O request packet) ; R4 - address of the PCB (process control block) ; R5 - address of the UCB (unit control block) ; R6 - address of the CCB (channel control block) ; R7 - bit number of the I/O function code ; R8 - address of the FDT table entry for this routine ; R9-R11 - scratch registers ; P1(AP) - Address of watchpt structures ; P2(AP) - Count of watchpt structures ; ; Outputs: ; ; The routine must preserve all registers except R0-R2, and ; R9-R11. ; ;-- UNIVERSAL_SYMBOL LD_ENABLE_WATCH ;LD_ENABLE_WATCH: CLRL R9 ; Nothing charged yet MOVL P1(AP),R0 ; Get buffer address MULL3 P2(AP),#LDWATCHPT_K_LENGTH,R1 ; Get buffer length BEQL 30$ ; Nothing there MOVQ R0,R10 ; Save address and size JSB G^EXE$WRITECHK ; Check buffer for read access MOVL P2(AP),R2 ; Get number of entries MOVL R2,IRP$L_OBCNT(R3) ; Save it ; ; Validate inputbuffer ; 10$: BBC #LDWATCHPT_V_FILE,- ; Check for virtual file mode LDWATCHPT_W_FLAGS(R0),60$ TSTL LDWATCHPT_L_LBN(R0) ; Zero not allowed for VBN BEQL 40$ PUSHR #^M MOVL LDWATCHPT_L_SBK(R0),R0 ; Get the SBK address MOVL #SBK$K_LENGTH,R1 ; Get the SBK length JSB G^EXE$WRITECHK ; Check if SBK is readable MOVL SBK$L_FCB(R0),R1 ; Get the FCB address POPR #^M BGEQ 20$ ; Must be system address CMPB FCB$B_TYPE(R1),#DYN$C_FCB ; Check if it is a FCB BNEQ 20$ ; No, return error TSTW FCB$W_REFCNT(R1) ; File open? BEQL 20$ ; No, error CMPL LDWATCHPT_L_LBN(R0),- ; Check if not too big FCB$L_FILESIZE(R1) BGTRU 40$ ; Too big, quit MOVL FCB$L_WLFL(R1),R1 ; Get any wcb CMPL R5,WCB$L_ORGUCB(R1) ; File must be on our device BNEQ 50$ ; Other device, quit BBC #WCB$V_COMPLETE,- ; Must be completely mapped WCB$B_ACCESS(R1),20$ MOVL UCB$L_VCB(R5),R1 ; Get VCB TSTW VCB$W_RVN(R1) ; Relative volume number BEQL 70$ ; Branch if a not a volume set MOVZWL #SS$_NOTVOLSET,R0 ; File watchpoint on volumeset not allowed BRW 130$ 20$: MOVZWL #SS$_FORMAT,R0 ; FCB invalid or not completely mapped BRW 130$ 30$: MOVZWL #SS$_BADPARAM,R0 ; Bad parameter BRW 130$ 40$: MOVZWL #SS$_ILLBLKNUM,R0 ; Illegal lbn BRW 130$ 50$: MOVZWL #SS$_DEVREQERR,R0 ; File not on our device BRW 130$ 60$: BBS #LDWATCHPT_V_NOLBN,- ; Check for non-transfer function LDWATCHPT_W_FLAGS(R0),70$ CMPL LDWATCHPT_L_LBN(R0),- ; Check if not too big UCB$L_MAXBLOCK(R5) BGTRU 40$ ; Too big, quit 70$: MOVW LDWATCHPT_W_ACTION(R0),R1 ; Get action CMPW R1,#WATCH_ACTION_MAX ; Legal action ? BGTRU 30$ ; No IFPRIV CMKRNL,80$ ; Sufficient to do everything CMPW R1,#WATCH_ACTION_CRASH ; Crash system? BEQL 120$ ; Yes, no priv CMPL IRP$L_PID(R3),UCB$L_PID(R5) ; Do we own the device? BNEQ 110$ ; No, action not allowed ; ; We charge seperately for every packet. We need to do this because ; EXE$DEBIT_BYTCNT_BYTLM_NW rounds the size up to 64 bytes. If we would ; charge for it in one chunk we would get problems crediting the buffers ; which we need to do one by one. ; 80$: PUSHL R0 ; Save, destroyed later MOVL #LDWATCHENT_K_LENGTH,R1 ; Bytecount to charge ADDL2 R1,R9 ; Keep track of total charged JSB G^EXE$DEBIT_BYTCNT_BYTLM_NW ; Check quota BLBS R0,90$ ; Enuf left ADDL2 #4,SP ; Adjust stack MOVZWL #SS$_EXBYTLM,R0 ; Out of bytlim quota BRB 130$ 90$: ADDL3 #LDWATCHPT_K_LENGTH,(SP)+,R0 ; Point to next input entry DECL R2 ; Next packet BLEQ 100$ BRW 10$ 100$: MOVQ R10,R0 ; Get buffer address and bytecount BSBW LD_GETBUF ; Get buffer BLBC R0,130$ ; Error JMP G^EXE$QIODRVPKT ; Finish in start I/O 110$: MOVZWL #SS$_NOPRIV,R0 ; Priv or ownership required BRB 130$ 120$: MOVZWL #SS$_NOCMKRNL,R0 ; CMKRNL priv required 130$: TSTL R9 ; Any BYTCNT charged? BEQL 140$ ; No PUSHL R0 ; Save status MOVL R9,R0 ; Amount to return JSB G^EXE$CREDIT_BYTCNT_BYTLM ; Return quota MOVL (SP)+,R0 ; Restore status 140$: JMP G^EXE$ABORTIO ; Abort the I/O .SBTTL LD_DISABLE_WATCH, Disable watchpoints FDT routine ;+++ ; LD_DISABLE_WATCH, Disable watchpoints FDT routine ; ; Functional description: ; ; This is the FDT routine to disable watchpoints ; ; Inputs: ; ; R0-R2 - scratch registers ; R3 - address of the IRP (I/O request packet) ; R4 - address of the PCB (process control block) ; R5 - address of the UCB (unit control block) ; R6 - address of the CCB (channel control block) ; R7 - bit number of the I/O function code ; R8 - address of the FDT table entry for this routine ; R9-R11 - scratch registers ; P1(AP) - Address of watchpt structures ; P2(AP) - Count of watchpt structures ; ; Outputs: ; ; The routine must preserve all registers except R0-R2, and ; R9-R11. ; ;-- UNIVERSAL_SYMBOL LD_DISABLE_WATCH ;LD_DISABLE_WATCH: CLRL IRP$L_OBCNT(R3) ; Zero count MOVL P2(AP),R1 ; Something specified? BEQL 10$ ; Remove all MOVL R1,IRP$L_OBCNT(R3) ; Save it MULL2 #LDWATCHPT_K_LENGTH,R1 ; Get buffer length MOVL P1(AP),R0 ; Get buffer address JSB G^EXE$WRITECHK ; Check buffer for read access BSBW LD_GETBUF ; Get systembuffer BLBC R0,20$ ; Error 10$: JMP G^EXE$QIODRVPKT ; Finish in start I/O 20$: JMP G^EXE$ABORTIO ; Abort I/O .SBTTL LD_GET_WATCH, Get watchpoint info FDT routine ;+++ ; LD_GET_WATCH, Get watchpoint info FDT routine ; ; Functional description: ; ; This routine retrieves info about current watchpoints, ; as well as the suspended process list. ; ; Inputs: ; ; R0-R2 - scratch registers ; R3 - address of the IRP (I/O request packet) ; R4 - address of the PCB (process control block) ; R5 - address of the UCB (unit control block) ; R6 - address of the CCB (channel control block) ; R7 - bit number of the I/O function code ; R8 - address of the FDT table entry for this routine ; R9-R11 - scratch registers ; P1(AP) - Address of watchpt structure buffer ; P2(AP) - Size in bytes of watchpt buffer ; ; Outputs: ; ; The routine must preserve all registers except R0-R2, and ; R9-R11. ; ;-- UNIVERSAL_SYMBOL LD_GET_WATCH ;LD_GET_WATCH: BBS #LDIO_V_INQUIRE,- ; Return list size? IRP$L_EXTEND(R3),10$ MOVL P1(AP),R0 ; Get buffer address MOVL P2(AP),R1 ; Get buffer length JSB G^EXE$READCHK ; Check buffer for write access BSBW LD_GETBUF1 ; Get systembuffer BLBC R0,20$ ; Error 10$: JMP G^EXE$QIODRVPKT ; Finish in start I/O 20$: JMP G^EXE$ABORTIO ; Abort the I/O .SBTTL LD_RESUME_WATCH, Resume suspended watchpoint threads FDT routine ;+++ ; LD_RESUME_WATCH, Resume suspended watchpoint threads FDT routine ; ; Functional description: ; ; This routine resumes threads which were suspended when a ; 'suspend' watchpoint was hit ; ; Inputs: ; ; R0-R2 - scratch registers ; R3 - address of the IRP (I/O request packet) ; R4 - address of the PCB (process control block) ; R5 - address of the UCB (unit control block) ; R6 - address of the CCB (channel control block) ; R7 - bit number of the I/O function code ; R8 - address of the FDT table entry for this routine ; R9-R11 - scratch registers ; P1(AP) - Address of watchpt structures ; P2(AP) - Count of watchpt structures ; ; Outputs: ; ; The routine must preserve all registers except R0-R2, and ; R9-R11. ; ;-- UNIVERSAL_SYMBOL LD_RESUME_WATCH ;LD_RESUME_WATCH: MOVL P1(AP),R0 ; Get buffer address MOVL P2(AP),R1 ; Get number of entries MOVL R1,IRP$L_OBCNT(R3) ; Save number of entries BEQL 10$ ; Nothing there MULL2 #LDWATCHPT_K_LENGTH,R1 ; Get buffer length JSB G^EXE$WRITECHK ; Check buffer for read access BSBW LD_GETBUF ; Get systembuffer BLBC R0,20$ ; Error 10$: JMP G^EXE$QIODRVPKT ; Finish in start I/O 20$: JMP G^EXE$ABORTIO ; Abort the I/O .SBTTL LD_GETBUF + LD_GETBUF1, Get and fill temporary buffer ;+++ ; LD_GETBUF, Get and fill temporary buffer ; LD_GETBUF1, Get temporary buffer ; ; Functional description: ; ; This routine allocates a buffer to use to get/send data to/from ; start I/O routines. The userbuffer data will be copied (LD_GETBUF ; only). ; ; Inputs: ; ; R0 - user buffer address ; R1 - user buffer bytecount ; R3 - address of the IRP (I/O request packet) ; R4 - address of the PCB (process control block) ; R5 - address of the UCB (unit control block) ; R6 - address of the CCB (channel control block) ; R7 - bit number of the I/O function code ; R8 - address of the FDT table entry for this routine ; R9-R11 - scratch registers ; ; Outputs: ; ; R0 - status ; R2 - sytem buffer address ; IRP$L_SVAPTE - systembuffer addres ; ; The routine must preserve all registers except R0-R2, and ; R9-R11. ;--- .ENABLE LSB UNIVERSAL_SYMBOL LD_GETBUF1 ;LD_GETBUF1: CLRL -(SP) ; Flag not to fill buffer BRB 10$ UNIVERSAL_SYMBOL LD_GETBUF ;LD_GETBUF: MOVZBL #1,-(SP) ; Flag to fill buffer 10$: MOVAB 12(R1),R1 ; Set size of system buffer PUSHR #^M JSB G^EXE$DEBIT_BYTCNT_ALO ; Allocate a system buffer BLBC R0,30$ ; Any quota errors ? POPR #^M MOVL R2,IRP$L_SVAPTE(R3) ; Save system buffer address MOVW R1,IRP$W_BOFF(R3) ; Save system buffer length MOVAB 12(R2),(R2) ; Insert address of data area MOVL R0,4(R2) ; Insert address of user buffer BLBC (SP),20$ ; Branch if no fill PUSHR #^M ; Destroyed by MOVC MOVC3 IRP$W_BCNT(R3),(R0),12(R2) ; Copy data from user- to systembuffer POPR #^M 20$: MOVZWL #SS$_NORMAL,R0 ; Okay BRB 40$ 30$: POPR #^M ; Error return 40$: ADDL2 #4,SP ; Adjust stack RSB .DISABLE LSB .SBTTL LD_MAKE_FILE_RESNAM, Form private resource name ;+++ ; LD_MAKE_FILE_RESNAM, Form private resource name ; ; Functional description: ; ; Form resourcename for lock to coordinate clusterwide access to ; logical disk file or replaced device ; ; For a file this resourcename consists of: ; ; 9 bytes: '$LOGDISK_' ; 1 byte: 1 for private mounted volume, ; 2 for system wide ; 12 bytes: volume lockname ; 3 bytes: 0 ; 1 word: FID ; 1 word: SEQ ; 1 word: RVN ; ; For a device this will be: ; ; 9 bytes: '$LOGDISK_' ; 1 byte: 0 ; 1 byte: physical devicename length ; 20 bytes: alloclass devicename ; ; Inputs: ; ; R3 IRP address ; R4 PCB address ; R5 UCB address ; R9 FCB address (connect 'file' only) ; ; Outputs:None. ; ; Implicit outputs: Resource name in UCB is written ; ;--- UNIVERSAL_SYMBOL LD_MAKE_FILE_RESNAM ;LD_MAKE_FILE_RESNAM: PUSHR #^M MOVAB UCB$T_LD_FILE_RESNAM(R5),R1 ; Setup pointer to resource name MOVZBL #31,(R1)+ ; Fill length MOVAB 4(R1),(R1)+ ; And address MOVL #^A/$LOG/,(R1)+ ; "$LOGDISK_" MOVL #^A/DISK/,(R1)+ MOVB #^A/_/,(R1)+ BBC #LDIO_V_REPLACE,- ; Replace drive? IRP$L_EXTEND(R3),10$ CLRB (R1)+ ; Filler PUSHL R4 ; Save PCB PUSHL R1 ; Save current pointer CLRB (R1)+ ; Length byte, filled in later JSB G^SCH$IOLOCKR ; Lock the IO database CLRQ (R1) ; Clear buffer CLRQ 8(R1) CLRL 16(R1) MOVZBL #20,R0 ; Buffer size MOVZBL #1,R4 ; DVI$_ALLDEVNAM PUSHL R5 ; Save UCB MOVL UCB$L_LD_PDUCB(R5),R5 ; Get the phys. disk UCB JSB G^IOC$CVT_DEVNAM ; Get alloclass devicename MOVL (SP)+,R5 ; Restore UCB MOVL (SP)+,R0 ; Recover pointer MOVB R1,(R0) ; Save length MOVL (SP)+,R4 ; Restore PCB JSB G^SCH$IOUNLOCK ; Unlock the IO database BRW 50$ 10$: MOVB #1,R2 ; Assume private mounted MOVL UCB$L_LD_PDUCB(R5),R0 ; Get physical device UCB TSTL UCB$L_PID(R0) ; Private mounted? BNEQ 20$ ; Yes INCB R2 ; System mounted, make it '2' 20$: MOVB R2,(R1)+ MOVL UCB$L_VCB(R0),R2 ; Get VCB TSTW VCB$W_RVN(R2) ; Relative volume number BEQL 30$ ; Branch if not a volume set MOVL VCB$L_RVT(R2),R0 ; Fetch RVT address BEQL 30$ ; Not there MOVAB RVT$T_VLSLCKNAM(R0),R2 ; Use this as lockname BRB 40$ 30$: MOVAB VCB$T_VOLCKNAM(R2),R2 ; Lock name from here 40$: MOVL (R2)+,(R1)+ ; Copy 12 bytes lockname MOVL (R2)+,(R1)+ MOVL (R2)+,(R1)+ CLRW (R1)+ ; Followed by 3 bytes 0 CLRB (R1)+ MOVW FCB$W_FID_NUM(R9),(R1)+ ; Insert File ID MOVW FCB$W_FID_SEQ(R9),(R1)+ ; SEQ MOVW FCB$W_FID_RVN(R9),(R1)+ ; RVN 50$: POPR #^M RSB ; C'est toute .SBTTL LD_MAKE_DEV_RESNAM, Form private resource name for device ;+++ ; LD_MAKE_DEV_RESNAM, Form private resource name for device ; ; Functional description: ; ; Form resourcename for lock to coordinate clusterwide access to ; logical disk device ; ; The resource name is constructed as follows: ; ; 9 bytes: '$LOGDISK_' ; 1 byte: 0 ; 1 byte: LD devicename length ; 20 bytes: alloclass devicename ; ; Inputs: ; ; R3 IRP address ; R4 PCB address ; R5 UCB address ; ; Outputs:None. ; ; Implicit outputs: Resource name in UCB is written ; ;--- UNIVERSAL_SYMBOL LD_MAKE_DEV_RESNAM ;LD_MAKE_DEV_RESNAM: PUSHR #^M MOVAB UCB$T_LD_DEV_RESNAM(R5),R1 ; Setup pointer to resource name MOVZBL #31,(R1)+ ; Fill length MOVAB 4(R1),(R1)+ ; And address MOVL #^A/$LOG/,(R1)+ ; "$LOGDISK_" MOVL #^A/DISK/,(R1)+ MOVB #^A/_/,(R1)+ CLRB (R1)+ ; Filler PUSHL R4 ; Save PCB PUSHL R1 ; Save current pointer CLRB (R1)+ ; Length byte, filled in later JSB G^SCH$IOLOCKR ; Lock the IO database CLRQ (R1) ; Clear buffer CLRQ 8(R1) CLRL 16(R1) MOVZBL #20,R0 ; Buffer size MOVZBL #1,R4 ; DVI$_ALLDEVNAM JSB G^IOC$CVT_DEVNAM ; Get alloclass devicename MOVL (SP)+,R0 ; Recover pointer MOVB R1,(R0) ; Save length MOVL (SP)+,R4 ; Restore PCB JSB G^SCH$IOUNLOCK ; Unlock the IO database POPR #^M RSB ; C'est toute .PAGE .SBTTL LD_FDT_DSE, Data secutiry erase fdt routine ;+++ ; LD_FDT_DSE, Data secutiry erase fdt routine ; ; Functional description: ; ; This is the FDT routine for the Data Security Erase operation. ; The byte count (P2) is stored in IRP$L_BCNT. The starting logical ; block (P3) is stored in IRP$L_MEDIA. Control is transfered to ; EXE$QIODRVPKT, thus queueing the I/O request to the driver's start ; I/O routine. ; ; Inputs: ; ; R0-R2 - scratch registers ; R3 - address of the IRP (I/O request packet) ; R4 - address of the PCB (process control block) ; R5 - address of the UCB (unit control block) ; R6 - address of the CCB (channel control block) ; R7 - bit number of the I/O function code ; R8 - address of the FDT table entry for this routine ; R9-R11 - scratch registers ; AP - address of the 1st function dependent QIO parameter ; P2(AP) - Byte count ; P3(AP) - Starting logical block ; ; Outputs: ; ; IRP$L_BCNT(R3) - Byte count ; IRP$L_MEDIA(R3) - Starting logical block ; ; The routine must preserve all registers except R0-R2, and ; R9-R11. ; ;-- UNIVERSAL_SYMBOL LD_FDT_DSE ;LD_FDT_DSE: ; Data security erase FDT routine MOVL P2(AP),IRP$L_BCNT(R3) ; Setup erase byte count MOVL P3(AP),IRP$L_MEDIA(R3) ; Setup erase starting LBN JMP G^EXE$QIODRVPKT ; Send request to STARTIO .SBTTL LD_FDT_SHAD_WCHECK - Check write to shadow member for privs ;+++ ; ; LD_FDT_SHAD_RWCHECK - Check read/write to shadow mbr for privilege ; ; Functional Description: ; ; Allow only processes with SYS privilege to perform WRITES to ; Host Based Shadowing shadow set members. ; ; Inputs: ; ; R3 IRP address ; R5 UCB address (member) ; ; Implicit inputs: None. ; ; Outputs:None. ; ; Implicit outputs: None. ; ; Condition codes: ; ; SS$_ILLIOFUNC - I/O directed to shadow set member by a process ; that doesn't have sys priv. ;--- UNIVERSAL_SYMBOL LD_FDT_SHAD_WCHECK ;LD_FDT_SHAD_WCHECK: BBC #DEV$V_SHD,- ; If this device is not a shadow UCB$L_DEVCHAR2(R5),10$ ; set member, quit MOVL IRP$L_ARB(R3),R0 ; Get ARB address BEQL 20$ ; If ARB absent, exit ASSUME PRV$V_SYSPRV LT 32 BBC #PRV$V_SYSPRV,ARB$Q_PRIV(R0),20$; No SYSPRV, illegal 10$: RSB ; Continue FDT processing 20$: MOVZBL #SS$_ILLIOFUNC,R0 ; Set error status JMP G^EXE$ABORTIO ; Complete I/O request .SBTTL LD_FDT_CRESHAD - CRESHAD FDT routine .SBTTL LD_FDT_REMSHAD - REMSHAD FDT routine ;+++ ; ; LD_FDT_CRESHAD - CRESHAD FDT routine ; LD_FDT_REMSHAD - REMSHAD FDT routine ; ; Functional Description: ; ; Dispatch CRESHAD and REMSHAD requests to shadowing driver. ; ; Inputs: ; ; R3 IRP address ; R5 UCB address (member) ; ; Implicit inputs: Dispatch vector filled in. ; ; Outputs:None. ; ; Implicit outputs: None. ; ; Condition codes: ; ; SS$_ILLIOFUNC - Dispatch vector not set up. ; SS$_DEVNOTSHR - Shadowset exists already somewhere in the cluster. ; ; ; The routine must preserve all registers except R0-R2, and ; R9-R11. ;--- UNIVERSAL_SYMBOL LD_FDT_CRESHAD ;LD_FDT_CRESHAD: ; ----> IO$_CRESHAD UNIVERSAL_SYMBOL LD_FDT_REMSHAD ;LD_FDT_REMSHAD: ; ----> IO$_REMSHAD MOVL G^EXE$GL_HBS_PTR,R0 ; Shadow Dispatcher BGEQ 10$ ; Illegal if not filled in JMP (R0) ; Jump to dispatcher 10$: MOVZBL #SS$_ILLIOFUNC,R0 ; Set error status JMP G^EXE$ABORTIO ; Complete I/O request .SBTTL LD_CONTROL_INIT, Controller initialization routine ;+++ ; LD_CONTROL_INIT, Readies controller for I/O operations ; ; Functional description: ; ; The operating system calls this routine in 3 places: ; ; at system startup ; during driver loading and reloading ; during recovery from a power failure ; ; This routine is a NOP for driver reloading and power failure recovery. ; For system startup and driver loading it allocates a CDDB. ; ; ; Inputs: ; ; R4 - address of the CSR (controller status register) ; R5 - address of the IDB (interrupt data block) ; R6 - address of the DDB (device data block) ; R8 - address of the CRB (channel request block) ; ; Outputs: ; ; The routine must preserve all registers except R0-R3. ; ;--- UNIVERSAL_SYMBOL LD_CONTROL_INIT ;LD_CONTROL_INIT: ; Initialize controller MOVB #SPL$C_IOLOCK8,- ; Init device spin lock index. CRB$B_FLCK(R8) PUSHQ R4 ; Save CSR & IDB CLRL R4 ; Init errorflag MOVZWL IDB$W_UNITS(R5),R0 ; Get number of units BRB 20$ ; ; Check for the correct UCB size if we're reloaded. If the size differs, ; then turn the unit offline to avoid problems. ; 10$: MOVL IDB$L_UCBLST(R5)[R0],R1 ; Get UCB BEQL 20$ ; Not present CMPW UCB$W_SIZE(R1),#UCB$K_LD_UCBLEN ; Expected size? BEQL 20$ ; Yes ; ; Something's seriously wrong here. The UCB size of the currently loaded ; driver is not the same as the new driver. We may not continue, but since ; the controllerinit routine does not return an exit status we cannot ; signal it to SYSGEN. What we do is set the unit offline here. ; CLRL CRB$L_AUXSTRUC(R8) ; Flag error BICW2 #UCB$M_ONLINE,UCB$W_STS(R1) ; Switch unit offline BICW2 #DEV$M_MSCP,- ; Zero this bit. Otherwise UCB$L_DEVCHAR2(R1) ; 'show device' has problems ; if the CDDB is 0. MOVZBL #1,R4 ; Flag something's wrong 20$: SOBGEQ R0,10$ ; Next device TSTL R4 ; Did we have a problem? BNEQ 30$ ; Yes, quit TSTL CRB$L_AUXSTRUC(R8) ; Check if CDDB present BEQL 40$ ; Branch if not there 30$: POPQ R4 ; Restore registers RSB ; Otherwise, return to caller ; ; Create fork thread to finish controller init. ; 40$: MOVL R6,R4 ; Restore DDB MOVL R8,R5 ; Fork with CRB PUSHAB 30$ ; Fake return address FORK ; ; Get pool for CDDB. ; MOVZWL #CDDB$K_LENGTH,R1 ; Size of CDDB JSB G^EXE$ALONONPAGED ; Allocate some pool BLBC R0,50$ ; Branch if error PUSHR #^M ; Save registers. MOVC5 #0,(SP),#0,R1,(R2) ; Zero entire block. POPR #^M ; Restore saved registers. ; ; Initialize necessary CDDB fields. ; MOVW R1,CDDB$W_SIZE(R2) ; Size ASSUME CDDB$B_SUBTYPE EQ CDDB$B_TYPE+1 MOVW #>,- CDDB$B_TYPE(R2) MOVL R5,CDDB$L_CRB(R2) ; CRB address MOVL R4,CDDB$L_DDB(R2) ; DDB address MOVL R2,CRB$L_AUXSTRUC(R5) ; Save CDDB address in CRB. MOVL DDB$L_UCB(R4),R5 ; Get UCB BISW2 #DEV$M_MSCP,- ; Set mscp bit. Now safe UCB$L_DEVCHAR2(R5) ; because we've got a CDDB MOVL R2,UCB$L_CDDB(R5) ; Also in UCB (If first load) MOVAL CDDB$L_CDRPQFL(R2),- ; Init CDRP queue CDDB$L_CDRPQFL(R2) MOVAL CDDB$L_CDRPQFL(R2),- CDDB$L_CDRPQBL(R2) MOVAL CDDB$L_RSTRTQFL(R2),- ; Init restart CDRP queue CDDB$L_RSTRTQFL(R2) MOVAL CDDB$L_RSTRTQFL(R2),- CDDB$L_RSTRTQBL(R2) MOVL G^CLU$GL_ALLOCLS,- ; Allocation class CDDB$L_ALLOCLS(R2) MOVW CDDB$L_ALLOCLS(R2),- ; Allocation class CDDB$Q_CNTRLID(R2) MOVW UCB$W_UNIT(R5),- ; Unit number CDDB$Q_CNTRLID+2(R2) MOVL UCB$L_MEDIA_ID(R5),- ; Media_id CDDB$Q_CNTRLID+4(R2) BRB 60$ ; fails then second load ; needs to store this. 50$: MOVL DDB$L_UCB(R4),R5 ; Get UCB BICW2 #DEV$M_MSCP,- ; Zero this bit. Otherwise UCB$L_DEVCHAR2(R5) ; 'show device' has problems ; if the CDDB is 0. 60$: RSB ; Return .SBTTL LD_CLONED_UCB, Cloned UCB initialization ;+++ ; LD_CLONED_UCB, Cloned UCB initialization ; ; Functional description: ; ; This routine is called by the $ASSIGN System Service to allow the ; driver to initialize the cloned UCB. The driver is called with ; process context. ; ; Inputs: ; R0 = SS$_NORMAL ; R2 = address of cloned UCB ; R3 = DDT address ; R4 = PCB address ; R5 = address of the template UCB ; IPL = ASTDEL ; ; Outputs: ; ; R5 = address of cloned UCB ; Destroyed = R5 ; ;-- UNIVERSAL_SYMBOL LD_CLONED_UCB ;LD_CLONED_UCB: MOVL R2,R5 ; Copy UCB address MOVL UCB$L_CPID(R5),UCB$L_LD_CPID(R5); Save charge pid CLRL UCB$L_CPID(R5) ; Zero ID MOVW UCB$W_CHARGE(R5),- ; Save charged amount UCB$W_LD_CHARGE(R5) CLRW UCB$W_CHARGE(R5) ; Zero charge BISW2 #UCB$M_NOCNVRT,UCB$W_DEVSTS(R5) ; Inhibit logical to ; physical xlation. ; ; Fall through to the LD_UNIT_INIT routine to initialize the cloned UCB. ; .SBTTL LD_UNIT_INIT, Unit initialization routine ;+++ ; LD_UNIT_INIT, Readies unit for I/O operations ; ; Functional description: ; ; The operating system calls this routine after calling the ; controller initialization routine: ; ; at system startup ; during driver loading ; during recovery from a power failure ; during initialization of a cloned UCB ; ; ; Inputs: ; ; R3 - address of the CSR (controller status register) ; R5 - address of the UCB (unit control block) ; ; IPL = IPL$_ASTDEL (jump/flow into from LD_CLONED_UCB) ; = IPL$_POWER (powerfail recovery and unit initialization) ; ; Outputs: ; ; The routine must preserve all registers except R0-R1. ; R0 = status (used only on alpha) ; ;--- UNIVERSAL_SYMBOL LD_UNIT_INIT ;LD_UNIT_INIT: ; Initialize Unit MOVL UCB$L_CRB(R5),R0 ; Get CRB address TSTL CRB$L_AUXSTRUC(R0) ; Get CDDB address out of the CRB. BNEQ 10$ ; Okay BICW2 #UCB$M_ONLINE,UCB$W_STS(R5) ; Mark cloned unit as offline 10$: BBS #UCB$V_POWER,UCB$L_STS(R5),20$ ; Did power fail ? MOVAL UCB$L_LD_AIOFL(R5),- ; Init the Act. I/O queue UCB$L_LD_AIOFL(R5) MOVAL UCB$L_LD_AIOFL(R5),- UCB$L_LD_AIOBL(R5) MOVAL UCB$L_LD_IOBFL(R5),- ; Init LDIOB free queue UCB$L_LD_IOBFL(R5) MOVAL UCB$L_LD_IOBFL(R5),- UCB$L_LD_IOBBL(R5) MOVAL UCB$L_LD_IRPFL(R5),- ; Init forward IRP free queue UCB$L_LD_IRPFL(R5) MOVAL UCB$L_LD_IRPFL(R5),- UCB$L_LD_IRPBL(R5) MOVAL UCB$L_LD_TRCMUTEXQFL(R5),- ; Init trace mutex wait queue UCB$L_LD_TRCMUTEXQFL(R5) MOVAL UCB$L_LD_TRCMUTEXQFL(R5),- UCB$L_LD_TRCMUTEXQBL(R5) MOVAL UCB$L_LD_TRCWAITQFL(R5),- ; Init trace data wait queue UCB$L_LD_TRCWAITQFL(R5) MOVAL UCB$L_LD_TRCWAITQFL(R5),- UCB$L_LD_TRCWAITQBL(R5) MOVAL UCB$L_LD_WATCHQFL(R5),- ; Init watch queue UCB$L_LD_WATCHQFL(R5) MOVAL UCB$L_LD_WATCHQFL(R5),- UCB$L_LD_WATCHQBL(R5) MOVAL UCB$L_LD_WATCHPNDQFL(R5),- ; Init watch pending queue UCB$L_LD_WATCHPNDQFL(R5) MOVAL UCB$L_LD_WATCHPNDQFL(R5),- UCB$L_LD_WATCHPNDQBL(R5) CLRL UCB$L_LD_WATCHCNT(R5) ; No entries yet MOVZWL #^XFFFF,UCB$L_LD_TRCMUTEX(R5) ; Init trace mutex CLRW UCB$W_LD_FLAGS(R5) ; Clear flags ; ; Setup UCB CDDB field. ; 20$: MOVZWL #SS$_NORMAL,R0 ; This will be returned to our ; caller (may be $ASSIGN) FORK ; Fork to allow controller init ; routine to complete first MOVL UCB$L_CRB(R5),R0 ; Get CRB address MOVL CRB$L_AUXSTRUC(R0),- ; Get CDDB address out of the CRB. UCB$L_CDDB(R5) BEQL 40$ ; Did not get one MOVL R5,UCB$L_DP_ALTUCB(R5) ; Point to ourself because CDP bit ; is set (to prevent MSCP serving) TSTW UCB$W_UNIT(R5) ; Is this unit 0? BNEQ 30$ ; BR if it is CLRW UCB$W_UNIT_SEED(R5) ; Start with fresh unit ; ; Setup MSCP stuff since some drivers need it (STdriver) ; 30$: MOVW UCB$W_UNIT(R5),- ; MSCP unit number UCB$W_MSCPUNIT(R5) BISW2 #UCB$M_ONLINE,UCB$W_STS(R5) ; Switch unit online 40$: MOVZWL #SS$_NORMAL,R0 ; Assume success BBS #UCB$V_ONLINE,UCB$W_STS(R5),50$ ; On-line? MOVZWL #SS$_DEVOFFLINE,R0 ; Flag off-line 50$: RSB .SBTTL LD_DRV_UNLOAD, driver unloading routine ;+++ ; LD_DRV_UNLOAD, Driver unloading routine ; ; Functional description: ; ; The operating system calls this routine after a SYSGEN ; RELOAD command, IPL wil be IPL$_POWER. ; ; An eventually allocated trace- or watchpoint buffer will be ; returned to pool. ; ; Inputs: ; ; R6 - Address of DDB ; R10 - Address of DPT ; ; Outputs: ; ; ; Due to a bug in SYSGEN we will NOT get the DDB address in R6, but zero. ; Byebye VAX!! (This has been fixed in V6.0). ; Because of this we will find the DDB ourselves. ; ; This routine may use all registers. ;--- UNIVERSAL_SYMBOL LD_DRV_UNLOAD ;LD_DRV_UNLOAD: ; Unload driver .IF NDF V6 BSBW FIND_DDB ; Get the DDB .ENDC MOVL DDB$L_UCB(R6),R5 ; Get first UCB address MOVL UCB$L_CRB(R5),R7 ; Get CRB address CLRL R4 ; No PCB 10$: BSBW LD_DEALLOC_TRCBUF ; Get rid of trace buffer BSBW LD_DEALLOC_WATCHBUF ; Get rid of watch buffers MOVL UCB$L_LINK(R5),R5 ; Try next unit BNEQ 10$ ; It's there MOVZWL #SS$_NORMAL,R0 RSB ; .IF NDF V6 FIND_DDB: ; ; Not many checks needed as SYSGEN already verified our existance... ; MOVAB DPT$T_NAME(R10),R9 ; Get addr of driver name MOVZBL (R9)+,R8 ; Get size of driver name MOVAL G^IOC$GL_DEVLIST,R6 ; Get address of device listhead 10$: MOVL DDB$L_LINK(R6),R6 ; Get addr of next ddb MOVAB DDB$T_DRVNAME(R6),R1 ; Get addr of driver name MOVZBL (R1)+,R0 ; Get size of driver name CMPC5 R0,(R1),#0,R8,(R9) ; Driver names match? BNEQ 10$ ; Br if not RSB ; Return with DDB address in R6 .ENDC .SBTTL LD_START, Start I/O routine ;+++ ; LD_START - Start a transmit or receive operation ; ; Functional description: ; ; ; Inputs: ; ; R3 - address of the IRP (I/O request packet) ; R5 - address of the UCB (unit control block) ; ; Outputs: ; ; R0 - 1st longword of I/O status: contains status code and ; number of bytes transferred ; R1 - 2nd longword of I/O status: device-dependent ; ; The routine must preserve all registers except R0-R2 and R4. ; ;--- UNIVERSAL_SYMBOL LD_START ;LD_START: ; Process an I/O packet ; ; We will clear the BSY flag here so that if we are stalled in LD_TRACE ; other threads may start already, and won't be queued to our UCB. Since ; we don't go through normal I/O completion (REQCOM) these I/O's will be ; hanging there for the rest of the system's life. LD_TRACE takes care of ; multiple threads. ; BICW2 #UCB$M_BSY,UCB$W_STS(R5) ; Clear the busy flag EXTZV #IRP$V_FCODE,#IRP$S_FCODE,- IRP$W_FUNC(R3),R4 ; Extract the function code ASSUME IRP$S_FCODE LE 7 ; Allow byte mode dispatch ; ; Functions allowed with inactive device ; CMPB R4,#IO$_LD_CONTROL ; Private control function? BNEQ 10$ ; No BRW LD_START_CONTROL ; Handle our own function codes 10$: BSBW LD_CHECK_WATCH ; Check for watchpoint BLBS IRP$L_EXTEND(R3),20$ ; Action done by CHECK_WATCH BBS #UCB$V_LD_CONSTS,- ; Device in DISCONNECT state ? UCB$W_LD_FLAGS(R5),30$ MOVZWL #SS$_DEVINACT,R0 ; Set status to dev. inactive 20$: BRW LD_DONE ; Complete the I/O 30$: MOVL UCB$L_LD_PDUCB(R5),R0 ; Get UCB of phys. disk BBC #UCB$V_ONLINE,UCB$W_STS(R0),40$ ; Unit offline? BBS #IRP$V_PHYSIO,IRP$W_STS(R3),50$ ; If set, phys. I/O function BBS #UCB$V_VALID,UCB$W_STS(R5),50$ ; If volume valid ? MOVZWL #SS$_VOLINV,R0 ; Set status to volume invalid BRW LD_DONE ; And complete the I/O ; ; Someone pulled the device underneath us (it went offline). ; This may happen if we replaced a raid virtual unit with an LD ; device. Subsequent unbinding of the raid array may take place ; without us knowing about it. Return SS$_DRVERR to the user ; (SS$_DEVOFFLINE would kick in mount verification, which i don't ; want since this is a fatal error. ; 40$: MOVZWL #SS$_DRVERR,R0 ; Return drive error status BRW LD_DONE ; And complete the I/O ASSUME IRP$S_FCODE LE 7 ; Allow byte mode dispatch 50$: DISPATCH R4,TYPE=B,<- ; Dispatch according to function ,- ; ^X00 ,- ; ^X01 ,- ; ^X02 ,- ; ^X04 ,- ; ^X08 ,-; ^X0A ,-; ^X0B ,- ; ^X0C ,- ; ^X11 > ; ^X15 MOVZWL #SS$_ILLIOFUNC,R0 ; Set illegal I/O function code BRB LD_DONE ; And complete the I/O UNIVERSAL_SYMBOL LD_PACKACK ;LD_PACKACK: BISW2 #UCB$M_VALID,UCB$W_STS(R5) ; Set volume valid bit BRB LD_NORMAL ; And complete the I/O UNIVERSAL_SYMBOL LD_AVAILABLE ;LD_AVAILABLE: UNIVERSAL_SYMBOL LD_UNLOAD ;LD_UNLOAD: BICW2 #UCB$M_VALID,UCB$W_STS(R5) ; Clear the volume valid bit ; Fall through into LD_NORMAL UNIVERSAL_SYMBOL LD_DRVCLR ;LD_DRVCLR: UNIVERSAL_SYMBOL LD_SEEK ;LD_SEEK: UNIVERSAL_SYMBOL LD_NOP ;LD_NOP: UNIVERSAL_SYMBOL LD_NORMAL ;LD_NORMAL: BBC #UCB$V_LD_REPLACE,- ; Replaced drive? UCB$W_LD_FLAGS(R5),10$ BRW LD_PROCESS_IO ; More to do for replaced drive 10$: CLRL R2 ; Dummy LBN MOVZWL #SS$_NORMAL,R0 ; Set status to success UNIVERSAL_SYMBOL LD_DONE ;LD_DONE: ; Driver processing is finished. CLRL R1 ; Clear second status longword LD_DONE1: BSBW LD_SAVE_TRACE ; Save trace data BSBW LD_RESUME_WATCH_THREAD ; Resume eventual suspended watch thread REQCOM ; Complete I/O. ;+++ ; The IRP is now setup to be transferred to the physical disk. The phys. ; disk UCB is taken and it's FLCK is compared to our FLCK. If our FLCK is ; lower, we raise IPL to synchronize. We queue the IRP to the Phys. disk ; driver, and simply return. ; The phys. disk driver will call I/O completion to get rid of the IRP etc. ; Because we do not know if the Phys. disk requires the Block nr in phys. , ; or Logical format, we first convert it to Logical, because that is what ; we need, add the Starting LBN of the LD File, and go through the conver- ; sion process again, like in the FDT rtn's. ;--- UNIVERSAL_SYMBOL LD_DSE ;LD_DSE: BBC #UCB$V_LD_NODSE,- ; Check for DSE support on UCB$W_LD_FLAGS(R5),- ; physical drive LD_TRANSFER_W MOVZWL #SS$_NORMAL,R0 ; Fake success ASHL #16,IRP$L_BCNT(R3),R1 ; Move lower half of bytecount BISL2 R1,R0 ; Combine status and lower part of count MOVZWL IRP$L_BCNT+2(R3),R1 ; Move upper half of bytecount BRW LD_DONE1 UNIVERSAL_SYMBOL LD_TRANSFER_W ;LD_TRANSFER_W: BBC #DEV$V_SWL,UCB$L_DEVCHAR(R5),- ; Write protect? LD_TRANSFER MOVZWL #SS$_WRITLCK,R0 ; Yes, return write-lock status BRW LD_DONE UNIVERSAL_SYMBOL LD_TRANSFER ;LD_TRANSFER: MOVL IRP$L_MEDIA(R3),R2 ; Save for later BBS #UCB$V_LD_VIRTUAL,- ; Check for virtual I/O UCB$W_LD_FLAGS(R5),20$ BBC #IRP$V_PHYSIO,IRP$W_STS(R3),10$ ; Is it a phys. I/O ? BBS #IRP$V_SHDIO,IRP$W_STS2(R3),10$ ; Is it shadowing I/O ? BSBW LD_CNVRTOLOG ; Convert to a log. I/O 10$: ASHL #-9,IRP$L_BCNT(R3),R0 ; Calculate the page count ADDL2 IRP$L_MEDIA(R3),R0 ; Calculate the end DECL R0 ; Adjust CMPL R0,UCB$L_MAXBLOCK(R5) ; Does it fit on our disk ? BLEQU LD_PROCESS_IO ; Yep, skip BRW 30$ ; Return error 20$: ASHL #-9,IRP$L_BCNT(R3),R0 ; Calculate the page count ADDL2 IRP$L_MEDIA(R3),R0 ; Calculate the end DECL R0 ; Adjust MOVL UCB$L_LD_FCB(R5),R1 ; Get FCB CMPL R0,FCB$L_EFBLK(R1) ; Does it fit on our disk ? BLEQU LD_PROCESS_IO ; Yep, skip 30$: MOVZWL #SS$_ILLBLKNUM,R0 ; Set return status BRW LD_DONE ; And complete the I/O UNIVERSAL_SYMBOL LD_PROCESS_IO ;LD_PROCESS_IO: MOVL IRP$L_BCNT(R3),R1 ; Set original byte count PUSHR #^M MOVL R1,R6 ; Copy in to R6 ADDL3 #1,IRP$L_MEDIA(R3),R7 ; Copy LBN -> R7, VBN start at 1 ; ; Allocate and initialize an LDIOB to describe the I/O and the connec- ; tion to the physical disk. Insert the LDIOB in the active UCB I/O queue ; BSBW LD_ALLO_LDIOB ; Allocate a LIODB block BLBS R0,5$ ; Ok POPR #^M BRW LD_DONE ; Quit on error 5$: MOVL R2,R4 MOVW #SS$_NORMAL,LDIOB_W_IOST(R4) ; Initialize return status MOVL IRP$L_PID(R3),LDIOB_L_PID(R4) ; Set PID val in LDIOB MOVL R3,LDIOB_L_IRP(R4) ; Set IRP adr in LDIOB CLRQ LDIOB_Q_ST_TIME(R4) ; Zero start time CLRL LDIOB_L_ELAPSED(R4) ; Zero elapsed time TSTL UCB$L_LD_TRCBUF(R5) ; Trace active? BEQL 10$ ; No, save overhead MOVL IRP$L_MEDIA(R3),- ; Copy Media address LDIOB_L_MEDIA(R4) MOVL IRP$L_BCNT(R3),LDIOB_L_BCNT(R4) ; Copy Bytecount MOVW IRP$W_FUNC(R3),LDIOB_W_FUNC(R4) ; Copy Functioncode READ_SYSTIME LDIOB_Q_ST_TIME(R4) ; Start time 10$: INSQUE LDIOB_L_QFL(R4),- ; Insert at end of queue @UCB$L_LD_AIOBL(R5) MOVL UCB$L_LD_WCB(R5),R2 ; Get our WCB address ; ; For every mapped I/O segment, allocate and initialize a forward IRP. ; The transfer parameters are copied into the forward IRP, and the IRP ; is queued to the LDIOB. ; ; R2 - WCB ; R3 - IRP (org) ; R4 - LDIOB ; R5 - UCB (org) ; R6 - Byte count ; R7 - Segment block number ; 30$: PUSHR #^M BBS #UCB$V_LD_VIRTUAL,- ; Check for virtual I/O UCB$W_LD_FLAGS(R5),32$ BBC #UCB$V_LD_REPLACE,- ; Skip mapping if replace UCB$W_LD_FLAGS(R5),35$ 32$: MOVL UCB$L_LD_PDUCB(R5),R5 ; Get the PDUCB CLRL R2 ; Remaining bytecount MOVL IRP$L_MEDIA(R3),R1 ; First LBN BRB 40$ 35$: MOVL R7,R0 ; Restore segment number MOVL R6,R1 ; Restore bytecount BSBW LD_MAPVBLK ; Map a section BLBS R0,40$ ; Total map failure = FATAL BUG_CHECK INCONSTATE,FATAL ; ; R5 now contains the address of the UCB on which this part of the ; file is located. ; 40$: MOVL R5,R8 ; Save physical UCB address MOVL 12(SP),R5 ; Recover LD UCB address MOVL R3,R0 ; Copy IRP to R0 BSBW LD_ALLO_FWIRP ; Allocate a FWIRP block SUBL3 R2,R6,IRP$L_BCNT(R3) ; Set the byte count MOVAB LD_COMPLETE,IRP$L_PID(R3) ; Initialize callback address MOVW IRP$W_BOFF(R0),IRP$W_BOFF(R3) ; Set the byte offset MOVB IRP$B_PRI(R0),IRP$B_PRI(R3) ; Set the priority MOVW IRP$W_FUNC(R0),IRP$W_FUNC(R3) ; Get the function BBC #UCB$V_LD_VIRTUAL,- ; Check for virtual I/O UCB$W_LD_FLAGS(R5),43$ MOVL UCB$L_LD_WCB(R5),- ; Get our WCB address, IRP$L_WIND(R3) ; used for Virtual I/O 43$: MOVL IRP$L_SVAPTE(R0),- ; Set the SVAPTE IRP$L_SVAPTE(R3) CLRW IRP$W_STS(R3) ; Init status BBC #UCB$V_LD_REPLACE,- ; Skip status copy if replace UCB$W_LD_FLAGS(R5),45$ MOVW IRP$W_STS(R0),IRP$W_STS(R3) ; Initialize status BRB 50$ 45$: BBC #IRP$V_FUNC,IRP$W_STS(R0),50$ ; Chk the R/W flag BISW #IRP$M_FUNC,IRP$W_STS(R3) ; Set the R/W flag 50$: MOVL R2,R6 ; Copy bytecount it to R6 BBS #UCB$V_LD_REPLACE,- ; Skip if replace UCB$W_LD_FLAGS(R5),60$ SUBL3 #1,R7,R2 ; Copy it to R2, VBN start at 1 SUBL2 IRP$L_MEDIA(R0),R2 ; Calculate buffer offset ASHL #2,R2,R2 ; Add offset in buffer in PTE's ADDL3 R2,IRP$L_SVAPTE(R0),- ; Set the SVAPTE IRP$L_SVAPTE(R3) ASHL #-9,IRP$L_BCNT(R3),R0 ; Calculate the page count ADDL2 R0,R7 ; Add it to the blk number 60$: MOVL R5,IRP$L_LD_LDUCB(R3) ; Set the LDUCB MOVL R4,IRP$L_LD_LDIOB(R3) ; Set the LDIOB MOVL R1,R0 ; Set logical block number MOVL R1,IRP$L_MEDIA(R3) ; Assume virtual I/O PUSHL R5 ; Save LD UCB MOVL R8,R5 ; Get the PDUCB MOVL R5,IRP$L_UCB(R3) ; Set the PDUCB BBS #UCB$V_LD_VIRTUAL,- ; Check for virtual I/O UCB$W_LD_FLAGS(R5),65$ JSB G^IOC$CVTLOGPHY ; Convert to physical structure ; and fill IRP$L_MEDIA 65$: INSQUE IRP$L_LD_FWDQFL(R3),- ; Place FWIRP in LDIOB @LDIOB_L_FWDQBL(R4) ADAWI #1,LDIOB_W_IRPCNT(R4) ; Increment number of FW IRPs MOVL (SP)+,R5 ; Restore LD UCB ; ; Queue the I/O to the physical driver. If the device is not busy then ; pass the IRP to the START I/O routine. If the device is busy, then ; place it in the I/O wait queue. ; MOVZBL UCB$B_FLCK(R5),R2 ; Get our forklock index CMPB UCB$B_FLCK(R5),UCB$B_FLCK(R8) ; Compare (negative index) BLSS 70$ ; Ok, ours is larger or equal MOVZBL UCB$B_FLCK(R8),R2 ; Get the lowest of the 2 70$: FORKLOCK LOCK=R2,- ; Synchronize SAVIPL=-(SP),- PRESERVE=NO PUSHL R2 ; Save lock PUSHL R5 ; Save our UCB address MOVL R8,R5 ; Setup Phys. disk UCB address INCW UCB$W_QLEN(R5) ; Bump device queue length BBSS #UCB$V_BSY,UCB$W_STS(R5),80$ ; Check if device is busy JSB G^IOC$INITIATE ; Initiate the I/O function BRB 90$ ; Common code flow 80$: MOVAL UCB$L_IOQFL(R5),R2 ; Get addr. of I/O queue listhd JSB G^EXE$INSERTIRP ; Insert IRP in device queue 90$: MOVL (SP)+,R5 ; Restore our UCB MOVL (SP)+,R2 ; Restore lock FORKUNLOCK LOCK=R2,- ; Release lock NEWIPL=(SP)+,- PRESERVE=NO,- CONDITION=RESTORE POPR #^M TSTL R6 ; Anything left to map ? BLEQU 100$ ; No, complete BRW 30$ ; Yes map rest 100$: POPR #^M RSB ; And simply return ;+++ ; Convert a physical block into a logical block. ; Modify IRP$L_MEDIA into LBN instead of TRK/CYL/SEC. ; LBN = (CYL * (TRACKS PER CYL) + TRACK) * (SECTORS PER TRACK) + SECTOR ;--- UNIVERSAL_SYMBOL LD_CNVRTOLOG ;LD_CNVRTOLOG: PUSHR #^M MOVZBL UCB$B_TRACKS(R5),R4 ; Get T/C MOVZWL IRP$L_MEDIA+2(R3),R0 ; Get C MULL2 R0,R4 ; R4=C*(T/C) MOVZBL IRP$L_MEDIA+1(R3),R2 ; Get T ADDL R4,R2 ; R2=C*(T/C)+T MOVZBL UCB$B_SECTORS(R5),R4 ; Get S/T MULL2 R4,R2 ; R2=(C*(T/C)+T)*(S/T) MOVZBL IRP$L_MEDIA(R3),R4 ; Get S ADDL2 R4,R2 ; R2=(C*(T/C)+T)*(S/T)+S MOVL R2,IRP$L_MEDIA(R3) ; Put LBN into IRP POPR #^M RSB ; Return to caller .SBTTL LD_MAPVBLK, Map virtual to logical block ;+++ ; LD_MAPVBLK, Map virtual to logical block ; ; Functional description: ; ; This routine is called to map a virtual block to a logical ; block using a mapping window. ; ; Inputs: ; ; R0 - Virtual block number ; R1 - Number of bytes to map ; R2 - WCB address ; R3 - address of the IRP (I/O request packet) ; R5 - address of the UCB (unit control block) ; ; Output: ; ; R0 - Low bit clear = total mapping failure ; R0 - Low bit set = partial map with: ; R1 = Logical block number of first block ; R2 = Number of unmapped bytes ; R5 = UCB of physical unit ; ; The routine must preserve all registers except R0-R2 and R4. ; ;--- UNIVERSAL_SYMBOL LD_MAPVBLK ;LD_MAPVBLK: PUSHR #^M ; Save registers MOVAB -1(R0),R7 ; Save start vbn -1 MOVL R2,R3 ; Get copy of window address MOVL WCB$L_ORGUCB(R2),R5 ; Get ucb of volume containing the file ; ; The window may consist of a chain of wcb segments. search through the ; chain until we find one which is beyond the desired vbn or we reach ; the end of the chain. ; 10$: CMPL R0,WCB$L_STVBN(R3) ; Check vbn against start vbn of window BLSSU 20$ ; Branch if vbn precedes window MOVL R3,R2 ; Else advance to this window segment MOVL WCB$L_LINK(R2),R3 ; Look at next window segment BNEQ 10$ ; Branch to look at if it exists 20$: MOVZWL WCB$W_NMAP(R2),R3 ; Get count of retrieval pointers BEQL 40$ ; Branch if empty window MOVAL WCB$L_STVBN(R2),R4 ; Point to starting vbn SUBL2 (R4)+,R0 ; Subtract starting vbn from desired BLSSU 40$ ; Branch if vbn precedes window .IF DF V6 ASSUME WCB$L_P1_COUNT EQ WCB$L_STVBN+4 ASSUME WCB$L_COUNT EQ 0 ASSUME WCB$L_LBN EQ 4 ASSUME WCB$B_RVN EQ 8 ASSUME WCB$C_MAP_PTR_LENGTH EQ 12 .ENDC ; ; Scan the window, subtracting the count field of each pointer from the ; current relative block number. ; 30$: .IF DF V6 MOVL (R4)+,R9 ; Get count field of retrieval pointer SUBL2 R9,R0 ; Subtract from relative block number .IFF MOVZWL (R4)+,R1 ; Get count field of retrieval pointer SUBL2 R1,R0 ; Subtract from relative block number .ENDC BLSSU 50$ ; Branch if vbn located in this pointer .IF DF V6 ADDL2 #8,R4 ; Skip lbn and rvn field of pointer .IFF TSTL (R4)+ ; Skip lbn field of pointer .ENDC SOBGTR R3,30$ ; Loop thru window ; Vbn is beyond window 40$: CLRL R8 ; Indicate no blocks mapped MOVL WCB$L_ORGUCB(R2),R5 ; Redirect ucb to volume containing the file BRB 70$ ; Return failure ; ; Found the retrieval pointer containing the starting vbn. R0 now ; contains a negative value which is the number of blocks between ; the starting vbn and the end of the pointer. ; 50$: MNEGL R0,R8 ; Save # blocks mapped past start vbn .IF DF V6 ADDL2 (R4)+,R9 ; First lbn beyond this pointer ADDL3 R9,R0,R1 ; Compute starting lbn MOVL (R4)+,R0 ; Get rvn field .IFF ADDL2 (R4)+,R1 ; First lbn beyond this pointer ADDL2 R1,R0 ; Compute starting lbn .ENDC ; ; If the next retrieval pointer is contiguous with the one found, add ; in its count to handle the case where a transfer spans two pointers. ; Note that the greatest number of contiguous pointers a transfer can ; span is two. ; DECL R3 ; See if there is another pointer BLEQ 60$ ; Branch if none .IF DF V6 MOVL (R4)+,R3 ; Get count of next retrieval pointer CMPL R9,(R4) ; See if the next pointer is contiguous .IFF MOVZWL (R4)+,R3 ; Get count of next retrieval pointer CMPL R1,(R4) ; See if the next pointer is contiguous .ENDC BNEQ 60$ ; Branch if not ADDL2 R3,R8 ; Add to # blocks mapped ; ; Extract the lbn and rvn components of the starting "lbn" and switch ; to the right ucb if this is a multi-volume set. ; 60$: .IF DF V6 MOVZBL R0,R0 ; Extract rvn (R1 already has starting lbn) .IFF EXTZV #0,#24,R0,R1 ; Extract lbn part EXTZV #24,#8,R0,R0 ; Extract rvn .ENDC BEQL 70$ ; Branch if not volume set MOVL WCB$L_RVT(R2),R6 ; Get relative volume table addr .IF DF V6 CMPB R0,RVT$B_NVOLS(R6) ; Volume mapped within ucblst? BGTRU 90$ ; No, force windowturn .ENDC MOVL RVT$L_UCBLST-4(R6)[R0],R5 ; Get the right ucb address ; ; Check the range of vbn's provided by the map pointer against the ; file size recorded in the fcb. Reduce it if the fcb indicates a ; smaller file size than the window. ; 70$: BBS #WCB$V_NOTFCP,- ; Skip check if no fcb WCB$B_ACCESS(R2),110$ MOVL WCB$L_FCB(R2),R6 ; Get fcb address SUBL3 R7,FCB$L_FILESIZE(R6),R4 ; Compute blocks to physical eof BLEQU 90$ ; Branch if vbn past end of file CMPL R8,R4 ; Compare against blocks mapped BLEQU 80$ ; Branch if less mapped by window MOVL R4,R8 ; Else limit to file size ; ; For read I/O's, check the range of vbn's provided by the map pointer ; against the current highwater mark and set the appropriate status ; flags. Limit the length of the transfer to highwater mark. By ; checking reads here, highwater mark flagging and checking is done on ; a segment by segment basis. Writes must be flagged for the entire ; I/O, and hence are handled in sysacpfdt. ; 80$: MOVL 4(SP),R3 ; Get back irp address BBC #IRP$V_FUNC,- ; Branch if this is a write IRP$W_STS(R3),110$ BICB #IRP$M_START_PAST_HWM!IRP$M_END_PAST_HWM,- IRP$W_STS2(R3) ; Init highwater mark flags INCL R7 ; Back to true start vbn SUBL3 R7,FCB$L_HIGHWATER(R6),R7 ; Blocks to highwater BGTRU 100$ ; Branch if not past hwm TSTL FCB$L_HIGHWATER(R6) ; See if highwater marking is on BEQL 110$ ; Branch if not BISB #IRP$M_START_PAST_HWM,- ; Note past highwater mark IRP$W_STS2(R3) 90$: CLRL R8 ; Indicate no blocks mapped BRB 110$ ; And exit 100$: CMPL R8,R7 ; Does the transfer extend past highwater BLEQU 110$ ; Branch if not MOVL R7,R8 ; Limit read to highwater mark ; ; See if the entire transfer is mapped contiguously. ; 110$: CLRL R0 ; Assume failure ASHL #9,R8,R8 ; Convert to # bytes mapped BEQL 130$ ; Branch if no blocks mapped SUBL R8,(SP) ; Subtract from bytes desired BGEQU 120$ ; Branch if not total map CLRL (SP) ; Zero indicates complete map 120$: MOVL #SS$_NORMAL,R0 ; Indicate success 130$: POPR #^M ; Restore registers RSB .SBTTL LD_START_CONTROL, Start LD control routine ;+++ ; LD_START_CONTROL, Start LD control routine ; ; Functional description: ; ; This is the completion of the control processing. This has to be ; in system context since we may need to fork when we use the ; forklevel interface to the lockmanager. ; ; Inputs: ; ; R3 - address of the IRP (I/O request packet) ; R5 - address of the UCB (unit control block) ; ; The routine must preserve all registers except R0-R2 and R4. ; ;--- UNIVERSAL_SYMBOL LD_START_CONTROL ;LD_START_CONTROL: ASSUME LDIO_S_FUNC EQ 8 DISPATCH IRP$L_EXTEND(R3),TYPE=B,<- ; Dispatch according to function ,- ,- ,- ,- ,- ,- ,- > MOVZWL #SS$_ILLIOFUNC,R0 ; Set illegal I/O function code CLRL R1 REQCOM .SBTTL LD_START_CONNECT, LD Start connnect routine ;+++ ; LD_START_CONNECT, LD Start connnect routine ; ; Functional description: ; ; This is the completion of the connect processing. This has to be ; in system context since we may need to fork when we use the ; forklevel interface to the lockmanager. ; ; Inputs: ; ; R3 - address of the IRP (I/O request packet) ; R5 - address of the UCB (unit control block) ; ; Outputs: ; ; R0 - 1st longword of I/O status: contains status code and ; number of bytes transferred ; R1 - 0 ; ; The routine must preserve all registers except R0-R2 and R4. ; ;--- UNIVERSAL_SYMBOL LD_START_CONNECT ;LD_START_CONNECT: BBC #LDIO_V_SHARE,- ; Shared access? IRP$L_EXTEND(R3),10$ MOVL UCB$L_LD_PDUCB(R5),R1 ; Get the phys. disk UCB BBS #DEV$V_CLU,- ; Cluster wide visible ? UCB$L_DEVCHAR2(R1),10$ MOVZWL #SS$_ACCONFLICT,R0 ; Not cluster-wide visible BRW 140$ 10$: BSBW LD_ENQ_LD_LOCK ; Get lock of container file or device BLBC R0,20$ ; It's not allowed BSBW LD_ENQ_DEV_LOCK ; Get lock of LD device BLBS R0,30$ ; It's allowed ; ; Save status in UCB instead of the stack since $LCK_DEQ may ; fork, and we need a flat stack for that ; MOVL R0,UCB$L_LD_SAVST(R5) ; Save status $LCK_DEQ LKID=UCB$Q_LD_FILE_LKSB+4(R5),- VALBLK=UCB$A_LD_FILE_LVB(R5) ; Get rid of file lock MOVL UCB$L_LD_SAVST(R5),R1 ; Get original error BLBC R0,20$ ; Dequeue Error MOVL R1,R0 ; Return original error 20$: BRW 140$ 30$: BBC #LDIO_V_REPLACE,- ; Replace drive? IRP$L_EXTEND(R3),40$ BRW 60$ ; Skip file manipulation 40$: MOVL UCB$L_LD_FCB(R5),R2 ; Recover FCB address INCW FCB$W_REFCNT(R2) ; Increment the reference count INCW FCB$W_ACNT(R2) ; Increment the access count MOVL UCB$L_LD_PDUCB(R5),R1 ; Get the phys. disk UCB MOVL UCB$L_VCB(R1),R0 ; Get it's VCB ADAWI #1,VCB$W_TRANS(R0) ; Bump the transaction count TSTW VCB$W_RVN(R0) ; Relative volume number BEQL 50$ ; Branch if not a volume set BISW2 #UCB$M_LD_ONVOLSET,- ; Flag container file on a UCB$W_LD_FLAGS(R5) ; volumeset ASSUME FCB$W_FID_NUM+2 EQ FCB$W_FID_SEQ 50$: MOVL FCB$W_FID_NUM(R2),- ; Save FID for crosscheck UCB$W_LD_FID_NUM(R5) MOVW FCB$W_FID_RVN(R2),- UCB$W_LD_FID_RVN(R5) ; ; Check if the specified containerfile resides on an NFS ; mounted disk. In that case we need to do virtual I/O ; to the file, this will be indicated by the UCB$V_LD_VIRTUAL ; bit set in UCB$W_LD_FLAGS. ; MOVL UCB$L_DDB(R1),R1 ; Get the DDB CMPL DDB$T_NAME_STR(R1),- #^A/DNFS/ BNEQ 55$ ; No match BISW2 #UCB$M_LD_VIRTUAL,- ; Indicate virtual access needed UCB$W_LD_FLAGS(R5) 55$: ; ; Convert the WCB to a shared WCB. This must be done in process context as ; the FILSYS spinlock is needed, and we currently have IOLOCK8 which has a ; higher rank. LD_RETURN_QUOTA will queue an ast to the requesting process ; which will do the job. By using a special kernel ast we make sure that ; it is done before the user deaccesses the file. ; MOVL IRP$L_PID(R3),R4 ; Process to credit MOVL UCB$L_LD_WCB(R5),R1 ; Get WCB BSBW LD_RETURN_QUOTA ; Make WCB shared & return quota BRW 110$ ; ; Replace drive processing ; ; Take out devicelock for physical device to protect against access from ; another cluster member. If it fails then the device is in use, we will ; check later on if it was by another LDdriver or someone else. ; 60$: MOVQ R3,UCB$L_LD_FR3(R5) ; Save R3 + R4 MOVAB UCB$T_LD_PD_RESNAM+4(R5),R1 ; Setup pointer to resource name MOVAB 4(R1),(R1)+ ; And address MOVL #^A/SYS$/,(R1)+ ; "SYS$" MOVZBL #16,R0 ; Buffer size MOVZBL #1,R4 ; DVI$_ALLDEVNAM PUSHL R5 ; Save UCB MOVL UCB$L_LD_PDUCB(R5),R5 ; Get the phys. disk UCB JSB G^IOC$CVT_DEVNAM ; Get alloclass devicename MOVL (SP)+,R5 ; Restore UCB ADDL3 #4,R1,- ; Save length UCB$T_LD_PD_RESNAM(R5) ENQ_LOCK MODE=#LCK$K_EXMODE,- LKSBL=UCB$Q_LD_PD_LKSB(R5),- RESNAME=UCB$T_LD_PD_RESNAM(R5),- CMPLADR=80$,- EFLAGS= CMPW R0,#SS$_NOTQUEUED ; Any one else interested? BNEQ 75$ ; No ; ; Someone owns the devicelock in an incompatible way. Check if it's another ; LDdriver by enqueueing a NL lock and checking bit 15 of the lock value block. ; If it's set then another LDdriver allocated the device for replacement, in ; that case we may continue ; ENQ_LOCK MODE=#LCK$K_NLMODE,- LKSBL=UCB$Q_LD_PD_LKSB(R5),- RESNAME=UCB$T_LD_PD_RESNAM(R5),- ERROR=75$,- EFLAGS= MOVQ UCB$L_LD_FR3(R5),R3 ; Restore registers MOVZWL UCB$Q_LD_PD_LKSB(R5),R0 ; Enqeueue status BLBC R0,75$ ; Error ; ; Get rid of NL lock now that we have the lockvalue block ; $LCK_DEQ LKID=UCB$Q_LD_PD_LKSB+4(R5) BLBC R0,75$ ; Deqeueue error CLRL UCB$Q_LD_PD_LKSB+4(R5) ; Not queued anymore ; ; Check for bit 15, if set by another LDdriver we may continue ; BBC #15,UCB$A_LD_PD_LVB+DAL$W_FLAGS(R5),70$ MOVL UCB$L_LD_PDUCB(R5),R1 ; Get the phys. disk UCB BRW 100$ ; Continue 70$: MOVZWL #SS$_DEVALLOC,R0 ; In use 75$: BRW 90$ ; Quit ; ; Completion of ENQ ends up here ; 80$: MOVQ UCB$L_LD_FR3(R5),R3 ; Restore registers MOVZWL UCB$Q_LD_PD_LKSB(R5),R0 ; Check completion status BLBC R0,75$ ; Something's wrong ; ; No we've got the devicelock in EX mode. If we did not specify shared access ; we're done. The devicelock remains set to protect use againts other users. ; BBS #LDIO_V_SHARE,- ; Shared access wanted? IRP$L_EXTEND(R3),85$ BRW 95$ ; ; Set bit 15 (currently not used) in the FLAGS field of the devicelock's lockvalue ; block to signal that we own the device. The fields are defined in the $DALDEF ; macro. Furthermore, convert the lock to PW and back to EX to update the valueblock. ; 85$: MOVL #^X8000,- ; Flag we own the device UCB$A_LD_PD_LVB+DAL$W_FLAGS(R5) CLRL UCB$A_LD_PD_LVB+4(R5) ; Zero rest of lockvalue block CLRL UCB$A_LD_PD_LVB+8(R5) CLRL UCB$A_LD_PD_LVB+12(R5) ENQ_LOCK MODE=#LCK$K_PWMODE,- LKSBL=UCB$Q_LD_PD_LKSB(R5),- ERROR=90$,- EFLAGS= MOVZWL UCB$Q_LD_PD_LKSB(R5),R0 BLBC R0,90$ ENQ_LOCK MODE=#LCK$K_EXMODE,- LKSBL=UCB$Q_LD_PD_LKSB(R5),- ERROR=90$,- EFLAGS= MOVQ UCB$L_LD_FR3(R5),R3 ; Restore registers MOVZWL UCB$Q_LD_PD_LKSB(R5),R0 BLBS R0,95$ ; ; Some error occurred. Make sure that we get rid of the $LOGDISK locks. ; 90$: MOVL UCB$L_LD_FR3(R5),R3 ; Restore register MOVL R0,R4 ; Save status $LCK_DEQ LKID=UCB$Q_LD_FILE_LKSB+4(R5) ; Get rid of file lock $LCK_DEQ LKID=UCB$Q_LD_DEV_LKSB+4(R5) ; Get rid of device lock MOVL R0,R1 ; Return eventual error in R1 MOVL R4,R0 ; Restore status BRW 150$ ; Return error 95$: MOVL UCB$L_LD_PDUCB(R5),R1 ; Get the phys. disk UCB MOVL UCB$Q_LD_PD_LKSB+4(R5),- ; Save lockid UCB$L_LOCKID(R1) 100$: MOVL UCB$L_ORB(R5),- ; Save ORB UCB$L_LD_ORBSAV(R5) MOVL UCB$L_ORB(R1),- ; Get protection from physical device UCB$L_ORB(R5) MOVB UCB$B_DEVTYPE(R1),- ; Copy device type UCB$B_DEVTYPE(R5) ASSUME UCB$B_TRACKS EQ UCB$B_SECTORS+1 ASSUME UCB$W_CYLINDERS EQ UCB$B_TRACKS+1 MOVL UCB$B_SECTORS(R1),- ; Copy sectors, tracks and cylinders UCB$B_SECTORS(R5) MOVL UCB$L_MAXBLOCK(R1),- ; Copy maximum block UCB$L_MAXBLOCK(R5) EXTV #DEV$V_SWL,#1,- ; Extract write-lock bit UCB$L_DEVCHAR(R1),R0 INSV R0,#DEV$V_SWL,#1,- ; Insert (for CD-ROM) UCB$L_DEVCHAR(R5) INCW UCB$W_REFC(R1) ; Mark our interest BICL2 #DEV$M_AVL,- ; Make unavailable for others UCB$L_DEVCHAR(R1) BISW2 #UCB$M_LD_REPLACE,- ; Set special connect UCB$W_LD_FLAGS(R5) BISL2 #,- UCB$L_DEVCHAR(R5) ; Set characteristics again ; Needed after replace of CDrom ; ; Common exit ; 110$: MOVL UCB$L_LD_PDUCB(R5),R1 ; Get the phys. disk UCB MOVL UCB$L_MAXBCNT(R1),- ; Copy maximum bytecount UCB$L_MAXBCNT(R5) CMPB UCB$B_DEVTYPE(R1),- ; Check if we connected to #DT$_RAM_DISK ; a DECRAM disk BNEQ 120$ ; No BISW2 #UCB$M_LD_DECRAM,- ; Flag DECRAM in use UCB$W_LD_FLAGS(R5) 120$: MOVL UCB$L_DDT(R1),R1 ; Get DDT address (Phys. dev) MOVL DDT$L_FDT(R1),R1 ; Get FDT address BBS #IO$_DSE,(R1),125$ ; Does the device support DSE? BISW2 #UCB$M_LD_NODSE,- ; Not supported (Raid driver) UCB$W_LD_FLAGS(R5) ; (DPdriver) 125$: BICL2 #UCB$M_DELETEUCB,UCB$L_STS(R5) ; Make UCB non-deletable BBC #LDIO_V_SHARE,- ; Shared accessable? IRP$L_EXTEND(R3),130$ BISL2 #DEV$M_CLU,UCB$L_DEVCHAR2(R5) ; Make cluster wide visible BISW2 #UCB$M_LD_SHARE,- ; Set status UCB$W_LD_FLAGS(R5) 130$: ADAWI #1,LD_REFCNT ; Count number of 'connects' BISL2 #DPT$M_NOUNLOAD,- ; Prevent reloading of driver DPT$TAB+DPT$L_FLAGS BISW2 #UCB$M_LD_CONSTS,- ; Set status to connected UCB$W_LD_FLAGS(R5) MOVZWL #SS$_NORMAL,R0 ; Set status to success 140$: CLRL R1 150$: BSBW LD_SAVE_TRACE ; Save trace data REQCOM ; Complete the I/O .SBTTL LD_START_DISCONNECT, LD Start disconnnect routine ;+++ ; LD_START_DISCONNECT, LD Start disconnnect routine ; ; Functional description: ; ; This is the completion of the disconnect processing. This has to be ; in system context since we may need to fork when we use the ; forklevel interface to the lockmanager. ; ; Inputs: ; ; R3 - address of the IRP (I/O request packet) ; R5 - address of the UCB (unit control block) ; ; Outputs: ; ; R0 - 1st longword of I/O status: contains status code and ; number of bytes transferred ; R1 - 0 ; ; The routine must preserve all registers except R0-R2 and R4. ; ;--- UNIVERSAL_SYMBOL LD_START_DISCONNECT ;LD_START_DISCONNECT: CMPL UCB$L_LD_AIOFL(R5),- ; Any I/O's pending ? @UCB$L_LD_AIOFL(R5) BEQL 20$ ; No BBSSI #UCB$V_LD_DISPEN,- ; Set disconnect pending UCB$W_LD_FLAGS(R5),10$ MOVQ R3,UCB$L_FR3(R5) ; Save context MOVAB LD_START_DISCONNECT,- ; Address to resume UCB$L_FPC(R5) ; (done by LD_COMPLETE) 10$: RSB ; Back to caller 20$: BBS #UCB$V_LD_REPLACE,- ; Check if replaced device UCB$W_LD_FLAGS(R5),30$ MOVL UCB$L_LD_FCB(R5),R2 ; Copy FCB address DECW FCB$W_REFCNT(R2) ; Decr. the reference count DECW FCB$W_ACNT(R2) ; Decr. the access count MOVL UCB$L_LD_PDUCB(R5),R0 ; Get the physical disk UCB MOVL UCB$L_VCB(R0),R0 ; Get it's VCB ADAWI #-1,VCB$W_TRANS(R0) ; Decrement the transaction count BRW 50$ ; ; Replaced drive processing ; 30$: TSTL UCB$Q_LD_PD_LKSB+4(R5) ; Did we own the lock? BEQL 40$ ; No $LCK_DEQ LKID=UCB$Q_LD_PD_LKSB+4(R5) ; Get rid of devicelock BLBS R0,40$ ; Success BBS #LDIO_V_ABORT,- ; Check if abort is specified IRP$L_EXTEND(R3),40$ BRW 110$ ; Unexpected error 40$: MOVL UCB$L_LD_PDUCB(R5),R0 ; Get the physical disk UCB CLRL UCB$L_LOCKID(R0) ; Zero ID DECW UCB$W_REFC(R0) ; We're not interested anymore BISL2 #DEV$M_AVL,UCB$L_DEVCHAR(R0) ; Make available again MOVL UCB$L_LD_ORBSAV(R5),- ; Restore ORB UCB$L_ORB(R5) ; ; Common path ; 50$: $LCK_DEQ LKID=UCB$Q_LD_FILE_LKSB+4(R5) ; Get rid of lock ; ; Save status in UCB instead of the stack since $LCK_DEQ may ; fork, and we need a flat stack for that ; MOVL R0,UCB$L_LD_SAVST(R5) ; Save status $LCK_DEQ LKID=UCB$Q_LD_DEV_LKSB+4(R5) ; Get rid of lock BLBC R0,60$ ; Error MOVL UCB$L_LD_SAVST(R5),R0 ; Get previous status BLBS R0,70$ ; Success 60$: BBC #LDIO_V_ABORT,- ; Continue if abort is specified IRP$L_EXTEND(R3),110$ ; ; Deallocate cached LDIOB blocks ; 70$: REMQUE @UCB$L_LD_IOBFL(R5),R0 ; Get a packet from free queue BVS 80$ ; Branch if none JSB G^EXE$DEANONPAGED ; Get rid of buffer BRB 70$ ; Get next one ; ; Deallocate cached FWIRP blocks ; 80$: REMQUE @UCB$L_LD_IRPFL(R5),R0 ; Get a packet from free queue BVS 90$ ; Branch if none JSB G^EXE$DEANONPAGED ; Get rid of buffer BRB 80$ ; Get next one 90$: BISL2 #UCB$M_DELETEUCB,UCB$L_STS(R5) ; Make UCB deletable ADAWI #-1,LD_REFCNT ; Count down #of 'connects' BNEQ 100$ ; Some left BICL2 #DPT$M_NOUNLOAD,- ; Allow reloading of driver DPT$TAB+DPT$L_FLAGS 100$: MOVZWL #SS$_NORMAL,R0 ; Set status to success 110$: CLRL R1 BSBW LD_SAVE_TRACE ; Save trace data REQCOM .SBTTL LD_ENQ_LD_LOCK, Enqueue file- or device lock for LD device ;+++ ; LD_ENQ_LD_LOCK, Enqueue file- or device lock for LD device ; ; Functional description: ; ; This routine will enqueue the file lock of the specified file and ; device. It will check if the file is used on another node anywhere ; in the cluster. If true then it's only allowed if we ordered shared ; access and if the remote devicename and geometry matches ours so ; that XQP locking will work. ; ; The locking protocol is as follows: ; ; First we will enqueue an EX-mode lock on our resource. If it is granted ; immediately (SS$_SYNCH returned) then it means that we are the first ; one ever getting this lock. We will then fill the value block and ; convert the lock down to CR (specifying a blocking ast routine), and ; exit with success. ; ; If we don't get it immediately then it means that another thread owns ; the lock. In that case we implicitely triggered the blocking ast ; routine by enqueueing the EX lock. This blocking ast routine will then ; convert the lock back to NL, and it will immediately try to convert ; it back to CR. After we get the lock, we will check the value block ; and then either dequeue it or convert it back to CR. After that action ; the blocking ast routine will convert the lock back to CR. ; ; If we encounter an invalid lock value block during this process we will ; just rewrite it with the same contents. It's not documented, but we can ; rely on the value being equal to what it was before it became invalid. ; (courtesy of Sandy Snaman, it may be documented in the future). ; ; ; Inputs: ; ; R3 - address of the IRP (I/O request packet) ; R5 - address of the UCB (unit control block) ; UCB$T_LD_FILE_RESNAM(R5) - Resource name ; ; IOLOCK8 (=SCS) forklock held ; ; Outputs: ; ; R0 - status ; SS$_NORMAL - Success ; SS$_FILALRACC - File in use in an incompatible way ; other - Returned by lockmanager ; ; Only R3-R5 are preserved as we may fork. ; ;--- UNIVERSAL_SYMBOL LD_ENQ_LD_LOCK ;LD_ENQ_LD_LOCK: MOVL (SP)+,UCB$L_LD_SAVEPC(R5) ; Save caller's address ; because we may fork MOVQ R3,UCB$L_LD_FR3(R5) ; Save registers ENQ_LOCK MODE=#LCK$K_EXMODE,- LKSBL=UCB$Q_LD_FILE_LKSB(R5),- RESNAME=UCB$T_LD_FILE_RESNAM(R5),- CMPLADR=50$,- EFLAGS= CMPW R0,#SS$_SYNCH ; Are we the only one? BNEQ 40$ ; No MOVQ UCB$L_LD_FR3(R5),R3 ; Restore registers ; ; We are the first one queueing a lock for this resource. Setup the ; lock value block, convert the lock back to CR and exit with success. ; MOVAB UCB$A_LD_FILE_LVB(R5),R1 ; Setup pointer to lock value block MOVL UCB$L_DDB(R5),R2 ; Get DDB MOVL DDB$L_ALLOCLS(R2),- ; Setup allocation class LDFLVB_L_ALLOCLS(R1) MOVW UCB$W_UNIT(R5),- ; Unit number LDFLVB_W_UNIT(R1) MOVW UCB$W_CYLINDERS(R5),- ; Cylinders LDFLVB_W_CYLINDERS(R1) MOVB UCB$B_TRACKS(R5),- ; Tracks LDFLVB_B_TRACKS(R1) MOVB UCB$B_SECTORS(R5),- ; Sectors LDFLVB_B_SECTORS(R1) MOVL UCB$L_MAXBLOCK(R5),- ; Maximum blocknumber LDFLVB_L_MAXBLOCK(R1) CLRB LDFLVB_B_FLAGS(R1) ; Init flags BBC #LDIO_V_SHARE,- ; Shared access? IRP$L_EXTEND(R3),20$ BISB #LDFLVB_M_SHARE,- LDFLVB_B_FLAGS(R1) ; Set flags 20$: ENQ_LOCK MODE=#LCK$K_CRMODE,- LKSBL=UCB$Q_LD_FILE_LKSB(R5),- BLKADR=LD_ENQ_FILE_BLKRTN,- EFLAGS=,- ERROR=90$ ; ; Lock is converted to CR. ; 30$: MOVQ UCB$L_LD_FR3(R5),R3 ; Restore registers MOVZWL UCB$Q_LD_FILE_LKSB(R5),R0 ; Get completion status 40$: BRW 90$ ; Exit with completion status ; ; Lock is granted as EX. ; 50$: MOVQ UCB$L_LD_FR3(R5),R3 ; Restore registers MOVZWL UCB$Q_LD_FILE_LKSB(R5),R0 ; Get completion status ; ; Check for an invalid value block. If that's the case then we can rely on the ; fact that the contents of the valueblock are the same as they were before. ; (courtesy of Sandy Snaman). By either DEQing or converting to CR we will ; revalidate it. ; CMPL R0,#SS$_VALNOTVALID ; Value block invalid? BEQL 60$ ; Yes, deal with it later BLBC R0,90$ ; Something's wrong ; ; We now have the lock in EX mode. Check the lock value block to see ; if we match. ; 60$: MOVAB UCB$A_LD_FILE_LVB(R5),R1 ; Setup pointer to lock value block BBC #LDFLVB_V_SHARE,- ; Check for shared access LDFLVB_B_FLAGS(R1),80$ BBC #LDIO_V_SHARE,- ; Shared access requested? IRP$L_EXTEND(R3),80$ MOVL UCB$L_DDB(R5),R2 ; Get DDB CMPL DDB$L_ALLOCLS(R2),- ; Check allocation class LDFLVB_L_ALLOCLS(R1) BNEQ 80$ ; No match CMPW UCB$W_UNIT(R5),- ; Unit number LDFLVB_W_UNIT(R1) BNEQ 80$ ; No match CMPW UCB$W_CYLINDERS(R5),- ; Cylinders LDFLVB_W_CYLINDERS(R1) BNEQ 80$ ; No match CMPB UCB$B_TRACKS(R5),- ; Tracks LDFLVB_B_TRACKS(R1) BNEQ 80$ ; No match CMPB UCB$B_SECTORS(R5),- ; Sectors LDFLVB_B_SECTORS(R1) BNEQ 80$ ; No match CMPL UCB$L_MAXBLOCK(R5),- ; Maximum blocknumber LDFLVB_L_MAXBLOCK(R1) BNEQ 80$ ; No match BRW 20$ ; Convert down to CR and exit ; ; Dequeue the lock since we're not allowed to use the file or device. ; Specify the lock valueblock so that in case it was not valid dequeue ; will rewrite it. ; 80$: $LCK_DEQ LKID=UCB$Q_LD_FILE_LKSB+4(R5),- VALBLK=UCB$A_LD_FILE_LVB(R5) ; Get rid of file lock BLBC R0,90$ ; Error MOVZWL #SS$_FILALRACC,R0 ; Assume file in use BBC #LDIO_V_REPLACE,- ; Replace drive? IRP$L_EXTEND(R3),90$ MOVZWL #SS$_DEVALLOC,R0 ; Device In use 90$: JMP @UCB$L_LD_SAVEPC(R5) ; Resume thread .SBTTL LD_ENQ_FILE_BLKRTN, Enqueue file lock blocking ast routine ;+++ ; LD_ENQ_FILE_BLKRTN, Enqueue LD lock blocking ast routine ; ; Functional description: ; ; This routine will be called by the lockmanager when someone else ; tries to enqueue an incompatible lock on the LD file resource name. ; ; We will convert the lock back to NL, and then we immediately try ; to convert it back to CR. If this second conversion is successfull ; we will just exit. ; ; If the second conversion reveals a 'value block invalid' error we ; must revalidate the lock. We do this by converting the lock back ; to NL, up to EX, back to NL and then back to CR. This looks ; over-complicated but this is needed because more than one thread ; may be active at the same time, and we must make sure that we won't ; block other threads which in the conversion process. The second ; conversion to NL is needed because we are not allowed to convert a ; lock from EX to CR while specifying QUECVT. ; ; Inputs: ; ; R5 - address of the UCB (unit control block) ; ; IOLOCK8 (=SCS) forklock held ; ; Outputs: ; ; Only R3-R5 are preserved as we may fork. ; ;--- UNIVERSAL_SYMBOL LD_ENQ_FILE_BLKRTN ;LD_ENQ_FILE_BLKRTN: ENQ_LOCK MODE=#LCK$K_NLMODE,- LKSBL=UCB$Q_LD_FILE_LKSB(R5),- EFLAGS=,- ERROR=60$ BLBC UCB$Q_LD_FILE_LKSB(R5),20$ ; Check completion status ; ; Lock is converted to NL, convert back to CR. Specify QUECVT so that ; the requesting thread gets it's chance to acquire the lock. ; 10$: ENQ_LOCK MODE=#LCK$K_CRMODE,- LKSBL=UCB$Q_LD_FILE_LKSB(R5),- BLKADR=LD_ENQ_FILE_BLKRTN,- EFLAGS=,- ERROR=60$ ; ; Lock is converted to CR. ; MOVZWL UCB$Q_LD_FILE_LKSB(R5),R0 ; Get final status CMPL R0,#SS$_VALNOTVALID ; Value block invalid? BEQL 40$ ; Yes, deal with it later BLBS R0,30$ ; No error, exit 20$: BRW 60$ ; Bugcheck on all other errors 30$: RSB ; ; We have an invalid lock value block. Convert the lock to NL, then to EX, ; then to NL to rewrite the lockvalue block, and finally to CR. ; 40$: ENQ_LOCK MODE=#LCK$K_NLMODE,- LKSBL=UCB$Q_LD_FILE_LKSB(R5),- EFLAGS=,- ERROR=60$ BLBC UCB$Q_LD_FILE_LKSB(R5),20$ ; Get status ; ; Lock is converted to NL, convert back to EX. ; ENQ_LOCK MODE=#LCK$K_EXMODE,- LKSBL=UCB$Q_LD_FILE_LKSB(R5),- EFLAGS=,- ERROR=60$ MOVZWL UCB$Q_LD_FILE_LKSB(R5),R0 ; Get status CMPL R0,#SS$_VALNOTVALID ; Value block invalid? BEQL 50$ ; Acceptable BLBC R0,60$ ; Check completion status ; ; Lock is converted to EX, convert back to NL to rewrite the value block. ; 50$: ENQ_LOCK MODE=#LCK$K_NLMODE,- LKSBL=UCB$Q_LD_FILE_LKSB(R5),- EFLAGS=,- ERROR=60$ BLBC UCB$Q_LD_FILE_LKSB(R5),60$ ; Quit on error BRW 10$ ; Ok, convert back to CR and exit ; ; Bugcheck since there's nobody to return this error to.... ; 60$: BUG_CHECK INCONSTATE,FATAL ; Unexpected error from lockmanager .SBTTL LD_ENQ_DEV_LOCK, Enqueue device lock for LD device ;+++ ; LD_ENQ_DEV_LOCK, Enqueue device lock for LD device ; ; Functional description: ; ; This routine will enqueue the device lock of the specified device. ; It will check if the device is used on another node anywhere ; in the cluster. If true then it's only allowed if we ordered shared ; access and if the remote filename matches ours so that XQP locking ; will work. ; ; The locking protocol is as follows: ; ; First we will enqueue an EX-mode lock on our resource. If it is granted ; immediately (SS$_SYNCH returned) then it means that we are the first ; one ever getting this lock. We will then fill the value block and ; convert the lock down to CR (specifying a blocking ast routine), and ; exit with success. ; ; If we don't get it immediately then it means that another thread owns ; the lock. In that case we implicitely triggered the blocking ast ; routine by enqueueing the EX lock. This blocking ast routine will then ; convert the lock back to NL, and it will immediately try to convert ; it back to CR. After we get the lock, we will check the value block ; and then either dequeue it or convert it back to CR. After that action ; the blocking ast routine will convert the lock back to CR. ; ; If we encounter an invalid lock value block during this process we will ; just rewrite it with the same contents. It's not documented, but we can ; rely on the value being equal to what it was before it became invalid. ; (courtesy of Sandy Snaman, it may be documented in the future). ; ; ; Inputs: ; ; R3 - address of the IRP (I/O request packet) ; R5 - address of the UCB (unit control block) ; UCB$T_LD_DEV_RESNAM(R5) - Resource name ; ; IOLOCK8 (=SCS) forklock held ; ; Outputs: ; ; R0 - status ; SS$_NORMAL - Success ; SS$_FILALRACC - File in use in an incompatible way ; other - Returned by lockmanager ; ; Only R3-R5 are preserved as we may fork. ; ;--- UNIVERSAL_SYMBOL LD_ENQ_DEV_LOCK ;LD_ENQ_DEV_LOCK: MOVL (SP)+,UCB$L_LD_SAVEPC(R5) ; Save caller's address ; because we may fork MOVQ R3,UCB$L_LD_FR3(R5) ; Save registers ENQ_LOCK MODE=#LCK$K_EXMODE,- LKSBL=UCB$Q_LD_DEV_LKSB(R5),- RESNAME=UCB$T_LD_DEV_RESNAM(R5),- CMPLADR=50$,- EFLAGS= CMPW R0,#SS$_SYNCH ; Are we the only one? BEQL 5$ ; Yes BRW 90$ ; Exit 5$: MOVQ UCB$L_LD_FR3(R5),R3 ; Restore registers ; ; We are the first one queueing a lock for this resource. Setup the ; lock value block, convert the lock back to CR and exit with success. ; MOVAB UCB$A_LD_DEV_LVB(R5),R1 ; Setup pointer to lock value block MOVL UCB$L_DDB(R5),R2 ; Get DDB MOVZBL DDB$B_NAME_LEN(R2),R0 ; Name length CMPL R0,#6 ; Max we can handle BLEQ 10$ ; Okay BRW 80$ ; Too much 10$: PUSHR #^M INCB R0 ; Length including len field MOVC3 R0,DDB$T_NAME(R2),- ; Move name + length LDDLVB_T_DEVNAM(R1) POPR #^M ASSUME LDDLVB_W_SEQ EQ LDDLVB_W_FID+2 CLRL LDDLVB_W_FID(R1) ; Zero block CLRW LDDLVB_W_RVN(R1) BBS #LDIO_V_REPLACE,- ; Replace drive? IRP$L_EXTEND(R3),15$ MOVL UCB$L_LD_FCB(R5),R0 ; Recover FCB address MOVW FCB$W_FID_NUM(R0),- ; Insert File ID LDDLVB_W_FID(R1) MOVW FCB$W_FID_SEQ(R0),- ; SEQ LDDLVB_W_SEQ(R1) MOVW FCB$W_FID_RVN(R0),- ; RVN LDDLVB_W_RVN(R1) 15$: MOVB DDB$L_ALLOCLS(R2),- ; Fill allocation class LDDLVB_B_ALLOCLS(R1) MOVW UCB$W_UNIT(R5),- ; Unit number LDDLVB_W_UNIT(R1) 20$: ENQ_LOCK MODE=#LCK$K_CRMODE,- LKSBL=UCB$Q_LD_DEV_LKSB(R5),- BLKADR=LD_ENQ_DEV_BLKRTN,- EFLAGS=,- ERROR=90$ ; ; Lock is converted to CR. ; 30$: MOVQ UCB$L_LD_FR3(R5),R3 ; Restore registers MOVZWL UCB$Q_LD_DEV_LKSB(R5),R0 ; Get completion status BRW 90$ ; Exit with completion status ; ; Lock is granted as EX. ; 50$: MOVQ UCB$L_LD_FR3(R5),R3 ; Restore registers MOVZWL UCB$Q_LD_DEV_LKSB(R5),R0 ; Get completion status ; ; Check for an invalid value block. If that's the case then we can rely on the ; fact that the contents of the valueblock are the same as they were before. ; (courtesy of Sandy Snaman). By either DEQing or converting to CR we will ; revalidate it. ; CMPL R0,#SS$_VALNOTVALID ; Value block invalid? BEQL 60$ ; Yes, deal with it later BLBC R0,90$ ; Something's wrong ; ; We now have the lock in EX mode. Check the lock value block to see ; if we match. ; 60$: MOVAB UCB$A_LD_DEV_LVB(R5),R1 ; Setup pointer to lock value block MOVL UCB$L_DDB(R5),R2 ; Get DDB MOVZBL DDB$B_NAME_LEN(R2),R0 ; Name length INCB R0 ; Check length field too PUSHR #^M CMPC3 R0,DDB$T_NAME(R2),- ; Check name + length LDDLVB_T_DEVNAM(R1) POPR #^M BNEQ 80$ ; No match BBS #LDIO_V_REPLACE,- ; Replace drive? IRP$L_EXTEND(R3),70$ MOVL UCB$L_LD_FCB(R5),R0 ; Recover FCB address CMPW FCB$W_FID_NUM(R0),- ; Check File ID LDDLVB_W_FID(R1) BNEQ 80$ ; No match CMPW FCB$W_FID_SEQ(R0),- ; SEQ LDDLVB_W_SEQ(R1) BNEQ 80$ ; No match CMPW FCB$W_FID_RVN(R0),- ; RVN LDDLVB_W_RVN(R1) BNEQ 80$ ; No match 70$: CMPB DDB$L_ALLOCLS(R2),- ; Check allocation class LDDLVB_B_ALLOCLS(R1) BNEQ 80$ ; No match CMPW UCB$W_UNIT(R5),- ; Unit number LDDLVB_W_UNIT(R1) BNEQ 80$ ; No match BRW 20$ ; Convert down to CR and exit ; ; Dequeue the lock since we're not allowed to use the file or device. ; Specify the lock valueblock so that in case it was not valid dequeue ; will rewrite it. ; 80$: $LCK_DEQ LKID=UCB$Q_LD_DEV_LKSB+4(R5),- VALBLK=UCB$A_LD_DEV_LVB(R5) ; Get rid of file lock BLBC R0,90$ ; Error MOVZWL #SS$_DEVALLOC,R0 ; Device In use 90$: JMP @UCB$L_LD_SAVEPC(R5) ; Resume thread .SBTTL LD_ENQ_DEV_BLKRTN, Enqueue device lock blocking ast routine ;+++ ; LD_ENQ_DEV_BLKRTN, Enqueue device lock blocking ast routine ; ; Functional description: ; ; This routine will be called by the lockmanager when someone else ; tries to enqueue an incompatible lock on the LD device resource name. ; ; We will convert the lock back to NL, and then we immediately try ; to convert it back to CR. If this second conversion is successfull ; we will just exit. ; ; If the second conversion reveals a 'value block invalid' error we ; must revalidate the lock. We do this by converting the lock back ; to NL, up to EX, back to NL and then back to CR. This looks ; over-complicated but this is needed because more than one thread ; may be active at the same time, and we must make sure that we won't ; block other threads which in the conversion process. The second ; conversion to NL is needed because we are not allowed to convert a ; lock from EX to CR while specifying QUECVT. ; ; Inputs: ; ; R5 - address of the UCB (unit control block) ; ; IOLOCK8 (=SCS) forklock held ; ; Outputs: ; ; Only R3-R5 are preserved as we may fork. ; ;--- UNIVERSAL_SYMBOL LD_ENQ_DEV_BLKRTN ;LD_ENQ_DEV_BLKRTN: ENQ_LOCK MODE=#LCK$K_NLMODE,- LKSBL=UCB$Q_LD_DEV_LKSB(R5),- EFLAGS=,- ERROR=60$ BLBC UCB$Q_LD_DEV_LKSB(R5),20$ ; Check completion status ; ; Lock is converted to NL, convert back to CR. Specify QUECVT so that ; the requesting thread gets it's chance to acquire the lock. ; 10$: ENQ_LOCK MODE=#LCK$K_CRMODE,- LKSBL=UCB$Q_LD_DEV_LKSB(R5),- BLKADR=LD_ENQ_DEV_BLKRTN,- EFLAGS=,- ERROR=60$ ; ; Lock is converted to CR. ; MOVZWL UCB$Q_LD_DEV_LKSB(R5),R0 ; Get final status CMPL R0,#SS$_VALNOTVALID ; Value block invalid? BEQL 40$ ; Yes, deal with it later BLBS R0,30$ ; No error, exit 20$: BRW 60$ ; Bugcheck on all other errors 30$: RSB ; ; We have an invalid lock value block. Convert the lock to NL, then to EX, ; then to NL to rewrite the lockvalue block, and finally to CR. ; 40$: ENQ_LOCK MODE=#LCK$K_NLMODE,- LKSBL=UCB$Q_LD_DEV_LKSB(R5),- EFLAGS=,- ERROR=60$ BLBC UCB$Q_LD_DEV_LKSB(R5),20$ ; Get status ; ; Lock is converted to NL, convert back to EX. ; ENQ_LOCK MODE=#LCK$K_EXMODE,- LKSBL=UCB$Q_LD_DEV_LKSB(R5),- EFLAGS=,- ERROR=60$ MOVZWL UCB$Q_LD_DEV_LKSB(R5),R0 ; Get status CMPL R0,#SS$_VALNOTVALID ; Value block invalid? BEQL 50$ ; Acceptable BLBC R0,60$ ; Check completion status ; ; Lock is converted to EX, convert back to NL to rewrite the value block. ; 50$: ENQ_LOCK MODE=#LCK$K_NLMODE,- LKSBL=UCB$Q_LD_DEV_LKSB(R5),- EFLAGS=,- ERROR=60$ BLBC UCB$Q_LD_DEV_LKSB(R5),60$ ; Quit on error BRW 10$ ; Ok, convert back to CR and exit ; ; Bugcheck since there's nobody to return this error to.... ; 60$: BUG_CHECK INCONSTATE,FATAL ; Unexpected error from lockmanager .SBTTL LD_START_TRACE, Start I/O trace routine ;+++ ; LD_START_TRACE, Start I/O trace routine ; ; Functional description: ; ; This is the completion of the retrieve tracedata processing. This has to be ; in system context since we need to fork when we find that there is no ; tracedata available. ; ; Inputs: ; ; R3 - address of the IRP (I/O request packet) ; R5 - address of the UCB (unit control block) ; ; Outputs: ; ; None. ; ; The routine must preserve all registers except R0-R2 and R4. ;--- UNIVERSAL_SYMBOL LD_START_TRACE ;LD_START_TRACE: INSQUE (R3),@UCB$L_LD_TRCWAITQBL(R5) ; Save wait IRP in queue ; ; Return to caller. Since we don't want the I/O to complete now (there's ; no trace data when we get here) we return to our caller instead of ; going through REQCOM. When LD_TRACE writes new data into the buffer ; it will queue an AST to the process, and the process will resume at ; LD_TRACE_AST which will copy the new data to the user and complete ; the I/O. ; RSB ; Return to caller .SBTTL LD_TRACE_AST, Display data AST completion routine ;+++ ; LD_TRACE_AST, Display data AST completion routine ; ; Functional description: ; ; This is the final completion of the display processing. ; This routine is called as an AST routine in the context of the ; process issueing the I/O. We will move the newly available trace ; data to the user's buffer, and complete the I/O. ; ; Inputs: ; ; R4 - address of the PCB (process control block) ; R5 - address of the ACB ; ; The routine must preserve all registers except R0-R5. ;--- UNIVERSAL_SYMBOL LD_TRACE_AST ;LD_TRACE_AST: MOVL ACB$L_ASTPRM(R5),R3 ; Restore IRP MOVL R5,R0 ; Address of ACB JSB G^EXE$DEANONPAGED ; Get rid of ACB MOVL IRP$L_UCB(R3),R5 ; Restore UCB BSBW LD_MOVE_TRACE ; Move data to user BLBC R0,10$ ; Missed count in R1 on error DIVL3 #LDTRCENT_K_LENGTH,R2,R1 ; Convert size to #of entries 10$: CLRL IRP$L_SVAPTE(R3) ; No more SVAPTE FORKLOCK LOCK=UCB$B_FLCK(R5),- ; Raise to Fork level SAVIPL=-(SP) MOVL R3,UCB$L_IRP(R5) ; Restore IRP JSB G^IOC$REQCOM ; Complete the I/O request FORKUNLOCK LOCK=UCB$B_FLCK(R5),- ; Release lock NEWIPL=(SP)+,- ; Remain ast IPL$_ASTDEL PRESERVE=NO RSB .SBTTL LD_START_ENABLE_WATCH, Start I/O enable watch processing ;+++ ; LD_START_ENABLE_WATCH, Start I/O enable watch processing ; ; Functional description: ; ; This is the completion of the enable watchpoint processing. ; Buffers will be allocated, and if any buffer which was specified ; by the user already exists we will return the charge for this ; buffer to the user by ast. ; ; Inputs: ; ; R3 - address of the IRP (I/O request packet) ; R5 - address of the UCB (unit control block) ; IRP$L_OBCNT - number of entries in userbuffer ; ; Outputs: ; ; None. ; ; The routine must preserve all registers except R0-R2 and R4. ;--- UNIVERSAL_SYMBOL LD_START_ENABLE_WATCH ;LD_START_ENABLE_WATCH: MOVL IRP$L_SVAPTE(R3),R2 ; Get systembuffer address MOVL (R2),R2 ; Get userbuffer address MOVL IRP$L_OBCNT(R3),R4 ; Get count 10$: MOVAL UCB$L_LD_WATCHQFL(R5),R1 ; Get queue listhead BSBW LD_FIND_WATCH_ENTRY_ENA ; Find matching entry BLBS R0,30$ ; Not found CMPW LDWATCH_W_ACTION(R1),- ; Suspend? #WATCH_ACTION_SUSPEND BNEQ 20$ ; No BSBW LD_DRAIN_WATCH_THREAD ; Get rid of watchpoints 20$: BSBW LD_COPY_WATCH ; Found exact match, copy new ; parameters except pid MOVL #LDWATCHENT_K_LENGTH,R1 ; Number of bytes already there PUSHR #^M MOVL IRP$L_PID(R3),R4 ; Process to credit BSBW LD_RETURN_QUOTA ; Give quota back to user POPR #^M BRW 40$ 30$: PUSHL R2 ; New entry, alloc buffer PUSHL R1 MOVZWL #LDWATCHENT_K_LENGTH,R1 ; This size JSB G^EXE$ALONONPAGED ; Get buffer BLBC R0,50$ ; Out of resources MOVB #DYN$C_BUFIO,LDWATCH_B_TYPE(R2) ; Fill type CLRW LDWATCH_W_FLAGS(R2) ; Zero flags MOVW R1,LDWATCH_W_SIZE(R2) ; And size MOVAL LDWATCH_L_SUSPFL(R2),- ; Init suspend queue LDWATCH_L_SUSPFL(R2) MOVAL LDWATCH_L_SUSPFL(R2),- LDWATCH_L_SUSPBL(R2) CLRL LDWATCH_L_SUSPCNT(R2) ; Nothing suspended yet MOVL R2,R1 ; New buffer address MOVL 4(SP),R2 ; Get entry address BSBW LD_COPY_WATCH_NEW ; Copy parameters MOVL R1,R2 ; Restore buffer addres MOVL (SP)+,R1 ; Restore listhead INSQUE (R2),LDWATCH_L_FLINK(R1) ; Insert new packet INCL UCB$L_LD_WATCHCNT(R5) ; Count packet MOVL (SP)+,R2 40$: ADDL2 #LDWATCHPT_K_LENGTH,R2 ; Point to next input entry SOBGTR R4,45$ ; For all buffers MOVZWL #SS$_NORMAL,R0 ; Success BRB 60$ 45$: BRW 10$ ; Branch assist 50$: ADDL2 #8,SP ; Adjust for error return 60$: MOVL UCB$L_LD_WATCHCNT(R5),R1 ; Return current count REQCOM .SBTTL LD_START_DISABLE_WATCH, Start I/O disable watch processing ;+++ ; LD_START_DISABLE_WATCH, Start I/O disable watch processing ; ; Functional description: ; ; This is the completion of the disable watchpoint processing. ; If a watchpoint was found with suspended threads queued to it ; then these threads will be released. ; ; Inputs: ; ; R3 - address of the IRP (I/O request packet) ; R5 - address of the UCB (unit control block) ; IRP$L_OBCNT - number of entries in userbuffer ; ; Outputs: ; ; None. ; ; The routine must preserve all registers except R0-R2 and R4. ;--- UNIVERSAL_SYMBOL LD_START_DISABLE_WATCH ;LD_START_DISABLE_WATCH: MOVZWL #SS$_DATALOST,R0 ; Assume nothing to do MOVL UCB$L_LD_WATCHCNT(R5),R4 ; Get packet count BNEQ 10$ ; Something to do BRW 100$ 10$: PUSHL R6 ; May not be destroyed CLRL R6 ; Nothing removed yet MOVL IRP$L_SVAPTE(R3),R2 ; Get systembuffer address BEQL 20$ ; Not there, remove all MOVL (R2),R2 ; Get userbuffer address MOVL IRP$L_OBCNT(R3),R4 ; Get requested count 20$: MOVAL UCB$L_LD_WATCHQFL(R5),R1 ; Get queue listhead TSTL R2 ; Get rid of alll entries? BEQL 30$ ; Yes BSBW LD_FIND_WATCH_ENTRY_DIS ; Find matching entry BLBS R0,70$ ; No exact match BRB 40$ ; Got it, remove 30$: MOVL (R1),R1 ; Point to next entry MOVAL UCB$L_LD_WATCHQFL(R5),R0 ; End of list CMPL R0,R1 ; At the end? BEQL 60$ ; Yes 40$: REMQUE LDWATCH_L_FLINK(R1),R0 ; Exact match, remove DECL UCB$L_LD_WATCHCNT(R5) ; Count packet INCL R6 ; Flag we've removed something MOVL R0,R1 ; Use correct input BSBW LD_DRAIN_WATCH_THREAD ; Get rid of watchpoints PUSHR #^M ; Destroyed by deallocate BBC #LDWATCH_V_FILE,- ; Check for virtual file mode LDWATCH_W_FLAGS(R0),50$ MOVL LDWATCH_L_FCB(R0),R2 ; Get FCB pointer DECW FCB$W_REFCNT(R2) ; Decr. reference count DECW FCB$W_ACNT(R2) ; Decr. access count MOVL UCB$L_VCB(R5),R2 ; Get it's VCB ADAWI #-1,VCB$W_TRANS(R2) ; Bump the transaction count 50$: MOVL LDWATCH_L_PID(R0),R4 ; Process to credit JSB G^EXE$DEANONPAGED ; Return buffer to pool MOVL #LDWATCHENT_K_LENGTH,R1 ; Amount to return BSBW LD_RETURN_QUOTA ; Give quota back to user POPR #^M BRB 20$ 60$: TSTL R2 ; Next input entry BEQL 80$ ; All entries 70$: ADDL2 #LDWATCHPT_K_LENGTH,R2 ; Point to next input entry 80$: SOBGTR R4,20$ ; Check all packets MOVZWL #SS$_NORMAL,R0 ; Assume success TSTL R6 ; Anything removed? BNEQ 90$ ; Yes MOVZWL #SS$_DATACHECK,R0 ; Entry not found 90$: MOVL (SP)+,R6 ; Restore this baby 100$: MOVL UCB$L_LD_WATCHCNT(R5),R1 ; Return current count REQCOM .SBTTL LD_COPY_WATCH, Copy watchpoint info into existing block .SBTTL LD_COPY_WATCH_NEW, Copy watchpoint info into new block ;+++ ; LD_COPY_WATCH, Copy watchpoint info into existing block ; LD_COPY_WATCH_NEW, Copy watchpoint info into new block ; ; Functional description: ; ; This routine copies the data from the user's parameter block ; into a watchpoint block ; ; Inputs: ; ; R1 - address of the driver allocated watchblock ; R2 - address of the users watchpt block ; R3 - address of the IRP (I/O request packet) ; R5 - address of the UCB (unit control block) ; ; Outputs: ; ; None. ; ;--- UNIVERSAL_SYMBOL LD_COPY_WATCH_NEW ;LD_COPY_WATCH_NEW: MOVL IRP$L_PID(R3),LDWATCH_L_PID(R1) ; Move pid MOVL IRP$L_UCB(R3),LDWATCH_L_UCB(R1) ; And UCB BBS #LDWATCHPT_V_FILE,- ; Check for virtual file mode LDWATCHPT_W_FLAGS(R2),10$ MOVL LDWATCHPT_L_LBN(R2),- ; Move logical blocknumber LDWATCH_L_LBN(R1) BRW LD_COPY_WATCH 10$: PUSHR #^M MOVL LDWATCHPT_L_SBK(R2),R4 ; Get SBK address MOVL SBK$L_FCB(R4),R4 ; Get FCB address MOVL R4,LDWATCH_L_FCB(R1) INCW FCB$W_REFCNT(R4) ; Incr. reference count INCW FCB$W_ACNT(R4) ; Incr. access count MOVL UCB$L_VCB(R5),R8 ; Get it's VCB ADAWI #1,VCB$W_TRANS(R8) ; Bump the transaction count MOVL FCB$L_WLFL(R4),R7 ; Get any WCB ASSUME FCB$W_FID_NUM+2 EQ FCB$W_FID_SEQ MOVL FCB$W_FID_NUM(R4),- ; Save FID LDWATCH_W_FID_NUM(R1) MOVW FCB$W_FID_RVN(R4),- LDWATCH_W_FID_RVN(R1) MOVL LDWATCHPT_L_LBN(R2),- ; Move logical blocknumber LDWATCH_L_VBN(R1) ; to virtual block for file MOVL LDWATCHPT_L_LBN(R2),R6 ; Get virtual blocknumber 20$: MOVZWL WCB$W_NMAP(R7),R3 ; Number of mapping pointers .IF DF V6 ASSUME WCB$L_P2_COUNT EQ WCB$L_P1_COUNT+12 MOVAL WCB$L_P1_COUNT(R7),R5 ; Address of first mapping pointer 30$: MOVL (R5)+,R4 ; Count .IFF ASSUME WCB$W_P2_COUNT EQ WCB$W_P1_COUNT+6 MOVAW WCB$W_P1_COUNT(R5),R5 ; Address of first mapping pointer 30$: MOVZWL (R5)+,R4 ; Count .ENDC CMPL R6,R4 ; Within this segment? BLEQ 40$ ; Yes SUBL2 R4,R6 ; Account for size of this segment TSTL (R5)+ ; Skip lbn .IF DF V6 TSTL (R5)+ ; Skip RVN .ENDC SOBGTR R3,30$ ; Next segment MOVL WCB$L_LINK(R7),R7 ; Next WCB BNEQ 20$ ; Try this one ; ; We should never come here. The VBN was checked in the FDT routine ; to be within the limits of the file. ; BUG_CHECK INCONSTATE,FATAL 40$: DECL R6 ; Adjust for VBN = 1 ADDL3 R6,(R5)+,LDWATCH_L_LBN(R1) ; Save converted LBN, skip to RVN TSTW VCB$W_RVN(R8) ; Relative volume number BEQL 50$ ; Branch if not a volume set MOVZBL (R5),R0 ; Get RVN MOVL VCB$L_RVT(R8),R6 ; Fetch RVT address MOVL RVT$L_UCBLST-4(R6)[R0],- ; Get the right ucb address LDWATCH_L_UCB(R1) CMPL 8(SP),LDWATCH_L_UCB(R1) ; Is it our own UCB? BEQL 50$ ; Yes, no problem BISW2 #LDWATCH_M_ONVOLSET,- ; Flags this extent is on a LDWATCH_W_FLAGS(R1) ; volumeset 50$: POPR #^M ; ; Fall into LD_COPY_WATCH ; UNIVERSAL_SYMBOL LD_COPY_WATCH ;LD_COPY_WATCH: BICW3 #^C,- ; Mask unused bits LDWATCHPT_W_FLAGS(R2),R0 BISW2 R0,LDWATCH_W_FLAGS(R1) ; Options MOVW LDWATCHPT_W_ACTION(R2),- ; What to do LDWATCH_W_ACTION(R1) MOVW LDWATCHPT_W_FUNC(R2),- ; On what function LDWATCH_W_FUNC(R1) MOVW LDWATCHPT_W_RETCODE(R2),- ; What to return LDWATCH_W_RETCODE(R1) RSB .SBTTL LD_FIND_WATCH_ENTRY_ENA, Locate a watchpoint entry for enable ;+++ ; LD_FIND_WATCH_ENTRY_ENA, Locate a watchpoint entry for enable ; ; Functional description: ; ; This routine locates a watchpoint entry. Entries are ; inserted in ascending order, so when we are past one ; entry we don't need to look any further. A special case ; is signalled if the NOLBN flag is set. This is the case ; for a non-transfer function. We can have more of these ; for different functions. ; ; Inputs: ; ; R1 - address of ucb watchqueue entry listhead ; R2 - address of the users watchpt block ; ; Outputs: ; ; R0 - 0 if found an exact match ; 1 if not found ; R1 - if R0 = 0 entry address ; if R0 = 1 address if insertion ; ;--- UNIVERSAL_SYMBOL LD_FIND_WATCH_ENTRY_ENA ;LD_FIND_WATCH_ENTRY_ENA: MOVL R1,R0 ; Save for later 10$: MOVL (R1),R1 ; Next entry CMPL R1,R0 ; End of queue ? BEQL 60$ ; Yes BBS #LDWATCH_V_NOLBN,- ; Non-transfer function? LDWATCH_W_FLAGS(R1),40$ CMPW LDWATCHPT_W_FLAGS(R2),- ; Same flags? LDWATCH_W_FLAGS(R1) BNEQ 10$ ; No BBC #LDWATCH_V_FILE,- ; File watchpoint? LDWATCH_W_FLAGS(R1),20$ CMPL LDWATCHPT_L_LBN(R2),- ; Check VBN for matching block LDWATCH_L_VBN(R1) BRB 30$ 20$: CMPL LDWATCHPT_L_LBN(R2),- ; Matching block? LDWATCH_L_LBN(R1) 30$: BEQL 40$ ; Yes BGTRU 10$ ; Not a higher number yet BRB 50$ ; No match 40$: CMPW LDWATCHPT_W_FUNC(R2),- ; Matching function? LDWATCH_W_FUNC(R1) BNEQ 10$ ; No CMPW LDWATCHPT_W_ACTION(R2),- ; Matching action? LDWATCH_W_ACTION(R1) BEQL 80$ ; Yes ; ; Here everything matches except action ; Determine if action may be added ; CMPW LDWATCHPT_W_ACTION(R2),- ; Adding OPCOM action? #WATCH_ACTION_OPCOM BEQL 50$ ; Yes ; ; Add some action other than OPCOM. Make sure we don't overwrite ; a current OPCOM action. ; CMPW LDWATCH_W_ACTION(R1),- ; Current packet OPCOM action? #WATCH_ACTION_OPCOM BNEQ 80$ ; No, modify action of current packet ; ; We must check the next packet for a match. ; BRW 10$ ; ; Make sure that a packet with an action for OPCOM is in front of ; other actions for the same lbn and function in the queue. This makes ; sure that later on in LD_CHECK_WATCH we will always issue an opcom ; message before we do any other specified action. To insert an entry ; before the current location we have to provide the address of the ; predecessor. ; 50$: MOVL LDWATCH_L_BLINK(R1),R1 ; Pick up address BRB 70$ ; of previous entry ; ; Nothing found in the queue. Insert new entry after the last one ; 60$: MOVL LDWATCH_L_BLINK(R0),R1 ; Pick up address ; of last entry 70$: MOVZBL #1,R0 ; Not found BRB 90$ 80$: CLRL R0 ; Exact match 90$: RSB .SBTTL LD_FIND_WATCH_ENTRY_DIS, Locate a watchpoint entry for disable ;+++ ; LD_FIND_WATCH_ENTRY_DIS, Locate a watchpoint entry for disable ; ; Functional description: ; ; This routine locates a watchpoint entry. Entries are ; inserted in ascending order, so when we are past one ; entry we don't need to look any further. A special case ; is signalled if the NOLBN flag is set. This is the case ; for a non-transfer function. We can have more of these ; for different functions. ; ; Inputs: ; ; R1 - address of ucb watchqueue entry listhead ; R2 - address of the users watchpt block ; ; Outputs: ; ; R0 - 0 if found an exact match ; 1 if not found ; R1 - if R0 = 0 entry address ; if R0 = 1 last entry address ; ;--- UNIVERSAL_SYMBOL LD_FIND_WATCH_ENTRY_DIS ;LD_FIND_WATCH_ENTRY_DIS: PUSHL R3 MOVL R1,R0 ; Save for later 10$: MOVL (R1),R1 ; Next entry CMPL R1,R0 ; End of queue ? BEQL 40$ ; Yes EXTZV #LDWATCHPT_V_CHARS,- ; Extract characteristics bits #LDWATCHPT_S_CHARS,- LDWATCHPT_W_FLAGS(R2),R3 CMPZV #LDWATCH_V_CHARS,- ; Check against current packet #LDWATCH_S_CHARS,- LDWATCH_W_FLAGS(R1),R3 BNEQ 10$ ; No match BBS #LDWATCH_V_NOLBN,- ; Non-transfer function? LDWATCH_W_FLAGS(R1),60$ BBC #LDWATCH_V_FILE,- ; File watchpoint? LDWATCH_W_FLAGS(R1),20$ CMPL LDWATCHPT_L_LBN(R2),- ; Check VBN for matching block LDWATCH_L_VBN(R1) BRB 30$ 20$: CMPL LDWATCHPT_L_LBN(R2),- ; Matching block? LDWATCH_L_LBN(R1) 30$: BEQL 50$ ; Yes BGTRU 10$ ; Not a higher number yet 40$: MOVZBL #1,R0 ; Not found BRB 80$ 50$: BBS #LDWATCHPT_V_REMOVE_ALL,- ; Remove all? LDWATCHPT_W_FLAGS(R2),70$ 60$: CMPW LDWATCHPT_W_FUNC(R2),- ; Matching function? LDWATCH_W_FUNC(R1) BNEQ 10$ ; No CMPW LDWATCHPT_W_ACTION(R2),- ; Matching action? LDWATCH_W_ACTION(R1) BNEQ 10$ ; No 70$: CLRL R0 ; Exact match 80$: MOVL (SP)+,R3 ; Restore register RSB .SBTTL LD_START_GET_WATCH, Start I/O get watch info processing ;+++ ; LD_START_GET_WATCH, Start I/O get watch info processing ; ; Functional description: ; ; This is the completion of the get watchpoint processing. ; ; Inputs: ; ; R3 - address of the IRP (I/O request packet) ; R5 - address of the UCB (unit control block) ; ; Outputs: ; ; None. ; ; The routine must preserve all registers except R0-R2 and R4. ; UNIVERSAL_SYMBOL LD_START_GET_WATCH ;LD_START_GET_WATCH: MOVZWL #SS$_DATALOST,R0 ; Assume nothing to do MOVL UCB$L_LD_WATCHCNT(R5),R1 ; Get packet count BNEQ 10$ ; Something to do BRW 50$ 10$: BBS #LDIO_V_INQUIRE,- ; Return list size? IRP$L_EXTEND(R3),40$ MOVZWL #SS$_IVBUFLEN,R0 ; Assume buffer too small MULL3 R1,#LDWATCHPT_K_LENGTH,R2 ; Get size needed CMPL IRP$L_BCNT(R3),R2 ; Enough space? BLSSU 60$ ; No MOVL IRP$L_SVAPTE(R3),R4 ; Get systembuffer address MOVL (R4),R4 ; Get userbuffer address MOVAL UCB$L_LD_WATCHQFL(R5),R0 ; Get watchqueue listhead MOVL R0,R2 ; Remember start 20$: MOVL (R0),R0 ; Get next entry CMPL R0,R2 ; At the end? BEQL 40$ ; Yes CLRL LDWATCHPT_L_SBK(R4) ; Not returned MOVL LDWATCH_L_LBN(R0),- ; Move logical blocknumber LDWATCHPT_L_LBN(R4) ASSUME LDWATCH_W_ACTION EQ LDWATCH_W_FLAGS+2 MOVL LDWATCH_W_FLAGS(R0),- ; Flags and action LDWATCHPT_W_FLAGS(R4) ASSUME LDWATCH_W_RETCODE EQ LDWATCH_W_FUNC+2 MOVL LDWATCH_W_FUNC(R0),- ; Function and returncode LDWATCHPT_W_FUNC(R4) BBC #LDWATCH_V_FILE,- ; Check for virtual file mode LDWATCH_W_FLAGS(R0),30$ ASSUME LDWATCH_W_FID_NUM+2 EQ LDWATCH_W_FID_SEQ MOVL LDWATCH_W_FID_NUM(R0),- ; Save FID LDWATCHPT_W_FID_NUM(R4) MOVW LDWATCH_W_FID_RVN(R0),- LDWATCHPT_W_FID_RVN(R4) MOVL LDWATCH_L_VBN(R0),- ; Move virtual blocknumber LDWATCHPT_L_LBN(R4) 30$: ADDL2 #LDWATCHPT_K_LENGTH,R4 ; Next entry BRB 20$ 40$: MOVZWL #SS$_NORMAL,R0 ; Return with count in R1 50$: BBS #LDIO_V_INQUIRE,- ; Return list size only? IRP$L_EXTEND(R3),60$ MULL3 #LDWATCHPT_K_LENGTH,R1,R2 ; Total count MOVL R2,IRP$L_BCNT(R3) ; Save for postprocessing INSV R2,#16,#16,R0 ; Merge bytecount 60$: REQCOM .SBTTL LD_START_GET_SUSPEND_LIST, Start I/O get suspend list processing ;+++ ; LD_START_GET_SUSPEND_LIST, Start I/O get suspend list processing ; ; Functional description: ; ; This is the completion of the get suspend list processing. ; ; Inputs: ; ; R3 - address of the IRP (I/O request packet) ; R5 - address of the UCB (unit control block) ; ; Outputs: ; ; None. ; ; The routine must preserve all registers except R0-R2 and R4. ; UNIVERSAL_SYMBOL LD_START_GET_SUSPEND_LIST ;LD_START_GET_SUSPEND_LIST: MOVZWL #SS$_DATALOST,R0 ; Assume nothing to do MOVL UCB$L_LD_WATCHCNT(R5),R1 ; Get packet count BNEQ 10$ ; Something to do BRW 100$ ; Branch assist 10$: CLRL R1 ; No entries yet MOVAL UCB$L_LD_WATCHQFL(R5),R0 ; Get watchqueue listhead MOVL R0,R2 ; Remember start 20$: MOVL (R0),R0 ; Get next entry CMPL R0,R2 ; At the end? BEQL 30$ ; Yes ADDL2 LDWATCH_L_SUSPCNT(R0),R1 ; Count number of entries BRB 20$ 30$: BBS #LDIO_V_INQUIRE,- ; Return list size? IRP$L_EXTEND(R3),90$ MULL3 R1,#LDSUSPLST_K_LENGTH,R2 ; Get size needed CMPL IRP$L_BCNT(R3),R2 ; Enough space? BGEQU 35$ ; Yes MOVZWL #SS$_IVBUFLEN,R0 ; Buffer too small BRW 110$ ; Exit with error 35$: MOVL IRP$L_SVAPTE(R3),R4 ; Get systembuffer address MOVL (R4),R4 ; Get userbuffer address MOVAL UCB$L_LD_WATCHQFL(R5),R0 ; Get watchqueue listhead MOVL R0,R2 ; Remember start 40$: MOVL (R0),R0 ; Get next entry CMPL R0,R2 ; At the end? BEQL 90$ ; Yes TSTL LDWATCH_L_SUSPCNT(R0) ; Get count of this lbn BEQL 40$ ; None here PUSHR #^M MOVAL LDWATCH_L_SUSPFL(R0),R1 ; Point to suspend queue MOVL R1,R2 ; Remember end 50$: MOVL IRP$L_IOQFL(R1),R1 ; Get next entry CMPL R1,R2 ; At the end? BEQL 80$ ; Yes PUSHR #^M BBC #IRP$V_SHDIO,IRP$W_STS2(R1),55$ ; Is this shadowing I/O? MOVL IRP$L_MIRP(R1),R1 ; Yes, get master irp 55$: MOVL IRP$L_PID(R1),R0 ; Get PID JSB G^EXE$IPID_TO_EPID ; Make external PID MOVL R0,LDSUSPLST_L_PID(R4) ; Move PID POPR #^M BBS #LDWATCH_V_FILE,- ; Check for virtual file mode LDWATCH_W_FLAGS(R0),60$ MOVL LDWATCH_L_LBN(R0),- ; Move LBN LDSUSPLST_L_LBN(R4) BRB 70$ 60$: MOVL LDWATCH_L_VBN(R0),- ; Move VBN LDSUSPLST_L_LBN(R4) ASSUME LDSUSPLST_W_ACTION EQ LDSUSPLST_W_FLAGS+2 70$: MOVL LDWATCH_W_FLAGS(R0),- ; Move flags and action LDSUSPLST_W_FLAGS(R4) ASSUME LDSUSPLST_W_RETCODE EQ LDSUSPLST_W_FUNC+2 MOVL LDWATCH_W_FUNC(R0),- ; Move function and retcode LDSUSPLST_W_FUNC(R4) ADDL2 #LDSUSPLST_K_LENGTH,R4 ; Next entry BRB 50$ 80$: POPR #^M BRB 40$ 90$: MOVZWL #SS$_NORMAL,R0 100$: BBS #LDIO_V_INQUIRE,- ; Return list size only? IRP$L_EXTEND(R3),110$ MULL3 #LDSUSPLST_K_LENGTH,R1,R2 ; Total count MOVL R2,IRP$L_BCNT(R3) ; Save for postprocessing INSV R2,#16,#16,R0 ; Merge bytecount 110$: REQCOM .SBTTL LD_START_RESUME_WATCH, Start I/O resume watch processing ;+++ ; LD_START_RESUME_WATCH, Start I/O resume watch processing ; ; Functional description: ; ; This is the completion of the resume watchpoint processing. ; Any previously suspended thread which is specified will be restarted. ; ; Inputs: ; ; R3 - address of the IRP (I/O request packet) ; R5 - address of the UCB (unit control block) ; IRP$L_OBCNT - number of entries in userbuffer ; ; Outputs: ; ; None. ; ; The routine must preserve all registers except R0-R2 and R4. ; UNIVERSAL_SYMBOL LD_START_RESUME_WATCH ;LD_START_RESUME_WATCH: MOVZWL #SS$_DATALOST,R0 ; Assume nothing to do TSTL UCB$L_LD_WATCHCNT(R5) ; Check packet count BEQL 70$ ; Nothing to do PUSHL R6 ; May not be destroyed CLRL R6 ; Nothing resumed yet MOVL IRP$L_SVAPTE(R3),R2 ; Get systembuffer address BEQL 10$ ; Nothing there MOVL (R2),R2 ; Get userbuffer address 10$: MOVL IRP$L_OBCNT(R3),R4 ; Get wanted count MOVAL UCB$L_LD_WATCHQFL(R5),R1 ; Point to listhead MOVL R1,R0 ; Save start for later 20$: MOVL LDWATCH_L_FLINK(R1),R1 ; Next entry CMPL R1,R0 ; End of queue? BEQL 40$ ; Yes TSTL R2 ; All of them? BEQL 30$ ; Yes CMPW LDWATCH_W_FUNC(R1),- ; Right function? LDWATCHPT_W_FUNC(R2) BNEQ 20$ ; No BBS #LDWATCH_V_FILE,- ; Check for virtual file mode LDWATCH_W_FLAGS(R1),25$ CMPL LDWATCH_L_LBN(R1),- ; Right lbn? LDWATCHPT_L_LBN(R2) BNEQ 20$ ; No BRB 30$ 25$: CMPL LDWATCH_L_VBN(R1),- ; Right vbn? LDWATCHPT_L_LBN(R2) BNEQ 20$ ; No 30$: CMPW LDWATCH_W_ACTION(R1),- ; Right action? #WATCH_ACTION_SUSPEND BNEQ 20$ ; No BSBW LD_DRAIN_WATCH_THREAD ; Get rid of watchpoints INCL R6 ; We resumed something BRB 20$ ; Next packet 40$: TSTL R2 ; Everything done? BEQL 50$ ; Yes ADDL2 #LDWATCHPT_K_LENGTH,R2 ; Next entry of input SOBGTR R4,20$ ; Count down 50$: MOVZBL #SS$_NORMAL,R0 ; Assume success TSTL R6 ; Done something? BNEQ 60$ ; Yes MOVZWL #SS$_DATACHECK,R0 ; Nothing done 60$: MOVL (SP)+,R6 ; Recover this one 70$: CLRL R1 REQCOM .SBTTL LD_DRAIN_WATCH_THREAD, drain suspended watch queue ;+++ ; LD_DRAIN_WATCH_THREAD, drain suspended watch queue ; ; Functional description: ; ; This routine resumes all pending watchpoint threads. ; ; Inputs: ; ; R1 - address of LDWATCH block ; R5 - address of the UCB (unit control block) ; ; Outputs: ; ; None. ; ;--- UNIVERSAL_SYMBOL LD_DRAIN_WATCH_THREAD ;LD_DRAIN_WATCH_THREAD: TSTL LDWATCH_L_SUSPCNT(R1) ; Get count of suspended threads BEQL 40$ ; Nothing PUSHR #^M ; Save registers 10$: REMQUE @LDWATCH_L_SUSPFL(R1),R3 ; Remove entry BBSS #UCB$V_LD_FKBBSY,- ; Forkblock busy? UCB$W_LD_FLAGS(R5),20$ MOVQ IRP$L_FR3(R3),- ; Get IRP and function UCB$L_FR3(R5) MOVL IRP$L_FPC(R3),- ; Restore Fork trace PC UCB$L_FPC(R5) JSB G^EXE$QUEUE_FORK ; Queue forkthread BRB 30$ 20$: INSQUE (R3),@UCB$L_LD_WATCHPNDQBL(R5) ; Insert in watchpending queue 30$: DECL LDWATCH_L_SUSPCNT(R1) ; Count packet BNEQ 10$ ; More to do POPR #^M 40$: RSB .SBTTL LD_RESUME_WATCH_THREAD, resume eventual suspended watch thread ;+++ ; LD_RESUME_WATCH_THREAD, resume eventual suspended watch thread ; ; Functional description: ; ; This routine resumes one pending watchpoint thread. ; ; Inputs: ; ; R3 - address of the IRP (I/O request packet) ; R5 - address of the UCB (unit control block) ; ; Outputs: ; ; None. ; ;--- UNIVERSAL_SYMBOL LD_RESUME_WATCH_THREAD ;LD_RESUME_WATCH_THREAD: PUSHR #^M REMQUE @UCB$L_LD_WATCHPNDQFL(R5),R3 ; Remove from watchpending queue BVS 10$ ; Nothing there MOVQ IRP$L_FR3(R3),- ; Get IRP and function UCB$L_FR3(R5) MOVL IRP$L_FPC(R3),- ; Restore Fork trace PC UCB$L_FPC(R5) JSB G^EXE$QUEUE_FORK ; Queue forkthread BRB 20$ 10$: BICW2 #UCB$M_LD_FKBBSY,- ; Reset Forkblock busy flag UCB$W_LD_FLAGS(R5) 20$: POPR #^M RSB .SBTTL LD_CHECK_WATCH, Check for a watchpoint ;+++ ; LD_CHECK_WATCH, Check for a watchpoint ; ; Functional description: ; ; This routine will check if a watchpoint is set for the current I/O ; request. If true, the action we will do depends on the specified ; function for the watchpoint. ; ; We currently have 4 options: ; ; 1. WATCH_ACTION_SUSPEND: Suspend a thread ; 2. WATCH_ACTION_CRASH: Crash the system ; 3. WATCH_ACTION_ERROR: Return an error ; 4. WATCH_ACTION_OPCOM: Send a message to OPCOM ; ; #1 will suspend the current thread. We do this by popping our caller's ; PC into a forkblock and returning to our caller's caller. Later on we can ; resume this thread by queueing the forkblock to the forkdispatcher. ; ; #2 will crash the system if we find a matching watchpoint. ; ; #3 will return a user-specified error for the current thread. ; ; #4 will send an errormessage about the process accessing the watchpoint ; to OPCOM ; ; Inputs: ; ; R3 - address of the IRP (I/O request packet) ; R5 - address of the UCB (unit control block) ; ; Outputs: ; ; R0 - status ; IRP$L_EXTEND - if 0 no action done, continue as usual ; - if 1, R0 contains the error to return ; ; If a thread is suspended we return to our caller's caller ; without R0 set as that's not needed then. ; ; The routine must preserve all registers except R0-R2.. ; ;--- UNIVERSAL_SYMBOL LD_CHECK_WATCH ;LD_CHECK_WATCH: CLRL IRP$L_EXTEND(R3) ; Clear our flag TSTL UCB$L_LD_WATCHCNT(R5) ; Any watchpoint set? BNEQ 10$ ; Yes ; ; For the future: check if FILE watchpoint is on other member of a volumeset. ; We have to check all devices for that. ; ; BBC #UCB$M_LD_ONVOLSET,- ; Check if container file ; UCB$W_LD_FLAGS(R5),5$ ; on a volumeset 5$: BRW 120$ ; Branch assist 10$: MOVAL UCB$L_LD_WATCHQFL(R5),R1 ; Get queue listhead MOVL R1,R0 ; Remember the end 20$: MOVL LDWATCH_L_FLINK(R1),R1 ; Next entry CMPL R1,R0 ; End of queue ? BEQL 5$ ; Yes CMPW LDWATCH_W_FUNC(R1),#^XFFFF ; Allow all functions? BEQL 30$ ; Yes, check LBN too CMPW IRP$W_FUNC(R3),- ; Match of functioncode LDWATCH_W_FUNC(R1) ; including modifiers? BNEQ 20$ ; No EXTZV #IRP$V_FCODE,#IRP$S_FCODE,- ; Extract the function code IRP$W_FUNC(R3),R2 CMPL R2,#IO$_READPBLK ; Is this a read? BEQL 30$ ; Yes, check lbn CMPL R2,#IO$_WRITEPBLK ; or a write? BEQL 30$ ; Yes, check lbn CMPL R2,#IO$_WRITECHECK ; Writecheck? BEQL 30$ ; Yes, check lbn CMPL R2,#IO$_DSE ; Erase? BNEQ 50$ ; No 30$: CMPL IRP$L_UCB(R3),- ; Matching UCB (volumeset?) LDWATCH_L_UCB(R1) BNEQ 20$ ; No CMPL IRP$L_MEDIA(R3),- ; Matching block? LDWATCH_L_LBN(R1) BGTRU 20$ ; No, requested block above wpt ASHL #-9,IRP$L_BCNT(R3),R2 ; Convert bytes to blocks BITL #511,IRP$L_BCNT(R3) ; Multiple of 512 bytes? BNEQ 40$ ; No DECL R2 ; Adjust 40$: ADDL2 IRP$L_MEDIA(R3),R2 ; Calculate last block CMPL R2,LDWATCH_L_LBN(R1) ; Matching block? BLSSU 20$ ; No ; ; We've got a match. Now check what to do with it. ; 50$: DISPATCH LDWATCH_W_ACTION(R1),TYPE=W,<- ; Dispatch according to function ,- ,- ,- > BUG_CHECK INCONSTATE,FATAL ; We should never come here 60$: MOVZWL LDWATCH_W_RETCODE(R1),R0 ; Pick up errorcode BRB 110$ ; ; Crash the system. R1 = LDWATCH, R3 = IRP, R5 = UCB ; 70$: BUG_CHECK RSVD_LP,FATAL ; Byebye, that's what's been asked for 80$: MOVL (SP)+,IRP$L_FPC(R3) ; Save caller's PC in forkblock MOVQ R3,IRP$L_FR3(R3) ; Save registers INSQUE (R3),@LDWATCH_L_SUSPBL(R1) ; Insert in suspend queue INCL LDWATCH_L_SUSPCNT(R1) ; Count packet and return to caller's caller BRB 120$ ; Return to caller's caller 90$: PUSHL R0 ; Save end of chain BSBW LD_SEND_OPCOM ; Queue message to OPCOM BLBC R0,100$ ; Trouble MOVL (SP)+,R0 BRW 20$ ; Check next packet 100$: ADDL2 #4,SP ; Restore SP 110$: INCL IRP$L_EXTEND(R3) ; Flag action needed 120$: RSB .SBTTL LD_SEND_OPCOM, Send a message about a touched watchpoint to OPCOM ;+++ ; LD_SEND_OPCOM, Send a message about a touched watchpoint to OPCOM ; ; Functional description: ; ; This routine allocates and fills a buffer with watchpoint info ; and queues this buffer to LD_OPCOM_AST as an exec-mode AST ; routine in the context of the issueing process. The AST routine ; will then invoke the system-service $SNDOPR to process the request. ; We use an EXEC-mode AST because $SNDOPR is an execmode system service, ; which cannot be called from kernel mode. We don't want to use a ; usermode AST because we then need to modify the pageprotection ; of the ACB to allow user-read access. If there is no issueing process ; (a mount-verification thread for example) then we queue the AST to ; the OPCOM. ; ; Inputs: ; ; R1 - LDWATCH structure address ; R3 - address of the IRP (I/O request packet) ; R5 - address of the UCB (unit control block) ; ; Outputs: ; ; R0 - Status ; ; The routine must preserve all registers except R0 and R2 ; ;--- UNIVERSAL_SYMBOL LD_SEND_OPCOM ;LD_SEND_OPCOM: PUSHR #^M MOVL IRP$L_PID(R3),R4 ; I/O from a process? BGTR 20$ ; Yes (no mount-verification thread) ; ; This thread has no PID but a system-space address (mount-verification for example). ; We must queue an AST to a process, but we don't know which. We can't use the ; SWAPPER for this purpose because it never leaves kernel-mode, so an exec-mode ; ast would not get delivered. We can queue the ast to OPCOM. If it's not there, ; then there's no need to process the opcom request anyway. ; BSBW LD_FIND_OPCOM ; Check if OPCOM is running BLBS R0,20$ ; Yes MOVZWL #SS$_NORMAL,R0 ; No need to bother this thread, dismiss 10$: BRW 60$ ; Exit 20$: MOVZWL #LDSNDOPRLST_K_LENGTH,R1 ; This size JSB G^EXE$ALONONPAGED ; Get buffer BLBC R0,10$ ; Quit MOVW R1,LDSNDOPRLST_W_SIZE(R2) ; Save size MOVL (SP),R1 MOVB #DYN$C_ACB,- ; Used as an ACB LDSNDOPRLST_B_TYPE(R2) CLRL LDSNDOPRLST_L_KAST(R2) ; Assume normal process CMPL R4,IRP$L_PID(R3) ; Just a normal process? BEQL 30$ ; Yes INCL LDSNDOPRLST_L_KAST(R2) ; Special process 30$: MOVL R4,LDSNDOPRLST_L_PID(R2) ; Issueing process or OPCOM MOVB #,- ; Exec mode, astroutine will LDSNDOPRLST_B_RMOD(R2) ; deallocate this buffer MOVAB LD_OPCOM_AST,- ; Routine to execute LDSNDOPRLST_L_AST(R2) MOVL R2,LDSNDOPRLST_L_ASTPRM(R2) ; Setup our block as parameter BBS #LDWATCH_V_FILE,- ; Check for virtual file mode LDWATCH_W_FLAGS(R1),40$ MOVL LDWATCH_L_LBN(R1),- ; Move lbn LDSNDOPRLST_L_LBN(R2) BRB 50$ 40$: MOVL LDWATCH_L_VBN(R1),- ; Move vbn for filemode LDSNDOPRLST_L_LBN(R2) 50$: ASSUME LDSNDOPRLST_W_ACTION EQ LDSNDOPRLST_W_FLAGS+2 MOVL LDWATCH_W_FLAGS(R1),- ; Move flags and action LDSNDOPRLST_W_FLAGS(R2) ASSUME LDSNDOPRLST_W_RETCODE EQ LDSNDOPRLST_W_FUNC+2 MOVL LDWATCH_W_FUNC(R1),- ; Move function and retcode LDSNDOPRLST_W_FUNC(R2) ASSUME LDSNDOPRLST_W_FID_NUM+2 EQ LDSNDOPRLST_W_FID_SEQ MOVL LDWATCH_W_FID_NUM(R1),- ; Save FID LDSNDOPRLST_W_FID_NUM(R2) MOVW LDWATCH_W_FID_RVN(R1),- LDSNDOPRLST_W_FID_RVN(R2) PUSHR #^M MOVZWL #LDSNDOPRLST_K_DEVNAM-1,R0 ; Buffersize MOVAB LDSNDOPRLST_T_DEVNAM+1(R2),R1 ; Buffer for devicename MOVL #-2,R4 ; DVI$_DISPLAY_DEVNAM JSB G^IOC$CVT_DEVNAM ; Get devicename in readable form MOVB R1,LDSNDOPRLST_T_DEVNAM(R2) ; Save returned length MOVL R2,R5 ; ACB in R5 MOVZBL #PRI$_IOCOM,R2 ; Set up priority increment JSB G^SCH$QAST ; Queue the AST POPR #^M BLBS R0,60$ ; All ok ; ; We can still get a NONEXPR error back, despite the check at the start of ; this routine for the presence of our process or OPCOM. If OPCOM went away ; we may get the error. This could normally only happen on SMP systems. ; CMPL R0,#SS$_NONEXPR ; Not found? BNEQ 70$ ; No, something else is wrong MOVZWL #SS$_NORMAL,R0 ; Pretend nothing's wrong 60$: POPR #^M RSB 70$: BUG_CHECK INCONSTATE,FATAL ; We want to know about this .SBTTL LD_FIND_OPCOM, Find the pid of the OPCOM process ;+++ ; LD_FIND_OPCOM, Find the pid of the OPCOM process ; ; Functional description: ; ; This routine locates the internal pid of the OPCOM process. ; ; Inputs: ; ; None ; ; Outputs: ; ; R0 - Status ; R4 - Internal pid of opcom ; ; The routine must preserve all registers except R0 and R4 ; ;--- UNIVERSAL_SYMBOL LD_FIND_OPCOM ;LD_FIND_OPCOM: PUSHR #^M LOCK LOCKNAME=SCHED,- ; Lock sched database SAVIPL=-(SP),- ; Save current ipl PRESERVE=NO ; R0 may be destroyed MOVL G^SCH$GL_PCBVEC,R0 ; Point to process vector CLRL R1 ; Setup index 10$: MOVL (R0)[R1],R2 ; Get a PCB entry CMPL R2,G^SCH$AR_NULLPCB ; NULL entry? BEQL 20$ ; Yes, get next CMPL PCB$L_UIC(R2),- ; UIC match ([1,4])? #^X00010004 BNEQ 20$ ; No MOVZBL PCB$T_LNAME(R2),R3 ; Get bytecount of process name INCL R3 ; Check count as well PUSHR #^M CMPC3 R3,PCB$T_LNAME(R2),- ; Check for OPCOM OPCOM_NAME POPR #^M BNEQ 20$ ; No match MOVL PCB$L_PID(R2),R4 ; Get internal pid MOVZWL #SS$_NORMAL,R0 ; Success! BRB 30$ 20$: AOBLEQ G^SCH$GL_MAXPIX,R1,10$ ; Next entry MOVZWL #SS$_NONEXPR,R0 ; Not found 30$: UNLOCK LOCKNAME=SCHED,- ; Unlock sched database NEWIPL=(SP)+,- ; Restore previous ipl CONDITION=RESTORE ; Restore access count on lock POPR #^M RSB .SBTTL LD_RETURN_QUOTA, Return quota of charged buffer to user ;+++ ; LD_RETURN_QUOTA, Return quota of charged buffer to user ; ; Functional description: ; ; This routine returns the quota of a buffer we charged for ; back to a user. Since we may be in system context we will ; queue an ast to the user to return the quota. If the input ; parameter is < 0 it is a system space address of a WCB for ; which we need to return the quota. ; ; Inputs: ; ; R1 - number of bytes to return to BYTCNT and BYTLM, or WCB pointer ; R4 - pid of process to credit ; R5 - UCB ; ; Outputs: ; ; R0 - status ; ; The routine must preserve all registers except R0-R2 and R4. ; ;--- UNIVERSAL_SYMBOL LD_RETURN_QUOTA ;LD_RETURN_QUOTA: PUSHR #^M BSBW LD_ALLO_LDIOB ; Get ACB BLBC R0,10$ ; Check for errors MOVL R2,R5 ; Get ACB address MOVL R1,ACB$L_ASTPRM(R5) ; Save bytecount to return MOVL R4,ACB$L_PID(R5) ; Save PID MOVB #,- ; Special kernel mode ast ACB$B_RMOD(R5) MOVAB LD_QUOTA_AST,- ; Routine to execute ACB$L_KAST(R5) MOVZBL #PRI$_IOCOM,R2 ; Set up priority increment JSB G^SCH$QAST ; Queue the AST 10$: POPR #^M ; Restore registers RSB .SBTTL LD_QUOTA_AST, Return quota action routine ;+++ ; LD_QUOTA_AST, Return quota action routine ; ; Functional description: ; ; This routine is called as an AST routine in the context of the ; process issueing the I/O. ; ; This routine has two functions, depending on ACB$L_ASTPRM: ; ; If < 0, it is a pointer to a WCB for which we have ; to return the quota to the user. ; If > 0 it is the number of bytes to credit a user. ; ; Inputs: ; ; R4 - address of the PCB (process control block) ; R5 - address of the ACB ; ACB$L_ASTPRM(R5) - if > 0: Number of bytes to return ; if < 0: WCB address ; ; The routine must preserve all registers except R0-R5. ; UNIVERSAL_SYMBOL LD_QUOTA_AST ;LD_QUOTA_AST: MOVL ACB$L_ASTPRM(R5),R3 ; Get parameter MOVL R5,R0 ; Address of ACB JSB G^EXE$DEANONPAGED ; Get rid of ACB MOVL R3,R0 BGTR 20$ ; Byte count ; ; Remove the pointer from the channel to the window, and return the ; previously allocated bytecount quota to the user. ; BBSS #WCB$V_SHRWCB,- ; Make WCB a shared structure WCB$B_ACCESS(R0),10$ ; if not already done JSB G^MMG$RET_BYT_QUOTA ; Return byte count quota 10$: ADAWI #1,WCB$W_REFCNT(R0) ; Count another reference BRB 30$ 20$: JSB G^EXE$CREDIT_BYTCNT_BYTLM ; Return quota 30$: MOVZWL #SS$_NORMAL,R0 RSB .SBTTL LD_COMPLETE, I/O completion routine ;+++ ; LD_COMPLETE, I/O Completion routine. ; ; This routine locates the IRP whose address is into R5 in the ; LDIO list, removes the entry from the queue, restores the PID ; and inserts the IRP back in the I/O completion queue. ; ; Inputs: ; ; R5 - IRP ; R1 - Address of LD_COMPLETE rtn ; IPL - IPL$_IOPOST ; ; This routine may use R0 to R5. ; ;--- UNIVERSAL_SYMBOL LD_COMPLETE ;LD_COMPLETE: MOVL R5,R3 ; Save IRP MOVL IRP$L_LD_LDUCB(R5),R5 ; Get UCB address logical disk FORKLOCK LOCK=UCB$B_FLCK(R5),- ; Raise to Fork level PRESERVE=NO MOVL UCB$L_LD_PDUCB(R5),R4 ; Get the physical disk UCB FORKLOCK LOCK=UCB$B_FLCK(R4),- ; Raise to Fork level SAVIPL=-(SP),- ; (Physical device) PRESERVE=NO DECW UCB$W_QLEN(R4) ; Decrease queue length FORKUNLOCK LOCK=UCB$B_FLCK(R4),- ; Release lock NEWIPL=(SP)+,- PRESERVE=NO,- CONDITION=RESTORE MOVL IRP$L_LD_LDIOB(R3),R2 ; Get LDIOB address ; ; Remove the forwarded IRP from the LDIOB queue, and deallocate it. If ; this was the last outstanding IRP, then complete the I/O. Before de- ; allocation, update the accumulated byte count and status fields. ; REMQUE IRP$L_LD_FWDQFL(R3),R0 ; Remove forwarded IRP SUBL2 #IRP$L_LD_FWDQFL,R0 ; Point to real IRP section ADDL2 IRP$L_IOST1+2(R0),- ; Count accumulated byte count LDIOB_L_ABCNT(R2) BLBS IRP$L_IOST1(R0),10$ ; Check the return status MOVW IRP$L_IOST1(R0),LDIOB_W_IOST(R2); Update if errors 10$: BSBW LD_DEAL_FWIRP ; Deallocate the FWIRP ADAWI #-1,LDIOB_W_IRPCNT(R2) ; Decrement ref. count BEQL 20$ ; Now complete the I/O BRW 75$ 20$: MOVL LDIOB_L_IRP(R2),R3 ; Restore correct IRP ; ; Set the return status and the accumulated byte count in the IRP. ; MOVW LDIOB_W_IOST(R2),IRP$L_IOST1(R3); Copy the return status MOVL LDIOB_L_ABCNT(R2),- ; And the byte count IRP$L_IOST1+2(R3) CLRW IRP$L_IOST1+6(R3) ; Zero unused word REMQUE LDIOB_L_QFL(R2),R4 ; Remove LDIOB from queue ; ; LD_SAVE_TRACE_ALT calls LD_TRACE which may fork! ; PUSHAB 70$ ; Return in case of fork BSBW LD_SAVE_TRACE_ALT ; Save trace data and dealloc LDIOB JSB G^IOC$POST_IRP ; Insert IRP back in post queue BSBW POST_PACKACK ; Post packack processing INCL UCB$L_OPCNT(R5) ; Count I/O (Logical device) BBC #UCB$V_LD_DISPEN,- ; Is a disconnect pending ? UCB$W_LD_FLAGS(R5),40$ PUSHR #^M MOVQ UCB$L_FR3(R5),R3 ; Restore R3,R4 JSB @UCB$L_FPC(R5) ; Resume the fork thread POPR #^M 40$: .IF DF MDDRIVER_WORKAROUND ; ; Work around MDdriver bug. If an erase function is given to DECRAM, then the ; iosb does not contain the bytecount but zero. In a certain configuration ; (DSA1: -> LDA1: -> MDA1:) this will lead to a loop since the XQP attempts ; to erase a number of blocks, and it looks in the returned bytecount to see ; how much is left to do. ; BBC #UCB$V_LD_DECRAM,- ; Connected to DECRAM disk? UCB$W_LD_FLAGS(R5),50$ CMPZV #IRP$V_FCODE,#IRP$S_FCODE,- ; Check the function code IRP$W_FUNC(R3),#IO$_DSE ; Erase? BNEQ 50$ ; No MOVL IRP$L_BCNT(R3),IRP$L_IOST1+2(R3); Return correct bytecount 50$: .ENDC .IF DF DUDRIVER_WORKAROUND ; ; Hack around DUdriver bug. The DUdriver may stall an I/O request by queueing ; an IRP to the physical device UCB when UCB$L_RWAITCNT is not zero. This may ; happen when a connection runs out of credits. When the credits are returned, ; the DUdriver never looks at the UCB$L_IOQFL queue with pending packets, ; which causes everything to wait for this packet. Triggering mount ; verification for the device will solve it, because in mv's return path a ; check is made for a non empty i/o queue. We work around it by checking ; for stalled I/O's in our return path. This is not guaranteed to work ; 100% of the time, but since i made this modification i could not get things ; to go wrong. ; PUSHL R5 ; Save logical UCB MOVL UCB$L_LD_PDUCB(R5),R5 ; Get the physical disk UCB BBS #DEV$V_SCSI,UCB$L_DEVCHAR2(R5),60$ ; Leave SCSI devices alone FORKLOCK LOCK=UCB$B_FLCK(R5),- ; Raise to Fork level SAVIPL=-(SP),- ; (Physical device) PRESERVE=NO JSB G^SCS$UNSTALLUCB ; Check for queued i/o's FORKUNLOCK LOCK=UCB$B_FLCK(R5),- ; Release lock NEWIPL=(SP)+,- PRESERVE=NO,- CONDITION=RESTORE 60$: MOVL (SP)+,R5 .ENDC ; RSB ; Return to IOPOST rtn ; ; We will return here either after we successfully processed all ; data, or after a fork of LD_TRACE. In either case, release our ; forklock. If we are resumed after a fork, the forkdispatcher ; will call us again with the forklock held. ; 70$: BSBW LD_RESUME_WATCH_THREAD ; Resume eventual suspended watch thread 75$: FORKUNLOCK LOCK=UCB$B_FLCK(R5),- ; Release lock NEWIPL=#IPL$_IOPOST,- PRESERVE=NO,- CONDITION=RESTORE RSB ; Either ; UNIVERSAL_SYMBOL POST_PACKACK ;POST_PACKACK: EXTZV #IRP$V_FCODE,#IRP$S_FCODE, - IRP$W_FUNC(R3),R1 ; Extract the function code CMPW R1,#IO$_PACKACK ; Packack? BNEQ 10$ ; No BBC #UCB$V_LD_REPLACE,- ; Check if replaced device UCB$W_LD_FLAGS(R5),10$ ; ; Setup device characteristics in case they were not available at ; drive connect time. ; MOVL UCB$L_LD_PDUCB(R5),R4 ; Get the physical disk UCB MOVB UCB$B_DEVTYPE(R4),- ; Copy device type UCB$B_DEVTYPE(R5) ASSUME UCB$B_TRACKS EQ UCB$B_SECTORS+1 ASSUME UCB$W_CYLINDERS EQ UCB$B_TRACKS+1 MOVL UCB$B_SECTORS(R4),- ; Copy sectors, tracks UCB$B_SECTORS(R5) ; and cylinders MOVL UCB$L_MAXBLOCK(R4),- ; Copy maximum block UCB$L_MAXBLOCK(R5) 10$: RSB .SBTTL LD_CANCEL, Generic Cancel I/O routine ;+++ ; LD_CANCEL, Generic Cancel I/O routine ; ; Functional description: ; ; This routine cancels all outstanding I/O for the specified ; channel and PID. We also check if the IRP is in the trace ; wait queue and remove that if true. We also check if there ; was a watchpoint suspended and process that too. ; ; Inputs: ; ; R2 - Channel index number ; R3 - IRP from UCB$L_IRP ; R4 - PCB ; R5 - UCB ; R8 - Cancel reason code, one of : ; CAN$C_CANCEL If called through $CANCEL ; system service. ; CAN$C_DASSGN If called through $DASSGN ; or $DALLOC system service. ; ; Outputs: ; ; The routine must preserve all registers exept R0-R3 ; ;--- UNIVERSAL_SYMBOL LD_CANCEL ;LD_CANCEL: CMPL R8,#CAN$C_DASSGN ; Called through $DASSGN ? BEQL 10$ ; Yes, no active I/O BSBW LD_CANCEL_IO ; Cancel this request 10$: BSBW LD_CANCEL_TRACE ; Cancel pending trace thread BSBW LD_CANCEL_WATCH ; Cancel suspended watchpoint BBC #UCB$V_DELETEUCB,- ; Only if we're going away UCB$L_STS(R5),20$ BICL2 #DEV$M_CLU,UCB$L_DEVCHAR2(R5) ; Reset cluster wide visible MOVL UCB$L_LD_CPID(R5),UCB$L_CPID(R5); Restore charge pid MOVW UCB$W_LD_CHARGE(R5),- ; Restore charged amount UCB$W_CHARGE(R5) 20$: RSB ; Return .SBTTL LD_CANCEL_IO, Cancel I/O routine ;+++ ; LD_CANCEL_IO, Cancels I/O operations in progress ; ; Functional description: ; ; This routine cancels all outstanding I/O for the specified ; channel and PID. We search the active IO list for LDIOB's ; containing that specific PID. If found, the channel number ; in the IRP is checked against the cancel channel number. ; If a match is found the IRP is traced to the physical disk ; driver where it is either removed from the IO wait queue ; and put into the post-processing queue, or the physical ; disk it's cancel IO routine is called, with the correct ; registers. ; ; Inputs: ; ; R2 - Channel index number ; R3 - IRP from UCB$L_IRP ; R4 - PCB ; R5 - UCB ; R8 - Cancel reason code ; ; Outputs: ; ; The routine must preserve all registers exept R0-R3 ; The routine may set the UCB CANCEL bit in UCB$W_STS ; ;--- UNIVERSAL_SYMBOL LD_CANCEL_IO ;LD_CANCEL_IO: PUSHL R6 MOVAL UCB$L_LD_AIOFL(R5),R0 ; Get addr. active IO list MOVL R0,R1 ; Copy it to R1 10$: MOVL LDIOB_L_QFL(R1),R1 ; Get an entry CMPL R1,R0 ; Back at start ? BEQL 30$ ; Yes, whole list done PUSHL R0 ; Save R0 CMPL LDIOB_L_PID(R1),IRP$L_PID(R3) ; Is this the right process ? BNEQ 20$ ; No MOVL LDIOB_L_IRP(R1),R0 ; Get IRP address CMPW IRP$W_CHAN(R0),R2 ; Is this the right channel ? BEQL 40$ ; Yes 20$: MOVL (SP)+,R0 ; Copy for next block BRB 10$ ; Loop through list 30$: MOVL (SP)+,R6 RSB 40$: MOVAL LDIOB_L_FWDQFL(R1),R0 ; Extract forwarded IRP MOVL R0,R6 ; Copy it to R0 MOVL (R0),R0 ; Get next packet in queue CMPL R6,R0 ; Back at start ? BEQL 20$ ; Continue loop PUSHR #^M ; Save some registers we need MOVL UCB$L_LD_PDUCB(R5),R3 ; Get the phys.disk UCB MOVZBL UCB$B_FLCK(R5),R1 ; Get our FLCK CMPB UCB$B_FLCK(R5),UCB$B_FLCK(R3) ; Compare Forklock index BLSS 50$ ; Ok, ours is larger or equal MOVZBL UCB$B_FLCK(R3),R1 ; Get the lowest of the 2 50$: MOVL R3,R5 ; Change to Phys.disk UCB FORKLOCK LOCK=R1,- ; Synchronize SAVIPL=-(SP) MOVAL UCB$L_IOQFL(R5),R3 ; Get addr. of wait queue list MOVL R3,R1 ; Copy it into R1 60$: MOVL IRP$L_IOQFL(R1),R1 ; Get next packet in the queue CMPL R1,R3 ; End of queue ? BEQL 80$ ; Not found, call CANCELIO CMPL R1,R0 ; Is this the one ? BNEQ 60$ ; No, search the rest REMQUE IRP$L_IOQFL(R1),R3 ; Remove it from the queue BBC #IRP$V_BUFIO,IRP$W_STS(R3),70$ ; Direct or buffered IO ? BICW2 #IRP$M_FUNC,IRP$W_STS(R3) ; Clear buffered read 70$: MOVW #SS$_CANCEL,IRP$L_IOST1(R3) ; Set return status JSB G^IOC$POST_IRP ; Insert IRP back in post queue BRB 90$ 80$: MOVL UCB$L_DDT(R5),R0 ; Get address of DDT MOVL UCB$L_IRP(R5),R3 ; Get current IO packet address JSB @DDT$L_CANCEL(R0) ; Call cancel IO rtn 90$: FORKUNLOCK LOCK=UCB$B_FLCK(R5),- ; Release lock NEWIPL=(SP)+,- PRESERVE=NO,- CONDITION=RESTORE POPR #^M ; Restore saved registers BRW 20$ ; Continue search .SBTTL LD_CANCEL_TRACE, Cancel trace I/O routine ;+++ ; LD_CANCEL_TRACE, Cancel trace I/O routine ; ; Functional description: ; ; This routine checks if there's an IRP in the trace wait queue and ; transfer that to the post-processing if true. We can't use the input ; IRP since it may currently not be valid (it comes from UCB$L_IRP), ; so we check against the PID from the PCB. ; ; Inputs: ; ; R2 - Channel index number ; R3 - IRP from UCB$L_IRP ; R4 - PCB ; R5 - UCB ; R8 - Cancel reason code ; ; Outputs: ; ; The routine must preserve all registers exept R0-R3 ; ;--- UNIVERSAL_SYMBOL LD_CANCEL_TRACE ;LD_CANCEL_TRACE: MOVAL UCB$L_LD_TRCWAITQFL(R5),R0 ; Get addr. of waiting trace irp MOVL R0,R1 ; Copy it to R1 10$: MOVL IRP$L_IOQFL(R1),R1 ; Get an entry CMPL R1,R0 ; Back at start ? BEQL 20$ ; Yes, whole list done CMPL IRP$L_PID(R1),PCB$L_PID(R4) ; Is this the right process ? BNEQ 10$ ; No CMPW IRP$W_CHAN(R1),R2 ; Is this the right channel ? BNEQ 10$ ; No CLRL IRP$L_SVAPTE(R1) ; No more SVAPTE MOVZWL #SS$_CANCEL,IRP$L_IOST1(R1) ; Set return status PUSHR #^M REMQUE IRP$L_IOQFL(R1),R3 ; Remove it JSB G^IOC$POST_IRP ; Insert IRP back in post queue POPR #^M 20$: RSB .SBTTL LD_CANCEL_WATCH, Cancel watch I/O routine ;+++ ; LD_CANCEL_WATCH, Cancel watch I/O routine ; ; Functional description: ; ; This routine will check if there's a suspended watchpoint ; pending for the I/O. If true we will remove it. We can't ; use the input IRP since it may currently not be valid (it ; comes from UCB$L_IRP), so we check against the PID from ; the PCB. ; ; Inputs: ; ; R2 - Channel index number ; R3 - IRP from UCB$L_IRP ; R4 - PCB ; R5 - UCB ; R8 - Cancel reason code ; ; Outputs: ; ; The routine must preserve all registers exept R0-R3 ; ;--- UNIVERSAL_SYMBOL LD_CANCEL_WATCH ;LD_CANCEL_WATCH: TSTL UCB$L_LD_WATCHCNT(R5) ; Any watchpoint active? BEQL 40$ ; No MOVAL UCB$L_LD_WATCHQFL(R5),R0 ; Get addr. of watchpoint entry MOVL R0,R1 ; Save for later 10$: MOVL LDWATCH_L_FLINK(R1),R1 ; Get an entry CMPL R1,R0 ; Back at start ? BEQL 40$ ; Yes, whole list done TSTL LDWATCH_L_SUSPCNT(R1) ; Anything in suspend queue? BEQL 10$ ; No PUSHR #^M MOVAL LDWATCH_L_SUSPFL(R1),R0 ; Point to suspend listhead MOVL R0,R1 ; Save for later 20$: MOVL IRP$L_IOQFL(R1),R1 ; Get waiting irp CMPL R1,R0 ; Back at start? BEQL 30$ ; Yes CMPL IRP$L_PID(R1),PCB$L_PID(R4) ; Is this the right process ? BNEQ 20$ ; No CMPW IRP$W_CHAN(R1),R2 ; Is this the right channel ? BNEQ 20$ ; No MOVZWL #SS$_CANCEL,IRP$L_IOST1(R1) ; Set return status REMQUE IRP$L_IOQFL(R1),R3 ; Remove it JSB G^IOC$POST_IRP ; Insert IRP back in post queue POPR #^M DECL LDWATCH_L_SUSPCNT(R1) ; Count the one we removed BRW 10$ 30$: POPR #^M ; Restore, continue scan BRW 10$ 40$: RSB .SBTTL LD_ALLO_LDIOB, Allocate I/O data block ;+++ ; LD_ALLO_LDIOB, Allocate I/O data block ; ; Functional description: ; ; This routine allocates and initializes an I/O datablock which is used ; for I/O requests as well as trace information. It tries to get a block ; from the free queue, if that fails we get one from the system's pool. ; ; Inputs: ; ; R5 - UCB address ; ; Output: ; ; R0 - Status ; R2 - LDIOB address ; ; The routine must preserve all registers except R0 and R2. ; ;--- UNIVERSAL_SYMBOL LD_ALLO_LDIOB ;LD_ALLO_LDIOB: REMQUE @UCB$L_LD_IOBFL(R5),R2 ; Get a packet from free queue BVC 20$ ; Branch if got one PUSHL R1 MOVZWL #LDIOB_K_LENGTH,R1 ; Get packet length JSB G^EXE$ALONONPAGED ; Allocate packet from pool BLBC R0,10$ ; Check the return status MOVW R1,LDIOB_W_SIZE(R2) ; Setup size field MOVB #DYN$C_BUFIO,LDIOB_B_TYPE(R2) ; Setup type field 10$: MOVL (SP)+,R1 ; Restore register BLBC R0,30$ ; Return on error 20$: CLRL LDIOB_L_ABCNT(R2) ; Initialize accumulated byte count CLRW LDIOB_W_IRPCNT(R2) ; Initialize irp count MOVAL LDIOB_L_FWDQFL(R2),- ; Initialize forward IRP queue LDIOB_L_FWDQFL(R2) MOVAL LDIOB_L_FWDQFL(R2),- LDIOB_L_FWDQBL(R2) CLRB LDIOB_B_RSVD(R2) ; Clear reserved field MOVZWL #SS$_NORMAL,R0 ; Success 30$: RSB ; Return .SBTTL LD_ALLO_FWIRP, Allocate a forward IRP ;+++ ; LD_ALLO_FWIRP, Allocate a forward IRP ; ; Functional description : ; ; This routine allocates a FW IRP. It first tries to grab one from the ; free queue. If this fails, it allocates one from pool, and initializes ; it. ; ; Inputs : ; ; R5 - UCB ; ; Outputs : ; ; R3 - New allocated IRP ; R4 - LDIOB ; R5 - UCB ; ;--- UNIVERSAL_SYMBOL LD_ALLO_FWIRP ;LD_ALLO_FWIRP: PUSHR #^M MOVZWL #IRP$K_LD_IRPLEN,R1 ; Get packet length REMQUE @UCB$L_LD_IRPFL(R5),R3 ; Get a packet from free queue BVC 10$ ; Branch if got one JSB G^EXE$ALONONPAGED ; Allocate packet from pool BLBC R0,20$ ; Check the return status MOVL R2,R3 ; Copy it to R3 10$: PUSHR #^M MOVC5 #0,(SP),#0,R1,(R3) ; Zero the packet POPR #^M MOVW R1,IRP$W_SIZE(R3) ; Initialize packet size ASSUME IRP$B_TYPE+1 EQ IRP$B_RMOD MOVW #DYN$C_IRP,IRP$B_TYPE(R3) ; Initialize packet type POPR #^M RSB ; Return 20$: BUG_CHECK INCONSTATE,FATAL .SBTTL LD_DEAL_FWIRP, Deallocate forward IRP ;+++ ; LD_DEAL_FWIRP, Deallocate forward IRP ; ; Functional description : ; ; This routine restores forward IRPs in the FW IRP free queue. ; ; Inputs : ; ; R0 - FWIRP ; R5 - UCB ; ; Outputs : ; ; R5 - UCB ; ;--- UNIVERSAL_SYMBOL LD_DEAL_FWIRP ;LD_DEAL_FWIRP: INSQUE IRP$L_IOQFL(R0),- ; Insert packet in free queue @UCB$L_LD_IRPBL(R5) RSB ; Return ;+++ ; LD_DEAL_LDIOB, Deallocate LDIOB ; ; Functional description : ; ; This routine restores LDIOBs in the LDIOB free queue. ; ; Inputs : ; ; R0 - LDIOB ; R5 - UCB ; ; Outputs : ; ; None ; ;--- UNIVERSAL_SYMBOL LD_DEAL_LDIOB ;LD_DEAL_LDIOB: INSQUE LDIOB_L_QFL(R0),- ; Insert packet in free queue @UCB$L_LD_IOBBL(R5) RSB ; Return .SBTTL LD_SAVE_TRACE, Save trace I/O data .SBTTL LD_SAVE_TRACEALT, Save trace I/O data, alternate entrypoint ;+++ ; LD_SAVE_TRACE - Save trace I/O data ; LD_SAVE_TRACE_ALT - Save trace I/O data, alternate entrypoint ; ; Functional description: ; ; This routine is called just before going to REQCOM to save the eventual ; trace data. ; ; Inputs for LD_SAVE_TRACE: ; ; R0/R1 - Return status ; R3 - address of the IRP (I/O request packet) ; R5 - address of the UCB (unit control block) ; ; This routine must preserve all registers except R2 and R4. ; ; Inputs for LD_SAVE_TRACE_ALT: ; ; R3 - address of the IRP (I/O request packet) ; R4 - address of LDIOB ; R5 - address of the UCB (unit control block) ; ; This routine must preserve all registers except R0-R2 and R4. ; ;--- UNIVERSAL_SYMBOL LD_SAVE_TRACE ;LD_SAVE_TRACE: MOVL R3,UCB$L_IRP(R5) ; Restore IRP TSTL UCB$L_LD_TRCBUF(R5) ; Start of buffer BNEQ 5$ ; Got one BRW 30$ 5$: MOVQ R0,-(SP) ; Save status BSBW LD_ALLO_LDIOB ; Allocate pool to hold ; temporary tracedata BLBC R0,20$ ; Exit on error MOVQ (SP)+,LDIOB_Q_STAT(R2) ; Save in temp IOSB MOVL R2,R4 MOVL IRP$L_MEDIA(R3),- LDIOB_L_MEDIA(R4) ; LBN MOVL IRP$L_BCNT(R3),- LDIOB_L_BCNT(R4) ; Bytecount MOVW IRP$W_FUNC(R3),- LDIOB_W_FUNC(R4) ; Functioncode CLRL LDIOB_L_PID(R4) ; Assume no pid BBS #IRP$V_MVIRP,IRP$W_STS(R3),10$ ; Mount verify IRP? BBS #IRP$V_SHDIO,IRP$W_STS2(R3),10$ ; Is it shadowing I/O ? MOVL IRP$L_PID(R3),R0 ; Get IPID JSB G^EXE$IPID_TO_EPID ; Make external PID MOVL R0,LDIOB_L_PID(R4) ; EPID 10$: READ_SYSTIME LDIOB_Q_EN_TIME(R4) ; End time MOVQ LDIOB_Q_EN_TIME(R4),- LDIOB_Q_ST_TIME(R4) ; Start time (Same as end time ; for synchronous functions) CLRL LDIOB_L_ELAPSED(R4) ; Zero elapsed time BRW LD_TRACE 20$: ADDL2 #8,SP ; Recover scratch space 30$: RSB UNIVERSAL_SYMBOL LD_SAVE_TRACE_ALT ;LD_SAVE_TRACE_ALT: MOVL R3,UCB$L_IRP(R5) ; Restore IRP TSTL UCB$L_LD_TRCBUF(R5) ; Start of buffer BEQL 20$ ; Not there, quit ; ; See if the 'start_time' is filled-in. If not it means that the ; tracebuffer was allocated when an I/O request has been started but ; not completed yet. Forget this entry if true. ; MOVQ LDIOB_Q_ST_TIME(R4),R0 ; Test for a start time BEQL 20$ ; Not set MOVQ IRP$L_IOST1(R3),- ; IOSB contents LDIOB_Q_STAT(R4) MOVL LDIOB_L_PID(R4),R0 ; Get IPID CLRL LDIOB_L_PID(R4) ; Assume no pid BBS #IRP$V_MVIRP,IRP$W_STS(R3),10$ ; Mount verify IRP? BBS #IRP$V_SHDIO,IRP$W_STS2(R3),10$ ; Is it shadowing I/O ? JSB G^EXE$IPID_TO_EPID ; Make external PID MOVL R0,LDIOB_L_PID(R4) ; EPID 10$: READ_SYSTIME LDIOB_Q_EN_TIME(R4) ; End time CLRL LDIOB_L_ELAPSED(R4) ; Zero elapsed time ; (Not used on VAX) BRW LD_TRACE 20$: MOVL R4,R0 ; Point to LDIOB BSBW LD_DEAL_LDIOB ; Get rid of buffer RSB .SBTTL LD_TRACE, Trace I/O IRP data ;+++ ; LD_TRACE - Trace I/O IRP data ; ; Functional description: ; ; This routine is called by the start I/O routine to log information ; about the I/O request in a tracebuffer. ; ; The synchronisation of this buffer and the associated pointers is done ; by means of a mutex in the UCB. This is done to avoid having to synchronize ; access to the UCB with a Forklock at IPL 8. If we should do it that way, ; we would have to lock the pages of the userbuffer which would receive the ; data in memory. Since this buffer may be several Megabytes in size, this ; would need a very big WSQUOTA, or splitting the buffer in pieces. It would ; also mean that when we get the data out of the buffer we would be at IPL 8 ; for an extended time which would block several other things. By using a ; mutex we can remain at IPL 2 in the FDT routine when we get the data from ; the buffer, thus allowing pagefaults. We only must be careful when we want ; to write new data into the buffer, and we find the mutex locked. In that ; case we will queue the current IRP to the mutex wait queue in the UCB. The ; thread which locked the Mutex has to check the queue on exit, and resume ; any thread which was blocked. ; ; Inputs: ; ; R3 - address of the IRP (I/O request packet) ; R4 - address of LDIOB ; R5 - address of the UCB (unit control block) ; ; The LDIOB in R4 will be deallocated when we're finished. ; ; The routine must preserve all registers except R0-R2 and R4. ;--- UNIVERSAL_SYMBOL LD_TRACE ;LD_TRACE: MOVL (SP)+,UCB$L_LD_TRCPC(R5) ; Save Fork trace PC 20$: LOCK_TRACE ACCESS=WRITE,- ; Lock the mutex CONTEXT=SYSTEM BLBS R0,40$ ; Got it MOVAB IRP$L_FQFL(R3),R3 ; Point to FKB within IRP MOVAB 30$,FKB$L_FPC(R3) ; Save return addres MOVB #DYN$C_FRK,FKB$B_TYPE(R3) ; Insert structure type MOVL R4,FKB$L_FR4(R3) ; Preserve R4 MOVB UCB$B_FLCK(R5),FKB$B_FLCK(R3) ; Set proper spinlock FORKLOCK LOCK=UCB$B_FLCK(R5),- ; Make sure synch is okay SAVIPL=-(SP),- PRESERVE=NO INSQUE (R3),@UCB$L_LD_TRCMUTEXQBL(R5) ; Save IRP in queue FORKUNLOCK LOCK=UCB$B_FLCK(R5),- NEWIPL=(SP)+,- PRESERVE=NO,- CONDITION=RESTORE RSB ; Return to caller's caller ; ; Fork thread, called with R5 = forkblock (is our FKB in IRP), ; R3 and R4 restored from forkblock ; 30$: SUBL3 #IRP$L_FQFL,R5,R3 ; Restore IRP pointer MOVL IRP$L_UCB(R3),R5 ; Restore UCB BRB 20$ ; And try again 40$: PUSHL UCB$L_LD_TRCPC(R5) ; Restore Fork trace PC PUSHL R4 ; Save R4 MOVL UCB$L_LD_TRCBUF(R5),R4 ; Start of buffer MOVL UCB$L_LD_TRCBUFPTR(R5),R1 ; Get current pointer ADDL3 #LDTRCENT_K_LENGTH,R1,R0 ; Point to next free place ADDL3 UCB$L_LD_TRCBUFSIZ(R5),R4,R2 ; Calculate end of buffer CMPL R0,R2 ; Past the end? BLEQU 60$ ; No MOVL R1,UCB$L_LD_TRCWRAP(R5) ; Flag buffer wraparound INCL UCB$L_LD_TRCLOST(R5) ; Count lost data MOVL R4,R1 ; Reset ptr to start of buffer 60$: MOVL #LDTRCENT_K_LENGTH,R0 ; Length of LDTRCENT MOVL (SP),R4 ; Source info from LDIOB MOVAB LDIOB_L_PID(R4),R4 ; Point to trace data CLRW LDIOB_W_IOST-LDIOB_L_PID(R4) ; Clear reserved field 70$: PUSHL R3 ; Save IRP PUSHL R5 ; Save UCB MOVC3 R0,(R4),(R1) ; Transfer data to tracebuffer MOVL (SP)+,R5 ; Restore UCB MOVL R3,UCB$L_LD_TRCBUFPTR(R5) ; Save new bufferpointer MOVQ (SP)+,R3 ; Restore R3 and R4 UNLOCK_TRACE CONTEXT=SYSTEM,- ; Unlock the trace mutex NEWDATA=YES ; Check if new data available MOVQ LDIOB_Q_STAT(R4),-(SP) ; Save I/O status MOVL R4,R0 ; Point to LDIOB BSBW LD_DEAL_LDIOB ; Get rid of buffer MOVQ (SP)+,R0 ; Restore status RSB .SBTTL LD_OPCOM_AST, AST routine to send a message to OPCOM ;+++ ; LD_OPCOM_AST, AST routine to send a message to OPCOM ; ; Functional description: ; ; We will run here in exec mode. Sine this part of the driver is located ; in non-paged-pool and the pageprotection is ERKW we may not write to ; any local variables. Hence we do all the work on the exec-stack. ; We are called in in the context of the process issueing the I/O request ; to send a message to OPCOM. If the I/O request came from a system-thread ; (Mount-verification for example) then the current process will be the ; OPCOM. ; ; Layout of the stack we use for scratch storage: ; ; 0(SP) - FAO control string length ; 4(SP) - FAO control string address ; 8(SP) - FAO return length ; 12(SP) - SNDOPR buffer length ; 16(SP) - SNDOPR buffer address ; 20(SP) - FAO output buffer length ; 24(SP) - FAO output buffer address ; 28(SP) - PID ; 32(SP) - Imagename ; 36(SP) - Devicename ; 40(SP) - Function ; 44(SP) - LBN ; 48(SP) - FID_NUM ; 52(SP) - FID_SEQ ; 56(SP) - FID_RVN ; 60(SP) - SNDOPR message header ; 64(SP) - SNDOPR filler ; 68(SP) - FAO output buffer ; ; Inputs: ; ; 4(AP) - address of ACB which contains our parameters (LDSNDOPRLST) ; ; The routine must preserve all registers except R0-R1 ;--- UNIVERSAL_ENTRY LD_OPCOM_AST,<^M> ; .ENTRY LD_OPCOM_AST,^M ; SUBL2 #324,SP ; Allocate scratch space MOVL 4(AP),R2 ; Point to argument block BBC #LDWATCH_V_FILE,- ; File watchpoint? LDSNDOPRLST_W_FLAGS(R2),10$ MOVL #VBN_WP_LEN,(SP) ; FAO string length MOVAB VBN_WP,4(SP) ; FAO conversion format (VBN) MOVZWL LDSNDOPRLST_W_FID_NUM(R2),48(SP); File id number MOVZWL LDSNDOPRLST_W_FID_SEQ(R2),52(SP); File id sequence MOVZWL LDSNDOPRLST_W_FID_RVN(R2),56(SP); File id relative volume BRB 20$ 10$: MOVL #LBN_WP_LEN,(SP) ; FAO string length MOVAB LBN_WP,4(SP) ; FAO conversion format (LBN) 20$: MOVAB 60(SP),16(SP) ; SNDOPR messagebuffer address MOVZWL LDSNDOPRLST_W_FUNC(R2),40(SP) ; Function MOVL LDSNDOPRLST_L_LBN(R2),44(SP) ; Logical block number MOVAB LDSNDOPRLST_T_DEVNAM(R2),36(SP) ; Device name MOVAB NONESTR,32(SP) ; Assume no imagename CLRL 28(SP) ; Assume no process id MOVL LDSNDOPRLST_L_PID(R2),R0 ; Internal process id TSTL LDSNDOPRLST_L_KAST(R2) ; Special process? BNEQ 30$ ; Yes JSB G^EXE$IPID_TO_EPID ; Make external PID MOVL R0,28(SP) ; Process id MOVAL G^IAC$GL_IMAGE_LIST,R0 ; Get adress of image list CMPL (R0),R0 ; Something there? BEQL 10$ ; No MOVL (R0),R0 ; Get adress of first ICB MOVAB 20(R0),32(SP) ; Point to image name 30$: MOVL #>,- 60(SP) ; OPCOM flags CLRL 64(SP) ; Terminator MOVAB 68(SP),24(SP) ; FAO output buffer address MOVZWL #256,20(SP) ; FAO output buffer length PUSHAL 28(SP) ; Parameter list PUSHAQ 24(SP) ; Output buffer PUSHAW 16(SP) ; FAO return length PUSHAQ 12(SP) ; FAO control string descriptor CALLS #4,G^SYS$FAOL ; Format the buffer BLBC R0,40$ ; Trouble MOVZWL 8(SP),R0 ; Length of converted string ADDL3 #8,R0,12(SP) ; OPCOM message header length + overhead MOVAB 12(SP),R0 ; Address of message descriptor $SNDOPR_S (R0) ; Send message to OPCOM BLBS R0,50$ ; Okay CMPL R0,#SS$_MBFULL ; OPCOM mailbox full? BEQL 50$ ; Yes, message lost 40$: BUG_CHECK INCONSTATE ; Issue a non-fatal bugcheck ; ; Now get back into kernel mode to deallocate our parameter buffer. ; We are allowed to go into kernelmode since we're now running ; in exec mode ; 50$: $CMKRNL_S ROUTIN=LD_OPCOM_DEALLOC,- ARGLST=4(AP) ; ACB to deallocate RET .SBTTL LD_OPCOM_DEALLOC, dealloc opcom parameter buffer ;+++ ; LD_OPCOM_DEALLOC, dealloc opcom parameter buffer ; ; Functional description: ; ; We will return the parameter buffer from LD_OPCOM_AST ; to pool. ; ; Inputs: ; ; (AP) - address of ACB ; ; The routine must preserve all registers except R0-R1 ;--- UNIVERSAL_ENTRY LD_OPCOM_DEALLOC,<^M<>> ; .ENTRY LD_OPCOM_DEALLOC,^M<> ; MOVL (AP),R0 ; Get old parameterbuffer pointer JSB G^EXE$DEANONPAGED ; Return to pool RET .SBTTL LD_END, End of driver ;+++ ; Label that marks the end of the driver ;--- UNIVERSAL_SYMBOL LD_END ;LD_END: ; Last location in driver .END