.title ATG_FT_PATCH - ATG FT Access Port Name Support .ident /© '92 ATG X1.05/ ;++ ; Module: ATG_FT_PATCH ; Package: MMTS ; Author: Nick de Smith (NICK@NCDLAB.ULCC.AC.UK,PSI%234213300154::NICK) ; Creation date: 23-Oct-91 ; ; © 1992, Copyright Applied Telematics Group Ltd. and Nick de Smith ; All rights reserved. ; ; This software is supplied for information only. No guarantee ; is supplied for this software, and no liability will be ; accepted for any action resulting from the use of this ; software or the information contained herein. ; ; Under no circumstances may this software be used for ; commercial gain, including its sale, lease or loan. ; ; This software may be copied only with the inclusion of this ; copyright notice. ; The author is prepared to enter into correspondence with ; interested parties, but will not necessarily maintain this ; software. ; ; Revision history: ; ; Edit Edit date By Why ; X01.05 03-Jan-92 NMdS Remove reference to UCB$L_LOCKID. ; X01.04 11-Nov-91 NMdS Added in ATG_FT_UNLOAD ; X01.03 08-Nov-91 NMdS Make into a loadable image ; X01.02 07-Nov-91 NMdS Add in UCB$L_LOCKID as creator's PID ; X01.01 23-Oct-91 NMdS First attempt ; ; Abstract: ; ; This code provides access port name support for the DEC FTDRIVER ; (Pseudo Terminal Port driver). ; ; The whole essence of the way this code is implemented is to try ; to be transparent to any DEC implementation of access port name ; support for FTDRIVER. The code is paranoid about damaging the VMS ; system, and should therefore be safe in all environments. ; ; This code is copied into SYS$COMMON:[SYS$LDR] by ATG_FT_INSTALL.COM ; and is loaded into the VMS executive by ATG_FT_START.EXE, which is ; called from SYS$MANAGER:ATG_FT_START.COM ; ; Build: ; ; $ Macro /List=ATG_FT_PATCH.LIS ATG_FT_PATCH.MAR ; $ Link /NoSysShr /NoTraceback - ; /Share=ATG_FT_PATCH - ; /Map=ATG_FT_PATCH /Full /Cross - ; /Symbol=ATG_FT_PATCH - ; SYS$INPUT/OPTION ; ATG_FT_PATCH, - ; SYS$LIBRARY:STARLET/Include=(SYS$DOINIT),- ; SYS$SYSTEM:SYS.STB/Selective_Search ; VECTOR_TABLE=SYS$SYSTEM:SYS.STB ; COLLECT=NONPAGED_READONLY_PSECTS/ATTRIBUTES=RESIDENT,- ; EXEC$NONPAGED_CODE ; COLLECT=NONPAGED_READWRITE_PSECTS/ATTRIBUTES=RESIDENT,- ; EXEC$NONPAGED_DATA ; COLLECT=PAGED_READONLY_PSECTS,- ; EXEC$PAGED_CODE ; COLLECT=PAGED_READWRITE_PSECTS,- ; EXEC$PAGED_DATA ; COLLECT=INITIALIZATION_PSECTS/ATTRIBUTES=INITIALIZATION_CODE,- ; EXEC$INIT_CODE,- ; EXEC$INIT_000,- ; EXEC$INIT_001,- ; EXEC$INIT_002,- ; EXEC$UNL_000,- ; EXEC$UNL_001,- ; EXEC$UNL_002,- ; EXEC$INIT_PFNTBL_000,- ; EXEC$INIT_PFNTBL_001,- ; EXEC$INIT_PFNTBL_002,- ; EXEC$INIT_SSTBL_000,- ; EXEC$INIT_SSTBL_001,- ; EXEC$INIT_SSTBL_002 ; $! ; ;-- .sbttl Declarations .library "SYS$LIBRARY:LIB" .link "SYS$SYSTEM:SYS.STB" /Selective_Search ; Save typing later ;+ ; External Definitions ;- $candef ; $CANCEL status definitions $dptdef ; Driver Prologue Table definitions $dyndef ; Data structure type codes $ftucbdef ; FTDRIVER UCB extensions $ftvecdef ; FTDRIVER port vector offsets $ipldef ; Synchronization IPL values $irpdef ; I/O request packed definitions $ldrimgdef ; Loadable code image descriptor $pcbdef ; Software PCB fields $sfdef ; Saved frame offsets $ssdef ; Define system statii $ttyucbdef ; TTY UCB extensions $ttyvecdef ; TTY port vector offsets $ucbdef ; UCB definitions ;+ ; Local definitions ;- FT_C_ACCPORNAM_LENGTH = 64 ; Size of access port name buffer FT_C_HEADER = 12 ; Size of NPP buffer header .sbttl ATG_FT_INIT - Initialise ATG FTDRIVER patches ;+ ; ATG_FT_INIT - Initialise ATG FTDRIVER patches ; ; Functional Description: ; ; This routine loads a set of patches for FTDRIVER into non-paged pool. ; The patches intercept calls to FT$CREATE and FT$CANCEL (for CAN$C_DASSGN ; calls only). ; ; Inputs: ; ; R4 => LDRIMG$ data block for this loadable image ; ; Outputs: ; ; R0 = Final status of load ; SS$_NORMAL Successful load of the patches ; SS$_LOADER Code is already loaded ; SS$_NOSUCHDEV FTDRIVER was not found ; ; Notes: ;- DECLARE_PSECT EXEC$INIT_CODE INITIALIZATION_ROUTINE - ATG_FT_INIT ATG_FT_INIT: pushr #^m bsbw FIND_FTDRIVER ; Locate FTDRIVER blbc r0, 100$ ; Exit if not found ; ; Check that the existing port create and cancel routines lie within the ; selected driver. If they do not, there is a more than fair chance that ; this program, or another like it, has been run once already. ; movzwl DPT$W_SIZE(r1), r2 ; R2 = Length of driver addl2 r1, r2 ; R2 => 1st byte past driver cmpl PORT_FT_CREATE(r6), r2 ; Is create routine outside driver? bgequ 120$ ; Branch if yes (its above). cmpl PORT_FT_CREATE(r6), r1 ; Is create routine inside driver? blssu 120$ ; Branch if no (its below). cmpl PORT_CANCEL(r6), r2 ; Is cancel routine outside driver? bgequ 120$ ; Branch if yes (its above). cmpl PORT_CANCEL(r6), r1 ; Is cancel routine inside driver? blssu 120$ ; Branch if no (its below). ; ; So... routines are still inside the driver. ; Use the stored addresses to patch our loadable code. make sure ; that we can patch the code by making the patch area EXEC writeable. ; movl #PRT$C_EW, r0 ; R0 = patch area page protection movab END_OF_PATCH, r1 ; R1 = ending VA movab START_OF_PATCH, r2 ; R2 = starting VA subl2 r2, r1 ; R1 = size of code addl #511, r1 ; Round up to next pagelet ashl #-9, r1, r1 ; R1 = pagelet count movab START_OF_PATCH, r2 ; R2 = starting VA jsb g^EXE$SET_PAGE_PROTECTION ; Go change protection ; movl PORT_FT_CREATE(r6), CREATE_PATCH movl PORT_CANCEL(r6), CANCEL_PATCH ; ; Now patch the port vector table of FTDRIVER. ; Note that the order is important. The cancel routine should be installed first ; else a device could be created with an access port name buffer, and then very ; quickly deleted, without our being able to sort it out. ; moval ATG_FT_CANCEL, PORT_CANCEL(r6) moval ATG_FT_CREATE, PORT_FT_CREATE(r6) ; ; All done. ; movzwl #SS$_NORMAL, r0 ; Successful completion 100$: popr #^m rsb 120$: movzwl #SS$_LOADER, r0 ; Say loader addresses invalid brb 100$ .sbttl ATG_FT_UNLOAD - Unload ATG FTDRIVER patches ;+ ; ATG_FT_UNLOAD - Unload ATG FTDRIVER patches ; ; Functional Description: ; ; This routine unloads a set of patches for FTDRIVER which were initially ; connected by ATG_FT_INIT above. ; ; Inputs: ; ; R2 => LDRIMG$ data block for this loadable image ; ; Outputs: ; ; R0 = Final status of unload ; SS$_NORMAL Successful unload of the patches ; SS$_LOADER Code is not loaded ; SS$_NOSUCHDEV FTDRIVER was not found ; All others preserved ; ; Notes: ; ; We should never get SS$_NOSUCHDEV here as FTDRIVER is not unloadable, ; and thus should always be there. ;- .if defined SYS$DOINIT_UNL ; Include when SYS$DOINIT_UNL is in STARLET.OLB INITIALIZATION_ROUTINE - ATG_FT_UNLOAD, - UNLOAD=1 ; Its an UNLOAD routine ATG_FT_UNLOAD: pushr #^m bsbw FIND_FTDRIVER ; Try to locate FTDRIVER blbc r0, 100$ ; Exit on error ;... movzwl #SS$_NORMAL, r0 ; Successful completion 100$: popr #^m rsb .endc .sbttl FIND_FTDRIVER - Locate FTDRIVER port vector ;+ ; FIND_FTDRIVER - Locate FTDRIVER port vector ; ; Functional Description: ; ; This routine locates the port vector table of FTDRIVER. ; ; Inputs: ; ; None ; ; Outputs: ; ; R0 = Final status of search ; SS$_NORMAL Successful find of FTDRIVER ; SS$_NOSUCHDEV FTDRIVER was not found ; R1 => FTDRIVER's DPT ; R6 => FTDRIVER's PORT_VECTOR table ; R2,R3,R4 destroyed ; ; Notes: ;- FTDRIVER:.ascic "FTDRIVER" ; Name of driver to patch FIND_FTDRIVER: movab FTDRIVER, r3 ; R3 => name of driver to find movab g^IOC$GL_DPTLIST, r1 ; Get head of DPT list movl r1, r4 ; R4 = start of list (to detect end) 10$: movl (r1), r1 ; R1 => DPT of a driver cmpl r1, r4 ; Are we at end of list? beqlu 110$ ; Branch if no more to check movab DPT$T_NAME(r1), r2 ; R2 => of counted name string movzbl (r2)+, r0 ; R0 = length of name string cmpb (r3), r0 ; Are lengths the same? bneq 10$ ; NEQ no, keep looking pushr #^m ; CMPC changes R0-R3 cmpc3 r0, 1(r3), (r2) ; Is this correct driver name? popr #^m ; Restore registers bneq 10$ ; NEQ then keep looking ; Found a driver of the right name! movzwl DPT$W_VECTOR(r1), r0 ; R0 = offset to vector addl3 r0, r1, r6 ; R6 => port vector movzwl #SS$_NORMAL, r0 ; Return success to caller 100$: rsb 110$: movzwl #SS$_NOSUCHDEV, r0 ; Say driver was not found... brb 100$ ; ...and exit .sbttl ATG_FT_LOADED_CODE - Code to patch FTDRIVER DECLARE_PSECT EXEC$NONPAGED_CODE START_OF_PATCH: ; ; Header is designed to enable our code to be found easily in NPP ; .ascii "X01.05 © 1992, ATG Ltd. FTDRIVER Access Port Name support" ATG_FT_CREATE: jsb g^ATG_FT_CREATE_X jmp @#0 ; Filled in with FT$CREATE address CREATE_PATCH = <. - 4> ATG_FT_CANCEL: jsb g^ATG_FT_CANCEL_X jmp @#0 ; Filled in with FT$CANCEL address CANCEL_PATCH = <. - 4> END_OF_PATCH: .sbttl ATG_FT_CREATE_X - Perform driver device creations operations ;+ ; ATG_FT_CREATE_X - Perform driver device creations operations ; ; Functional Description: ; ; The device creation routine is called from the PTD$CREATE system service ; to finish creating a pseudo terminal. ; This routine replaces the FT$CREATE routine in the FTDRIVER ; port vector tables, in order to create an access port name ; structure if one is not created by FT$CREATE. It does this ; by immediatly calling FT$CREATE as a co-routine, and ; checking to see if there is a buffer. If not, one is added. ; This rather convoluted scheme is necessary to try to be ; compatable with future versions of FTDRIVER that may include ; access port name support. ; ; Synchronization: ; ; This routine is called with the I/O database mutex held for write access ; at IPL$_ASTDEL. ; ; Inputs: ; ; R2 => New UCB ; R4 => PCB ; R6 = Channel access mode ; R7 = Channel number ; R8 => CCB ; R9 => where to write channel number ; R10 => Characteristics buffer ; R11 = Characteristics buffer length ; AP => Argument list ; (not used by this routine, but passed on) ; ; Outputs: ; ; R0 = Final status ; SS$_NORMAL ; SS$_EXASTLM ; SS$_INSFMEM ; R1-R11 Preserved ; ; Notes: ;- ATG_FT_CREATE_X: jsb @(sp)+ ; Call FT$CREATE as a co-routine ; ; R0 = Completion status of FT$CREATE ; R2 => New UCB ; R4 => PCB ; R6 = Channel access mode ; R7 = Channel number ; R8 => CCB ; blbc r0, 100$ ; Skip on any errors pushr #^m ; Save registers used movl r2, r5 ; R5 => New UCB ; ; The following line of code has to be removed as this field is used by ; the UCB create/delete processing code to restore quota to a process. ; (IOC$CREDIT_UCB/IOC$DEBIT_UCB). ; ; movl PCB$L_EPID(r4), UCB$L_LOCKID(r5) ; Save creators's PID ; bbs #TTY$V_PC_ACCPORNAM, UCB$W_TT_PRTCTL(r5), 90$ ; Skip if buffer there tstl UCB$L_TT_ACCPORNAM(r5) ; Is there a buffer? bnequ 80$ ; Yes, so don't allocate one movzbl #, r1 ; R1 = buffer size jsb g^EXE$ALONONPAGED ; Try to allocate a buffer blbc r0, 90$ ; Br if error movw r1, IRP$W_SIZE(r2) ; Set real buffer size movb #DYN$C_UNC, IRP$B_TYPE(r2) ; Set type (Universal Context!) movb #DYN$C_FTRD, IRP$B_RMOD(r2) ; Add a second check byte clrb FT_C_HEADER(r2) ; No initial access port name movab FT_C_HEADER(r2), UCB$L_TT_ACCPORNAM(r5) ; Save block address 80$: bisl #TTY$M_PC_ACCPORNAM, UCB$W_TT_PRTCTL(r5) ; Say buffer there 90$: popr #^m ; Restore saved registers 100$: rsb .sbttl ATG_FT_CANCEL_X - Handle terminal driver cancel I/O requests ;+ ; ATG_FT_CANCEL_X - Handle terminal driver cancel I/O requests ; ; Functional Description: ; ; This cancel routine runs BEFORE the FT$CANCEL routine. If ; there is an access port name buffer, and it lies OUTSIDE the ; bound of the UCB, deallocate it. The idea behind this check ; is that DEC will almost certainly implement access port name ; support by allocating a buffer at the end of the UCB (the ; way all the other terminal port drivers do). If there is a ; buffer, and it lies inside the bounds of the UCB, we most ; certainly do not want to try to deallocate it! If the buffer ; is outside of the UCB, it was most probably allocated from ; NPP, and therefore we can free it. The assumption here is ; that should DEC use a NPP buffer, their FT$CANCEL routine ; will check that its still there BEFORE trying to deallocate ; it, rather than assuming that since FT$CREATE created one, ; it must be there. ; ; Synchronization: ; ; This routine is called holding the I/O database mutex for write access. ; It also holds the device lock and the fork lock. ; ; Inputs: ; ; R2 => device STATE ; R3 = Contents of UCB$L_IRP ; R4 => PCB ; R5 => PHYUCB ; R6 = Channel ; R8 = Cancel reason: CAN$C_CANCEL or CAN$C_DASSGN ; R9 => LOGUCB ; ; Outputs: ; ; R0 Destroyed, all others preserved ; ; Notes: ;- ATG_FT_CANCEL_X: cmpl PCB$L_PID(r4), UCB$L_FT_IPID(r5) ; Same PID bneq 100$ ; NEQ just exit cmpw r6, UCB$W_FT_CHAN(r5) ; Channels match bneq 100$ ; NEQ just exit cmpw #CAN$C_DASSGN, r8 ; Deassign bneq 100$ ; NEQ no just exit pushr #^m ; Save registers used bbc #TTY$V_PC_ACCPORNAM, UCB$W_TT_PRTCTL(r5), 90$ ; Skip no port movl UCB$L_TT_ACCPORNAM(r5), r0 ; R0 => buffer to be deallocated beqlu 90$ ; No buffer (should never happen) movzwl UCB$W_SIZE(r5), r1 ; R1 = Size of FT UCB addl2 r5, r1 ; R1 => 1st byte beyond end of UCB cmpl r0, r1 ; Is it outside the UCB? (check #1) bgequ 10$ ; Yes, its one of ours cmpl r0, r5 ; Is it inside the UCB? bgequ 90$ ; Yes, so its not one of ours 10$: subl2 #FT_C_HEADER, r0 ; R0 => Start of NPP block (maybe!) cmpb IRP$B_TYPE(r0), #DYN$C_UNC ; Is it one of ours? (check #2) bnequ 90$ ; No, so give up now cmpb IRP$B_RMOD(r0), #DYN$C_FTRD ; Is it one of ours? (check #3) bnequ 90$ ; No, so give up now bicl #TTY$M_PC_ACCPORNAM, UCB$W_TT_PRTCTL(r5) ; Say buffer not there jsb g^EXE$DEANONPAGED ; Deallocate the buffer clrl UCB$L_TT_ACCPORNAM(r5) ; Clear buffer address 90$: popr #^m 100$: rsb .end