home
***
CD-ROM
|
disk
|
FTP
|
other
***
search
/
vsiftp.vmssoftware.com
/
VSIPUBLIC@vsiftp.vmssoftware.com.tar
/
FREEWARE
/
FREEWARE40.ZIP
/
jgd
/
jgdriver.mar
(
.txt
)
< prev
next >
Wrap
Microsoft Windows Help File Content
|
1994-04-07
|
55KB
|
1,302 lines
.TITLE JGDRiver ;skeleton driver implementing ucb linkage
.IDENT 'V01b'
; Author: Glenn C. Everhart
;evax = 1
;alpha=1
;bigpage=1
;addressbits=32
.if ndf,evax
.macro driver_data
.PSECT $$$105_PROLOGUE
.endm
.macro driver_code
.PSECT $$$115_DRIVER
.endm
.endc
; above for Alpha only.
; Function: Error-retry intercept driver for optical disks.
;x$$$dt=0
;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...
$ptedef
$vadef
$IRPDEF ;DEFINE I/O REQUEST PACKET
$irpedef
$PRDEF ;DEFINE PROCESSOR REGISTERS&
$SSDEF ;DEFINE SYSTEM STATUS CODES&
$UCBDEF ;DEFINE UNIT CONTROL BLOCK
$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 defs7
$RMSDEF ; RMS constants
; defs for acl hacking
$fibdef
$atrdef
p1=0 ; first qio param
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 ucb8
; USE THESE FIELDS TO HOLD OUR LOCAL DATA FOR VIRT DISK.K
; Add our stuff at the end to ensure we don't mess some fields up that some
; areas of VMS may want.C
; Leave thisfield first so we can know all diskswill have it at the
; same offset.5
$def ucb$l_oldfdt .blkl 1 ;fdt tbl of prior fdt chain
; 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 extentsA
; on every Nth extend, not every one. This should still help keep
; file extensions from preferentially picking up chaff
$def ucb$JGcontfil .blkb 80
$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 thisI
$def ucb$l_uniqid .blkl 1 ;driver-unique ID, gets filled in
; by DPT address for easy following
; by SDAJ
$def ucb$l_intcddt .blkl 1 ; Our interceptor's DDT address if<
; we are intercepted>
$def ucb$l_prevddt .blkl 1 ; previous DDT addressH
$def ucb$l_icsign .blkl 1 ; unique pattern that identifiesG
; this as a DDT intercept block
; NOTE: Jon Pinkley suggests that the DDT size should be encoded in part of thisI
; 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 ;safety1
$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.F
magic=^xF024F000 + ddt$k_length + <256*<ucb$s_ppdend-ucb$s_ppdbgn-16>>H
p.magic=^xF024F000 + ddt$k_length + <256*<ucb$s_ppdend-ucb$s_ppdbgn-16>>
;an ACE is there or not.0
$DEF UCB$L_JG_HOST_DESCR .BLKL 2 ;host dvc desc.
; Set FDT table start mask for each unit by keeping it here.1
; We need just enough to get back to user's FDTs.
$def ucb$l_fdtlgl .blkl 2 ;legal fcn msks
$def ucb$l_fdtbuf .blkl 2 ;buffered fcn msks%
$def ucb$l_fdtmfy .blkl 3 ;modify fcn
$def ucb$l_fdtbak .blkl 3 ;"go back" fcn0
$def ucb$l_vict .blkl 1 ;victim ucb for checking<
; The following lets us steal start-io and add error retries7
$def ucb$l_omedia .blkl 1 ;storage of orig. irp$l_media
$def ucb$l_ppid .blkl 1 ;store for irp$l_pid contents
$def ucb$l_retries .blkl 1 ;counter for i/o retries
$def ucb$l_hstartio .blkl 1 ;host driver start-io loc./
$def ucb$l_hstucb .blkl 1 ;host ucb (quick ref)
$DEF UCB$K_JG_LEN .BLKW 1 ;LENGTH OF UCB!
;UCB$K_JG_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 FIELDSA
; THAT ARE TO BE INITIALIZED DURING DRIVER LOADING AND RELOADING
driver_data
JG_UNITS=100
vd_units=jg_units
VJG$DPT::
.iif ndf,spt$m_xpamod,dpt$m_xpamod=0
.if df,evax
DPTAB - ;DPT CREATION MACRO$
END=JG_END,- ;END OF DRIVER LABEL0
ADAPTER=NULL,- ;ADAPTER TYPE = NONE (VIRTUAL)F
FLAGS=DPT$M_SMPMOD!dpt$m_xpamod!DPT$M_NOUNLOAD, - ;SET TO USE SMP,xa'
DEFUNITS=2,- ;UNITS 0 THRU 1 thru 31
step=1,-'
UCBSIZE=UCB$K_JG_LEN,- ;LENGTH OF UCB
MAXUNITS=JG_UNITS,- ;FOR SANITY...CAN CHANGE
NAME=JGDRIVER ;DRIVER NAME
.iff
DPTAB - ;DPT CREATION MACRO$
END=JG_END,- ;END OF DRIVER LABEL0
ADAPTER=NULL,- ;ADAPTER TYPE = NONE (VIRTUAL)F
FLAGS=DPT$M_SMPMOD!dpt$m_xpamod!DPT$M_NOUNLOAD, - ;SET TO USE SMP,xa'
DEFUNITS=2,- ;UNITS 0 THRU 1 thru 31
UCBSIZE=UCB$K_JG_LEN,- ;LENGTH OF UCB
MAXUNITS=JG_UNITS,- ;FOR SANITY...CAN CHANGE
NAME=JGDRIVER ;DRIVER NAME
.endc
DPT_STORE INIT ;START CONTROL BLOCK INIT VALUES8
DPT_STORE DDB,DDB$L_ACPD,L,<^A\F11\> ;DEFAULT ACP NAME3
DPT_STORE DDB,DDB$L_ACPD+3,B,DDB$K_PACK ;ACP CLASS
.IF NDF,VMS$V5
DPT_STORE UCB,UCB$B_FIPL,B,8 ;FORK IPL (VMS V4.X)"
.IFF ;DEFINE FOR VMS V5.X & LATERG
DPT_STORE UCB,UCB$B_FLCK,B,SPL$C_IOLOCK8 ;FORK IPL (VMS V5.X + LATER)
.ENDCC
; 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.
DPT_STORE UCB,UCB$L_DEVCHAR,L,- ;DEVICE CHARACTERISTICS
<DEV$M_SHR- ; SHAREABLE
!DEV$M_AVL- ; AVAILABLE
!DEV$M_IDV- ; INPUT DEVICE
!DEV$M_ODV- ; OUTPUT DEVICE
!DEV$M_RND> ; RANDOM ACCESS9
DPT_STORE UCB,UCB$L_DEVCHAR2,L,- ;DEVICE CHARACTERISTICS
<DEV$M_NNM> ; Prefix name with "node$" (like rp06)
DPT_STORE UCB,UCB$B_DEVCLASS,B,DC$_MISC ;DEVICE CLASS
DPT_STORE UCB,UCB$W_DEVBUFSIZ,W,512 ;DEFAULT BUFFER SIZEB
; 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,22 ;DEVICE IPL7
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
<UCB$M_NOCNVRT> ;...
; don't mess with LBN; leave alone so it's easier to hack on...
DPT_STORE REINIT ;START CONTROL BLOCK RE-INIT VALUESQ
; DPT_STORE CRB,CRB$L_INTD+VEC$L_ISR,D,VR_INT ;INTERRUPT SERVICE ROUTINE ADDRESS
.if ndf,evax
DPT_STORE CRB,CRB$L_INTD+VEC$L_INITIAL,- ;CONTROLLER INIT ADDRESS
D,JG_ctrl_INIT ;...=
DPT_STORE CRB,CRB$L_INTD+VEC$L_UNITINIT,- ;UNIT INIT ADDRESS
D,JG_unit_INIT ;...
.endc0
DPT_STORE DDB,DDB$L_DDT,D,JG$DDT ;DDT ADDRESSB
DPT_STORE UCB,UCB$L_UNIQID,D,DPT$TAB ;store DPT addressH
; (change "XX" to deviceJ
; mnemonic correct values)P
DPT_STORE UCB,UCB$L_ICSIGN,L,magic ; Add unique pattern (that mightM
; 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.
.if df,evax
DDTAB - ;DDT CREATION MACRO
DEVNAM=JG,- ;NAME OF DEVICE'
START=JG_STARTIO,- ;START I/O ROUTINE
FUNCTB=JG_FUNCTABLE,- ;FUNCTION DECISION TABLE
CTRLINIT=JG_CTRL_INIT,-
UNITINIT=JG_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
.iff
DDTAB - ;DDT CREATION MACRO
DEVNAM=JG,- ;NAME OF DEVICE'
START=JG_STARTIO,- ;START I/O ROUTINE
FUNCTB=JG_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
.endc
; FUNCTION DECISION TABLE
; THE FDT LISTS VALID FUNCTION CODES, SPECIFIES WHICH4
; CODES ARE BUFFERED, AND DESIGNATES SUBROUTINES TO2
; PERFORM PREPROCESSING FOR PARTICULAR FUNCTIONS.
; code chaining data:
chnflg: .long 1 ;chain or use our FDT chain flag...use ours if 0
myonoff:3
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
JG_FUNCTABLE:
newfdt:
FUNCTAB ,- ;LIST LEGAL FUNCTIONS
<NOP,- ; NO-OP,
FORMAT,- ; We use format to point to file
UNLOAD,- ; UNLOAD
PACKACK,- ; PACK ACKNOWLEDGE
AVAILABLE,- ; AVAILABLE&
SENSECHAR,- ; SENSE CHARACTERISTICS"
SETCHAR,- ; SET CHARACTERISTICS
SENSEMODE,- ; SENSE MODE
SETMODE,- ; SET MODE
READLBLK,- ; READ LOGICAL BLOCK$
WRITELBLK,- ; WRITE LOGICAL BLOCK$
READPBLK,- ; READ PHYSICAL BLOCK %
WRITEPBLK,- ; WRITE PHYSICAL BLOCK
READVBLK,- ; READ VIRTUAL BLOCK$
WRITEVBLK,- ; WRITE VIRTUAL BLOCK0
ACCESS,- ; ACCESS FILE / FIND DIRECTORY ENTRY&
ACPCONTROL,- ; ACP CONTROL FUNCTION0
CREATE,- ; CREATE FILE AND/OR DIRECTORY ENTRY
DEACCESS,- ; DEACCESS FILE
DELETE,- ; DELETE FILE AND/OR DIRECTORY ENTRY$
MODIFY,- ; MODIFY FILE ATTRIBUTES
MOUNT> ; MOUNT VOLUME
; no-op phys I/O for a test here...
FUNCTAB ,- ;BUFFERED FUNCTIONS
<NOP,-
FORMAT,- ; FORMAT
UNLOAD,- ; UNLOAD
PACKACK,- ; PACK ACKNOWLEDGE
AVAILABLE,- ; AVAILABLE&
SENSECHAR,- ; SENSE CHARACTERISTICS"
SETCHAR,- ; SET CHARACTERISTICS
SENSEMODE,- ; SENSE MODE
SETMODE,- ; SET MODE
ACCESS,- ; ACCESS FILE / FIND DIRECTORY ENTRY&
ACPCONTROL,- ; ACP CONTROL FUNCTION0
CREATE,- ; CREATE FILE AND/OR DIRECTORY ENTRY
DEACCESS,- ; DEACCESS FILE
DELETE,- ; DELETE FILE AND/OR DIRECTORY ENTRY$
MODIFY,- ; MODIFY FILE ATTRIBUTES
MOUNT> ; MOUNT VOLUME
; io$_format + modifiers (e.g. io$_format+128) as function code
; allows one to associate a JG unit and some other device; seeE
; the JG_format code comments for description of buffer to be passed.
functab JG_format,- ;point to host disk
<FORMAT>
; 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.
Functab fdtswitch,-'
<mount,modify,create,deaccess,access>
myfdtbgn=.J
; Leave a couple of these in place as an illustration. You would of courseM
; 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 ACPG
; when control is inserted at FDT time (ahead of the DEC ACP/XQP code!)
mymfy:
FuncTab MFYFilt,-(
<MODIFY> ;modify filter (e.g. extend)
myfdtend=.C
; Note that if we want to allow numerous disk drivers to be patched
; by this one there is not a unique path to the original fdt=
; routine. Therefore use a UCB cell for the patch, not a cell
; ahead of the FDT. That way each unit gets a good return
; path. That's why there's an "oldfdt" cell in the UCB here.
; Following contains all legal functions in mask...
; That way it can transfer all control to a "previous" FDT chain.
mybak:
FuncTab fdttoorig,-
<NOP,- ; NO-OP,
FORMAT,- ; We use format to point to file
UNLOAD,- ; UNLOAD
PACKACK,- ; PACK ACKNOWLEDGE
AVAILABLE,- ; AVAILABLE&
SENSECHAR,- ; SENSE CHARACTERISTICS"
SETCHAR,- ; SET CHARACTERISTICS
SENSEMODE,- ; SENSE MODE
SETMODE,- ; SET MODE
READLBLK,- ; READ LOGICAL BLOCK$
WRITELBLK,- ; WRITE LOGICAL BLOCK$
READPBLK,- ; READ PHYSICAL BLOCK %
WRITEPBLK,- ; WRITE PHYSICAL BLOCK
READVBLK,- ; READ VIRTUAL BLOCK$
WRITEVBLK,- ; WRITE VIRTUAL BLOCK0
ACCESS,- ; ACCESS FILE / FIND DIRECTORY ENTRY&
ACPCONTROL,- ; ACP CONTROL FUNCTION0
CREATE,- ; CREATE FILE AND/OR DIRECTORY ENTRY
DEACCESS,- ; DEACCESS FILE
DELETE,- ; DELETE FILE AND/OR DIRECTORY ENTRYP
CRESHAD,- ; Create a shadow set virtual u$O
DIAGNOSE,- ; Special pass-through function
REMSHAD,- ; Remove a shadow set member
DSE,- ;data security erase=
SETPRFPATH,- ; Set preferred path
READRCT,- ; Read RCT blockA
ADDSHAD,- ; Add a shadow set member
SHADMV,- ; Invoke shadow set mount verification
SEEK,- ;SEEK CYLINDER4
RECAL,- ;RECALIBRATE4
DRVCLR,- ;DRIVE CLEAR5
RELEASE,- ;RELEASE PORT
OFFSET,- ;OFFSET HEADS
RETCENTER,- ;RETURN HEADS TO CENTERLINE
SEARCH,- ;SEARCH FOR SECTOR7
READPRESET,- ;READ IN PRESET
WRITEHEAD,- ;WRITE HEADER AND DATA=
READHEAD,- ;READ HEADER AND DATA
WRITECHECKH,- ;WRITE CHECK HEADER AND DATA6
STARTSPNDL,- ;START SPINDLE?
WRITETRACKD,- ;WRITE TRACK DESCRIPTOR
READTRACKD,- ;READ TRACK DESCRIPTOR>
COPYSHAD,- ; Do shadow set copies$
MODIFY,- ; MODIFY FILE ATTRIBUTES
MOUNT> ; MOUNT VOLUME
; Now the "standard" disk FDT routines needed to let ODS-2 work (or ods-1 !)I
; (Where we are doing read - or possibly write- virtual by hand ourselves
; we may never get to these BTW...)(
FUNCTAB +ACP$READBLK,- ;READ FUNCTIONS#
<READLBLK,- ; READ LOGICAL BLOCK
READPBLK,-!
READVBLK- ; READ VIRTUAL BLOCK
FUNCTAB +ACP$WRITEBLK,- ;WRITE FUNCTIONS%
<WRITELBLK,- ; WRITE LOGICAL BLOCK
WRITEPBLK,-
WRITEVBLK- ; WRITE VIRTUAL BLOCK
FUNCTAB +ACP$ACCESS,- ;ACCESS FUNCTIONS
<ACCESS,- ; ACCEESS FILE / FIND DIRECTORY ENTRY0
CREATE- ; CREATE FILE AND/OR DIRECTORY ENTRY
FUNCTAB +ACP$DEACCESS,- ;DEACCESS FUNCTION
<DEACCESS- ; DEACCESS FILE
FUNCTAB +ACP$MODIFY,- ;MODIFY FUNCTIONS
<ACPCONTROL,- ; ACP CONTROL FUNCTIONm0
DELETE,- ; DELETE FILE AND/OR DIRECTORY ENTRY$
MODIFY- ; MODIFY FILE ATTRIBUTES
>p&
FUNCTAB +ACP$MOUNT,- ;MOUNT FUNCTION
<MOUNT> ; MOUNT VOLUMEC
FUNCTAB +EXE$LCLDSKVALID,- ;LOCAL DISK VALID FUNCTIONS 6
<UNLOAD,- ;UNLOAD VOLUME7
AVAILABLE,- ;UNIT AVAILABLEl9
PACKACK> ;PACK ACKNOWLEDGE
FUNCTAB +EXE$ZEROPARM,- ;ZERO PARAMETER FUNCTIONS5
<UNLOAD,- ; UNLOADN
PACKACK,- ; PACK ACKNOWLEDGE
AVAILABLE> ; AVAILABLEr0
FUNCTAB +EXE$ONEPARM,- ;ONE PARAMETER FUNCTION
<FORMAT- ; FORMAT
> *
FUNCTAB +EXE$SENSEMODE,- ;SENSE FUNCTIONS'
<SENSECHAR,- ; SENSE CHARACTERISTICSE
SENSEMODE- ; SENSE MODE
>D'
FUNCTAB +EXE$SETCHAR,- ;SET FUNCTIONSF#
<SETCHAR,- ; SET CHARACTERISTICS
SETMODE- ; SET MODE
.long -1,-1 ; catch-all mask%
fcnca: .long 0 ;fill in in unit initR
VD_UCBTBL::O
.BLKL VD_UNITSO
.LONG 0,0 ;SAFETY3
.long 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0 ;more safety
; offset address table
v_unm=0 E
; Note: code elsewhere assumes that the xxvc macro generates 8 bytes.;?
; If .address generates more than 4, it breaks as coded here!!!K
.macro xxvc lblct
.address vd_fxs'lblct
.globl vd_fxs'lblct
.long 0
.endm
VD_VOADT::
.rept <vd_units+4>B
xxvc \v_unm
v_unm = <v_unm+1>d
.endr
driver_code
.if df,evax
fcae: .jsb_entry
.iff
fcae:f
.endc+
movzwl #SS$_BADPARAM,r0 ;illegal parametert
clrl r1
jmp g^exe$abortio
; fdtswitch - A
; Based on state of "myonoff" variable either enable or disable1D
; my FDT processing, allowing the FDT chain to remain always intact.C
; This needs to be the first of a chain of FDT entries added to thed
; FDT processing of a driver.H
.if df,evax
fdtswitch: .jsb_entry
.iffI
fdtswitch:
.endc
tstl fdtonoff ;global on/off
bneq 1$
rsb ;go to next FDT if null_5
1$: addl2 #<myfdtend-myfdtbgn>,r8 ;pass our fdt codesT
rsb ;return to std
; fdttoorig - A
; This entry continues FDT processing at the point after the newtC
; entries by returning to the original FDT chain at the point wherenD
; that chain begins. (It is presumed that FDT entries will always be@
; added ahead of existing ones due to the nonreturning nature ofA
; FDT processing.) This is done instead of simply duplicating the
; DEC FDT entries because in this way multiple FDT patches canA
; coexist, as would be impossible if this trick were not used. As '
; can be seen, its overhead is minimal.hA
; The old FDT location is kept in the UCB for our device becauseiD
; that allows us to get back to different FDTs when several drivers'$
; FDT chains are pointed here first.
.if df,evax!
fdttoorig: .jsb_entry,output=<r8>d
.iffl
fdttoorig:
.endc?
; As a performance feature, use a switch to let us just use theeA
; FDT chain here rather than continuing an old one. This needs tod@
; be settable externally since there is no need to return down a.
; chain unless something else is IN the chain.
; Control this with chnflg
; tstl chnflg )
; beql 2$ ;just continue if chnflg is 0e
pushl r0 =
; (this routine gets called a fair bit and if GETJGUCB can bed!
; called less, things speed up.) /
jsb getJGucb ;get UCB for JG unit from stolen
;one
tstl r0 ;r0 is return UCB&
bgeq 1$ ;if not negative, not a UCB*
tstl ucb$l_oldfdt(r0) ;a prior fdt exist?
beql 1$E
movl ucb$l_oldfdt(r0),r8 ;point to original FDT point 8
addl2 #<16-12>,r8 ;pass the 2 entry masksD
1$: ;back up since sysqioreq adds 12
popl r0F
2$: rsb ;off to the previous FDT routines.
; GETJGUCB - Find JG: UCB address, given r5 points to UCB of the patchedF
; device. Return the UCB in R0, which should return 0 if we can't find
; it.dB
; This routine is called a lot and therefore is made as quick as0
; it well can be, especially for the usual case.
.if df,evax
getJGucb: .jsb_entry
.iffa
getJGucb:
.endc!
; clrl r0 ;no UCB initially found
pushl r10(
pushl r11 ;faster than pushr supposedly
; pushr #^m<r10,r11>D
; Assumes that R5 is the UCB address of the device that has had someA
; code intercepted and that we are in some bit of code that knowsS?
; it is in an intercept driver. Also assumes R11 may be used ascD
; scratch registers (as is true in FDT routines). Control returns at:
; label "err" if the DDT appears to have been clobbered byA
; something not following this standard, if conditional "chk.err"m
; is defined.f-
; Entry: R5 - victim device UCB addressl0
; Exit: R11 - intercept driver UCB address
chk.err=0uF
movl ucb$l_ddt(r5),r10 ;get the DDT we currently have2
; note we know our virtual driver's DPT address!!!D
movab VJG$dpt,r11 ;magic pattern is DPT addr.9
; lock this section with forklock so we can safely removet3
; entries at fork also. Use victim device forklock.r1
; (don't preserve r0 since we clobber it anyway.)B=
forklock lock=ucb$b_flck(r5),savipl=-(sp),preserve=NOF4
2$: cmpl <ucb$l_uniqid-ucb$a_vicddt>(r10),R11=
;this our own driver?D@
; 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.
bneq 5$ ;check next in chain if not us A
; At this point R10 contains the DDT address within the intercept F
; driver's UCB. Return the address of the intercept driver's UCB next.O
movab <0-ucb$a_vicddt>(r10),r11 ;point R11 at the intercept UCB.7
; brb 4$ ; note in this layout we can comment this out.B
4$:;?
forkunlock lock=ucb$b_flck(r5),newipl=(sp)+,preserve=NOE%
; NOW clobber r0 and put things back.)
movl r11,r0
; popr #^m<r10,r11>!
popl r11O&
popl r10 ;supposedly faster than popr
rsbA
; Make very sure this DDT is inside a UCB bashed according to ourA=
; specs. The "p.magic" number reflects some version info too.R3
; If this is not so, not much sense searching more.L9
5$: cmpl <ucb$l_icsign-ucb$a_vicddt>(r10),#p.magic;C
bneq 3$ ;exit if this is nonstd bash
; follow DDT block chain to next saved DDT.,5
movl <ucb$l_prevddt-ucb$a_vicddt>(r10),r10LI
;point R10 at the next DDT in theR
;chain F
bgeq 3$ ; (error check if not negative)9
brb 2$ ;then check againd
3$:h$
clrl r11 ;return 0 if nothing found
brb 4$O
; Few macros for long distance branches...
.macro beqlw lbl,?lbl2
bneq lbl2
brw lbl
lbl2:
.endm
.macro bneqw lbl,?lbl2D
beql lbl2
brw lbl
lbl2:
.endm
.macro bleqw lbl,?lbl2T
bgtr lbl2
brw lbl
lbl2:
.endm
.macro bgeqw lbl,?lbl2M
blss lbl2
brw lbl
lbl2:d
.endm)
; allocate does not zero its result area.CE
; This macro makes it easy to zero an allocated area before using it.B@
; Leaves no side effects...just zeroes the area for "size" bytes
; starting at "addr".E
.macro zapz addr,size3
pushr #^m<r0,r1,r2,r3,r4,r5> ;save regs from movc5R
movc5 #0,addr,#0,size,addrS2
popr #^m<r0,r1,r2,r3,r4,r5> ;save regs from movc5
.endm
.SBTTL Our FDT Filter Routines
.if df,evax<
mfyfilt: .jsb_entry ;filter on MODIFY requests (e.g. extend)
.iffT1
mfyfilt: ;filter on MODIFY requests (e.g. extend)B
.endc
; JG_format - bash host disk tables to point at ours.c
; With no function modifiers, this routine takes as arguments the nameE
; of the host disk (the real disk where the virtual disk will exist),RB
; the size of the virtual disk, and the LBN where the virtual diskE
; will start. After these are set up, the device is put online and isC
; software enabled.N
; 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 buffero
.if df,evax
JG_format: .jsb_entryf
.iff'
JG_format:
.endc<
bicw3 #io$m_fcode,irp$w_func(r3),r0 ;mask off function code)
bneq 20$ ;branch if modifiers, specialC)
;thus, normal io$_format will do nothing.L
rsb ;regular processingR
100$:C0
popr #^m<r0,r1,r2,r3,r4,r5,r6,r7,r8,r9,r10,r11>
10$:+
movzwl #SS$_BADPARAM,r0 ;illegal parameterI
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(r3) ;paranoia, don't need to do this...1
pushr #^m<r0,r1,r2,r3,r4,r5,r6,r7,r8,r9,r10,r11>L$
movl p1(ap),r0 ;get buffer address
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$O0
clrl chnflg ;if 0 or neg. size don't chain...
70$:$
movab (r0),- ;name of "real" disk
ucb$l_JG_host_descr+4(r5)G5
subl3 #8,p2(ap),- ;set length of name in descriptorR
ucb$l_JG_host_descr(r5)
bleq 100$ ;bad length4
movab ucb$l_JG_host_descr(r5),r1 ;descriptor for...-
jsb g^ioc$searchdev ;search for host deviceE!
blbs r0,30$ ;branch on successE
; fail the associate...S0
popr #^m<r0,r1,r2,r3,r4,r5,r6,r7,r8,r9,r10,r11>=
movzwl #ss$_nosuchdev+2,r0 ;make an error, usually a warninge
clrl r1$
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?d
bneq 31$ ;if neq it's unmung
jsb mung ;go mung target...e
brb 32$
31$:H
; Be sure we unmung the correct disk or we can really screw up a system.-
cmpl r11,ucb$l_vict(r5) ;undoing right disk?A)
bneq 32$ ;if not skip out, do nothing.
jsb umung ;unmung target
32$:3
; bisw #ucb$m_valid,ucb$w_sts(r5) ;set volume validS3
; bisw #ucb$m_online,ucb$w_sts(r5) ;set unit onlineD5
; movl ucb$l_irp(r5),r3 ;restore r3, neatness counts;0
popr #^m<r0,r1,r2,r3,r4,r5,r6,r7,r8,r9,r10,r11>!
movzwl #ss$_normal,r0 ;successY'
jmp g^exe$finishioc ;wrap things up.L
.if df,evax
mung: .jsb_entry
.iffB
mung:
.endc=
; steal DDT from host. Assumes that the intercept UCB addressOB
; 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 isDC
; pointed to by R11. All registers are preserved explicitly so thatUB
; surrounding code cannot be clobbered. R0 is returned as a statusC
; code so that if it returns with low bit clear, it means somethingDG
; went wrong so the bash did NOT occur. This generally means some otherAF
; code that does not follow this standard has grabbed the DDT already.A
; The following example assumes the code lives in a driver so theE3
; unique ID field and magic number are set already. 6
movab fcae,fcnca ;ensure final FDT entry is filled in7
pushr #^m<r1,r2,r3,r4,r5,r6,r7,r8,r9,r10,r11> 5
; Acquire victim's fork lock to synchronize all this.Y7
movl #ss$_normal,r0 ;assume successt"
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-
; see if this DDT is the same as the originaluF
movl ucb$l_ddb(r11),r9 ;the ddb$l_ddt is the originalG
cmpl ddb$l_ddt(r9),r10 ;bashing driver the first time?d3
beql 1$ ;if eql yesnG
; driver was bashed already. Check that the current basher followed thet)
; standard. Then continue if it looks OK..9
cmpl <ucb$l_icsign-ucb$a_vicddt>(r10),#p.magicuF
;does the magic pattern exist?6
; if magic pattern is missing things are badly messed.E
beql 2$ ;if eql looks like all's wellm:
movl #2,r0 ;say things failed=
brw 100$ ;(brb might work too)
2$:t<
; set our new ddt address in the previous interceptor's slotB
movab ucb$a_vicddt(r5),<ucb$l_intcddt-ucb$a_vicddt>(r10)H
;store next-DDT address relativeC
;to the original victim ones
1$:aD
movl r10,ucb$l_prevddt(r5) ;set previous DDT address upI
clrl ucb$l_intcddt(r5) ;clear intercepting DDT initiallyo
pushl r5&
; copy a little extra for good luck...J
movc3 #<ddt$k_length+12>,(r10),ucb$a_vicddt(r5) ;copy the DDTO
popl r5 ;get UCB pointer back (movc3 bashes it)r
; Here make whatever mods to the DDT you need to.o
; FOR EXAMPLE make the following mods to the FDT pointer7
; (These assume the standard proposed for FDT pointers)NH
movab ucb$a_vicddt(r5),r8 ;get a base register for the DDTE
movl r5,JG_functable+fdt_prev ;save old FDT ucb addressK:
movl ddt$l_fdt(r10),ucb$l_oldfdt(r5) ;save orig. fdt addrK
movl ucb$l_uniqid(r5),JG_functable+fdt_idnt ;save unique ID alsoD9
; copy legal and buffered entry masks of original driver.EA
; HOWEVER, set mask for format entry to be nonbuffered here sinceO
; we deal with it.
pushr #^m<r6,r7,r8,r9>/;
movab ucb$l_fdtlgl(r5),r9 ;our function table dummy in UCB-+
movl ddt$l_fdt(r10),r7 ;victim's FDT table C
; We want all functions legal in the victim's FDT table to be legala
; here. &
movl (r7),(r9)+ ;1st half legal mask'
movl 4(r7),(r9)+ ;2nd half legal mask *
movl 8(r7),(r9)+ ;1st half buffered mask+
movl 12(r7),(r9)+ ;2nd half buffered mask 6
; Now copy in our modify & back-to-original FDT cells.B
; Thus every unit has its own legal & buffered masks, then goes to0
; original FDT, and we don't mess with OUR FDTs.2
; (Also original FDT tables aren't messed either.)&
movl mymfy,(r9)+ ; modify template 1
movl mymfy+4,(r9)+ ; & 2!
movl mymfy+8,(r9)+ ;and addressS@
; Set -1 to set ALL possible function bits so we always go back.*
movl #-1,(r9)+ ;then catch-all "go back""
movl #-1,(r9)+ ; to original fdt*
movl mybak+8,(r9) ; and address of same.
popr #^m<r6,r7,r8,r9>D
movab ucb$l_fdtlgl(r5),ddt$l_fdt(r8) ;point at our FDT table<
movl ddt$l_start(r8),ucb$l_hstartio(r5) ;save host start-io0
movl r11,ucb$l_hstucb(r5) ;save backpointer too7
movab stealstart,ddt$l_start(r8) ;point at our startio 8
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)D@
; Now the DDT used for the victim device unit is that of our UCBI
; and will invoke whatever special processing we need. This processing in C
; the example here causes the intercept driver's FDT routines to be C
; used ahead of whatever was in the original driver's FDTs. BecauseVE
; the DDT is modified using the UCB pointer only, target device units B
; 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$:F6
forkunlock lock=ucb$b_flck(r11),newipl=(sp)+,-
preserve=YESA7
popr #^m<r1,r2,r3,r4,r5,r6,r7,r8,r9,r10,r11>F
.if df,evax
umung: .jsb_entryA
.iff
umung:
.endc
; Entry: R11 points at victim device UCB and current driver is the oneK
; desiring to remove its entry from the DDT chain. Thus its xx$dpt: address E
; is the one being sought. ("Current driver" here means the interceptD
; driver.)F
; It is assumed that the driver knows that the DDT chain was patched4
; so that its UCB contains an entry in the DDT chain.
pushr #^m<r0,r1,r2,r3,r4,r5,r10,r11>0
movl r11,r5 ;hereafter use r5 as victim's UCBF
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 DDTF
movl r10,r0 ;save ucb$l_ddt addr for laterC
movab DPT$TAB,r11 ;magic pattern is DPT addr. 9
; lock this section with forklock so we can safely removeC3
; entries at fork also. Use victim device forklock.
forklock lock=ucb$b_flck(r5),savipl=-(sp),preserve=YES4
2$: cmpl <ucb$l_uniqid-ucb$a_vicddt>(r10),R11=
;this our own driver?E?
beql 1$ ;if eql yes, end searchH
.if df,chk.err9
cmpl <ucb$l_icsign-ucb$a_vicddt>(r10),#p.magicaD
bneqw 4$ ;exit if this is nonstd bash
.endc ;chk.err+
; follow DDT block chain to next saved DDT.;5
movl <ucb$l_prevddt-ucb$a_vicddt>(r10),r10 I
;point R10 at the next DDT in thes.
;chain
.if df,chk.errF
bgeqw 4$ ; (error check if not negative)
.endc ;chk.err9
brb 2$ ;then check againf
1$:vA
; At this point R10 contains the DDT address within the intercept,F
; driver's UCB. Return the address of the intercept driver's UCB next.M
tstl <ucb$l_intcddt-ucb$a_vicddt>(r10) ;were we intercepted?eC
bgeq 3$ ;if geq no, skip back-fixupt/
; we were intercepted. Fix up next guy in line. L
movl <ucb$l_intcddt-ucb$a_vicddt>(r10),r11 ;point at interceptorS
movl <ucb$l_prevddt-ucb$a_vicddt>(r10),<ucb$l_prevddt-ucb$a_vicddt>(r11)n
3$:DE
; if we intercepted someone, fix up our intercepted victim to skip byb
; us also.I
movl <ucb$l_prevddt-ucb$a_vicddt>(r10),r2 ;did we intercept 9
;original driver?cA
cmpl r2,r1 ;test if this is original <
beql 5$ ;if eql yes, no bashB
; replace previous intercept address by ours (which might be zero)R
movl <ucb$l_intcddt-ucb$a_vicddt>(r10),<ucb$l_intcddt-ucb$a_vicddt>(r2)
5$:s>
; Here remove FDT entries from the list if they were modified.=
; This needs a scan of the FDT chain starting at the victim'sFB
; ddt$l_fdt pointer and skipping around any entry that has address
; JG_functable:tB
; The FDT chain is singly linked. The code here assumes everybody
; plays by the same rules!G
; NOTE: Omit this code if we didn't insert our FDT code in the chain!!!m;
movl ddt$l_fdt(r0),r1 ;start of FDT chainiA
movab JG_functable,r2 ;address of our FDT tablea
clrl r3;
movab <0-ucb$a_vicddt>(r10),r4 ;initially point at our ucbeD
; Also set the JG device offline when we unbash it. This is a simple@
; flag that ctl prog. can use to tell if it's been used already.
.if df,evax/
bicl #<ucb$m_valid!ucb$m_online>,ucb$l_sts(r4)s
.iffs/
bicw #<ucb$m_valid!ucb$m_online>,ucb$w_sts(r4)
.endcA
6$: cmpl r1,r2 ;current fdt point at us?nA
beql 7$ ;if eql yes, fix up chainq@
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 A
;;;BUT not all UCBs will have the fdt offset at the same place!!!oF
;;;HOWEVER we will leave this in, putting the oldfdt field first after
;;;the regular UCB things.D
bgeq 8$ ;if not sys addr, no messin'?
brb 6$ ;look till we find one.u
7$:s*
;r3 is 0 or fdt pointing to our block next
;r1 points at our fdt blockbD
tstl r3 ;if r3=0 nobody points at us9
bgeq 8$ ;so nothing to doy
movl fdt_prev(r1),r4
bgeq 17$h.
movl ucb$l_oldfdt(r4),-(sp) ;save old fdt loc
movl fdt_prev(r3),r4e
blss 18$a
tstl (sp)+o
brb 17$
18$: movl (sp)+,ucb$l_oldfdt(r4)N
17$: movl fdt_prev(r1),fdt_prev(r3) ;else point our next-fdt pointer at7
;last fdt addr.t
; Finally if the victim UCB DDT entry points at ours, make it point atF
; our predecessor. If it points at a successor, we can leave it alone.J
cmpl r10,r0 ;does victim ucb point at our DDT?A
bneq 4$ ;if not cannot replace itn?
movl <ucb$l_prevddt-ucb$a_vicddt>(r10),ucb$l_ddt(r5)
4$:o@
forkunlock lock=ucb$b_flck(r5),newipl=(sp)+,preserve=YES-
popr #^m<r0,r1,r2,r3,r4,r5,r10,r11>mK
;copy our prior DDT ptr to next one
; stealstart - start-io entry '
; Must eventually call host's start-io.,
; entry: r3=IRP, r5=host UCB
toorgj: brw toorgd
awab: brw away
.if df,evax
stealstart: .jsb_entry
.iffu
stealstart:D
.endc
pushl r51%
jsb getjgucb ;find intercept UCB now
tstl r0 ;did we find it?*
bgeq awab ;no, scram, but probably hang.&
cmpl ucb$l_hstucb(r0),r5 ;right host?
beql 70$ ;if ok, leave-
movl r5,ucb$l_hstucb(r0) ;else put it in nowa
70$:(
movl r0,r5 ;point at intercept ucb now<
bitl #1048576,ucb$l_ctlflgs(r5) ;user want error reduction?
beql toorgj ;if not skip out9
; besure this is read or write, else just start orig. one.<
EXTZV #IRP$V_FCODE,- ; Extract I/O function code)
#IRP$S_FCODE,- ;i!
IRP$W_FUNC(R3),R0
ASSUME IRP$S_FCODE LE 7 ; Allow byte mode dispatch!
cmpl r0,#io$_writepblk ;too low?
blss toorgj
cmpl r0,#io$_readpblk
bgtr toorgjC
; gotta arrange to get back after done the I/O and to reissue it ifi/
; errors happened and we're not out of count...
.iif ndf,maxtries,maxtries=128cG
;We'll keep the info in the UCB for debugging, but when the host driver J
; that we're intercepting does a request completion, it will unbusy itselfN
; and dequeue anything else that was in the device queue. As a result, we needG
; to track when an IRP has already been modified in this pass, and mustrF
; also just go directly to the original code where that should happen.)
; To accomplish this we need storage for:
; 1. Original irp$l_pid
; 2. Original irp$l_mediaL
; 3. Current retry count (and maybe use hi word as a flag that we have this
; IRP)
; Since I don't want to mess anything up in the regular IRP, just allocateF
; a buffer and use the keydesc slot to point at it. If user has a key,J
; we let the i/o by and he takes his chances with device errors. Advertise-
; that opticals don't support dec encryption.m
; If user has something in the key field leave this IRP alone.$
bbs #irp$v_key,irp$w_sts(r3),toorgj?
; key field should be free. Grab a bit of pool & point at it ifs
; nothing's there.
vv.magic=0
val.magic=^x76543210
vv.retries=4
vv.media=8
vv.pid=12a?
; Set the key bit and go grab an area of mem. to hold our info.v"
bbss #irp$v_key,irp$w_sts(r3),16$-
16$: clrl irp$l_keydesc(r3) ; zero initially.e
pushr #^m<r0,r1,r2,r3,r4,r5>.$
movl #32,r1 ;get 8 longwords area,
jsb g^exe$alonpagvar ;get the space or fail.
blbc r0,55$ ; br if no space & just give up.
; got it.r-
movl r2,irp$l_keydesc(r3) ;save area address
vv.magic=0
val.magic=^x76543210
vv.retries=4
vv.media=8
vv.pid=12u
vv.bash=16-
movl #val.magic,vv.magic(r2) ;flag we got it
movl i^#maxtries,vv.retries(r2) ;save retry count?
movl irp$l_media(r3),vv.media(r2) ;save original media addressh;
movl irp$l_pid(r3),vv.pid(r2) ;save original pid addr too.h
popr #^m<r0,r1,r2,r3,r4,r5>
brb 56$
55$:?
bbcc #irp$v_key,irp$w_sts(r3),17$ ;clr key bit if we have nonea
popr #^m<r0,r1,r2,r3,r4,r5>.
brw toorg ;can't find pool, just let irp by
56$:@
movl I^#maxtries,ucb$l_retries(r5) ;set initial retry count up.6
movl irp$l_media(r3),ucb$l_omedia(r5) ;save lbn stuff5
movl irp$l_pid(r3),ucb$l_ppid(r5) ;save pid info tookD
movl r3,ucb$l_irp(r5) ;for debug keep irp addr in intercept ucb too:
; now set up IRP, then call the previous start-io point at<
; ucb$l_hstartio(r5) to do the work with registers put back.E
; For Alpha, the stack manipulation here is messy to track in machinec
; code, so do it in a register.e6
movl r11,-(sp) ; Free up ol' reliable R11 as scratch
movl r10,-(sp) ; Free R10 also.
movzwl ucb$w_unit(r5),r11 ; Need address cellI
; following assumes that addresses are 32 bits long so shift by 2 gets uso
; to an address offset.r7
ashl #2,r11,r11 ; to get ucb address back at i/o done,6
movab vd_ucbtbl,r10 ; Base of table of UCB addresses6
addl2 r11,r10 ; Make R10 point to cell for THIS UCB0
movl r5,(r10) ; Now save our UCB address there$
; (THIS ALLOWS US TO GET IT BACK...):
; This trick allows us to leave the rest of the IRP alone.
; Now the tricky bit.'C
; We must fill the appropriate address into IRP$L_PID for a call at1@
; I/O completion. We use a table of such routines, one per unit,=
; all of the same size so we can calculate the address of the3>
; routines. However, since the routine addresses can be almost9
; anywhere when the compiler gets done with them, we will E
; use a table constructed BY the compiler of pointers to them all andlI
; access via that instead of just forming the address directly. The tablem,
; entries will be left 2 longs in size each.J
; Table VD_VOADT is what we need. Note however that the .address operatorsH
; there probably need to change to some more general .linkage directive.0
movzwl ucb$w_unit(r5),r11 ; get our unit number&
; Each linkage pair is 8 bytes long...6
ashl #3,r11,r11 ; Make an offset to the linkage area)
movab vd_voadt,r10 ; get the table base 2
addl2 r10,r11 ; r11 now points at the link addr;
movl (r11),irp$l_pid(r3) ; Now point irp$l_pid at a proper1
.if ndf,evax#"
; must add vjg$dpt address to this#
movab vjg$dpt,r10 ;start of driver;4
addl2 r10,irp$l_pid(r3) ;now pid should get back ok
.endc
pushl r0o
movl irp$l_keydesc(r3),r07
movl irp$l_pid(r3),vv.bash(r0) ;save pid field we needb
popl r0'
; pointer to the desired procedureg0
; ; GET BACK CONTROL AT VD_FIXSPLIT (VIA JSB)
; ; WHEN HOST'S I/O IS DONE.w
movl (sp)+,r10 ; Restore R101
movl (sp)+,r11 ; get r11 back & clean stack now30
tstl irp$l_ioqfl(r3) ;i/o queue look sensible?#
blss 19$ ;if negative it might bea>
clrl irp$l_ioqfl(r3) ;else preemptively zero it for requeues0
19$: cmpl irp$l_ioqfl(r3),#^xff000000 ;too high?/
blssu 20$ ;if not real obvious leave it alonep
clrl irp$l_ioqfl(r3)
20$:7
; Now restore registers and go to the original routine.n*
; This is also where we come to try again.L
; Assumes host ucb address on stack, JG ucb address in R5, IRP address in R3
steal2: >
toorg: movl ucb$l_hstartio(r5),r0 ;address of original routine(
bgeq away ; if none, things are messed
.iif df,x$$$dt, jsb g^ini$brk!
popl r5 ; get back original UCBr!
movl r0,-(sp) ; save where to go
movl #1,r0 ; set ok status for now"
jmp @(sp)+ ; go to original code%
; jmp (r0) ;go to the original codel
away:
popl r5
.SBTTL CONTROLLER INITIALIZATION ROUTINEa
; JG_ctrl_INIT - CONTROLLER INITIALIZATION ROUTINE
; FUNCTIONAL DESCRIPTION:3
; noop
; INPUTS:r
; R4 - CSR ADDRESS
; R5 - IDB ADDRESS
; R6 - DDB ADDRESS
; R8 - CRB ADDRESS
; THE OPERATING SYSTEM CALLS THIS ROUTINE:
; - AT SYSTEM STARTUP)
; - DURING DRIVER LOADINGS(
; - DURING RECOVERY FROM POWER FAILURE<
; THE DRIVER CALLS THIS ROUTINE TO INIT AFTER AN NXM ERROR.
.if df,evax7
JG_ctrl_INIT: .jsb_entry ;JG CONTROLLER INITIALIZATIONs
.iffi,
JG_ctrl_INIT: ;JG CONTROLLER INITIALIZATION
.endc*
; CLRL CRB$L_AUXSTRUC(R8) ; SAY NO AUX MEM
movl #1,r01
RSB ;RETURNd-
.SBTTL INTERNAL CONTROLLER RE-INITIALIZATION
; INPUTS:
; R4 => controller CSR (dummy)
; R5 => UCBe
ctrl_REINIT:
RSB ; RETURN TO CALLER#
.SBTTL UNIT INITIALIZATION ROUTINEK
; JG_unit_INIT - UNIT INITIALIZATION ROUTINE
; FUNCTIONAL DESCRIPTION:
; THIS ROUTINE SETS THE JG: ONLINE.
; THE OPERATING SYSTEM CALLS THIS ROUTINE:d
; - 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.0
; ALL GENERAL REGISTERS (R0-R15) ARE PRESERVED.
.if df,evax2
JG_unit_INIT: .jsb_entry ;JG UNIT INITIALIZATION
.iffa3
JG_unit_INIT:; .jsb_entry ;JG UNIT INITIALIZATION
.endc.
movab fcae,fcnca ; set up catch-all final FDT>
; Don't set unit online here. Priv'd task that assigns JG unit<
; to a file does this to ensure only assigned JGn: get used.:
; BISW #UCB$M_ONLINE,UCB$W_STS(R5) ;SET UCB STATUS ONLINE
;limit size of JG: data buffers(
JG_bufsiz=81929
movl #JG_bufsiz,ucb$l_maxbcnt(r5) ;limit transfers to 8k 9
MOVB #DC$_MISC,UCB$B_DEVCLASS(R5) ;SET MISC DEVICE CLASSk
; NOTE: we may want to set this as something other than an RX classl>
; 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.7
; (There's no GOOD reason to disable MSCP, but care!!!)J9
movl #^Xb22d4001,ucb$l_media_id(r5) ; set media id as JGdG
; (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)mG
;;; NOTE: changed from fd1 type so MSCP will know it's a local disk andp8
;;; attempt no weird jiggery-pokery with the JG: device.G
; MSCP may still refuse to do a foreign drive too; jiggery-pokery later
; to test if there's occasion to do so.i
; Set up crc polynomiala6
; clrl chnflg ;initially set to use our chain of FDTs
movl #1,chnflgn
movl #1,r0m
RSB ;RETURN
.SBTTL START I/O ROUTINEk
; JG_STARTIO - START I/O ROUTINE
; FUNCTIONAL DESCRIPTION:b
; THIS FORK PROCESS IS ENTERED FROM THE EXECUTIVE AFTER AN I/O REQUESTs
; PACKET HAS BEEN DEQUEUED.
; INPUTS:t
; R3 - IRP ADDRESS (I/O REQUEST PACKET)e)
; R5 - UCB ADDRESS (UNIT CONTROL BLOCK) :
; IRP$L_MEDIA - PARAMETER LONGWORD (LOGICAL BLOCK NUMBER)
; OUTPUTS:
; R0 - FIRST I/O STATUS LONGWORD: STATUS CODE & BYTES XFEREDo/
; R1 - SECOND I/O STATUS LONGWORD: 0 FOR DISKS"
; THE I/O FUNCTION IS EXECUTED.
; ALL REGISTERS EXCEPT R0-R4 ARE PRESERVED.
.if df,evax.
JG_STARTIO: .jsb_entry ;START I/O OPERATION
.iffT#
JG_STARTIO: ;START I/O OPERATIONs
.endc
; PREPROCESS UCB FIELDS
; ASSUME RY_EXTENDED_STATUS_LENGTH EQ 8vD
; CLRQ UCB$Q_JG_EXTENDED_STATUS(R5) ; Zero READ ERROR REGISTER area.
; BRANCH TO FUNCTION EXECUTION 3
bbs #ucb$v_online,- ; if online set software validn
ucb$w_sts(r5),210$5
216$: movzwl #ss$_volinv,r0 ; else set volume invalidr'
brw resetxfr ; reset byte count & exite
210$:i;
; Unless we use this entry, we want to junk any calls here.e+
brb 216$ ;just always say invalid volume.m
; Get here for other start-io entries if the virtual disk code ise$
; commented out also, as it must be.!
FATALERR: ;UNRECOVERABLE ERRORm2
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 PKTe5
MNEGW IRP$W_BCNT(R3),UCB$W_BCR(R5) ; RESET BYTECOUNTc
; BRW FUNCXT
FUNCXT: ;FUNCTION EXIT&
CLRL R1 ;CLEAR 2ND LONGWORD OF IOSB
REQCOM ; COMPLETE REQUEST1
PWRFAIL: ;POWER FAILURE:
BICW #UCB$M_POWER,UCB$W_STS(R5) ;CLEAR POWER FAILURE BIT1
MOVL UCB$L_IRP(R5),R3 ;GET ADDRESS OF I/O PACKETB5
MOVQ IRP$L_SVAPTE(R3),- ;RESTORE TRANSFER PARAMETERSs
UCB$L_SVAPTE(R5) ;....$
BRW JG_STARTIO ;START REQUEST OVER
JG_INT::
JG_UNSOLNT::
POPR #^M<R0,R1,R2,R3,R4,R5>%
REI ;DUMMY RETURN FROM ANY INTERRUPTh
V_UNIT=0
V_UNM=1t
.if df,evax
VD_FXS0:: .jsb_entry input=<r5>t
.iff
VD_FXS0::e
.endc
MOVL I^#V_UNIT,R4
BSBW VD_FIXSPLIT ;GO HANDLE
RSBA
VD_FXPL==<.-VD_FXS0> ;LENGTH IN BYTES OF THIS LITTLE CODE SEGMENT_#
V_UNIT=V_UNIT+4 ;PASS TO NEXT UNITr
.MACRO XVEC LBLC
.if df,evax"
VD_FXS'LBLC: .jsb_entry input=<r5>
.iff
VD_FXS'LBLC:
.endc
MOVL I^#V_UNIT,R4
BSBW VD_FIXSPLIT
.ENDM-
.REPEAT <VD_UNITS+4> ; some extra for safetyl
XVEC \V_UNM#
V_UNIT=V_UNIT+4 ;PASS TO NEXT UNITc
V_UNM=V_UNM+1
.ENDR
.if df,evax
VD_FIXSPLIT: .jsb_entrya
.iffy
VD_FIXSPLIT:
.endc
; GET OLD PID..i
; IN OUR UCB$PPID LONGWORD... G
;some cleanup for host needed here. Note that r5 enters as IRP address.u/
; Use initial R5 to help reset host's system...
; .iif df,x$$$dt, jsb g^ini$brku)
PUSHL R4 ;r4 enters with JG unit number 0
movl r5,r3 ;put entering IRP addr in std place
MOVAB VD_UCBTBL,R5 -
ADDL2 (SP)+,R5 ;R5 NOW POINTS AT UCB ADDRESSa-
MOVL (R5),R5 ;NOW HAVE JG UCB ADDRESS IN R5e
; notice stack is now clean too..
movl r5,r4 ;we need the jg ucb at fork levelD
;Now we either restart the i/o if an error occurred, or go ahead and@
; complete it. In either case we must fork. Also we must fork on!
; the HOST'S UCB, not the JG UCB. 0
; Therefore get host ucb again and fork on that.2
movl ucb$l_hstucb(r5),r5 ;note jg ucb still in r4)
FORK ;go fork on our UCB now (vd: ucb)k9
; Now see if we need to reissue the I/O. If so, go do it.g0
; r4 should still be jg ucb, r5=host ucb, r3=irp
.if df,x$$$dt
jsb g^ini$brk2
cmpl r3,r3 ;irp. (look at @r3 at this point too.)
cmpl r4,r4 ;jg ucb addr
cmpl r5,r5 ;host ucb addr
.endcG
; Somehow we seem to get irp$l_ioqfl screwed up. Zero it if it is clear
; it's senseless.b5
tstl irp$l_ioqfl(r3) ;ioq should always be negativei
blss 19$ ;if + or 0 zero it
clrl irp$l_ioqfl(r3),?
19$: cmpl irp$l_ioqfl(r3),#^XFF000000 ;too high to be sensible?v
blssu 20$
clrl irp$l_ioqfl(r3)c
20$:(
movq irp$l_media(r3),r0 ;get i/o status5
blbs r0,40$ ;if status is OK, just finish up here. /
movl irp$l_keydesc(r3),r0 ;get buffer area loc2
bgeq 40$ )
decl vv.retries(r0) ;count retries down "
bleq 40$ ;if so also finish now4
;looks like we need to continue. Therefore go do so.J
; Note that at this point the stack is clean and r3 and r5 are irp and ucb&
; of host as his start-io will expect.K
; (This will nead some tweaks for axp procedure nesting. OK on Vax though.)i
; r5 points at host UCB now.:
; Now reset the media field so the IRP will work next time"
movl vv.media(r0),irp$l_media(r3)E
; If the host driver clobbered this field, we must ensure we get backnN
; here as soon as we hit the next start-io for this driver. Actually it shouldC
; be fixed like so now or we wouldn't be here...but be safe anyway.E7
movl vv.bash(r0),irp$l_pid(r3) ;arrange us to get backcE
; can't just call the original code since the driver may be busy with
; something else. Our fork synch doesn't completely prevent this, since M
; the relevant test is whether the driver is busy. Therefore call exe$insioqcl@
; to do it instead, relying on our tests in stealstart to detect(
; that this IRP has already been set up.I
; Note that we have left the irp$l_pid address still unchanged so that it$F
; still will get back here next time around, so again we can check it.:
; For this we insert in the original device queue so leave@
; r5 pointing at it. Note that steal2 entry wants original R5 on9
; the stack but no longer requires R5 pointing at JG UCB.s-
clrl irp$l_ioqfl(r3) ;be sure the irp is clrr
jsb g^exe$insioqc!
movl #1,r0 ;flag all seems well_
rsb ;return when done.D
;;; brw steal2
40$:+
; Reset the IRP to have the original returna=
; Thus the IRP will really complete next, not come back here.
1501$:6
; GRAB R0 AND R1 AS REQCOM IN HOST DRIVER LEFT THEM...%
MOVL IRP$L_MEDIA(R3),R0 ;GET BACK R0o"
MOVL IRP$L_MEDIA+4(R3),R1 ;AND R1@
; R0, R1 ARE AS HOST DRIVER LEFT THEM. R5 POINTS TO CORRECT UCB.
; Now restore the original IRP$L_MEDIA field of the IRP in case error F
; paths in IOC$REQCOM ever need it. Some very low XQP cache situationsD
; may occasionally need this, though in reasonable sysgen configs itE
; should never be needed. This is the one area that got bashed duringd;
; the earlier I/O completion processing in the host driver.
; This will then appear to be coming from the original driver.
pushr #^m<r0,r1,r2,r3,r4,r5> >
; Restore media and pid fields and deallocate the extra field.*
movl irp$l_keydesc(r3),r4 ;get our buffer:
; movl vv.media(r4),irp$l_media(r3) ;restore media address+
; for com$post use must leave status in IRP.0
movl vv.pid(r4),irp$l_pid(r3) ;restore pid alsoH
; now deallocate the buffer. We never get here unless one was allocated.
movl r4,r0 ;address to freeo
movl #32,r1 ;length to freed,
jsb g^exe$deanonpgdsiz ;free the pool againA
clrl irp$l_keydesc(r3) ;zero the key descriptor too for neatnessmG
bbcc #irp$v_key,irp$w_sts(r3),42$ ;if using encryption, never get herep
popr #^m<r0,r1,r2,r3,r4,r5>E
;(this may be the problem area; what unbusies the host driver & when?tD
; Host driver called reqcom to get here which cleared his unit busy.I
; If his unit is not busy now, this isn't really a problem. If it IS busycD
; however, this IS a problem, as we really have no business touchingE
; the host's UCB busy bit from here. Let's try using com$post insteadu
; to finish things up.)b2
;;; JSB G^IOC$REQCOM ; GO COMPLETE THE I/O REQUEST;
jsb g^com$post ; complete the request but leave busy ALONEb
RSB ; GET BACK TO HOST SOMETIME
JG_END: ;ADDRESS OF LAST LOCATION IN DRIVER
.ENDp