.TITLE SDDRIVER - VAX/VMS VIRT DISK DRIVER w/shadowing ; Striping Disk driver .IDENT 'V1a' ; Copyright 1994 Glenn C. Everhart ; All rights reserved ; gce 6/22/1991 add some extra stuff to handle SMP full checking ; more carefully... ; gce - striping driver 1/1994. ; .link 'sys$system:sys.stb'/sel ; clu$$str=0 ;define for dev$m_clu variant ; ; Changed by making it fd1 type and adding nodename prefix characteristic ; so that hopefully it will now work with MSCP. ; ; Added save/restore of IRP$L_MEDIA for second I/O completion (by VDDRIVER) ; so that error path code that assumes this field unaltered will work. 4/14/89 ; ;md$stat=1 ;x$$$dt=1 ;call xdelta ; Note: define symbol VMS$V5 to assemble in VMS V5.x or later. Default ; assembly without this definition produces a VMS V4.x driver. ; define v5$picky also for normal SMP operations vms$v5=1 v5$picky=1 ; Glenn C. Everhart, 2/2/1989 ; Merged in some of Marty Sasaki's changes ;USAPADDR=0 ; ; FACILITY: ; ; VAX/VMS VIRTUAL DISK DRIVER USING CONTIGUOUS FILES. ; ; AUTHOR: ; ; G. EVERHART ; ; ; ABSTRACT: ; ; THIS MODULE CONTAINS THE TABLES AND ROUTINES NECESSARY TO ; PERFORM ALL DEVICE-DEPENDENT PROCESSING OF AN I/O REQUEST ; FOR VMS VIRTUAL DISKS ON CONTIG FILES. ; ;This driver is intended to act as a striping driver, in which several ; disks are treated as a single unit with little bits of the storage ; alternately in each of the several units. The disk is described by ; size and number of storage units and also chunk size (size of each ; piece of the disk storage that is on each) and reserved blocks. ; The first reserved block of the first unit (if any) is used to hold ; a description of the stripeset which asnsd can check to ensure that ; the pieces have been given correctly for old stripesets. The reserved ; blocks exist at the start of each storage area, so resulting lbn = ; start lbn + reserved size. ; ; Several contiguous areas can be used if desired, though the assign ; program will allow only up to 2 at a time. ; Note an /append & /enable switch pair allow one to add more ; units. It is expected most stripesets will however have only 2 ; components. Note it makes no sense to stripe virtual disks on the same ; physical one. Should be OK to hang vd units on top of sd ones though, ; Also note we can declare this thing a cluster device and let each ; machine do its own i/o splitting & management. ;-- .PAGE .SBTTL EXTERNAL AND LOCAL DEFINITIONS ; ; EXTERNAL SYMBOLS ; .library /SYS$SHARE:LIB/ ; $ADPDEF ;DEFINE ADAPTER CONTROL BLOCK $CRBDEF ;DEFINE CHANNEL REQUEST BLOCK $DYNDEF ;define dynamic data types $DCDEF ;DEFINE DEVICE CLASS $DDBDEF ;DEFINE DEVICE DATA BLOCK $DEVDEF ;DEFINE DEVICE CHARACTERISTICS $DPTDEF ;DEFINE DRIVER PROLOGUE TABLE $EMBDEF ;DEFINE ERROR MESSAGE BUFFER $IDBDEF ;DEFINE INTERRUPT DATA BLOCK $IODEF ;DEFINE I/O FUNCTION CODES $DDTDEF ; DEFINE DISPATCH TBL... $ptedef $vadef $prvdef $IRPDEF ;DEFINE I/O REQUEST PACKET $irpedef $PRDEF ;DEFINE PROCESSOR REGISTERS $SSDEF ;DEFINE SYSTEM STATUS CODES $UCBDEF ;DEFINE UNIT CONTROL BLOCK $VECDEF ;DEFINE INTERRUPT VECTOR BLOCK $arbdef $pcbdef $jibdef p1=0 ; first qio param p2=4 p3=8 p4=12 p5=16 p6=20 ;6th qio param offsets .IF DF,VMS$V5 ;VMS V5 + LATER ONLY $SPLCODDEF $cpudef .ENDC ; ; UCB OFFSETS WHICH FOLLOW THE STANDARD UCB FIELDS ; $DEFINI UCB ;START OF UCB DEFINITIONS ;.=UCB$W_BCR+2 ;BEGIN DEFINITIONS AT END OF UCB .=UCB$K_LCL_DISK_LENGTH ;v4 def end of ucb ; USE THESE FIELDS TO HOLD OUR LOCAL DATA FOR VIRT DISK. ; Add our stuff at the end to ensure we don't mess some fields up that some ; areas of VMS may want. $DEF UCB$PPID .BLKL 1 ;PID OF ORIGINAL PROCESS FROM IRQ BLK ; host descriptor areas cntof: $DEF UCB$HUCB .BLKL 1 ;ADDRESS OF HOST UCB $DEF UCB$HLBN .BLKL 1 ;LBN OF HOST FILE $DEF UCB$HFSZ .BLKL 1 ;SIZE OF HOST FILE, BLKS ucbcntsz=.-cntof $def ucb$hucb2 .blkl 1 ;host UCB of file 2 $def ucb$hlbn2 .blkl 1 ;LBN of file 2 $def ucb$hfsz2 .blkl 1 ; One label block has 6 longs for fixed info plus 7 longs per ; container. This is enough for 17 containers. I dislike pushing ; things so allow up to 16 here. .blkl 30 ; Space for 10 more info sets .blkl 12 ;space for up to 4 more sets .blkl 3 ;safety ; NOTE: It is important to ENSURE that we never clobber IRP$L_PID twice! ; therefore, adopt convention that UCB$PPID is cleared whenever we put ; back the old PID value in the IRP. Only clobber the PID where ; UCB$PPID is zero!!! $DEF UCB$stats .BLKL 1 ;STATUS CODE SAVE AREA $DEF UCB$OBCT .BLKL 1 ;STORE FOR IRP$L_OBCNT too $def ucb$totfsz .blkl 2 ;Total size of virt dsk $def ucb$lmedia .blkl 1 ;storage for IRP$L_MEDIA $def ucb$owind .blkl 1 ; store irp$l_wind... $def ucb$osegv .blkl 1 ; and irp$l_segvbn $def ucb$l_SD_host_descr .blkl 2 ; char string descr ; $def ucb$vdcontfil .blkb 148 ; ; striping extra fields $def ucb$grnsiz .blkl 1 ;size of a stripe chunk in blocks $def ucb$irpcnt .blkl 1 ;count of IRPs for this i/o $def ucb$ncont .blkl 1 ;number container files $def ucb$bcntwk .blkl 1 ;work storage for byte count used $def ucb$sublbn .blkl 1 ;lbn of current sub-irp $def ucb$grnbas .blkl 1 ;number of reserved blocks this seg $def ucb$subsva .blkl 1 ;system virt addr for current IRP ; $def ucb$l_rclok .blkl 1 ;Flag that keeps us from finishing I/O till ;all IRPs are sent. $def ucb$q_SD_svaptetmp .blkl 2 $def ucb$l_SD_flag .blkl 1 ;sanity flag to ensure we have right driver ; next 2 left over from vqdriver $def ucb$shmd .blkl 1 ;shadow mode. 0=use file 1 only $def ucb$rwlk .blkl 1 ; read/write interlock. Initialize to 1 $def ucb$rwlbn .blkl 1 ; LBN of first block in special read/logical $def ucb$rwsz .blkl 1 ; size of special transfer $def ucb$llbn1 .blkl 1 ; last LBN of file 1 $def ucb$llbn2 .blkl 1 ; last LBN of file 2 $def ucb$ucbos .blkl 1 ; offset to ucb/lbn/fsz in ucb to use at fin-io $def ucb$rwdir .blkl 1 ;read (0) or write (1) I/O $def ucb$ercd1 .blkl 1 ; store for error code of 1st write of 2 ; $DEF UCB$K_SD_LEN .BLKW 1 ;LENGTH OF UCB ;UCB$K_SD_LEN=. ;LENGTH OF UCB $DEFEND UCB ;END OF UCB DEFINITONS .SBTTL STANDARD TABLES ; ; DRIVER PROLOGUE TABLE ; ; THE DPT DESCRIBES DRIVER PARAMETERS AND I/O DATABASE FIELDS ; THAT ARE TO BE INITIALIZED DURING DRIVER LOADING AND RELOADING ; .if df,clu$$str .globl sdalcls ;alloc class, set with link .endc .PSECT $$$105_PROLOGUE SD_UNITS=8 ; vms v6 needs xpamod flag on vax. SPTE fiddling here should be ; insensitive to xpa vs. non-xpa vaxen. .IIF NDF,DPT$M_XPAMOD,DPT$M_XPAMOD=0 VE$DPT:: DPTAB - ;DPT CREATION MACRO END=SD_END,- ;END OF DRIVER LABEL ADAPTER=NULL,- ;ADAPTER TYPE = NONE (VIRTUAL) FLAGS=,- ;smp ok DEFUNITS=2,- ;UNITS 0 THRU 1 UCBSIZE=UCB$K_SD_LEN,- ;LENGTH OF UCB MAXUNITS=SD_UNITS,- ;FOR SANITY...CAN CHANGE NAME=SDDRIVER ;DRIVER NAME DPT_STORE INIT ;START CONTROL BLOCK INIT VALUES 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 .if df,clu$$str DPT_STORE DDB,DDB$L_ALLOCLS,L,SDALCLS ;allocation class .endc .IF NDF,VMS$V5 DPT_STORE UCB,UCB$B_FIPL,B,8 ;FORK IPL (VMS V4.X) .IFF ;DEFINE FOR VMS V5.X & LATER DPT_STORE UCB,UCB$B_FLCK,B,SPL$C_IOLOCK8 ;FORK IPL (VMS V5.X + LATER) .ENDC ; NOTE THESE CHARACTERISTICS HAVE TO LOOK LIKE THE "REAL" DISK. DPT_STORE UCB,UCB$L_DEVCHAR,L,- ;DEVICE CHARACTERISTICS ; RANDOM ACCESS .if ndf,clu$$str DPT_STORE UCB,UCB$L_DEVCHAR2,L,- ;DEVICE CHARACTERISTICS ; Prefix name with "node$" (like rp06) .iff DPT_STORE UCB,UCB$L_DEVCHAR2,L,- ;DEVICE CHARACTERISTICS ; Declare this a cluster available dev .endc DPT_STORE UCB,UCB$B_DEVCLASS,B,DC$_DISK ;DEVICE CLASS DPT_STORE UCB,UCB$W_DEVBUFSIZ,W,512 ;DEFAULT BUFFER SIZE ; FOLLOWING DEFINES OUR DEVICE "PHYSICAL LAYOUT". It's faked here and ; this structure (64 sectors/trk, 1 trk/cyl, nn cylinders) forces ; VE: units to be in multiples of 64 blocks. It can be modified as ; appropriate. However, recall that one has 1 byte for sectors/trk ; and 16 bits for cylinder number and 1 byte for tracks/cylinder. ; The current structure allows vd: units as large as 65535*64 blocks ; (about 4 million blocks, or 2 gigabytes), which is probably big enough ; for most purposes. The actual size is set up in ASNVD which finds the ; number of cylinders to "fit" in the container file. For emulating other ; ODS-2 volumes, the appropriate physical structure should be emulated also. ; NO logic in this driver depends on this stuff. It just has to be there ; to keep INIT and friends happy. ; (In fact the current ASNVD image for VD: does this. We however ; have to preset SOMETHING.) DPT_STORE UCB,UCB$L_SD_FLAG,L,<^A\GCYS\> ;FLAG SDdriver DPT_STORE UCB,UCB$B_TRACKS,B,1 ; 1 TRK/CYL DPT_STORE UCB,UCB$B_SECTORS,B,64 ;NUMBER OF SECTORS PER TRACK DPT_STORE UCB,UCB$W_CYLINDERS,W,16 ;NUMBER OF CYLINDERS ; FAKE GEOMETRY TO MAKE TRANSLATION EASIER. HAVE PRIV'D IMAGE LATER ; RESET THE UCB$W_CYLINDERS TO WHATEVER'S DESIRED. JUST MAKE SURE IT'S ; A MULTIPLE OF 64 BLOCKS IN SIZE, WHICH OUGHT TO BE GOOD ENOUGH. DPT_STORE UCB,UCB$B_DIPL,B,21 ;DEVICE IPL DPT_STORE UCB,UCB$B_ERTMAX,B,10 ;MAX ERROR RETRY COUNT DPT_STORE UCB,UCB$W_DEVSTS,W,- ;INHIBIT LOG TO PHYS CONVERSION IN FDT ;... ; ; don't mess with LBN; leave alone so it's easier to hack on... ; DPT_STORE REINIT ;START CONTROL BLOCK RE-INIT VALUES ; DPT_STORE CRB,CRB$L_INTD+VEC$L_ISR,D,SD_INT ;INTERRUPT SERVICE ROUTINE ADDRESS DPT_STORE CRB,CRB$L_INTD+VEC$L_INITIAL,- ;CONTROLLER INIT ADDRESS D,SD_ctrl_INIT ;... DPT_STORE CRB,CRB$L_INTD+VEC$L_UNITINIT,- ;UNIT INIT ADDRESS D,SD_unit_INIT ;... DPT_STORE DDB,DDB$L_DDT,D,SD$DDT ;DDT ADDRESS DPT_STORE END ;END OF INITIALIZATION TABLE ; ; DRIVER DISPATCH TABLE ; ; THE DDT LISTS ENTRY POINTS FOR DRIVER SUBROUTINES WHICH ARE ; CALLED BY THE OPERATING SYSTEM. ; ;SD$DDT: DDTAB - ;DDT CREATION MACRO DEVNAM=SD,- ;NAME OF DEVICE START=SD_STARTIO,- ;START I/O ROUTINE FUNCTB=SD_FUNCTABLE,- ;FUNCTION DECISION TABLE CANCEL=0,- ;CANCEL=NO-OP FOR FILES DEVICE REGDMP=0,- ;REGISTER DUMP ROUTINE DIAGBF=0,- ;BYTES IN DIAG BUFFER ERLGBF=0 ;BYTES IN ;ERRLOG BUFFER ; ; FUNCTION DECISION TABLE ; ; THE FDT LISTS VALID FUNCTION CODES, SPECIFIES WHICH ; CODES ARE BUFFERED, AND DESIGNATES SUBROUTINES TO ; PERFORM PREPROCESSING FOR PARTICULAR FUNCTIONS. ; SD_FUNCTABLE: FUNCTAB ,- ;LIST LEGAL FUNCTIONS ; MOUNT VOLUME ; no-op phys I/O for a test here... FUNCTAB ,- ;BUFFERED FUNCTIONS ; MOUNT VOLUME ; NOTE SEPARATE CALL FOR PHYSICAL I/O SO WE CAN JUST CONVERT TO LOGICAL AND ; DO OUR THING... CONVERT TO A LOGICAL QIO THERE FOR "REAL" DRIVER ALSO ; SO IT CAN DO CONVERSION TO ITS IDEA OF PHYSICAL IF IT WISHES... ; ; LEAVE NORMAL ACP CALLS IN SO FILE STRUCTURED STUFF ON OUR VE: UNIT ; WILL WORK OK. ; functab crrmshd,- functab shdwck,- ;check write to shadow member ; Write VIRTUAL Block FUNCTAB +ACP$READBLK,- ;READ FUNCTIONS FUNCTAB +ACP$WRITEBLK,- ;WRITE FUNCTIONS FUNCTAB +ACP$ACCESS,- ;ACCESS FUNCTIONS FUNCTAB +ACP$DEACCESS,- ;DEACCESS FUNCTION FUNCTAB +ACP$MODIFY,- ;MODIFY FUNCTIONS FUNCTAB +ACP$MOUNT,- ;MOUNT FUNCTION ; MOUNT VOLUME FUNCTAB +EXE$LCLDSKVALID,- ;LOCAL DISK VALID FUNCTIONS ;PACK ACKNOWLEDGE FUNCTAB +EXE$ZEROPARM,- ;ZERO PARAMETER FUNCTIONS ; AVAILABLE FUNCTAB +EXE$ONEPARM,- ;ONE PARAMETER FUNCTION FUNCTAB +EXE$SENSEMODE,- ;SENSE FUNCTIONS FUNCTAB +EXE$SETCHAR,- ;SET FUNCTIONS .PAGE ; BLOCK OF UCB ADDRESSES SD_UCBTBL:: .BLKL SD_UNITS .LONG 0,0 ;SAFETY .PSECT $$$115_DRIVER .SBTTL FDT Routines ; shadow write check. Only allow write to shadowset members if privd. ; shdwck: bbs #dev$v_shd,ucb$l_devchar2(r5),10$ ;shadow set member? rsb ;if normal, return NOW. 10$: movl IRP$L_ARB(R3),R0 ; get ARB blk addr beql 99$ ; (lose if not there) ASSUME PRV$V_SYSPRV LT 32 bbc #PRV$V_SYSPRV,ARB$Q_PRIV(R0),99$; No SYSPRV, illegal rsb ;ok, return 99$: MOVZBL #SS$_ILLIOFUNC,R0 jmp g^exe$finishioc ;error return... ; ; ; Dummy create/remove shadow set member functions. ; Lets VMS do all the actual work. Just does the dispatching here. ; crrmshd: MOVL G^EXE$GL_HBS_PTR,R0 ;get host based shadowing pointer bgeq 10$ ;if not filled in, forget it. jmp (r0) 10$: MOVZBL #SS$_ILLIOFUNC,R0 ;illegal if no dispatcher jmp G^EXE$FINISHIOC ;so indicate this to caller ; .SBTTL CONTROLLER INITIALIZATION ROUTINE ; ++ ; ; SD_ctrl_INIT - CONTROLLER INITIALIZATION ROUTINE ; ; FUNCTIONAL DESCRIPTION: ; noop ; INPUTS: ; R4 - CSR ADDRESS ; R5 - IDB ADDRESS ; R6 - DDB ADDRESS ; R8 - CRB ADDRESS ; ; THE OPERATING SYSTEM CALLS THIS ROUTINE: ; - AT SYSTEM STARTUP ; - DURING DRIVER LOADING ; - DURING RECOVERY FROM POWER FAILURE ; THE DRIVER CALLS THIS ROUTINE TO INIT AFTER AN NXM ERROR. ;-- SD_ctrl_INIT: ;vd CONTROLLER INITIALIZATION CLRL CRB$L_AUXSTRUC(R8) ; SAY NO AUX MEM RSB ;RETURN .PAGE .SBTTL INTERNAL CONTROLLER RE-INITIALIZATION ; ; INPUTS: ; R4 => controller CSR (dummy) ; R5 => UCB ; ctrl_REINIT: RSB ; RETURN TO CALLER .PAGE .SBTTL UNIT INITIALIZATION ROUTINE ; MEDIA - MSCP media identifier to VMS device type conversion ; ; Functional description: ; ; This macro produces one entry in the MSCP media identifier to VMS ; device type conversion table. ; ; Parameters: ; ; dd the two character prefered device controller name ( the DD ; part of DDCn ) ; devnam the hardware device name ( e.g. RA81 ) ; dtname if DT$_'devnam' is not a legal VMS device type, this parameter ; gives the correct VMS device type for the device ( should be ; used only when DT$_'devnam' is not correct ) ;- .MACRO MEDIA DD, DEVNAM, DTNAME $$BEGIN$$=-1 $$MEDIA$$=0 $$S$$=27 .IRPC $$L$$,
$$TEMP$$ = ^A/$$L$$/ - ^X40 .IF GT $$TEMP$$ $$MEDIA$$ = $$MEDIA$$ + <$$TEMP$$ @ $$S$$> .ENDC $$S$$ = $$S$$ - 5 .ENDR .IRPC $$L$$, .IF GE <$$S$$ - 7> $$TEMP$$ = ^A/$$L$$/ - ^X40 .IF GT $$TEMP$$ $$MEDIA$$ = $$MEDIA$$ + <$$TEMP$$ @ $$S$$> .IF_FALSE .IIF LT $$BEGIN$$, $$BEGIN$$ = <17-$$S$$>/5 .ENDC $$S$$ = $$S$$ - 5 .ENDC .ENDR .IIF LT $$BEGIN$$, $$BEGIN$$ = 3 $$N$$ = %EXTRACT( $$BEGIN$$, 3, DEVNAM ) $$MEDIA$$ = $$MEDIA$$ + $$N$$ ; .LONG $$MEDIA$$ .ENDM MEDIA ;++ ; ; SD_unit_INIT - UNIT INITIALIZATION ROUTINE ; ; FUNCTIONAL DESCRIPTION: ; ; THIS ROUTINE SETS THE VD: ONLINE. ; ; THE OPERATING SYSTEM CALLS THIS ROUTINE: ; - AT SYSTEM STARTUP ; - DURING DRIVER LOADING ; - DURING RECOVERY FROM POWER FAILURE ; ; INPUTS: ; ; R4 - CSR ADDRESS (CONTROLLER STATUS REGISTER) ; R5 - UCB ADDRESS (UNIT CONTROL BLOCK) ; R8 - CRB ADDRESS ; ; OUTPUTS: ; ; THE UNIT IS SET ONLINE. ; ALL GENERAL REGISTERS (R0-R15) ARE PRESERVED. ; ;-- MEDIA SD,RP06 ;define our media-id SD_unit_INIT: ;vd UNIT INITIALIZATION ; Don't set unit online here. Priv'd task that assigns VD unit ; to a file does this to ensure only assigned VDn: get used. ; BISW #UCB$M_ONLINE,UCB$W_STS(R5) ;SET UCB STATUS ONLINE ;limit size of VD: data buffers SD_bufsiz=8192 ; (asnSD will set this to same as host...) ; movl #SD_bufsiz,ucb$l_maxbcnt(r5) ;limit transfers to 8k clrl ucb$l_rclok(r5) ;initially clear io done lock MOVB #DC$_DISK,UCB$B_DEVCLASS(R5) ;SET DISK DEVICE CLASS ; NOTE: we may want to set this as something other than an RX class ; disk if MSCP is to use it. MSCP explicitly will NOT serve an ; RX type device. For now leave it in, but others can alter. ; (There's no GOOD reason to disable MSCP, but care!!!) movl #$$MEDIA$$,ucb$l_media_id(r5) ; set media id as SD ; (note the id might be wrong but is attempt to get it.) (used only for ; MSCP serving.) MOVB #DT$_FD1,UCB$B_DEVTYPE(R5) ;Make it foreign disk type 1 ; (dt$_rp06 works but may confuse analyze/disk) ;;; NOTE: changed from fd1 type so MSCP will know it's a local disk and ;;; attempt no weird jiggery-pokery with the VD: device. ; MSCP may still refuse to do a foreign drive too; jiggery-pokery later ; to test if there's occasion to do so. RSB ;RETURN .PAGE .SBTTL FDT ROUTINES ;++ ; ; SD_ALIGN - FDT ROUTINE TO TEST XFER BYTE COUNT ; ; FUNCTIONAL DESCRIPTION: ; ; THIS ROUTINE IS CALLED FROM THE FUNCTION DECISION TABLE DISPATCHER ; TO CHECK THE BYTE COUNT PARAMETER SPECIFIED BY THE USER PROCESS ; FOR AN EVEN NUMBER OF BYTES (WORD BOUNDARY). ; ; INPUTS: ; ; R3 - IRP ADDRESS (I/O REQUEST PACKET) ; R4 - PCB ADDRESS (PROCESS CONTROL BLOCK) ; R5 - UCB ADDRESS (UNIT CONTROL BLOCK) ; R6 - CCB ADDRESS (CHANNEL CONTROL BLOCK) ; R7 - BIT NUMBER OF THE I/O FUNCTION CODE ; R8 - ADDRESS OF FDT TABLE ENTRY FOR THIS ROUTINE ; 4(AP) - ADDRESS OF FIRST FUNCTION DEPENDENT QIO PARAMETER ; ; OUTPUTS: ; ; IF THE QIO BYTE COUNT PARAMETER IS ODD, THE I/O OPERATION IS ; TERMINATED WITH AN ERROR. IF IT IS EVEN, CONTROL IS RETURNED ; TO THE FDT DISPATCHER. ; ;-- SD_ALIGN: ;CHECK BYTE COUNT AT P1(AP) RSB ;EVEN - RETURN TO CALLER .PAGE .SBTTL START I/O ROUTINE ;++ ; ; SD_STARTIO - START I/O ROUTINE ; ; FUNCTIONAL DESCRIPTION: ; ; THIS FORK PROCESS IS ENTERED FROM THE EXECUTIVE AFTER AN I/O REQUEST ; PACKET HAS BEEN DEQUEUED. ; ; INPUTS: ; ; R3 - IRP ADDRESS (I/O REQUEST PACKET) ; R5 - UCB ADDRESS (UNIT CONTROL BLOCK) ; IRP$L_MEDIA - PARAMETER LONGWORD (LOGICAL BLOCK NUMBER) ; ; OUTPUTS: ; ; R0 - FIRST I/O STATUS LONGWORD: STATUS CODE & BYTES XFERED ; R1 - SECOND I/O STATUS LONGWORD: 0 FOR DISKS ; ; THE I/O FUNCTION IS EXECUTED. ; ; ALL REGISTERS EXCEPT R0-R4 ARE PRESERVED. ; ;-- .if df,x$$$dt rwflg: .long 0 .endc ; In fact, using XDelta, I have *never* seen requeue called. REQUEUE: .if df,x$$$dt jsb g^ini$brk .endc JMP G^EXE$INSIOQ ; REQUEUE packet to ourselves ; return to our caller direct from insioq. ; (note this also sets busy, so it will NOT loop forever.) SD_STARTIO: ;START I/O OPERATION ; ; PREPROCESS UCB FIELDS ; ; ASSUME RY_EXTENDED_STATUS_LENGTH EQ 8 ; CLRQ UCB$Q_SD_EXTENDED_STATUS(R5) ; Zero READ ERROR REGISTER area. ; ; BRANCH TO FUNCTION EXECUTION bbs #ucb$v_online,- ; if online set software valid ucb$w_sts(r5),210$ 216$: movzwl #ss$_volinv,r0 ; else set volume invalid brw resetxfr ; reset byte count & exit 210$: tstl ucb$hucb(r5) ; do we have any host device? beql 216$ ; if eql no, flag invalid volume. ; THIS IS SAFETY FROM CONFIGURING FROM OUTSIDE tstl ucb$hucb2(r5) ;need at least 2 containers beql 216$ ;or we fail ; BEFORE GOING ON, WE WANT TO ENSURE THE UCB IS FREE. TSTL UCB$PPID(R5) ; MAKE SURE we haven't got ; a packet in process BNEQ REQUEUE ; IF a packet's in process, requeue ; back to this driver; do NOT process ; immediately! ; Note...never seems to get to requeue (xdelta would catch it!) ; (that's a good sign; should never get there.) bisw #ucb$m_online,ucb$w_sts(r5) ; set online bisw #ucb$m_valid,ucb$w_sts(r5) ;set valid ; set ourselves as owners of channel for VD: movl ucb$l_crb(r5),r0 movl crb$l_intd+vec$l_idb(r0),r0 ;get idb address ; cmpl r5,idb$l_owner(r0) ;are we owners? ; beql 214$ ; if eql yes, all's well ; REQPCHAN ; gain access to controller in "standard" way 214$: ; 10$:; BBS #IRP$V_PHYSIO,- ;IF SET - PHYSICAL I/O FUNCTION ; IRP$W_STS(R3),20$ ;... BBS #UCB$V_VALID,- ;IF SET - VOLUME SOFTWARE VALID UCB$W_STS(R5),20$ ;... MOVZWL #SS$_VOLINV,R0 ;SET VOLUME INVALID STATUS BRW RESETXFR ;RESET BYTE COUNT AND EXIT 20$: ; IF WE GET A SEGMENT TRANSFER HERE (LOGICAL I/O) ; IT MUST BE UPDATED FOR HOST AND SHIPPED OUT. ; OUR UCB HAS BLOCK NUMBER INFO... ; FIND OUT IF THIS IS LOGICAL OR PHYSICAL I/O FIRST. THEN IF IT IS BUGGER ; THE I/O PACKET USING UCB INFO AND SEND TO THE REAL DRIVER... ; ALSO ENSURE WE ARE UNBUSIED... ; ; Sanity check for assignments: hucb2 field zero implies shmd must be ; zero. tstl ucb$hucb2(r5) ;second UCB there? bneq 21$ ;if neq yes clrl ucb$shmd(r5) ;if not, ensure in single-file mode 21$: ; EXTZV #IRP$V_FCODE,#IRP$S_FCODE,IRP$W_FUNC(R3),R1 ; GET FCN CODE case r1,<- ; Dispatch to function handling routine unload,- ; Unload nop,- ; Seek NOP,- ; Recalibrate(unsupported) nop,- ; Drive clear NOP,- ; Release port(unsupported) NOP,- ; Offset heads(unsupported) NOP,- ; Return to center nop,- ; Pack acknowledge NOP,- ; Search(unsupported) NOP,- ; Write check(unsupported) WRITEDATA,- ; Write data READDATA,- ; Read data NOP,- ; Write header(unsupported) NOP,- ; Read header(unsupported) NOP,- ; Place holder NOP,- ; Place holder available,- ; Available (17) NOP,NOP,NOP,- ; 18-20 NOP,NOP,NOP,NOP,nop,nop,nop,NOP,NOP,nop,- ;21-30 NOP,NOP,NOP,NOP,nop,NOP,nop,nop,nop,NOP,- ;31-40 NOP,NOP,NOP,NOP,NOP,NOP,NOP,NOP,NOP,nop,- ;41-50 NOP,NOP,NOP,NOP,nop,NOP,NOP,NOP,NOP,NOP,- ;51-60 nop,- ;61 >,LIMIT=#1 nop: ;unimplemented function brw fexl rwcmnj: brw rwcmn readdata: movl #ucb$hucb,ucb$ucbos(r5) ;store address of file 1 clrl ucb$rwdir(r5) ;set read brw rwcmn writedata: movl #ucb$hucb,ucb$ucbos(r5) ;store address of file 1 movl #1,ucb$rwdir(r5) ;set write rwcmn: ; debug using sda to peek ; NOW VALIDATED I/O FCN... MODIFY AND SEND OFF ; check size of device OK first CMPL IRP$L_MEDIA(R3),UCB$totfsz(R5) ;BE SURE LBN OK blequ 65$ brw Fatalerr ;dismiss I/o if not ok block number 65$: ; HAVE TO BE CAREFUL WHAT WE SHIP TO REAL DRIVERS ; Now that we know IRP$L_MEDIA is ok in IRP, save it for restore at ; I/O completion by VDDRIVER ; (This also facilitates remodification for second pass on writes) movl irp$l_media(r3),ucb$lmedia(r5) ; Prepare to enter another context. ; SEND PKT OFF TO REAL DRIVER... ; First get address stuff to bash PID with MOVL IRP$L_PID(R3),UCB$PPID(R5) ; SAVE PROCESS ID IN VD: UCB movzwl irp$w_sts(r3),ucb$stats(r5) ;save original fcn code movl irp$l_obcnt(r3),ucb$obct(r5) ;store obcnt field movl irp$l_svapte(r3),ucb$q_SD_svaptetmp(r5) ;save svapte movl irp$l_bcnt(r3),ucb$q_SD_svaptetmp+4(r5) ;and orig bcnt ; (actually shouldn't need to worry about driver post processing... ; we do all that here via hack to change irp$l_pid field....) movl irp$l_wind(r3),ucb$owind(r5) ;store window ptr movl irp$l_segvbn(r3),ucb$osegv(r5) ;store segment vbn also MOVZWL UCB$W_UNIT(R5),-(SP) ; BUILD ADDRESS OF UCB STORE ASHL #2,(SP),(SP) ; WITH OFFSET * 4 MOVAB SD_UCBTBL,-(SP) ; GET TBL BASE IN STACK ADDL2 (SP)+,(SP) ; NOW ADD BASE + OFFSET MOVL R5,@(SP)+ ; AND STORE UCB ADDRESS IN SD_UCBTBL ; (THIS ALLOWS US TO GET IT BACK...) MOVZWL UCB$W_UNIT(R5),-(SP) ; BUILD ADDRESS OF ENTRY NOW MULL2 #SD_FXPL,(SP) ; MULTIPLY OFFSET BY SIZE OF ENTRY MOVAB SD_FXS0,IRP$L_PID(R3) ;AND BASH PID IN IRP SO WE ; GET BACK CONTROL AT SD_FIXSPLIT (VIA JSB) ; WHEN HOST'S I/O IS DONE. ADDL2 (SP)+,IRP$L_PID(R3) ;SET TO ENTER IN CORRECT ; UNIT'S ENTRY ; At this point we have set the original IRP so it can be ; cloned and modified, and the subordinate IRPs sent to the ; host device(s). clrl ucb$irpcnt(r5) ;initially no subordinate IRPs movl irp$l_media(r3),ucb$sublbn(r5) ;start LBN for irp movl irp$l_svapte(r3),ucb$subsva(r5) ;save sys virt addr at start clrq irp$l_media(r3) ;zero status return in orig. irp movzwl irp$w_bcnt(r3),ucb$bcntwk(r5) ;save bytecount needed ; For alpha we'll need to adjust irp$w_boff as well as irp$l_svapte ; here and may want to impose a restriction that the granule size ; be some multiple of page size for simplicity. We assume both ; are 512 bytes here. pushr #^m shadloop: pushr #^m ; first grab an IRP movl #irp$c_length,r1 ;length needed jsb g^exe$alononpaged ;get an IRP if we can (ok at fork ipl) blbs r0,45$ brw lose1 ;if we fail, no queue 45$: movb #dyn$c_irp,irp$b_type(r2) movw r1,irp$w_size(r2) ;set block type & size incl ucb$irpcnt(r5) ;count up irp use. ;(synch'd by ipl & forklock... ; not dependent on inst. atomicity.) ; r1=size of buff, r2=addr of buff movl r2,r6 ; save new irp addr movl r1,r7 ;and size popr #^m pushr #^m ; We now count down that there are extra IRPs yet to do. If it ; is still nonzero we prevent reqcom of original IRP yet. decl ucb$l_rclok(r5) ;count down extra irps bgeq 3334$ ;skip if nonnegative now clrl ucb$l_rclok(r5) ;don't let rclok go negative! 3334$: movl r3,r8 ;our irp addr pushr #^m movc3 r7,(r8),(r6) ;copy the IRP popr #^m ;save regs from movc!!! ; now adjust IRP fields for THIS IRP movl ucb$sublbn(r5),r0 ;lbn desired jsb getslb ;get device & lbn to start ; NOTE getslb assumes EQUAL SIZED container files!!! ; minimize bytes... ; note we use ucb$bcntwk(r5) not irp$l_bcnt(r3) since the irp bytecount ; is invariant during our subordinate jiggery-pokery. cmpw ucb$bcntwk(r5),r2 ;less than one seg to do? bgtru 3330$ ;if gtr we will be back later. Flag. movzwl ucb$bcntwk(r5),r2 ;r2 is now bytes we move here brb 418$ 3330$: ; Here we know we'll need another IRP and must ensure we don't ; count down to 0 if postprocessing beats us to it. We use this ; setting to keep a count down to zero from meaning we reqcom ; the initial IRP before sending all the subordinate ones we ; need to send out. incl ucb$l_rclok(r5) ;set the latch to lock out reqcom 418$: movl r2,irp$l_bcnt(r6) ;fill bytes to move in movl r1,irp$l_media(r6) ;r1 is lbn on host movab ucb$hucb(r5),r4 ;addr of 1st context blk ; get context blk address in ucb mull2 #ucbcntsz,r0 ;context blk number addl2 r0,r4 ;r4 now is context area movl (r4),irp$l_ucb(r6) ;set irp owner ucb correctly ; assume ucb$hlbn-ucb$hucb eq 4 addl2 4(r4),irp$l_media(r6) ;adjust host lbn ;now set up irp$l_svapte ; We assume initially that irp$l_svapte pointed to a contig. ; range of system addresses. NOTE VAX SPECIFIC STUFF HERE!!! movl ucb$subsva(r5),irp$l_svapte(r6) ;sys address ; How far shall we advance now for the next irp? ashl #-9,r2,r1 ;r1 is no. blks shifted ; (NOTE: ALPHA pagesize violates above...) addl2 r1,ucb$sublbn(r5) ashl #2,r1,r4 ;mult by 4 for pte addr addl2 r4,ucb$subsva(r5) ;now next time's svapte should be ok subl2 r2,ucb$bcntwk(r5) ;subt from data yet to move bgeq 3331$ clrl ucb$bcntwk(r5) ;clamp bytecount positive 3331$: popr #^m pushr #^m ; GRAB HOST UCB addr. into regs MOVL IRP$L_UCB(R6),R5 ;NOW POINT AT HOST UCB OURSELVES movl r6,r3 ;point at new IRP ; At this point we look a lot like a normal driver. r5 and r3 ; point at the ucb and irp. MOVL IRP$L_MEDIA(R3),R0 ;GET LBN TO CONVERT ; host driver expects its physical address JSB G^IOC$CVTLOGPHY ; LET THE EXEC DO IT .if ndf,vms$v5 ;ok for v4 and non smp v5 JSB G^EXE$INSIOQ ; INSERT PACKET INTO HOST'S QUEUE .iff ; insioqc handles locks right in v5 (et. seq. ...) JSB G^EXE$INSIOQC ; insert packet and keep forklock ;this reliably solves SMP problems .endc popr #^m ;get regs back tstl ucb$bcntwk(r5) ;got more to do? bleq 3337$ brw shadloop ;if more, fire off another sub irp 3337$: clrl ucb$l_rclok(r5) ;done now...allow reqcom popr #^m rsb ;finally return ; WE Now have queued the work to the real driver. Since the ; I/O may have splits, just await done return and let the ; SD_fixsplit processing get done our cleanup. Because we need ; to await this, just return with VD: unit STILL BUSY to ensure ; that we don't get thru here until we're GOOD AND READY! 402$: BRW FEXL ;Else, branch to execute function. lose1: popr #^m popr #^m clrl ucb$l_rclok(r5) ;let reqcom happen now tstl ucb$irpcnt(r5) ;ensure no imm. exit if subirp exists beql fatalerr ;if none, fatal err now rsb ; ; UNLOAD and AVAILABLE Functions ; Clear UCB$V_VALID in UCB$W_STS ; UNLOAD: AVAILABLE: ; BICW #UCB$M_VALID, - ;Clear sofware volume valid bit. ; UCB$W_STS(R5) ; BRB NORMAL ;Then complete the operation. ; ; OPERATON COMPLETION ; FEXL: ; dummy entry ... should never get here NORMAL: ;SUCCESSFUL OPERATION COMPLETE MOVZWL #SS$_NORMAL,R0 ;ASSUME NORMAL COMPLETION STATUS BRB FUNCXT ;FUNCTION EXIT FATALERR: ;UNRECOVERABLE ERROR MOVZWL #SS$_DRVERR,R0 ;ASSUME DRIVE ERROR STATUS RESETXFR: ; dummy entry ... should never really get here MOVL UCB$L_IRP(R5),R3 ;GET I/O PKT MNEGW IRP$W_BCNT(R3),UCB$W_BCR(R5) ; RESET BYTECOUNT ; BRW FUNCXT FUNCXT: ;FUNCTION EXIT CLRL R1 ;CLEAR 2ND LONGWORD OF IOSB REQCOM ; COMPLETE REQUEST .PAGE ; PWRFAIL: ;POWER FAILURE BICW #UCB$M_POWER,UCB$W_STS(R5) ;CLEAR POWER FAILURE BIT MOVL UCB$L_IRP(R5),R3 ;GET ADDRESS OF I/O PACKET MOVQ IRP$L_SVAPTE(R3),- ;RESTORE TRANSFER PARAMETERS UCB$L_SVAPTE(R5) ;... BRW SD_STARTIO ;START REQUEST OVER SD_INT:: SD_UNSOLNT:: POPR #^M REI ;DUMMY RETURN FROM ANY INTERRUPT ;; ; FIX SPLITS... ; RETURN IRP TO OUR UCB ADDRESS ; THEN REQCOM ; ; TRICK IS TO GET OUR UCB ADDRESS BACK WHEN WE REGAIN CONTROL. DO SO VIA ; JIGGERY-POKERY WITH THE ADDRESS WE CALL. STORE UCB ADDRESSES IN A TABLE ; INTERNALLY AND USE THE CALL ADDRESS TO GET WHERE WE ARE BACK AGAIN. ; ; Note: On entry, r5 points at the IRP we're to handle. We bash this ; information and regenerate it, since irp$l_ucb has already been ; bashed and can't locate our UCB anyway. Therefore we let the return ; address give us the VE: UCB address implicitly via local save and ; restore; each VE: unit returns to a different entry point which preloads ; r5 with a different offset. Once the ucb is located, ucb$l_irp gets ; back the IRP address. This is possibly extra work; one can imagine ; that some IRP fields like SEGVBN could be used to hold the VE: ucb ; address temporarily, since they are saved/restored internally. This ; would allow some address arithmetic to be dispensed with. The current ; method is merely intended to work and NOT force us to let the host driver ; (and maybe other host software) look at bogus IRP fields, and also ; let us remain blissfully ignorant of how VMS handles IRPEs for purposes ; of this driver. (ecch...having to figure out a way to keep i/o post ; processing out of OUR IRPEs (for sure) while using them for temporary ; storage... what a thought!) ; ; NOTE FOLLOWING CODE ASSUMES SD_UNITS IS 2 OR MORE. V_UNIT=0 V_UNM=1 SD_FXS0:: MOVL I^#V_UNIT,R4 BRW SD_FIXSPLIT ;GO HANDLE SD_FXPL==.-SD_FXS0 ;LENGTH IN BYTES OF THIS LITTLE CODE SEGMENT V_UNIT=V_UNIT+4 ;PASS TO NEXT UNIT .MACRO XVEC LBLC SD_FXS'LBLC: MOVL I^#V_UNIT,R4 BRW SD_FIXSPLIT .ENDM .REPEAT XVEC \V_UNM V_UNIT=V_UNIT+4 ;PASS TO NEXT UNIT V_UNM=V_UNM+1 .ENDR SD_FIXSPLIT: ; GET OLD PID.. ; IN OUR UCB$PPID LONGWORD... ; NOTE!!! PROBABLY NEEDS MODS FOR VMS V5!!! ;some cleanup for host needed here. Note that r5 enters as IRP address. ; Use initial R5 to help reset host's system... .if df,x$$$dt ; jsb g^ini$brk .endc movl irp$l_ucb(r5),r3 ;get host's UCB addr movl r5,r2 ;store entry IRP address for check later PUSHL R4 ;NEED TO WORK IN R5 MOVAB SD_UCBTBL,R5 ADDL2 (SP)+,R5 ;R5 NOW POINTS AT UCB ADDRESS MOVL (R5),R5 ;NOW HAVE OUR UCB ADDRESS IN R5 decw ucb$w_qlen(r3) ;cleanup host's q len as ioc$iopost would have bgeq 6$ clrw ucb$w_qlen(r3) ;force queue length zero 6$: ; notice stack is now clean too. movl r5,r4 FORK ;go fork on our UCB now (SD: ucb) movl r4,r5 MOVL R2,R3 ; POINT R3 AT IRP AGAIN. Note this is our "scratch" IRP movl ucb$l_irp(r5),r2 ;get original irp addr ; Accumulate bytecount & error status, then junk subordinate IRP movq irp$l_media(r3),r0 ;get this irp status bisw r0,irp$l_media(r2) ;r2 = orig irp addl2 r1,irp$l_media+4(r2) ;add count in ashl #-16,r0,r0 ;shift r0 down addw2 r0,irp$l_media+2(r2) ;add in bytes here ; now deallocate the IRP and count down till end. movl r3,r0 ;deallocate the irp jsb g^com$drvdealmem ;let it go... ; note we are again synch'd by forklock... tstl ucb$l_rclok(r5) ;is reqcom inhibited by more subirps expected? bneq 511$ decl ucb$irpcnt(r5) ;just handled the last IRP? bleq 107$ ;if so go complete orig. irp 511$: rsb 107$: movl r2,r3 ;now point at the original IRP 300$: TSTL UCB$PPID(R5) ; ENSURE PID IS NONZERO AS SAVED BEQL 15$ ; SKIP BASH IF NOT MOVL UCB$PPID(R5),IRP$L_PID(R3) ;RESTORE THE OLD PID ; since we may now have later parts of virtual, paging, or swapping I/O ; to do, restore saved byte counts and function codes. movl ucb$osegv(r5),irp$l_segvbn(r3) ;restore segment vbn also brb 1501$ 15$: .if df,x$$$dt jsb g^ini$brk ;mousetrap the illegal condition! .endc clrl irp$l_pid(r3) ;make sure we DON'T get back here anyway! ; this is actually an error condition and should NEVER occur... ; movl ucb$obct(r5),irp$l_obcnt(r3) ;restore orig byte cnt 1501$: CLRL UCB$PPID(R5) ; ZERO SAVED PID FIELD FOR CLEANLINESS MOVL R5,IRP$L_UCB(R3) ;RESTORE SD: AS UCB IN IRP TOO ; GRAB R0 AND R1 AS REQCOM IN HOST DRIVER LEFT THEM... MOVL IRP$L_MEDIA(R3),R0 ;GET BACK R0 MOVL IRP$L_MEDIA+4(R3),R1 ;AND R1 ; R0, R1 ARE AS HOST DRIVER LEFT THEM. R5 POINTS TO CORRECT UCB. ; ===> GO FOR IT !!! ; ; Now restore the original IRP$L_MEDIA field of the IRP in case error ; paths in IOC$REQCOM ever need it. Some very low XQP cache situations ; may occasionally need this, though in reasonable sysgen configs it ; should never be needed. This is the one area that got bashed during ; the earlier I/O completion processing in the host driver. ; MOVL UCB$LMEDIA(R5),IRP$L_MEDIA(R3) ; ; notice that for virtual I/O, the IRP's IRP$L_SEGVBN longword still ; has the starting VIRTUAL block number of the I/O request in the context ; of the virtual disk. This must be present as any second and later parts ; of the I/O request modify that field to compute where to go for the ; next I/O. Due to getting back here, the host driver need never know ; about this; it is basically doing ONLY physical and logical I/O where ; this sort of completion jiggery-pokery does not occur. ; - GCE ; Now go REALLY complete the I/O (possibly causing more I/O and certainly ; ensuring the VE: I/O queue is emptied and VE: unbusied after all is done.) JSB @#IOC$REQCOM ; GO COMPLETE THE I/O REQUEST IN VE: CONTEXT ; (OR DO I/O SPLIT NEXT PART IN VE: CONTEXT!) ; ALSO, RETURN **HERE**, SO WE CAN WRAP UP ALL ELSE. ; .IF NDF,VMS$V5 ; ENBINT ; RESTORE IPL TO IPL$_IOPOST ; .IFF ; FORKUNLOCK NEWIPL=(SP)+ ; .ENDC ; rsb exits the fork level. ; IPL4 level exited at fork above, with stack intact at that point. ; iopost saves/restores regs, so r5 bash is ok. RSB ; GET BACK TO HOST SOMETIME ; ; Partitioning function (from s93 sigtapes) ; Acts to find out what container file and what block within that ; file should be used for this piece of I/O and also how many ; bytes may be written and fit within this granule. ;getslb ; input: r5=ucb, r3=irp, r0=lbn to start ; output: r0=container number (0,1...ucb$ncont), r1=start lbn, ; r2 = number bytes that can be transferred in this chunk. getslb: pushr #^m clrl r1 ediv ucb$grnsiz(r5),r0,r8,r9 ;r8 = lbn/segsiz, r9=rem pushl r9 clrl r9 ;divd is 64 bits ediv ucb$ncont(r5),r8,r7,r6 ;r7 = seg on disk, r6=dsk index popl r9 mull3 r7,ucb$grnsiz(r5),r1 ;r1 = start lbn except offset addl2 ucb$grnbas(r5),r1 ;r1 = start lbn ; bytes allowed = 512*(segsz-rem) movl ucb$grnsiz(r5),r2 ;form max bytes subl2 r9,r2 ;subtract offset bgtr 1111$ ;must be positive movl #1,r2 ;clamp legal if not legal at first! 1111$: ashl #9,r2,r2 ;shift to multiply by 512 ; Get container file number now movl r6,r0 ;container offset addl2 r9,r1 ;update start LBN to correct one. popr #^m rsb SD_END: ;ADDRESS OF LAST LOCATION IN DRIVER .END