.TITLE JFDRiver ;skeleton driver implementing ucb linkage .IDENT 'V01h' mf$tst=0 ; save fib expansion stuff to see if we get there ; Copyright 1993,1994 Glenn C. Everhart ; All rights reserved ; Author: Glenn C. Everhart ; ; mods: ; 7/8/94 gce -step2 conversion begun ; remember to define xx$nor sometime to wrtchk args!! ; ; real_pvt=0 ;define to include code that on bit 2048 prevents opens on ;assigned devices, privs or not. .ntype __,R31 ; set EVAX nonzero if R31 is a register .if eq <__ & ^xF0> - ^x50 EVAX = 1 .iff ;EVAX = 0 .endc .if df,evax evax = 1 alpha=1 bigpage=1 addressbits=32 ; ;... EVAX=1 -> Step1 .iif ndf WCB$W_NMAP, evax=2 ;... EVAX=2 -> Step2 (ndf as of T2.0) .iif ndf WCB$W_NMAP, step2=1 ;... EVAX=2 -> Step2 (ndf as of T2.0) .endc ;x$$$dt=0 ; above for Alpha only. ; ; Glenn C. Everhart, November 1993 ; ;vms$$v6=0 ;add forvms v6 def'n vms$v5=1 ; define v5$picky also for SMP operation v5$picky=1 .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... .if df,step2 ddt$l_fdt=ddt$ps_fdt_2 .endc $ptedef $vadef $IRPDEF ;DEFINE I/O REQUEST PACKET $irpedef $PRDEF ;DEFINE PROCESSOR REGISTERS $SSDEF ;DEFINE SYSTEM STATUS CODES $UCBDEF ;DEFINE UNIT CONTROL BLOCK .if df,step2 $fdt_contextdef .endc $sbdef ; system blk offsets $psldef $prdef $acldef $rsndef ;define resource numbers $acedef $VECDEF ;DEFINE INTERRUPT VECTOR BLOCK $pcbdef $statedef $jibdef $acbdef $vcbdef $arbdef $wcbdef $ccbdef $fcbdef $phddef $RABDEF ; RAB structure defs $RMSDEF ; RMS constants ; defs for acl hacking $fibdef $atrdef 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. ; Leave thisfield first so we can know all diskswill have it at the ; same offset. ; ; $def ucb$l_hucbs .blkl 1 ;host ucb table ; ; Add other fields here if desired. ; $def ucb$l_ctlflgs .blkl 1 ;flags to control modes ; $def ucb$l_cbtctr .blkl 1 ;how many extents $def ucb$l_cbtini .blkl 1 ;init for counter ; preceding 2 fields allow specifying of contig-best-try extents ; on every Nth extend, not every one. This should still help keep ; file extensions from preferentially picking up chaff $def ucb$JFcontfil .blkb 80 $def ucb$l_asten .blkl 1 ;ast enable mask store ; $DEF ucb$l_minxt .blkl 1 ;min. extent $def ucb$l_maxxt .blkl 1 ;max extent $def ucb$l_frac .blkl 1 ;fraction to extend by $def ucb$l_slop .blkl 1 ;slop blocks to leave free ; DDT intercept fields ; following must be contiguous. $def ucb$s_ppdbgn ;add any more prepended stuff after this $def ucb$l_uniqid .blkl 1 ;driver-unique ID, gets filled in ; by DPT address for easy following ; by SDA $def ucb$l_intcddt .blkl 1 ; Our interceptor's DDT address if ; we are intercepted $def ucb$l_prevddt .blkl 1 ; previous DDT address $def ucb$l_icsign .blkl 1 ; unique pattern that identifies ; this as a DDT intercept block ; NOTE: Jon Pinkley suggests that the DDT size should be encoded in part of this ; unique ID so that incompatible future versions will be guarded against. $def ucb$s_ppdend $def ucb$a_vicddt .blkb ddt$k_length ; space for victim's DDT .blkl 4 ;safety $def ucb$l_backlk .blkl 1 ;backlink to victim ucb ; Make the "unique magic number" depend on the DDT length, and on the ; length of the prepended material. If anything new is added, be sure that ; this magic number value changes. magic=^xF024F000 + ddt$k_length + <256*> p.magic=^xF024F000 + ddt$k_length + <256*> $DEF UCB$L_JF_HOST_DESCR .BLKL 2 ;host dvc desc. ; ; Store copy of victim FDT table here for step 2 Alpha driver. ; assumes FDT table is 64+2 longs long $def ucb$l_myfdt .blkl 70 ;user FDT tbl copy + slop for safety $def ucb$l_oldfdt .blkl 1 ;fdt tbl of prior fdt chain $def ucb$l_vict .blkl 1 ;victim ucb, for unmung check $def ucb$l_mungd .blkl 1 ;munged flag, 1 if numg'd $def ucb$l_exempt .blkl 4 ;exempt PIDs .if df,mf$tst $def ucb$l_svfib .blkl 32 ;save user fib $def ucb$l_svexp .blkl 1 ;extend amount we chose for fib $def ucb$l_svoxp .blkl 1 ;original extend amt $def ucb$l_dpta .blkl 1 ;entry dptab addr $def ucb$l_mgca .blkl 1 ;magic no. .blkl 4 ;slop for more instrumentation .endc $DEF UCB$K_JF_LEN .BLKW 1 ;LENGTH OF UCB ;UCB$K_JF_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 ; driver_data JF_UNITS=300 JF$DPT:: .iif ndf,spt$m_xpamod,dpt$m_xpamod=0 DPTAB - ;DPT CREATION MACRO END=JF_END,- ;END OF DRIVER LABEL ADAPTER=NULL,- ;ADAPTER TYPE = NONE (VIRTUAL) FLAGS=DPT$M_SMPMOD!dpt$m_xpamod!DPT$M_NOUNLOAD, - ;SET TO USE SMP,xa DEFUNITS=2,- ;UNITS 0 THRU 1 thru 31 step=2,- UCBSIZE=UCB$K_JF_LEN,- ;LENGTH OF UCB MAXUNITS=JF_UNITS,- ;FOR SANITY...CAN CHANGE NAME=JFDRIVER ;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 DPT_STORE UCB,UCB$B_FLCK,B,SPL$C_IOLOCK8 ;FORK IPL (VMS V5.X + LATER) ; These characteristics for an intercept driver shouldn't look just ; like a real disk unless it is prepared to handle being mounted, etc. ; Therefore comment a couple of them out. Thus it won't look file oriented ; nor directory structured. DPT_STORE UCB,UCB$L_DEVCHAR,L,- ;DEVICE CHARACTERISTICS ; RANDOM ACCESS DPT_STORE UCB,UCB$L_DEVCHAR2,L,- ;DEVICE CHARACTERISTICS ; Prefix name with "node$" (like rp06) 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. 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 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$L_DEVSTS,L,- ;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,JF_INT ;INTERRUPT SERVICE ROUTINE ADDRESS DPT_STORE DDB,DDB$L_DDT,D,JF$DDT ;DDT ADDRESS DPT_STORE UCB,UCB$L_UNIQID,D,DRIVER$DPT ;store DPT address ; (change "XX" to device ; mnemonic correct values) DPT_STORE UCB,UCB$L_ICSIGN,L,magic ; Add unique pattern (that might ; bring back some memories in ; DOS-11 users) ; HISTORICAL NOTE: under DOS-11, one would get F012 and F024 errors ; on odd address and illegal instruction traps. If we don't have ; this magic number HERE, on the other hand, we're likely to see ; bugchecks in VMS due to uncontrolled bashing of UCB fields! 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. ; ;JF$DDT: DDTAB - ;DDT CREATION MACRO DEVNAM=JF,- ;NAME OF DEVICE START=JF_STARTIO,- ;START I/O ROUTINE FUNCTB=JF_FUNCTABLE,- ;FUNCTION DECISION TABLE CTRLINIT=JF_CTRL_INIT,- UNITINIT=JF_UNIT_INIT,- 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. ; .if ndf,x$ins myins: .blkl 20 ;driver based save areas .endc chnflg: .long 0 ;chain or use our FDT chain flag...use ours if 0 myonoff: fdtonoff: .long 0 ;switch my fdt stuff off if non-0 .ascii /flag/ ;define your own unique flag here; just leave it 4 bytes long! .long 0 ;fdt tbl from before patch fdt_chn = -12 fdt_prev = -4 fdt_idnt = -8 JF_FUNCTABLE: FDT_INI FDT_BUF - ; BUFFERED functions ; MOUNT VOLUME myfdtstart: ; io$_format + modifiers (e.g. io$_format+128) as function code ; allows one to associate a JF unit and some other device; see ; the JF_format code comments for description of buffer to be passed. fdt_act JF_format,- ;point to host disk ; ; First our very own filter routines ; ; Following FDT function should cover every function in the local ; FDT entries between "myfdtbgn" and "myfdtend", in this case just ; mount and modify. Its function is to switch these off or on at ; need. myfdtbgn=. ; Leave a couple of these in place as an illustration. You would of course ; need to insert your own if you're messing with FDT code, or remove these if ; you don't want to. The FDT switch logic is a waste of time and space if ; you do nothing with them... ; They don't actually do anything here, but could be added to. Throw in one ; to call some daemon at various points and it can act as a second ACP ; when control is inserted at FDT time (ahead of the DEC ACP/XQP code!) fdt_act MFYFilt,- ;modify filter (e.g. extend) myfdtend=. JF_ucb: JF_utb: .rept JF_units .long 0 .endr .long 0,0,0,0,0,0,0,0,0,0 driver_code ; ; GETJFUCB - Find JF: UCB address, given r5 points to UCB of the patched ; device. Return the UCB in R0, which should return 0 if we can't find ; it. ; This routine is called a lot and therefore is made as quick as ; it well can be, especially for the usual case. getJFucb: .jsb_entry output= ; clrl r0 ;no UCB initially found pushl r10 pushl r11 ;faster than pushr supposedly ; pushr #^m ; Assumes that R5 is the UCB address of the device that has had some ; code intercepted and that we are in some bit of code that knows ; it is in an intercept driver. Also assumes R11 may be used as ; scratch registers (as is true in FDT routines). Control returns at ; label "err" if the DDT appears to have been clobbered by ; something not following this standard, if conditional "chk.err" ; is defined. ; Entry: R5 - victim device UCB address ; Exit: R11 - intercept driver UCB address chk.err=0 movl ucb$l_ddt(r5),r10 ;get the DDT we currently have ; note we know our virtual driver's DPT address!!! movab DRIVER$dpt,r11 ;magic pattern is DPT addr. .if ndf,x$ins movl r10,myins movl r11,myins+4 movl r5,myins+8 movl (r10),myins+12 .endc ; lock this section with forklock so we can safely remove ; entries at fork also. Use victim device forklock. ; (don't preserve r0 since we clobber it anyway.) forklock lock=ucb$b_flck(r5),savipl=-(sp),preserve=NO .if ndf,x$ins movl (r10),myins+16 movl r11,myins+20 .endc 2$: cmpl (r10),R11 ;this our own driver? ; beql 1$ ;if eql yes, end search ; ; The somewhat odd layout here removes extra branches in the ; most common case, i.e., finding our driver the very first time ; through. The "bneq" branch next time is usually NOT taken. ; .branch_unlikely bneq 5$ ;check next in chain if not us ; At this point R10 contains the DDT address within the intercept ; driver's UCB. Return the address of the intercept driver's UCB next. movab <0-ucb$a_vicddt>(r10),r11 ;point R11 at the intercept UCB ; brb 4$ ; note in this layout we can comment this out. 4$: forkunlock lock=ucb$b_flck(r5),newipl=(sp)+,preserve=NO ; NOW clobber r0 and put things back. movl r11,r0 ; popr #^m popl r11 popl r10 ;supposedly faster than popr .iif ndf,x$ins, movl r0,myins+40 rsb ; Make very sure this DDT is inside a UCB bashed according to our ; specs. The "p.magic" number reflects some version info too. ; If this is not so, not much sense searching more. 5$: cmpl (r10),#p.magic bneq 3$ ;exit if this is nonstd bash ; follow DDT block chain to next saved DDT. movl (r10),r10 ;point R10 at the next DDT in the ;chain bgeq 3$ ; (error check if not negative) brb 2$ ;then check again ;1$: 3$: clrl r11 ;return 0 if nothing found brb 4$ ; ; Few macros for long distance branches... ; .macro beqlw lbl,?lbl2 bneq lbl2 brw lbl lbl2: .endm .macro bneqw lbl,?lbl2 beql lbl2 brw lbl lbl2: .endm .macro bleqw lbl,?lbl2 bgtr lbl2 brw lbl lbl2: .endm .macro bgeqw lbl,?lbl2 blss lbl2 brw lbl lbl2: .endm ; allocate does not zero its result area. ; This macro makes it easy to zero an allocated area before using it. ; Leaves no side effects...just zeroes the area for "size" bytes ; starting at "addr". .macro zapz addr,size pushr #^m ;save regs from movc5 movc5 #0,addr,#0,size,addr popr #^m ;save regs from movc5 .endm ; .SBTTL Our FDT Filter Routines ; These routines are edited from the JFDRiver versions to call ; getJFucb, assuming they are called with R5 pointing at the patched ; driver's UCB. ; 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 ; (AP) - ADDRESS OF FIRST QIO PARAMETER ; Filter routines. ; These do the interesting stuff. ; PopOut: popr #^m pors: ; Here need to return to the "standard" FDT routine. Do so by computing ; the address in the FDT table of the normal host and calling that, then ; returning. Thus the only FDT routines in THIS driver are the ones ; it needs for its own work, not any standard ones. This calls those. EXTZV #IRP$V_FCODE,#IRP$S_FCODE,IRP$L_FUNC(R3),R1 ; GET FCN CODE pushr #^m movl r1,r10 jsb getJFucb ;find JF UCB checking for extra links tstl r0 ;got it? bgeq 199$ ;if not skip out movl ucb$l_oldfdt(r0),r7 ;get address of previous FDT bgeq 199$ ;ensure ok... ; movl ucb$l_ddt(r5),r7 ;find FDT ; Here rely on the fact that we got here via our modified FDT call and that ; the orig. FDT is stored just a bit past the current one. ; movl (r7),r7 ;point at orig. FDT addl2 #8,r7 ;point at one of 64 fdt addresses movl (r7)[r10],r8 ;r7 is desired routine address ;now call the "official" FDT code pushl r6 ;ccb pushl r5 ;ucb pushl r4 ;pcb pushl r3 ;irp calls #4,(r8) ;Call the original routine popr #^m ; Now return as the original routine would. ret 199$: popr #^m movl #16,r0 call_abortio ret ; rsb mfyfilt: $driver_fdt_entry ;filter on MODIFY requests (e.g. extend) ; First do some preliminary checks for sanity. ; 1. Channel must NOT be kernel mode ; 2. Not a movefile .iif ndf,x$ins,movl r6,myins+24 tstl r6 ;is there a CCB (must be +) bleq pors ;if not skip out .iif ndf,x$ins,movzbl ccb$b_amod(r6),myins+28 .iif ndf,x$ins,movl irp$l_func(r3),myins+32 cmpb ccb$b_amod(r6),#1 ;knl mode access? bleq pors ;leave knl mode chnls alone! ;funct modifiers are bits 6-15 ; this is hex ffc0 ; Normal io$_modify should have no modifiers, so if it has it's ; for something else; leave that alone. ; modify is hex 1000 so skip that. 2000 seems to get set for extend ; so try & let it by. .if ndf,evax bitw #^xDFC0,irp$w_func(r3) ;this a movefile or other modifier? .iff bitl #^xDFC0,irp$l_func(r3) ;this a movefile or other modifier? .endc bneq pors ;if so ignore it here. pushr #^m ; original r5 now at 4(sp). Must get that to continue the ops. jsb getJFucb ;find JFDRiver ucb .iif ndf,x$ins, movl r0,myins+36 tstl r0 bgeqw popout movl r5,ucb$l_backlk(r0) ;save link'd ucb in ours too. movl r0,r5 ;point R5 at JF UCB ;make sure not a knl mode channel (leave the XQP channel alone!!!) cmpb ccb$b_amod(r6),#1 ;this the XQP's chnl? bleqw popout ; if so scram NOW. ; Now ensure that this call is not in the same JOB as the daemon. ; (This lets the daemon spawn processes to do some work.) bitl i^#2,ucb$l_ctlflgs(r5) ;look at mfy? bneqw mfycmn ;if neq yes ; (test later will see about space control if doing this) 701$: popr #^m brw pors mspcj: popl r0 brw popout mfycmn: ; here we can modify request fields in the FIB the user supplies to reduce ; fragmentation...e.g. set fib$l_exsz bigger or set fib$m_alconb bit ; in fib$w_exctl IFF fib$m_alcon is not set & set fib$m_aldef. ; pushl r0 .if ndf,evax movl p1(ap),r0 ;get fib .iff movl irp$l_qio_p1(r3),r0 .endc ; remember to define xx$nor sometime to wrtchk args!! .iif df,xx$nor,ifnord #4,4(r0),mspcj movl 4(r0),r0 ;...from descriptor .iif df,xx$nor,ifnord #4,fib$w_exctl(r0),mspcj bitw #fib$m_extend,fib$w_exctl(r0) ;extending at all? beqlw mspc ;if no extend, leave fib alone ; Because contiguous best try allocation flushes the entire extend cache, ; it can cause a tremendous performance hit. Therefore allow it to be ; separately switched so that the benefits of longer extents can be had ; if desired without forcing this flushing every time a file is extended. .if df,mf$tst ; store orig. fib if we see extend request at all. pushr #^m movab ucb$l_svfib(r5),r1 movc3 #64,(r0),(r1) ;save orig fib in 1st 64 bytes popr #^m .endc bitl i^#32,ucb$l_ctlflgs(r5) ;separate control for setting contig best try beql 1$ ; leave contig and contig-best-try alone bitw #,fib$w_exctl(r0) ;contig alloc? bneq 1$ ;if contig leave it alone ; allow this on every nth extend. ; This will allow periodic flushes of the extent cache but will let ; it not be made totally useless. By flushing the extent cache periodically ; we can try to reduce the fragmentation it induces. ; if bit 16384 is not set, do not set aldef. bitl i^#16384,ucb$l_ctlflgs(r5) ;set aldef all the time? beql 704$ bisw #,fib$w_exctl(r0) ;set to use vol default if 704$: ;bigger than program's decl ucb$l_cbtctr(r5) ;count down bgtr 1$ ;and if >0 don't set cbt yet movl ucb$l_cbtini(r5),ucb$l_cbtctr(r5) ;else reset counter bisw #,fib$w_exctl(r0) ;else turn on contig best ;try and turn on use of ;system default extension if ;larger than program default 1$: ; One can add code to check file size and bump extension by more than default if ; it's big (for example, extend by 10% of its' size, not by a few blocks at a time). pushr #^m bitw #,fib$w_exctl(r0) ;contig alloc? bneqw 222$ ;leave size alone for contig alloc movl ccb$l_wind(r6),r7 ;get window block bgeq 222$ ;guard movl wcb$l_fcb(r7),r8 ;and file control blkock bgeq 222$ ;guard movl fcb$l_filesize(r8),r6 ;get filesize beql 222$ ; It is suggested to divide by acp$gb_window instead of 10... ; this is the acp_window sysgen param (default 7), the number of retrieval pointers ; present per window by default. This has no direct relation to size, but one must ; expect at least one retrieval pointer needs to change. In the default situation ; say 1/4th of file size can be used. ; ; The fraction starts at 1/4, but can be anywhere from 1/1 to 1/1000 divl2 ucb$l_frac(r5),r6 ;get 1/4 of current size or so incl r6 ;plus one...for good luck cmpl r6,ucb$l_maxxt(r5) ;extending over max (nominally 120000) bleq 1222$ movl ucb$l_maxxt(r5),r6 ;clamp to max what we're forcing 1222$: cmpl r6,ucb$l_minxt(r5) ;if less than 10 leave alone too bgeq 1223$ movl ucb$l_minxt(r5),r6 ;at least grab this minimum 1223$: ; never try to grab over1/8 of total free space. movl ucb$l_backlk(r5),r8 ;get host ucb (set just above) bgeq 222$ ;(better be there) movl ucb$l_vcb(r8),r8 ;point at vcb bgeq 222$ movl vcb$l_free(r8),r8 ;no. blks free ashl #-3,r8,r8 ;free space /8 cmpl r6,r8 ;extent over freespc/8? ; bgtr 222$ ;if so don't push it here bleq 3223$ ;if not all still ok movl r8,r6 ;else clamp to free/8 3223$: cmpl r6,fib$l_exsz(r0) ;make sure we're increasing size bleq 222$ ;if less than user wants, leave alone ; if 4 bit is clear, allow size ctl always. Otherwise only if aldef set. bitl i^#4,ucb$l_ctlflgs(r5) beql 2222$ bitw #,fib$w_exctl(r0) ;set to use vol default if beql 222$ ;if aldef NOT set, leave size alone. 2222$: .if df,mf$tst pushr #^m movab ucb$l_svfib+64(r5),r1 movc3 #64,(r0),(r1) ;save orig fib in 1st 64 bytes popr #^m movl r6,ucb$l_svexp(r5) ;save exp. amt movl fib$l_exsz(r0),ucb$l_svoxp(r5) ;& original one .endc movl r6,fib$l_exsz(r0) ;fill in as new extend size 222$: popr #^m ; fall thru to space control mspc: popl r0 popr #^m movl #1,r0 brw pors ;++ ; ; JF_format - bash host disk tables to point at ours. ; ; With no function modifiers, this routine takes as arguments the name ; of the host disk (the real disk where the virtual disk will exist), ; the size of the virtual disk, and the LBN where the virtual disk ; will start. After these are set up, the device is put online and is ; software enabled. ; ; This routine does virtually no checking, so the parameters must be ; correct. ; ; Inputs: ; p1 - pointer to buffer. The buffer has the following format: ; longword 0 - (was hlbn) - flag for function. 1 to bash ; the targetted disk, 2 to unbash it, else ; illegal. ; longword 1 - virtual disk length, the number of blocks in ; the virtual disk. If negative disables ; FDT chaining; otherwise ignored. ; longword 2 through the end of the buffer, the name of the ; virtual disk. This buffer must be blank ; padded if padding is necessary ; ; ; p2 - size of the above buffer ;-- JF_format: $driver_fdt_entry bicw3 #io$m_fcode,irp$l_func(r3),r0 ;mask off function code bneq 20$ ;branch if modifiers, special ;thus, normal io$_format will do nothing. brw pors ;regular processing 100$: popr #^m 10$: movzwl #SS$_BADPARAM,r0 ;illegal parameter clrl r1 call_abortio ret ; jmp g^exe$abortio 20$: movl irp$l_qio_p1(r3),r0 ;buff address movl irp$l_qio_p2(r3),r1 ;buff length call_writechk ; jsb g^exe$writechk ;read access? doesn't return on error ; clrl irp$l_bcnt(r3) ;paranoia, don't need to do this... pushr #^m movl irp$l_qio_p1(r3),r0 movl (r0)+,r7 ;get option code bleq 100$ ;0 or negative illegal cmpl r7,#2 ;3 and up illegal too bgtr 100$ incl chnflg movl (r0)+,r6 ;size of virtual disk (ignored) bleq 70$ clrl chnflg ;if 0 or neg. size don't chain... 70$: movab (r0),- ;name of "real" disk ucb$l_JF_host_descr+4(r5) subl3 #8,irp$l_qio_p2(r3),- ucb$l_JF_host_descr(r5) bleq 100$ ;bad length movab ucb$l_JF_host_descr(r5),r1 ;descriptor for... jsb g^ioc$searchdev ;search for host device blbs r0,30$ ;branch on success ; fail the associate... popr #^m movzwl #ss$_nosuchdev+2,r0 ;make an error, usually a warning clrl r1 call_abortio ret ; jmp g^exe$abortio ;exit with error 30$: ;found the device ; r1 is target ucb address... ; move it to r11 to be less volatile movl r1,r11 cmpl r7,#1 ;bashing the target UCB? bneq 31$ jsb mung ;go mung target... brb 32$ 31$: ; Be sure we unmung the correct disk or we can really screw up a system. cmpl r11,ucb$l_vict(r5) ;undoing right disk? bneq 32$ ;if not skip out, do nothing. jsb umung ;unmung target 32$: ; bisw #ucb$m_valid,ucb$w_sts(r5) ;set volume valid ; bisw #ucb$m_online,ucb$w_sts(r5) ;set unit online ; movl ucb$l_irp(r5),r3 ;restore r3, neatness counts popr #^m movzwl #ss$_normal,r0 ;success call_finishioc do_ret=yes ; jmp g^exe$finishioc ;wrap things up. mung: .jsb_entry ; steal DDT from host. Assumes that the intercept UCB address ; is in R5 (that is, the UCB in which we will place the DDT copy), ; and that the UCB of the device whose DDT we are stealing is ; pointed to by R11. All registers are preserved explicitly so that ; surrounding code cannot be clobbered. R0 is returned as a status ; code so that if it returns with low bit clear, it means something ; went wrong so the bash did NOT occur. This generally means some other ; code that does not follow this standard has grabbed the DDT already. ; The following example assumes the code lives in a driver so the ; unique ID field and magic number are set already. tstl ucb$l_mungd(r5) ;already munged/not deassigned? beql 6$ rsb ;no dbl bash 6$: pushr #^m ; Acquire victim's fork lock to synchronize all this. movl #ss$_normal,r0 ;assume success forklock ucb$b_flck(r11),- savipl=-(sp),preserve=YES ; find the current DDT address from the UCB (leaving the copy in ; the DDB alone) movl ucb$l_ddt(r11),r10 ;point at victim's DDB ; fill in host ucb tbl (makes chnl handling faster) movab JF_ucb,ucb$l_hucbs(r5) movl ucb$l_hucbs(r5),r9 ;get ucb table movzwl ucb$w_unit(r5),r0 ;get unit no. moval (r9)[r0],r9 ;point into tbl movl r11,(r9) ;save target ucb addr in tbl ; see if this DDT is the same as the original movl ucb$l_ddb(r11),r9 ;the ddb$l_ddt is the original cmpl ddb$l_ddt(r9),r10 ;bashing driver the first time? beql 1$ ;if eql yes ; driver was bashed already. Check that the current basher followed the ; standard. Then continue if it looks OK. cmpl (r10),#p.magic ;does the magic pattern exist? ; if magic pattern is missing things are badly messed. beql 2$ ;if eql looks like all's well movl #2,r0 ;say things failed brw 100$ ;(brb might work too) 2$: ; set our new ddt address in the previous interceptor's slot movab ucb$a_vicddt(r5),(r10) ;store next-DDT address relative ;to the original victim one 1$: movl #1,ucb$l_mungd(r5) ;say we munged JF movl r10,ucb$l_prevddt(r5) ;set previous DDT address up clrl ucb$l_intcddt(r5) ;clear intercepting DDT initially 3$: pushl r5 ; copy a little extra for good luck... movc3 #,(r10),ucb$a_vicddt(r5) ;copy the DDT popl r5 ;get UCB pointer back (movc3 bashes it) ; ; Here make whatever mods to the DDT you need to. ; ; FOR EXAMPLE make the following mods to the FDT pointer ; (These assume the standard proposed for FDT pointers) movab ucb$a_vicddt(r5),r8 ;get a base register for the DDT movl r5,JF_functable+fdt_prev ;save old FDT ucb address movl ddt$l_fdt(r10),ucb$l_oldfdt(r5) movl ucb$l_uniqid(r5),JF_functable+fdt_idnt ;save unique ID also ; copy legal and buffered entry masks of original driver. ; HOWEVER, set mask for format entry to be nonbuffered here since ; we deal with it. pushr #^m movab ucb$l_myfdt(r5),r9 ;our function table dummy in UCB movl ddt$l_fdt(r10),r7 ;victim's FDT table ; We want all functions legal in the victim's FDT table to be legal ; here. pushr #^m ;preserve regs from movc movl #<68*4>,r0 ;byte count of a step 2 FDT movc3 r0,(r7),(r9) ;copy his FDT to ours popr #^m ;preserve regs from movc ; Now copy in our modify & back-to-original FDT cells. ; We will do this in our FDT table by having FDT definitions only ; for those functions in JFDRiver that we service locally. Thus ; all entry cells for the rest will point in the JF FDT to ; exe$illiofunc. movab g^exe$illiofunc,r8 ;get the magic address movab JF_functable,r10 ;r10 becomes JF FDT tbl addl2 #8,r10 ;point at functions addl2 #8,r9 ;his new FDT... movl #64,r11 ;64 functions 75$: cmpl (r10),r8 ;this function hadled in JF? beql 76$ ;if eql no, skip movl (r10),(r9) ;if we do it point his fdt at our fcn ; (NOTE: our functions MUST therefore call the previous FDT's functions at ; end of their processing.) 76$: cmpl (r10)+,(r9)+ ;pass the entry sobgtr r11,75$ ;do all functions ; JFDRiver FDT table. Last entry goes to user's original FDT chain. ; ; Thus we simply insert our FDT processing ahead of normal stuff, but ; all fcn msks & functions will work for any driver. popr #^m ; Now point the user's FDT at our bugger'd copy. movab ucb$l_myfdt(r5),ddt$l_fdt(r8) ;point at our FDT table clrl myonoff ;turn my FDTs on ; ; Finally clobber the victim device's DDT pointer to point to our new ; one. movab ucb$a_vicddt(r5),ucb$l_ddt(r11) ; Now the DDT used for the victim device unit is that of our UCB ; and will invoke whatever special processing we need. This processing in ; the example here causes the intercept driver's FDT routines to be ; used ahead of whatever was in the original driver's FDTs. Because ; the DDT is modified using the UCB pointer only, target device units ; that have not been patched in this way continue to use their old ; DDTs and FDTs unaltered. ; ; Processing complete; release victim's fork lock 100$: forkunlock lock=ucb$b_flck(r11),newipl=(sp)+,- preserve=YES popr #^m rsb umung: .jsb_entry ; ; Entry: R11 points at victim device UCB and current driver is the one ; desiring to remove its entry from the DDT chain. Thus its xx$dpt: address ; is the one being sought. ("Current driver" here means the intercept ; driver.) ; It is assumed that the driver knows that the DDT chain was patched ; so that its UCB contains an entry in the DDT chain pushr #^m ; .iif df,x$$$dt,jsb g^ini$brk ;***********************debug********* movl r11,r5 ;hereafter use r5 as victim's UCB movl ucb$l_ddt(r5),r10 ;get the DDT we currently have movl ucb$l_ddb(r5),r1 ;get ddb of victim movl ddb$l_ddt(r1),r1 ;and real original DDT movl r10,r0 ;save ucb$l_ddt addr for later movab DRIVER$DPT,r11 ;magic pattern is DPT addr. ; lock this section with forklock so we can safely remove ; entries at fork also. Use victim device forklock. forklock lock=ucb$b_flck(r5),savipl=-(sp),preserve=YES 2$: cmpl (r10),R11 ;this our own driver? beql 1$ ;if eql yes, end search .if df,chk.err cmpl (r10),#p.magic bneqw 4$ ;exit if this is nonstd bash .endc ;chk.err ; follow DDT block chain to next saved DDT. movl (r10),r10 ;point R10 at the next DDT in the ;chain .if df,chk.err bgeqw 4$ ; (error check if not negative) .endc ;chk.err brb 2$ ;then check again 1$: ; At this point R10 contains the DDT address within the intercept ; driver's UCB. Return the address of the intercept driver's UCB next. tstl (r10) ;were we intercepted? bgeq 3$ ;if geq no, skip back-fixup ; we were intercepted. Fix up next guy in line. movl (r10),r11 ;point at interceptor movl (r10),(r11) 3$: ; if we intercepted someone, fix up our intercepted victim to skip by ; us also. movl (r10),r2 ;did we intercept ;original driver? cmpl r2,r1 ;test if this is original beql 5$ ;if eql yes, no bash ; replace previous intercept address by ours (which might be zero) movl (r10),(r2) 5$: ; Here remove FDT entries from the list if they were modified. ; This needs a scan of the FDT chain starting at the victim's ; ddt$l_fdt pointer and skipping around any entry that has address ; JF_functable: ; The FDT chain is singly linked. The code here assumes everybody ; plays by the same rules! ; NOTE: Omit this code if we didn't insert our FDT code in the chain!!! movl ddt$l_fdt(r0),r1 ;start of FDT chain movab JF_functable,r2 ;address of our FDT table clrl r3 movab <0-ucb$a_vicddt>(r10),r4 ;initially point at our ucb ; Also set the JF device offline when we unbash it. This is a simple ; flag that ctl prog. can use to tell if it's been used already. bicl #,ucb$l_sts(r4) 6$: cmpl r1,r2 ;current fdt point at us? beql 7$ ;if eql yes, fix up chain movl r1,r3 ;else store last pointer movl fdt_prev(r1),r4 ;and point at next bgeq 8$ movl ucb$l_oldfdt(r4),r1 ;where last FDT pointer is in the ucb ;;;BUT not all UCBs will have the fdt offset at the same place!!! ;;;HOWEVER we will leave this in, putting the oldfdt field first after ;;;the regular UCB things. bgeq 8$ ;if not sys addr, no messin' brb 6$ ;look till we find one. 7$: ;r3 is 0 or fdt pointing to our block next ;r1 points at our fdt block tstl r3 ;if r3=0 nobody points at us bgeq 8$ ;so nothing to do movl fdt_prev(r1),r4 bgeq 17$ movl ucb$l_oldfdt(r4),-(sp) ;save old fdt loc movl fdt_prev(r3),r4 blss 18$ tstl (sp)+ brb 17$ 18$: movl (sp)+,ucb$l_oldfdt(r4) 17$: movl fdt_prev(r1),fdt_prev(r3) ;else point our next-fdt pointer at ;last fdt addr. 8$: ; ; Finally if the victim UCB DDT entry points at ours, make it point at ; our predecessor. If it points at a successor, we can leave it alone. cmpl r10,r0 ;does victim ucb point at our DDT? bneq 4$ ;if not cannot replace it movl (r10),ucb$l_ddt(r5) clrl (r10) ;zero JF munged flag 4$: forkunlock lock=ucb$b_flck(r5),newipl=(sp)+,preserve=YES popr #^m ;copy our prior DDT ptr to next one rsb .SBTTL CONTROLLER INITIALIZATION ROUTINE ; ++ ; ; JF_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. ;-- JF_ctrl_INIT: $driver_ctrlinit_entry ; CLRL CRB$L_AUXSTRUC(R8) ; SAY NO AUX MEM movl #1,r0 Ret ;RETURN .SBTTL INTERNAL CONTROLLER RE-INITIALIZATION ; ; INPUTS: ; R4 => controller CSR (dummy) ; R5 => UCB ; .SBTTL UNIT INITIALIZATION ROUTINE ;++ ; ; JF_unit_INIT - UNIT INITIALIZATION ROUTINE ; ; FUNCTIONAL DESCRIPTION: ; ; THIS ROUTINE SETS THE JF: 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. ; ;-- JF_unit_INIT: $driver_unitinit_entry ; Don't set unit online here. Priv'd task that assigns JF unit ; to a file does this to ensure only assigned JFn: get used. ; BISW #UCB$M_ONLINE,UCB$W_STS(R5) ;SET UCB STATUS ONLINE ;limit size of JF: data buffers JF_bufsiz=8192 movl #JF_bufsiz,ucb$l_maxbcnt(r5) ;limit transfers to 8k MOVB #DC$_MISC,UCB$B_DEVCLASS(R5) ;SET DISK DEVICE CLASS clrl ucb$l_mungd(r5) ;not mung'd yet .if ndf,x$ins movab myins,ucb$l_dpta(r5) ;point at driver save area ;so we can find it .endc ; 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!!!) movab DRIVER$DPT,ucb$l_uniqid(r5) movl #^Xb22d4001,ucb$l_media_id(r5) ; set media id as JF ; (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 JF: device. ; MSCP may still refuse to do a foreign drive too; jiggery-pokery later ; to test if there's occasion to do so. ; Set up crc polynomial movab JF_utb,ucb$l_hucbs(r5) ;host ucb table clrl chnflg ;initially set to use our chain of FDTs BICL #UCB$M_ONLINE,UCB$L_STS(R5) ;SET UCB STATUS OFFLINE movl #1,r0 ret ;++ ; ; JF_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. ; ;-- JF_STARTIO: $driver_start_entry ; ; PREPROCESS UCB FIELDS ; ; ASSUME RY_EXTENDED_STATUS_LENGTH EQ 8 ; CLRQ UCB$Q_JF_EXTENDED_STATUS(R5) ; Zero READ ERROR REGISTER area. ; ; BRANCH TO FUNCTION EXECUTION bbs #ucb$v_online,- ; if online set software valid ucb$l_sts(r5),210$ 216$: movzwl #ss$_volinv,r0 ; else set volume invalid brw resetxfr ; reset byte count & exit 210$: ; Unless we use this entry, we want to junk any calls here. brb 216$ ;just always say invalid volume. ; Get here for other start-io entries if the virtual disk code is ; commented out also, as it must be. ;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,environment=call ; COMPLETE REQUEST ; ;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 JF_STARTIO ;START REQUEST OVER ;JF_INT:: ;JF_UNSOLNT:: ; POPR #^M ; REI ;DUMMY RETURN FROM ANY INTERRUPT ;; JF_END: ;ADDRESS OF LAST LOCATION IN DRIVER .END