.TITLE VDDRIVER - VAX/VMS VIRT DISK DRIVER .IDENT 'V01-002' ; NOTE: THIS driver is a mod of VDDRIVER which has been somewhat ; retrofitted from what Marty Sasaki actually tried. It has been ; reported not to work right but clearly has some good stuff in ; it. Users are advised to stick with the original version for ; now, but may want to merge fixes in... ; ; 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 and optical disks. ; ; How this all works: ; Note: Original documentation written by Glenn Everhart was ; ripped out and rewritten by Marty Sasaki. ; ; The basic idea is that only actual reads and writes do any ; real i/o. All of the rest of the driver functionality is ; handled in the ACP. ; ; The read/write request is mappped onto the appropriate part of ; the real disk by adjusting the starting logical block number. ; The packet is then put onto the appropriate list for the ; real disk. ;-- .page .sbttl Modification History ; ; May 1987 Marty Sasaki of Ziff Davis Technical Information Co. ; 1. All error returns of media offline changed to volume invalid. ; This was done to avoid mount verification from happening on ; VD's. ; 2. Most of the code for physical i/o removed. ; 3. Original documentation removed, blame new documentation on ; MSS. ; 4. Change multi-way if into case for dispatch into i/o functions. ; 5. Change disk type from RX0? to FD_1 (foreign disk). ; 6. Remove references to RX drives. ; 7. Remove unnecessary UCB extention space ; 8. In start I/O, if device is online, automatically set it to be ; software valid. ; 9. Change VD_ALIGN to allow i/o's of less than 127 blocks only. ; This is done to match ODDRIVER restrictions. ; 10. Move code to set drive as unbusy before address conversion so ; that we don't have to save context for the INSIOQ call. ; 11. Remove PUSHL's and POPL's that saved context since they ; screwed with the stack level which is a no-no. ; 12. Change JSB to INSIOQ to a JMP to properly deal with the stack. ; .PAGE .SBTTL EXTERNAL AND LOCAL DEFINITIONS ; ; EXTERNAL SYMBOLS ; .library /SYS$SHARE:LIB/ $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 $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 $pcbdef $jibdef ; ; constants ; P1 = 0 ; First QIO parameter P2 = 4 ; Second QIO parameter P3 = 8 ; Third QIO parameter P4 = 12 ; Fourth QIO parameter P5 = 16 ; Fifth QIO parameter P6 = 20 ; Sixth QIO parameter ; ; UCB OFFSETS WHICH FOLLOW THE STANDARD UCB FIELDS ; $DEFINI UCB GLOBAL ;start of UCB definitions .=UCB$K_LCL_DISK_LENGTH ;v4 define end of system UCB $DEF UCB$L_VD_HOST_UCB .BLKL 1 ;address of host ucb $DEF UCB$L_VD_HOST_LBN .BLKL 1 ;lbn of host file $DEF UCB$L_VD_HOST_FILE_SIZE .BLKL 1 ;size of host file, blocks $DEF UCB$L_VD_HOST_DESCR .BLKL 2 ;char string descr $DEF ucb$l_debug .blkl 1 ;debugging stuff $DEF ucb$l_debug_1 .blkl 1 ;debugging stuff $DEF ucb$l_debug_2 .blkl 1 ;debugging stuff $DEF ucb$l_debug_3 .blkl 1 ;debugging stuff $DEF ucb$l_debug_4 .blkl 1 ;debugging stuff ; $DEF UCB$K_VD_LEN ;LENGTH OF UCB ;UCB$K_VD_LEN=. ;LENGTH OF UCB $DEFEND UCB ;END OF UCB DEFINITONS .SBTTL STANDARD TABLES ; ; DRIVER PROLOGUE TABLE ; .PSECT $$$105_PROLOGUE 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 is desired. Just make sure it ; is 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 ;... DPT_STORE REINIT ;START CONTROL BLOCK RE-INIT VALUES DPT_STORE CRB,CRB$L_INTD+VEC$L_INITIAL,- ;CONTROLLER INIT ADDRESS D,VD_CONTROL_INIT ;... DPT_STORE CRB,CRB$L_INTD+VEC$L_UNITINIT,- ;UNIT INIT ADDRESS D,VD_UNIT_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. ; DDTAB - ;DDT CREATION MACRO DEVNAM=VD,- ;NAME OF DEVICE START=VD_STARTIO,- ;START I/O ROUTINE FUNCTB=VD_FUNCTABLE,- ;FUNCTION DECISION TABLE ERLGBF=0 ;BYTES IN ERRLOG BUFFER ; ; FUNCTION DECISION TABLE ; VD_FUNCTABLE: FUNCTAB ,- ;LIST LEGAL FUNCTIONS ; MOUNT VOLUME FUNCTAB ,- ;BUFFERED FUNCTIONS ; MOUNT VOLUME FUNCTAB VD_ALIGN,- ;TEST ALIGNMENT FUNCTIONS FUNCTAB VD_FAKEOUT,- ; FAKEOUT FOR LOGICAL OR PHYSICAL I/O functab vd_format,- ;point to host disk ; ; 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_control_init - controller initialization routine ; ; functional description: ; This is a no-op ; 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_control_init: clrl crb$l_auxstruc(r8) ; say no aux mem rsb ;return .page .sbttl unit initialization routine ;++ ; ; vd_unit_init - unit initialization routine ; ; functional description: ; This routine is basically a no-op. We do a little ; housekeeping, but that is all. ; ; 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. ; ; Note: Usually, the unit is set online in the unit init routine. We ; don't do this here. It is done in the format routine, or by a ; privleged task when the VD is attached to a file or optical disk. ;-- VD_UNIT_INIT: movb #dc$_disk,ucb$b_devclass(r5) ;this is a disk movb #dt$_fd1,ucb$b_devtype(r5) ;make this a foreign drive rsb .page .sbttl FDT routines ;++ ; ; vd_format - point to proper location on the host disk ; ; 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 - starting LBN, where the virtual disk starts ; on the real disk. ; longword 1 - virtual disk length, the number of blocks in ; the virtual disk. ; 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 ;-- vd_format: bicw3 #io$m_fcode,irp$w_func(r3),r0 ;mask off function code bneq 20$ ;branch if modifiers, special rsb ;regular processing 10$: movzwl #SS$_BADPARAM,r0 ;illegal parameter clrl r1 jmp g^exe$abortio 20$: movl p1(ap),r0 ;buffer address movl p2(ap),r1 ;length of buffer jsb g^exe$writechk ;read access? doesn't return on error clrl irp$l_bcnt ;paranoia, don't need to do this... movl p1(ap),r0 ;get buffer address movl (r0)+,- ;move starting lbn ucb$l_vd_host_lbn(r5) blss 10$ movl (r0)+,- ;size of virtual disk ucb$l_vd_host_file_size(r5) bleq 10$ movl (r0),- ;name of "real" disk ucb$l_vd_host_descr+4(r5) subl3 #8,p2(ap),- ;set length of name in descriptor ucb$l_vd_host_descr(r5) bleq 10$ ;bad length moval ucb$l_vd_host_descr(r5),r1 ;descriptor for... jsb g^ioc$searchdev ;search for host device blbs r0,30$ ;branch on success movzwl #ss$_nosuchdev+2,r0 ;make an error, usually a warning clrl r1 jmp g^exe$abortio ;exit with error 30$: addl3 ucb$l_vd_host_file_size(r5),- ;end of virtual device ucb$l_vd_host_lbn(r5),r0 cmpl ucb$l_maxblock(r1),r0 ; < end of real disk? blss 10$ movl r1,ucb$l_vd_host_ucb(r5) ;stash the ucb bisw #ucb$m_valid,ucb$l_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 movzwl #ss$_normal,r0 ;success jmp g^exe$finishioc ;wrap things up. ;++ ; ; vd_align - FDT routine to check tranfer byte count ; ; Check to make sure that an even number of bytes are being ; transferred. This routine also makes sure that less than 65535 bytes ; are being transferred. This is done to make i/o processing simpler ; (we don't have to deal with ORB's). ; ; If the byte count is odd, or if the transfer length is greater than ; 65535, we abort the i/o with an error. Otherwise, we just do a RSB ; to continue with FDT processing. ; ; inputs: ; r3 - irp address (i/o request packet) ; r4 - pcb address (process control block) ; r5 - ucb address (unit control block) ;-- vd_align: ;check byte count at p2(ap) cmpl p2(ap),#65024 ;must be less than 127 blocks bgtru 10$ blbs 4(ap),10$ ;if lbs - odd byte count, branch rsb ;even - return to caller 10$: movzwl #ss$_ivbuflen,r0 ;set buffer alignment status jmp g^exe$abortio ;abort I/O ; ; vd_fakeout - where all of the work is done ; ; inputs: ; 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 function code bit number ; r8 = FDT dispatch address (ours on entry; host's on exit) ; r9, r10, r11, scratch ; ap = address of first function dependent parameter (P1) ; vd_fakeout: bbc #ucb$v_online,- ;if offline, then error ucb$l_sts(r5),101$ cmpl ucb$l_vd_host_file_size(r5),p3(ap) ;lbn of file > req lbn? bgtru 1$ ;if > then ok movzwl #ss$_badparam,r0 ;otherwise, badness brb 102$ 1$: ;now find host ucb tstl ucb$l_vd_host_ucb(r5) ;check for valid host UCB bneq 2$ 101$: movzwl #ss$_volinv,r0 ;return invalid volume status 102$: jmp g^exe$abortio 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 can then bash. ; ; If the parameters are all in system space we don't have to worry... ; ; As far as I know, the parameters are already copied into system ; space, so the code below can be (and probably is) conditionally not ; compiled. ; ; There is probably a bug in the following code since it doesn't ; preserve stack level, the call to allocirp could cause trouble. ; .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 movl pcb$l_jib(r4),r4 ;get jib address subl #irp$c_length,jib$l_bytcnt(r4) ;adjust quota count bitw #irp$m_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$m_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$m_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$m_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$l_vd_host_lbn(r5),8(ap) ;offset to host disk blocks movl ucb$l_vd_host_ucb(r5),r5 ;R5 now points to host's UCB movl ucb$l_ddt(r5),r0 ;get DDT so that we can get movl ddt$l_fdt(r0),r8 ;FDT movl r5,irp$l_ucb(r3) ;packet moves to host... 542$: ; ; Returning will result in going to the the first FDT routine of the ; host driver. Details can be found in sysqioreq.mar. ; addl #4,r8 ;pass first 2 quadwords of masks rsb ;"back" to system to get to host's FDT routines .page .sbttl vd_startio - the start i/0 routine ;++ ; ; 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. ; ; This routine should only be called as a result of virtual I/O, ; since most of the I/O requests should have been processed in ; the logical I/O FDT routine. ; ; inputs: ; r3 - IRP address ; r5 - ucb address ; 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. ;-- VD_STARTIO: ;START I/O OPERATION bbs #ucb$v_online,- ;if online, then set software valid ucb$l_sts(r5),10$ movzwl #ss$_volinv,r0 brw resetxfr ;reset byte count and exit 10$: bisl #ucb$m_online,ucb$l_sts(r5) ;set online 20$: extzv #irp$v_fcode,- ;extract function code #irp$s_fcode,irp$w_func(r3),r1 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 readdata: writedata: cmpl irp$l_media(r3),ucb$l_vd_host_file_size(r5) ;LBN okay? blss 10$ brw fatalerr 10$: ; ; Mark us as no longer busy. This is done here since we won't have any ; context left when we finally get back to this driver. We don't have ; to worry about timing problems since we don't relinquish control of ; our CRB and UCB until after it doesn't matter anymore ; 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 ; ; Now we fix up the media address. We have a logical address that ; needs to be converted to a physical address. We call an exec routine ; to actually do the conversion... ; movl ucb$l_vd_host_ucb(r5),irp$l_ucb(r3) ;fix up ptr in i/o pkt addl3 ucb$l_vd_host_lbn(r5),- ;point to LBN on host irp$l_media(r3),r0 movl ucb$l_vd_host_ucb(r5),r5 ;host UCB now ours jsb g^ioc$cvtlogphy ;let exec do it ; ; Now insert the packet into the i/o queue of the host UCB. ; jmp g^exe$insioq ;insert into queue jmp g^exe$qioreturn ;just go return at low prio ; ; 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 BICL #UCB$M_POWER,UCB$L_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 VD_INT:: VD_UNSOLNT:: POPR #^M REI ;DUMMY RETURN FROM ANY INTERRUPT VD_END: ;ADDRESS OF LAST LOCATION IN DRIVER .END