.TITLE VDDRIVER - VAX/VMS VIRT DISK DRIVER .IDENT 'V01-001' ;USAPADDR=0 ;d$$bug=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. ; ; Note: ; This driver will be for logical I/O and will inhibit physical ; block I/O as makes sense. ; It will have an FDT table that will look just like other ; disks for everything, but will NOT do buffered I/O. ; In its' FDT routines, which will be dummies, it will just ; modify logical block numbers in the I/O packets, set or ; clear the buffered bit in irp$w_STS according as the real ; driver's bit is set or clear, do a range check to make sure ; the LBN used is in the legal range for this particular unit ; ov VD:, and reset things to call the real driver's FDT ; routines and let IT do the work. It will unbusy itself ; before losing control. ; The idea is that only I/O "gets at the physical ; storage", so only that need be munged. Since this happens ; only for read/write logical/physical, we just leave OUR ; FDT routines in there for everything else. In just changing ; the buffered bit in the I/O we MAY mess up some quotas (this ; will eventually get cleaned up), but won't lose buffers or ; otherwise mess up things. Since most drivers have nonbuffered ; I/O, we'll usually be just fine. (Disk drivers, that is.) ; To call the "real" FDT routines we'll just reset the ; registers (and the IORP) to the real device and return. The ; exec routines that handle FDT routines will take it from there ; and do the FDT processing in the real driver. We just have to ; let them work... ; The FDT routines are called from SYSQIOREQ.MAR for ; future reference. ; We don't need to get control back, really; the real ; driver will finish off the I/O. We just need a few checks to ; ensure that a "real" driver exists and has been associated ; with this unit. ; ; Since virtual I/O gets converted to physical and sent direct ; to start-io, we have a trap in the start-io entry that basically ; duplicates the logical IO FDT fakeout with the added wrinkle ; that it converts block number to the host drive's physical media ; address by calling IOC$CVTLOGPHY (just as VMS does) after ; adjusting. It then unbusies this driver and goes to an exec ; routine that sets IPL back to zero and returns from the start ; i/o routine... ; The logical I/O FDT entry is left in for now. It seems ; cleaner to intercept I/O at that level than always at the start ; IO level, and it is used by file system routines. There's no point ; in queueing the request to VD: and then having to requeue to ; another driver when we can get it queued almost directly to the ; driver that'll do the work. So at the expense of a bit more ; code complexity we gain some logical clarity. ; ; Physical I/O (which is becoming less important as MSCP devices don't ; really have it) is faked out in the following way: ; 1. A fake "physical structure" on the virtual disk is ; assumed of 64 sectors/track, 1 track/cylinder. (This limits ; the size of the virtual disk to about 4 million blocks, ; which probably won't cause anyone too much grief; the ; structure can be altered if need be. It further implies that ; virtual disks are a multiple of 64 blocks long. This is ; enforced by ADVD which sets the UCB fields up.) ; 2. Physical I/O requests are just turned to logical ones by ; adjusting the I/O function value (in R7 in the FDT routine) ; and finding the logical block. Then they are passed to the ; vd_fakeout routine to be passed as logical I/O to the real ; driver which can turn them back to physical if it likes. ; ; Actually for this driver physical I/O is simply disabled. ; Virtual I/O presents more of a problem since it goes out and ; gets turned to logical (physical) I/O by VMS, then queued directly to ; the driver's start-io entry. By having virtual I/O come here ; to the VD: start-io entry point, we have to field it to the ; real driver somehow from there. This means some messing ; around with the I/O packet at that point. INIT is still happy ; with the driver and there is still a physical structure declared. ; It just never gets used. ; ;-- .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 $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 ; ; 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. $DEF UCB$W_VD_WPS .BLKW 1 ;Words per sector. $DEF UCB$W_VD_CS .BLKW 1 ;CONTROL STATUS REGISTER $DEF UCB$W_VD_DB .BLKW 1 ;UCB ADDRESS OF HOST DRIVER $DEF UCB$W_VD_DPN .BLKW 1 ;(LONGWORD) $DEF UCB$L_VD_DPR .BLKL 1 ;START LBN OF HOST CONTIG FILE $DEF UCB$L_VD_FMPR .BLKL 1 ; $DEF UCB$L_VD_PMPR .BLKL 1 ;PREVIOUS MAP REGISTER $DEF UCB$B_VD_ER .BLKB 1 ;SPECIAL ERROR REGISTER .BLKB 1 ;Reserved. $DEF UCB$B_VD_LCT .BLKB 1 ;LOOP COUNTER $DEF UCB$B_VD_XBA .BLKB 1 ;BUS ADDRESS EXTENSION BITS $DEF UCB$W_VD_PWC .BLKW 1 ;PARTIAL WORD COUNT $DEF UCB$W_VD_SBA .BLKW 1 ;SAVED BUFFER ADDRESS $DEF UCB$L_VD_XFER .BLKL 1 ;TRANSFER FUNCTION CSR BITS $DEF UCB$L_VD_LMEDIA .BLKL 1 ;LOGICAL MEDIA ADDRESS $DEF UCB$Q_VD_EXTENDED_STATUS ; Area into which we do READ ERROR .BLKQ 1 ; REGISTER command. $DEF UCB$Q_VD_SVAPTETMP ; Area in which we save UCB fields - .BLKQ 1 ; SVAPTE, BOFF, and BCNT. $DEF UCB$L_VD_MAPREGTMP ; Area in which we save CRB fields - .BLKL 1 ; MAPREG, NUMREG, and DATAPATH. $DEF UCB$L_VD_SAVECS .BLKL 1 ; Area in which we save CS and DB regs. ; Add our stuff at the end to ensure we don't mess some fields up that some ; areas of VMS may want. $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 ; $DEF UCB$K_VD_LEN .BLKW 1 ;LENGTH OF UCB ;UCB$K_VD_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 ; .PSECT $$$105_PROLOGUE VD$DPT:: DPTAB - ;DPT CREATION MACRO END=VD_END,- ;END OF DRIVER LABEL ADAPTER=NULL,- ;ADAPTER TYPE = NONE (VIRTUAL) DEFUNITS=2,- ;UNITS 0 THRU 1 UCBSIZE=UCB$K_VD_LEN,- ;LENGTH OF UCB MAXUNITS=8,- ;FOR SANITY...CAN CHANGE NAME=VDDRIVER ;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_FIPL,B,8 ;FORK IPL DPT_STORE UCB,UCB$L_DEVCHAR,L,- ;DEVICE CHARACTERISTICS ; RANDOM ACCESS DPT_STORE UCB,UCB$B_DEVCLASS,B,DC$_DISK ;DEVICE CLASS DPT_STORE UCB,UCB$W_DEVBUFSIZ,W,512 ;DEFAULT BUFFER SIZE 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+4,D,VD_INT ;INTERRUPT SERVICE ROUTINE ADDRESS DPT_STORE CRB,CRB$L_INTD+VEC$L_INITIAL,- ;CONTROLLER INIT ADDRESS D,VD_RX211_INIT ;... DPT_STORE CRB,CRB$L_INTD+VEC$L_UNITINIT,- ;UNIT INIT ADDRESS D,VD_RX02_INIT ;... DPT_STORE DDB,DDB$L_DDT,D,VD$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. ; ;VD$DDT: DDTAB - ;DDT CREATION MACRO DEVNAM=VD,- ;NAME OF DEVICE START=VD_STARTIO,- ;START I/O ROUTINE FUNCTB=VD_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. ; VD_FUNCTABLE: FUNCTAB ,- ;LIST LEGAL FUNCTIONS ; MOUNT VOLUME ; no-op phys I/O for a test here... FUNCTAB ,- ;BUFFERED FUNCTIONS ; MOUNT VOLUME FUNCTAB VD_ALIGN,- ;TEST ALIGNMENT FUNCTIONS FUNCTAB VD_FAKEOUT,- ; FAKEOUT FOR LOGICAL OR PHYSICAL I/O ; 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... ; FUNCTAB VD_PFAKEOUT,- ; FAKEOUT FOR LOGICAL OR PHYSICAL I/O ; ; ; LEAVE NORMAL ACP CALLS IN SO FILE STRUCTURED STUFF ON OUR VD: UNIT ; WILL WORK OK. ; 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$ZEROPARM,- ;ZERO PARAMETER FUNCTIONS ; AVAILABLE FUNCTAB +EXE$ONEPARM,- ;ONE PARAMETER FUNCTION FUNCTAB +EXE$SENSEMODE,- ;SENSE FUNCTIONS FUNCTAB +EXE$SETCHAR,- ;SET FUNCTIONS .PAGE .SBTTL CONTROLLER INITIALIZATION ROUTINE ; ++ ; ; VD_RX211_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. ;-- .PSECT $$$115_DRIVER VD_RX211_INIT: ;RX211 CONTROLLER INITIALIZATION CLRL CRB$L_AUXSTRUC(R8) ; SAY NO AUX MEM RSB ;RETURN .PAGE .SBTTL INTERNAL CONTROLLER RE-INITIALIZATION ; ; INPUTS: ; R4 => RX211 CSR ; R5 => UCB ; RX211_REINIT: RSB ; RETURN TO CALLER .PAGE .SBTTL UNIT INITIALIZATION ROUTINE ;++ ; ; VD_RX02_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. ; ;-- VD_RX02_INIT: ;RX02 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 MOVB #DC$_DISK,UCB$B_DEVCLASS(R5) ;SET DISK DEVICE CLASS MOVB #DT$_RX04,UCB$B_DEVTYPE(R5) ;ASSUME RX04 DEVICE TYPE RSB ;RETURN .PAGE .SBTTL FDT ROUTINES ;++ ; ; VD_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. ; ;-- VD_ALIGN: ;CHECK BYTE COUNT AT P1(AP) .if ndf,nolchk ; note: not fully tested but a MINOR mod... therefore conditioned. tstw 6(ap) ;test high order half of ; byte count specified bneq 10$ ; if bigger than 65k call error .endc .if df,d$$bug movl 4(ap),dbgv2 .endc BLBS 4(AP),10$ ;IF LBS - ODD BYTE COUNT RSB ;EVEN - RETURN TO CALLER 10$: MOVZWL #SS$_IVBUFLEN,R0 ;SET BUFFER ALIGNMENT STATUS JMP G^EXE$ABORTIO ;ABORT I/O .if df,d$$bug dbgv2: .blkl 8 .endc .PAGE .SBTTL START I/O ROUTINE ; .IF DF,PHYSIOK ;; VD_PFAKEOUT ;; Converts physical I/O to logical, checks, then passes a "logical" ;; I/O to the real driver... ;; ;VD_PFAKEOUT: ;; Since the dispatcher's only record this is a physical QIO is in R7 ;; at this point, modify to turn back into logical I/O. Be sure via ;; listing that these codes are a small constant... ; ADDL2 #,R7 ;ADJUST R7 FOR LOG I/O ;; ABOVE TURNS INTO A LOGICAL QIO... ;; MUST CONVERT BLOCK # TO LOGICAL ALSO. ;; NOTE ;; CYLINDER TRK SECT ; IS PHYS FORMAT ;; SINCE WE HAVE 64 SECT/TRK, WE JUST NEED TO TAKE LOW 6 BITS AND ;; THEN HIGH 16 BITS TO MAKE A VALID LBN... ;; ;; HERE ASSUME THAT 8(AP) IS IN SYSTEM SPACE SOMEWHERE (PER ;; TELECON WITH JOIM MCGLINCHEY) ; MOVL 8(AP),R0 ;GET PHYS NUMBER ; ASHL #-10,R0,R1 ;GET CYLINDER NUMBER SHIFTED TO R1 ; BICL2 #^C<^X3FFFC0>,R1 ;ISOLATE IT ; BICL2 #^C<^X3F>,R0 ;AND SECTOR NUMBER ; BISL2 R1,R0 ;NOW OR TOGETHER THE LBNS ; CMPL UCB$HFSZ(R5),R0 ;LBN OF FILE > REQ LBN? ; BGTRU 1$ ; IF GT THEN OK ; MOVZWL #SS$_BADPARAM,R0 ;IF NOT, BAD PARAMS ; JMP G^EXE$ABORTIO ;ABORT I/O ;1$: ;; NOW FIND HOST UCB ; TSTL UCB$HUCB(R5) ; BE SURE WE HAVE SOMETHING IN UCB ADDR ; BNEQ 2$ ; MOVZWL #SS$_VOLINV,R0 ;SET VOLUME INVALID STATUS ; JMP G^EXE$ABORTIO ;ABORT I/O ;2$: ;; If the parameters are all in system space we can of course just add to ;; the 8(ap) longword as is... ;; So in that case the code between the pushr and popr instructions below ;; can just be removed...that's why there's a conditional there... ; .IF DF,USRAPADDR ;; don't bother copying if args are in sys space, BUT if they're ;; in user space, copy arg list and change the copy. ; bitl #^x80000000,ap ; is AP high bit set? ; beqlu 650$ ; if 0 then better extend i/o pkt ; brw 651$ ; use this due to vax losing short branch range ;650$: ; pushr #^m ;; save regs around bashing by exe$allocirp etc... ; movl r3,r9 ;preserve iorp address ;; NOTE: I am not certain this is the right ipl for this routine ;; but for the moment will assume it is... ;; looks ok according to manuals... ; jsb G^exe$allocirp ;get iorp extension ;; address returns in r2 ; bitw #irp$v_extend,irp$w_sts(r9) ;already an irpe there? ; beql 20$ ;if eql no, set this one ; movl irp$l_extend(r9),r9 ;had an irpe so get its address ;18$: bitw #irpe$v_extend,irpe$w_sts(r9) ;ensure we get an irpe w/o ext ; beql 19$ ; if zero go grab this ; movl irpe$l_extend(r9),r9 ; else get next address ; beql 20$ ; if zero stop already ; brb 18$ ; and check again if not for more exts ;19$: movl r2,irpe$l_extend(r9) ; point chain at new irpe ; bisw2 #irpe$v_extend,irpe$w_sts(r9) ;and flag for next pass by others ; brb 21$ ; skip over bash of irp now since it was ; ; extended when we got here ;20$: ; movl r2,irp$l_extend(r9) ; bisw2 #irp$v_extend,irp$w_sts(r9) ;save extension address ; ; and flag for eventual ;21$: ; cleanup ;; hopefully the above logic will allow vd: units on other vd: ;; units. ; movl r2,r8 ; addl2 #irpe$w_size+4,r8 ;point r8 to new "AP" area ; movl (ap),(r8) ; movl 4(ap),4(r8) ; movl 8(ap),8(r8) ; copy p1 thru p4 ; movl 12(ap),12(r8) ; movl r8,ap ; now reset AP to copy of region. ;; note this uses some now-unused areas in the IRPE ; clrl irpe$l_extend(r2) ; clrw irpe$w_sts(r2) ;flag no IRPE extensions ;; (we get the iorp first; everybody after has to check for possible ;; further extensions. This may however disallow virtual disks on ;; virtual disks and should be cleaned up eventually by doing a ;; complete check for irpe's and linking to the end of a chain if ;; there are any such around...) ;; ; enbint #ipl$_astdel ; popr #^m ;651$: ; .ENDC ; R0 set above as "LBN" equivalent of physical block we got... set up ; and go treat the rest as a logical I/O... ; MOVL R0,8(AP) ;AND SAVE AS NEW LBN ; BRW VD_FAKOUB ;THEN HANDLE AS LOGICAL I/O ; .ENDC ; VD_FAKEOUT ; CALLED TO DO MOST OF THE REAL WORK... ; ; This entry does the work of making a contiguous file look like the ; real storage of the VD: unit. It is assumed that the UCB has been ; stuffed with the starting LBN, the size in blocks, and the UCB of ; the physical disk on which a contiguous file is to be used. This ; entry validates the transfer and changes the I/O packet to look like ; it was meant for the equivlent LBN within the file on the "host" disk. ; ; In order to handle virtual I/O, most of this logic is duplicated in ; the start-io entry area of this driver too. However, no IORP extension ; is needed there as the current design goes. ; ; Should we ever have to handle huge buffered requests (over 65535 ; bytes), we will have to add some logic to probably allocate an IRPE ; and get control back at the end of each I/O to fake out the full ; size transfer ourselves. There seems to be no very clean way to do ; this, and it isn't clear it's needed, so no such code exists here at all. ; The problem is that in a big transfer the I/O packet gets re-used ; but the packet then belongs to the host driver, not to ours, so ; the blocks will be wrongly numbered. Current code should just produce ; some sort of error for this. I know of no application that ever ; produces so long a disk transfer however, and suspect that it's only ; important for some other devices. I have not looked closely at this ; however. Perhaps the VD: FDT routines should just return an error ; on any transfer over 65535 bytes and do nothing, not even the first ; part of a transfer. ; ; ; ENTRY: ; R0= FDT ENTRY (OURS ON ENTRY; HOST'S ON EXIT) ; R1, R2 SCRATCH ; R3= I/O PKT ADDRESS ; R4= PCB ADDRESS ; R5= UCB ADDRESS (OURS ON ENTRY; HOST'S ON EXIT) ; R6 = CCB ADDRESS ; R7= I/O FCT CODE BIT NUMBER ; R8= FDT DISPATCH ADDRESS (ADD 12. TO IT EVERY PASS THRU INCLUDINBG FIRST) ; (OURS ON ENTRY; HOST'S ON EXIT) ; R9, R10, R11 = SCRATCH ; AP = ADDRESS OF 1ST FUNCT DEPENDENT PARAMETER VD_FAKEOUT: ; FIRST ENSURE ALL'S WELL FOR LBN RANGE CMPL UCB$HFSZ(R5),8(AP) ;LBN OF FILE > REQ LBN? BGTRU 1$ ; IF GT THEN OK MOVZWL #SS$_BADPARAM,R0 ;IF NOT, BAD PARAMS JMP G^EXE$ABORTIO ;ABORT I/O 1$: ; NOW FIND HOST UCB TSTL UCB$HUCB(R5) ; BE SURE WE HAVE SOMETHING IN UCB ADDR BNEQ 2$ MOVZWL #SS$_VOLINV,R0 ;SET VOLUME INVALID STATUS JMP G^EXE$ABORTIO ;ABORT I/O 2$: ; NOW ADJUST LBN REQUEST AND FAKE THINGS SO IT APPEARS LBN REQUEST ; IS FOR REAL DEVICE. ; ; ; Since the parameters at 0(ap) thru 12(ap) (4 longwords) ; may be in user space, we shouldn't really just add to them. ; Therefore we'll allocate an IORP extension here and copy ; the parameters into it, then reset the AP to point at the copies, which ; we'll bash... ; If the parameters are all in system space we can of course just add to ; the 8(ap) longword as is... ; So in that case the code between the pushr and popr instructions below ; can just be removed...hence the conditional... .IF DF,USRAPADDR bitl #^x80000000,ap ; is AP high bit set? beqlu 750$ ; if 0 then better extend i/o pkt brw 751$ 750$: pushr #^m ; save regs around bashing by exe$allocirp etc... movl r3,r9 ;preserve iorp address jsb G^exe$allocirp ;get iorp extension ; address returns in r2 bitw #irp$v_extend,irp$w_sts(r9) ;already an irpe there? beql 20$ ;if eql no, set this one movl irp$l_extend(r9),r9 ;had an irpe so get its address 18$: bitw #irpe$v_extend,irpe$w_sts(r9) ;ensure we get an irpe w/o ext beql 19$ ; if zero go grab this movl irpe$l_extend(r9),r9 ; else get next address beql 20$ ; if zero stop already brb 18$ ; and check again if not for more exts 19$: movl r2,irpe$l_extend(r9) ; point chain at new irpe bisw2 #irpe$v_extend,irpe$w_sts(r9) ;and flag for next pass by others brb 21$ ; skip over bash of irp now since it was ; extended when we got here 20$: movl r2,irp$l_extend(r9) bisw2 #irp$v_extend,irp$w_sts(r9) ;save extension address ; and flag for eventual 21$: ; cleanup ; hopefully the above logic will allow vd: units on other vd: ; units. movl r2,r8 addl2 #irpe$w_size+4,r8 ;point r8 to new "AP" area movl (ap),(r8) movl 4(ap),4(r8) movl 8(ap),8(r8) ; copy p1 thru p4 movl 12(ap),12(r8) movl r8,ap ; now reset AP to copy of region. ; note this uses some now-unused areas in the IRPE clrl irpe$l_extend(r2) clrw irpe$w_sts(r2) ;flag no IRPE extensions ; (we get the iorp first; everybody after has to check for possible ; further extensions. This may however disallow virtual disks on ; virtual disks and should be cleaned up eventually by doing a ; complete check for irpe's and linking to the end of a chain if ; there are any such around...) ; enbint #ipl$_astdel popr #^m 751$: .ENDC ; now finally adjust copy of p3 to fix up lbn to be used by real ; driver... VD_FAKOUB: ADDL UCB$HLBN(R5),8(AP) ; ADD THE LBN ON STACK BEFORE STORED MOVL UCB$HUCB(R5),R5 ; R5 NOW IS HOST'S UCB ADDRESS MOVL UCB$L_DDT(R5),R0 MOVL DDT$L_FDT(R0),R8 ; POINT AT HOST'S FDT AREA ; ; NOW FILL IN HOST UCB IN I/O PKT ; CMPL IRP$L_OBCNT(R3),IRP$L_BCNT(R3) ;SEE IF WE'RE ; GONNA NEED TO GET BACK TO THIS ; DRIVER FOR 2ND OR LATER SEGMENTED ; I/O PACKETS... ; BNEQ 542$ ; IF NOT SAME ASSUME SO MOVL R5,IRP$L_UCB(R3) ; PACKET MOVES TO HOST... 542$: ; ; Problem with only using an FDT routine is that virtual I/O ; comes thru virtual I/O FDT routines and is turned into logical (actually phys) ; I/O, BUT is not passed thru here. ; We have to have something to catch the virtual packets and ; reset their LBNs after window processing. We set the max byte ; count field in ADVD to inhibit ever getting transfers over ; 64K-1 bytes and it's doubtful that'll happen much anyway. But ; when we get a logical I/O coming in here we have to fix it up ; for the host. The LBN information in the FDT is just in 8(ap) ; and so is easily changed. During a transfer, we have to have the ; LBN info where the system can use it... ; LBN is stored in I/O packet at IRP$L_MEDIA as LBN here since we ; set the no-convert option. Therefore requeue to I/O driver of host ; after mod to that. ; ; NOTE WE DON'T WORRY ABOUT IT GETTING INTO THE WRONG QUEUE HERE ; BECAUSE IN THE FDT ROUTINES, IT ISN'T YET QUEUED TO THE DEVICE ; CCB$W_IOC HAS BEEN INCREMENTED, BUT HOST DRIVER WILL EVENTUALLY ; FINISH I/O ON THE PACKET AND DECREMENT THIS. THE DECREMENT OPERATION ; IS INDEXED BY CHANNEL NUMBER, NOT UNIT, SO THE CCB'S I/O PENDING ; COUNT WILL BE HANDLED OK. ; ; PASS FIRST 2 QUADWORDS OF MASKS ADDL #4,R8 ; WHEN WE RETURN WE GO TO 1ST FDT ; ROUTINE OF REAL DRIVER. SEE ; SYSQIOREQ.MAR FOR DETAILS. ; NOW ALL SET. ONLY LOGICAL I/O GETS HERE AND IT'LL NOW BE DISPATCHED ; FOR THE HOST DRIVER WITH THE MODIFIED I/O PACKET PARAMETERS. ; SINCE FDT PROCESSING JUST GOES MERRILY ALONG FOR ALL FDT ROUTINES, ; IT WILL NOW DO SO IN THE HOST DRIVER USING THE HOST DRIVER'S FDT ; ROUTINES, BUT WITH LBN CORRECTED TO POINT TO OUR ASSOCIATED FILE. ; THIS WILL GET US, WITH LITTLE OVERHEAD, TO A VIRTUAL DISK ON A CONTIGUOUS ; FILE. NOTE ONLY LOGICAL I/O IS TRANSFORMED; THE REST IS FILE-STRUCTURED ; STUFF AND WILL GO ON AS IF WE HAD REAL INDEPENDENT STORAGE ON THIS ; "DEVICE". RSB ; "BACK" TO SYSTEM TO GET TO HOST'S FDT'S NEXT. ;++ ; ; VD_STARTIO - START I/O ROUTINE ; ; FUNCTIONAL DESCRIPTION: ; ; THIS FORK PROCESS IS ENTERED FROM THE EXECUTIVE AFTER AN I/O REQUEST ; PACKET HAS BEEN DEQUEUED. ; ; SELDOM IF EVER CALLED DUE TO FDT THAT REROUTES MOST EVERYTHING TO ; HOST DRIVER. ; ; 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. ; ;-- UNLDJ: BRW UNLOAD AVLBJ: BRW AVAILABLE FATLJ: BRW FATALERR VD_STARTIO: ;START I/O OPERATION ; ; PREPROCESS UCB FIELDS ; ; ASSUME RY_EXTENDED_STATUS_LENGTH EQ 8 ; CLRQ UCB$Q_VD_EXTENDED_STATUS(R5) ; Zero READ ERROR REGISTER area. ; ; BRANCH TO FUNCTION EXECUTION 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$: CMPB #IO$_UNLOAD, R1 ;Unload function? BEQL UNLDJ ;Branch if yes. CMPB #IO$_AVAILABLE, R1 ;Available function? BEQL AVLBJ ;Branch if yes. ; IF WE GET A SEGMENT TRANSFER HERE (LOGICAL I/O) ; IT MUST BE UPDATED FOR HOST AND SHIPPED OUT. .IF NDF,PHSIOK ; THIS MAY BREAK IF PHYS I/O IS THERE... ; 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... ; ; If we add physical I/O we have to translate the IRP$L_MEDIA ; word here to a logical block, THEN go thru the rest of the ; logic here. Since we don't allow it in the legal function ; mask and INIT seems to accept this, there is no need to ; bother. Some commented code earlier shows how to do the physical ; to logical translation for our case. The general case isn't too ; much worse but no need to bother with either... ; EXTZV #IRP$V_FCODE,#IRP$S_FCODE,IRP$W_FUNC(R3),R1 ; GET FCN CODE CMPB #IO$_READLBLK,R1 BEQL 401$ ;READ LOGICAL - LET THRU CMPB #IO$_READPBLK,R1 ;PHYS? BEQL 401$ ;READ PHYS .. OK (SAME AS LOGICAL) CMPB #IO$_READVBLK,R1 BEQL 401$ CMPB #IO$_WRITEVBLK,R1 BEQL 401$ CMPB #IO$_WRITELBLK,R1 ;WRT LOG? BEQL 401$ ;WRITE LOGICAL .. OK CMPB #IO$_WRITEPBLK,R1 ;WRT PHYS? BEQL 401$ BRW 402$ ; BNEQ 402$ ;SOMETHING ELSE .. SKIP 401$: ; debug using sda to peek ; NOW VALIDATED I/O FCN... MODIFY AND SEND OFF .if df,d$$bug MOVL #1,DBGDTA ;TELL THAT WE GOT HERE MOVL R5,DBGDTA+4 ;SAVE OUR UCB ADDR MOVL R3,DBGDTA+8 ; AND I/O PKT MOVL IRP$L_MEDIA(R3),DBGDTA+12 ;STORE BLK NUMBER GIVEN MOVL IRP$L_OBCNT(R3),DBGDTA+16 ;ALSO ORIG BYTE CNT .endc CMPL IRP$L_MEDIA(R3),UCB$HFSZ(R5) ;BE SURE LBN OK BGTRU FATLJ ;IF NOT OK JUST DISMISS I/O ; HAVE TO BE CAREFUL WHAT WE SHIP TO READ DRIVER ; SEND PKT OFF TO REAL DRIVER... ADDL2 UCB$HLBN(R5),IRP$L_MEDIA(R3) ;ADJUST LBN IN IO PKT .if df,d$$bug MOVL IRP$L_MEDIA(R3),DBGDTA+20 MOVL UCB$HLBN(R5),DBGDTA+24 MOVL UCB$HFSZ(R5),DBGDTA+28 MOVL IRP$L_SEGVBN(R3),DBGDTA+32 .endc ; ; NOW we have to fix up the media address for the host... ; ... otherwise we confuse the heck out of things by making ; ... the host (who is expecting a track/sect/cyl number) get really ; ... goofy numbers. Cheat by using exec routine after a bit more messup. ; Ideally we should also avoid moving the packet (next) to host if ; any segmented I/O may occur but ignore that for now; it will be ; rare and I want to get this working in the 99.9% cases... MOVL UCB$HUCB(R5),IRP$L_UCB(R3) ;FIX UP PTR IN I/O PKT PUSHL R5 MOVL UCB$HUCB(R5),R5 ;NOW POINT AT HOST UCB OURSELVES ; PUSHL R0 MOVL IRP$L_MEDIA(R3),R0 ;GET LBN TO CONVERT JSB G^IOC$CVTLOGPHY ; LET THE EXEC DO IT ; NOW IRP$L_MEDIA IS FIXED UP... .if df,d$$bug MOVL IRP$L_MEDIA(R3),DBGDTA+36 ;SAVE FOR DEBUG EXAM .endc PUSHL R1 PUSHL R2 PUSHL R3 PUSHL R4 ; next op may mess up some regs. Also we cannot access the packet once ; we give it to the host driver thus: JSB G^EXE$INSIOQ ; INSERT PACKET INTO HOST'S QUEUE POPL R4 POPL R3 POPL R2 ; GUARANTEE R0-R5 LEFT ALONE POPL R1 POPL R0 POPL R5 ; NOW HAVE OUR OWN UCB ADDRESS BACK ; Here fake out a return. We must NOT allow I/O postprocessing to ; occur now since host driver will do it, but we have to get the ; driver back ready for more work and see that further pending ; I/O gets called. Most of the time there won't be anything ; pending since the driver "instantly" disposes of all its' ; load... SO WE WON'T BOTHER actually trying to recall THIS ; driver; just un-busy it and get out. ; ; add commented-out template code if we want local looping for next ; packet here... ; remque @ucb$l_ioqfl(r5),r3 ;get next i/o packet if any ; bvc 5403$ ;if vc we got one ; brb 5454$ ;5403$: brw vd_startio ;5454$: MOVL UCB$L_CRB(R5),R0 ; GET CRB LINK BICB #CRB$M_BSY,CRB$B_MASK(R0) ;UNBUSY CRB BICL #UCB$M_BSY,UCB$L_STS(R5) ;UNBUSY OURSELVES UCB ; just go return at low prio JMP G^EXE$QIORETURN 402$: .ENDC BRW FEXL ;Else, branch to execute function. ; ; 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 VD_STARTIO ;START REQUEST OVER .if df,d$$bug DBGDTA: .BLKL 6 ;AREA TO HOLD DISPLAYS .BLKL 20 .endc VD_INT:: VD_UNSOLNT:: POPR #^M REI ;DUMMY RETURN FROM ANY INTERRUPT VD_END: ;ADDRESS OF LAST LOCATION IN DRIVER .END