.title LOG -- System Log Pseudo Device .ident /V1.0/ .sbttl Author's Credits .nlist bex ;+ Author: Henry Tumblin(with credit to Reid Brown,Cutler, et Al) ; ; Date: Sep 17,1978 ; ; Version: 1.0 ; ; Machine/System: PDP-11/45 - IAS V2.0 (or RSX-11D V6.2B) ; ; Type/Language: Main/Assembler(MACRO) ; ; Residence: DB1:[1,20]LOG.MAC ; ; Build File: LOGBLD.CMD ; ; Abstract: This is a general purpose PSEUDO-DEVICE handler ; for RSX-11D or IAS. A more detailed description ; follows on the next page. ; ; Edits: ; ; Date By Reason ; ---------- ----- -------------------------------------------------- ; 05-JUN-78 HRT Entered and modified slightly to allow this handler ; to be used as a system logging device, which will ; grab all LOGIN and LOGOUT messages and store them ; in a disk file. ; 01-Jan-79 HRT Added function to return the users name in both ; ASCII and RAD-50 ;- .sbttl Theory of Operation ;+ 'LOG' - Utility Function Driver ; 'LGROOT' - Generalized Resource Task / Device Driver ; ; A resource task is designed to perform system/user interface ; functions which a user task cannot perform efficiently. These ; functions include the transmission of information from one task ; to another, the remapping of APR's for inter-task data transfers, ; the manipulation of sensitive "SCOM" information and any other ; System-level functions. A PSEUDO-device driver is usefull in ; fullfilling these functions for the following reasons: ; ; -it provides a known, tested interface ; -it allows for centralized control of system functions ; -it uses an efficient mechanism for inter-task comm. (QI/O's) ; -it uses system subroutines for efficient data transfer ; across address boundaries. ; ; 'RSRCTK' provides all of these features to the user in a ; generalized format which is based on the RSX-11D/IAS pseudo-device ; driver 'MO....'. All of the device driver specific functions such ; as waiting for requests, dequeing requests, handling express ; requests, validating requests and unloading are handled by a ; standard module. The only function code serviced is the write code, ; 'IO.WLB'. The user has full access to the QIO data from his ; driver routine. ; ; There is a one-to-one relationship between the QIO parameters ; used by the caller, and those passed to the resource device driver ; task. Words 0-5 are the same as for any normal I/O request, and all ; of the rules for those parameters apply as described in the ; Executive Reference Manual. Words 6-11 are the optional parameter ; words. These allow any six(6) words of information to be passed ; from the calling task to the driver task. (these might include a ; buffer address for the transmission of more than 6 words of ; additional data.) When the calling task issues the QIO, the ; system allocates an I/O request node in 'SCOM'. This request node ; is filled in with the QIO dpb information. When the I/O request is ; queued to the driver, an event flag is set and the task then dequeues ; the request. The address of the I/O request node is placed in ; location '$RQIO', and a jump(JMP) is taken to label '$START'. ;- ;+ The format and offsets of a request node (as copied from ; the guide to writing a device handler task) are as follows: ; ; ; WD. 00 (B 00) -- Foward Linkage ; WD. 01 (B 02) -- Backward Linkage ; WD. 02 (B 04) -- Node Accouting word(STD of Requestor) ; R.AT==06 WD. 03 (B 06) -- ATL Node of Requestor ; R.PR==10 ; WD. 04 (B 10) -- Priority (Byte) ; R.DP==11 ; (B 11) -- DPB size in words (Byte) ; R.LU==12 ; WD. 05 (B 12) -- Logical Unit Number (Byte) ; R.FN==13 ; (B 13) -- Event Flag Number (Byte) ; R.FC==14 ; WD. 06 (B 14) -- I/O Function Code ; R.SB==16 ; WD. 07 (B 16) -- Virtual Address of Status Block ; R.AE==20 ; WD. 10 (B 20) -- Virtual Address of AST Service Entry ; R.UI==22 ; WD. 11 (B 22) -- User Identification Code ; R.PC==22 ; (B 22) -- Programmer Code (Byte) ; R.GC==23 ; (B 23) -- Group Code (Byte) ; R.PB==24 ; WD. 12 (B 24) -- Parameter #1 ; ; WD. 13 (B 26) -- Parameter #2 ; ; WD. 14 (B 30) -- Parameter #3 ; ; WD. 15 (B 32) -- Parameter #4 ; ; WD. 16 (B 34) -- Parameter #5 ; ; WD. 17 (B 36) -- Parameter #6 ; R.PD==40 ; WD. 20 (B 40) -- Pud pointer for this request ; R.EL==42 ; WD. 21 (B 42) -- Error log buffer pointer/flag ; R.WA==44 ; WD. 22 (B 44) -- Flag byte for EXEC ; ; (B 45) -- Work area for device handlers ; ; WD. 23 (B 46) -- Work area for device handlers ; ; WD. 24 (B 50) -- Work area for device handlers ; R.IA==52 ; WD. 25 (B 52) -- ASR3 value for buffer base(-1 for scom) ; R.IB==54 ; WD. 26 (B 54) -- Intermediate buffer address ; R.UB==56 ; WD. 27 (B 56) -- User buffer address (intermediate X-fer) ; ; 'R.DP' and 'R.PB' are the most usefull offsets to the user routine. ; Note that DPB's may always be variable, and so the length (R.DP) ; should always be tested to insure the right number of parameter ; words were passed by the caller QIO. The maximum size for a DPB is ; 12 words (all six parameters specified), and the minimum size is ; six words. ;- ;+ Any normal task operation is legal for the pseudo device driver, ; but there are certain restrictions. Event flags 1-4 are reserved ; for use by the system in initiating the driver. The user should ; use flags 5 and above. FCS I/O is permitted, (and usefull) in ; many cases. Simple inter-task data transfers may be done via the ; ..BLXI and ..BLXO system subroutines. These routines require ; several registers to be preset with source and destination ; addresses. If either the source or destination addresses are ; illegal, the C-bit is set. They have a maximum limit of 4032 ; words for a single transfer. These and other more complex data ; transfer routines are detailed in the RSX-11D/IAS "Guide to ; Writing a Device Handler Task". ; ; Errors are handled by returning an error code to the calling ; task in the form of a negative number in the low byte of the ; I/O status block. '$STAT0' and '$STAT1' act as temporary ; holding buffers for values to be returned to the user, either ; as the result of an error or through a normal I/O done exit. ; ; To take an error exit, the user should : ; ; 1) Insure that all extraneous data are removed from the stack ; 2) Move the appropriate error code to '$STAT0' (low byte) ; 3) Jump to '$ERROR' ; ; This action will cause an error return without further processing ; to the user. The I/O done routine is called to complete the ; return, and a jump is taken to the wait loop. An example of a ; wait loop might be: ; ; . ; . ; CALL ..BLXO ;Transfer some data to the user ; BCC OK ;Continue if OK ; MOV #IE.SPC,$STAT0 ;Error -- out of address space ; JMP $ERROR ;Go tell the user about it ; OK: . ; . ;- ;+ When all of the processing required to service a request is ; done, (i.e. data transfers, file requests, etc.), the I/O ; request node must be released. This in turn causes I/O done ; processing to be performed for the requesting task. If the ; user specified an event flag in his/her DPB, it will now ; be set. If he/she specified an AST address, an AST will be ; queued. The device handler task indicates I/O done by calling ; the '$IODN' subroutine. This routine sets up R1= request node ; address, R2=0, R3 & R4= the the I/O status words '$STAT0', ; '$STAT1', and calls the system routine '..IODN'. Prior register ; values are lost and the user is therefore responsible for saving ; any values across the call. ; ; ; The users final function should be to jump to the idle loop to ; wait for another request. The idle loop address is '$WAIT'. ; The sequence is : ; ; JMP $WAIT ;Go to idle loop and wait. ;+ ; There are several symbols and variables of particular use to the ; user. These provide a convenient interface to the driver code, ; but protect the application portion of the resource task from ; having to understand the mechanics of the driver. They are ; as follows: ; ; $START - The label where control is passed when a request ; is dequeued. ; $WAIT - The IDLE loop address. Control is passed here while ; waiting for more request to be queued. ; $IODUN - The I/O completion subroutine. This routine returns ; the node to the pool, returns the status to the ; user, and causes his/her event flag to be set or ; an ASt to occur. ; $ERROR - The label to pass control to on an error. '$STAT0' ; should be set prior to the transfer with an error ; code. ; $STAT0 - I/O status word 1(1) in the user program.(holding word) ; $STAT1 - I/O status word 2(2) in the user program.(holding word) ; $TKNAM - The two character name of the pseudo device. This ; may be defined as a local symbol, a global symbol, ; or as a 'gbldef' in the TKB options. ; $RQIO - The address of the I/O request node currently being ; processed. ; $QIO$ - The address of a QIO DPB which the driver uses to ; report errors to the console at startup. By ; changing the parameter word values, it may be used ; for any purpose. ; $QIOAD - The address of the buffer address word in the QIO ; at $QIO$. ; $QIOLN - The address of the buffer length word in the QIO ; at $QIO$. ; $QIOVF - The address of the vertical forms control character ; in the QIO at $QIO$. ; $BUFF - The starting address of the 80 character buffer that ; the handler uses to output error messages. The ; ending address is '$BUFFE'. This buffer may be used ; for any user purpose. Its address is initially ; placed in the QIO at '$QIO$'. ;- ;+ ; Finally, when a task has been created and assembled, it ; must be task-built. The system must contain a PUD with ; a name to match that defined by the symbol '$TKNAM' above, ; and it should have the attribute 'PSEUDO'. The driver ; must be linked with the executive symbol table ([1,1]EXEC.STB) ; and be privileged. According to the device handlers guide, ; it should also have the following options: ; ; /-CP/-AB/-FX/-FP ; ; These are recommended, as they will not effect the driver ; very much (with the exception of /-CP). To follow the existing ; conventions, the taskname should be two letters followed by ; four periods. The two letters should match the PUD name and ; that defined by the symbol '$TKNAM', but using a different ; name will have no material effect. The UIC should be [1,1] ; to allow full priviledge, and the stack should be small - ; about 32 words. If more stack space is needed, it should be ; taken, because a stack underflow will clobber the PSW, which ; a driver is mapped into. The nominal amount of stack space ; usually used follows from the tradition that device drivers ; should be as small as possible. ; ; A typical build file might be: ; ; task/-CP/-FX/-AB/-FP/PR,map/CR=input,[1,1]EXEC.STB/SS ; ; TASK=XX.... ; UIC=[1,1] ; STACK=32 ; PRI=200 ; ASG=TI:5 ; LIBR=SYSRES:RO ; GBLDEF=$TKNAM:54130 !;define PUD name as 'XX' ; / ; ;- .sbttl Tell the assembler which libmacs to use .mcall C$C,DIR$,WSIG$S,EXIT$S,WTLO$,QIO$ .MCALL PUSH,POP,ALUN$S,MRKT$S .MACRO PRINT,BUFF,LEN,VFC .IF NB BUFF MOV BUFF,$QIOAD ; Set buffer address .ENDC .IF NB LEN MOV BUFF,$QIOLN ; Set buffer address .ENDC .IF NB VFC .IF IDN <#0>,VFC CLR $QIOVF .IFF MOV BUFF,$QIOVF ; Set buffer address .ENDC DIR$ #$QIO$ .ENDM .sbttl Symbol Definitions ; Event Flag Number Definitions EF1==1 EF2==2 EF3==3 EF4==4 EFIOD==4 ; I/O Done flag EFEXT==10 ; exit flag I.UN==46 ; Offset to User name in UTN I.DN==24 ; Offset to Device name in UTN I.TT==26 ; Offset to terminal unit # in UTN IE.ATP==-88. ; Define this since EXEC.STB doesn't ; SYS==1 ; System device ; Misc. parameter definitions SYLUN==1 ; System Logging Device (TT0:) C$C ; define carriage control characters $TKNAM == "LG ; Globally define the task name .sbttl Initialization Code ; Task initialization code executed on first load of ; the task only, and then is used for stack space. $INIT: ALUN$S #SYLUN,#"TT,#0 ; Assign the lun for echoing messages CALL NEWFIL ; Create the log file MOV #$UIT,R0 ; Set up registers to declare handler MOV #$TKNAM,R2 ; task resident. MOV #UF.RH,R3 ; set flag byte CALL ..DSUT ; declare handler task resident. BCC 2$ ; CC - then no errors DIR$ #$QIO$ ; Output error message to operator DIR$ #$WATAD ; Wait for I/O done EXIT$S ; And exit as gracefully as possible. 2$: DIR$ #PWRF ; Declare power fail AST DIR$ #SVTB ; Declare SST vector table MOV SP,TS ; Save top of stack pointer JSR SP,$WAIT ; Set stack pointer , go to wait loop .SBTTL QUEUE I/O DPB AND TABLE AREA ; ; QUEUE I/O DPB USED FOR ERROR MESSAGES $QIO$:: QIO$ IO.WLB,SYLUN,3,,,,<$BUFF,$ERRLN,40> ; $QIOAD==$QIO$+Q.IOPL+2 ; POINTER TO BUFFER ADDRESS PARM WORD (#1) $QIOLN==$QIO$+Q.IOPL+2 ; POINTER TO LENGTH PARM WORD (#2) $QIOVF==$QIO$+Q.IOPL+4 ; POINTER TO VCF PARM WORD (#3) ; ; ; DPB USED TO WAIT FOR FLAGS #1,2,3, OR 4 ; $WATAD:: WTLO$ 0,17 $WTMSK==$WATAD+4 .sbttl UIT table and function dispatch table ; UNIT INFORMATION TABLE $UIT:: $DISP ; POINTER TO FUNCTION DESCRIPTOR TABLE 1 ; MAX # OF UNITS IN TABLE 0 ; LAST NORMAL, EXPRESS NODES DEQUEUED 0 ; NEXT POINTER TO DEQUEUE 0 ; LAST DEQUEUED POINTER ; $PDPTR::0 ; PUD POINTER - FIRST UNIT $UITNR::0 ; REQUEST NODE - NORMAL 0 ; REQUEST NODE - EXPRESS ; ; FUNCTION CODE DISPATCH TABLE ; .MACRO FD,L,H,ADDR ; FDT TABLE ENTRIES .BYTE L,H .WORD ADDR .ENDM FD $DISP: ; **SEND AND REQUESTS NOT EXPECTED** 0 ; BAD SEND/REQUEST 0 ; GOOD SEND/REQUEST $DISPB: FD 0,0,$EQUPR ; FC = 0 - EXPRESS ; EXPRESS PROCESS ENTRY FD 010,160,WRTLOG ;; FC = 1 - WRITE FD 010,161,WAKUP ; (2) - Wakeup handler to check file FD 020,160,ATTC ; (3) - ATTACH FD 020,160,ATTC ; (4) - DETACH FD 010,000,PDSFUN ; (5) - SPECIAL FUNCTION CODES FD 010,000,EXDS1 ; (6) - NI FD 010,000,EXDS1 ; (7) - NI FD 010,000,EXDS1 ; (10)- NI FD 010,000,EXDS1 ; (11)- NI FD 010,000,EXDS1 ; (12)- NI FD 010,000,EXDS1 ; (13)- NI FD 010,000,EXDS1 ; (14)- NI FD 010,000,EXDS1 ; (15)- NI FD 010,000,EXDS1 ; (16)- NI FD 010,000,EXDS1 ; (17)- NI FD 010,000,EXDS1 ; (20)- NI FD 010,161,EXDS1 ; (21)- NI FD 010,162,WRTLOG ; (22)- WRITE VIRTUAL $DISPE: $MXFUN==<$DISPE-$DISPB>/4-1 ; VALUE OF MAX FUNCTION CODE ... ; ...IN THE DISPATCH TABLE .SBTTL INPUT-OUTPUT BUFFERS ; ; $BUFSZ = 80. ; DEFINE SIZE OF THE QIO MESSAGE BUFFER ; $BUFF:: .BLKB $BUFSZ ; DEFINE THE I/O BUFFER ; $BUFFE:: ; DEFINE END OF BUFFER ; ; INITIALIZATION ERROR MESSAGE IS EMBEDDED IN THE ; OUTPUT BUFFER FOR POSSIBLE "DECLARE AND SET" ERROR ; .=$BUFF .ASCII /"DECLARE AND SET" ERROR/ $ERRLN=.-$BUFF ; .=$BUFFE ; .EVEN ; ; LOCAL DATA ; $STAT0::.WORD 0 ; IO STATUS WORD 1 $STAT1::.WORD 0 ; IO STATUS WORD 2 $RQIO:: .WORD 0 ; IO REQUEST NODE POINTER $CRUIT::.WORD 0 ; ADDRESS OF CURRENT UIT POINTER .SBTTL DEQUEUE A REQUEST ; COMMON DRIVER IDLE LOOP - WAITFOR EVENT FLAGS $WAIT:: DIR$ #$WATAD ; WAIT FOR FLAGS #1,2,3 OR 4 ; EVENT FLAG HAS BEEN SET - START PROCESS MOV #IS.SUCC,$STAT0 ; ASSUME SUCCESSFUL I/O COMPLETION MOV .CRTSK,R0 ; CURRENT ATL NODE ADDRESS BIT #EF.XIR,A.EF(R0) ; EXPRESS QUEUE REQUEST? BEQ DQNM ; NO, GO TRY NORMAL MOV #$UIT,R0 ; SET UP TO DEQUEUE EXPRESS NODE CALL ..DQRE BCS $WAIT ; GO TO WAIT FOR IF NO DEQUEUE MOV #$UITNR+2,$CRUIT ; STORE UIT POINTER MOV R1,$RQIO ; SAVE THE REQUEST NODE ADDRESS ; EXPRESS OR NORMAL NODE EXTRACTED - PROCESS IT EXDS: CMPB R.FC+1(R1),#$MXFUN ; FUNCTION CODE TO LARGE FOR TABLE? BLOS EXDS2 ; BRANCH IF GOOD CODE (WITHIN TABLE) EXDS1: MOVB #IE.IFC,$STAT0 ; SET INVALID FUNCTION CODE BR EXPIOD ; GO TO I/O DONE ; EXPRESS OR NORMAL REQUEST FUNCTION CODE IS LEGAL - VALIDATE IT EXDS2: CALL ..VACC ; VALIDATE REQUEST BCC 1$ ; SKIP IF VALIDATION GOOD MOVB #IE.PRI,$STAT0 ; SET ERROR CODE(PRIVILEGE ERROR) BR EXPIOD ; AND BRANCH TO REPORT IT 1$: JMP ..DISP ; DISPATCH TO PROPER FUNCTION ; DEQUEUE A NORMAL REQUEST DQNM: BIT #EF.NIR,A.EF(R0) ; NORMAL REQUEST EF SET? BEQ TSIO ; BRANCH IF NOT ; MOV #$UIT,R0 ; DEQUEUE THE NORMAL REQUEST CALL ..DQRN BCS $WAIT ; GO TO WAIT FOR IF NO DEQUEUE ; MOV #$UITNR,$CRUIT ; STORE UIT POINTER MOV R1,$RQIO ; SAVE THE REQUEST NODE ADDRESS BR EXDS ; GO VALIDATE ; ; EVENT FLAG IS NOT EXPRESS OR NORMAL REQUEST - MAYBE I/O DONE TSIO: BIT #EFIOD,A.EF(R0) ; I/O DONE FLAG SET? BEQ $EXIT ; GO TO TASK EXIT IF NO FLAG BICB #EFIOD,A.EF(R0) ; CLEAR I/O DONE EF BISB #EF.NIR,$WTMSK ; SET NORMAL REQUEST IN WAIT MASK BISB #EFEXT,$WTMSK ; ALLOW SAMPLE OF EXIT BIT BR $WAIT ; GO TO WAITFOR $EXIT: MOV #$UIT,R0 ; UIT TABLE POINTER CALL ..DNRC ; DECLARE TASK NON-RESIDENT EXIT$S .SBTTL PROCESS AN EXPRESS REQUEST ; ENTRY FROM DISPATCH TABLE TO PROCESS EXPRESS REQUEST $EQUPR:: MOV R.AT(R1),R5 ; GET ATL NODE CMPB R.FC(R1),#IO.UNL ; CHECK IF EXIT REQUEST BEQ EXPEX ; BRANCH IF YES CMPB R.FC(R1),#IO.RDN ; IO RUNDOWN? BEQ 1$ ; BRANCH IF YES CMPB R.FC(R1),#IO.KIL ; ++001 KILL REQUEST? BNE EXDS1 ; BRANCH TO SET ERROR CODE MOV R5,R.PB(R1) ; SET ATL POINTER MOV A.TD(R5),R.PB+2(R1) ; STD ADDRESS TO PAR #2 CLRB R.FN(R1) ; CLEAR EVENT FLAG NUMBER ; TO PREVENT TASK FROM CONTINUING MOV (R2),R.PB+4(R1) ; PUD POINTER TO PAR #3 1$: CALL ..FLSH ; FLUSH PENDING REQUESTS EXPIOD: MOVB $STAT0,R3 ; PUT I/O STATUS IN R3 CLR R4 ; NO BYTE COUNT INVOLVED CLR R2 ; CLEAR UNITY DECREMENT COUNTER CALL ..IODN ; RELEASE REQUEST NODE CLR @$CRUIT ; CLEAR POINTER IN UIT BR $WAIT ; ++001 BRANCH TO COMMON WAIT POINT EXPEX: MOV .CRTSK,R0 ; ATL NODE POINTER BISB #EFEXT,A.EF(R0) ; SET EXIT EVENT FLAG BIT BR EXPIOD ; GO RELEASE NODE .SBTTL I/O DONE USER ROUTINE ; ; ; THE STATUS WORDS '$STAT0', '$STAT1' ARE COPIED TO R3, R4. ; THE RNA ADDRESS IS PLACED IN R1, AND R2 IS CLEARED (NORMAL DECREMENT). ; ON RETURN FROM '..IODN', THE UIT ENTRY IS ZEROED IN ANTICIPATION OF THE ; NEXT REQUEST. ; $IODUN:: MOV $RQIO,R1 ; SETUP REQUEST NODE POINTER CLR R2 ; SET R2 FOR NORMAL DECREMENT ON I/O COUNT MOV $STAT0,R3 ; COPY I/O STATUS WORD 1 MOV $STAT1,R4 ; AND I/O STATUS WORD 2 CALL ..IODN ; RELEASE THE NODE & FINISH UP THE REQUEST CLR @$CRUIT ; CLEAR THE UIT ENTRY POINTER FOR DEQUEUE RETURN .SBTTL ERROR EXIT ; ; CURRENTLY, THERE IS NO SPECIAL FUNCTION FOR ERROR PROCESSING, OTHER THAN ; DECLARING I/O DONE AND GOING TO THE WAITFOR IDLE LOOP. ; $ERROR:: CALL $IODUN ; PERFORM I/O DONE PROCESSING JMP $WAIT ; GO WAIT FOR MORE REQUESTS. .END $INIT