; ; **************************************************************** ; ; Copyright (c) 1992, Carnegie Mellon University ; ; All Rights Reserved ; ; Permission is hereby granted to use, copy, modify, and ; distribute this software provided that the above copyright ; notice appears in all copies and that any distribution be for ; noncommercial purposes. ; ; Carnegie Mellon University disclaims all warranties with regard ; to this software. In no event shall Carnegie Mellon University ; be liable for any special, indirect, or consequential damages ; or any damages whatsoever resulting from loss of use, data, or ; profits arising out of or in connection with the use or ; performance of this software. ; ; **************************************************************** ; .Title NETMACLIB - Macro level VMS I/O Interface Routines. .Ident '2.3-1' ; ; Comment out the following line if NOT building for VMS V4 (in other ; words comment out if you want a V5 driver). ; ; VMS_V4 = 1 .Enable SUP ; .Enable Debug,TraceBack .Disable Debug,TraceBack .Library /SYS$LIBRARY:LIB.MLB/ .PSECT Locked_Section,LONG ;++ ; ; Facility: ; ; Tektronix Network: BLISS-32 callable VMS I/O System Interface rtns. ; ; Abstract: ; ; 5) MOVBYT ; Move bytes fast with movc3 hardware instruction. ; ; 6) TIME_STAMP ; Get time in hundredths of seconds since system boot. ; Use EXE$GL_ABSTIM. This means that we really only have ; a resolution down to the second, but that shouldn't ; adversely affect anything for IP's purposes. Previous ; code had to go into kernel mode to read from processor ; register, but that was enormous overhead (IP seems to ; get the time for a lot of stuff while handling segments) ; and wouldn't work on a uVAX (PR$_TODR processor register ; is no longer defined in VMS V4 for that reason). ; ; 7) SWAPBYTES ; Swap bytes within a 16-bit word. ; ; 8) ZERO_BLK ; Zero block of bytes with movc5 instruction. ; ; 9) CALC_CHECKSUM ; ; Calculate a checksum for a block of memory. ; ; Author: ; ; Stan C. Smith 10-4-81 ; ; Mode: ; ; Both Kernel (VMS data structure access) & user mode routines. ; ; Assembly Instructions: ; ; MACRO MACLIB.MAR ; ; Modification History: ; ; *** Begin CMU change log *** ; ; 2.5 27-Nov-1989 Bruce R. Miller CMU Network Development ; Removed bias towards TCP processing in order to put TCP, UDP, ; ICMP, and IP on an equal footing. Changed m$cancel to be 15. ; ; 24-JAN-1988 Chris HO USC-UCS ; fixed V5 locks ; ; 2.4 23-Jan-89 Bruce R. Miller, Pete Neergaard, Marc Shannon. ; Corrected check for end of UCB chain when mounting and dismounting ; IP device. Modified routines Mount_IP_Device, and ; Set_IP_Device_Offline. Previously code would overshoot the end of ; the UCB list and attempt to dereference null pointer when executing ; in SMP context. ; ; 2.3 04-JUN-88 Lon Willett (Utah) and Dale Moore (CMU) ; Changed Checksuming routines and added additional code ; comments about checksumming. ; ; 2.2 30-Jul-87, Edit by VAF ; Implement CQ_DEQUEUE and CQ_ENQUEUE here. ; Minor cleanup in USER_REQUESTS_AVAIL. ; ; 2.1 23-Jan-87, Edit by VAF ; Device driver now passes the size of the UARG block so we ; know how much to copy into the ACP's local copy. Device driver ; must be version 2.8 or later and ACP version 6.0 or later. ; ; 2.0 23-Jul-86, Edit by VAF ; Pass protocol code and connection id (both from UCB) in ; cancel processing. ; ; 1.9 16-Jul-86, Edit by VAF ; Flush extraneous and confusing definitions. ; ; 1.8 1-JUL-86, Dale Moore ; Change references from THC to IPDRIVER. ; ; 1.7 22-May-86, Edit by VAF ; In CANCEL processing, pass UCB address in argument block. ; ; 1.6 7-May-86, Edit by VAF ; In CANCEL processing, convert internal PID to external PID. ; ;*** End CMU change log *** ; ; 1.1 [2-10-82] Stan Smith ; Orginal version ; ; 1.2 [6-10-82] Stan Smith ; Included device offline testing/setting back when mount occurs. ; This prevents users from doing IO to a non-existant ACP & hanging ; the user's process. ; ; 1.3 [8-16-82] Stan Smith ; Blank filled the ACP volume name "TCPACP" total length is 12 bytes. ; ; 1.4 [3-16-83] Stans Smith ; Force dev$m_avl off before we start to mount device "thc". ; Prevents users from doing IO to a device that's not mounted. ; ; 1.5 [6-11-83] stan ; During vms io posting (vms_io$post) if the irp is in error then ; force the io function to be a write thus preventing a useless ; copy from system buffer to user buffer. On success, if the ; io function is a read (irp$v_func=true in irp$w_sts), then ; set the actual # of bytes received in irp$w_bcnt instead of ; original requested amount. save some time. ;-- .SBTTL System & Local Symbol definitions ; VMS External Definitions. $ARBDEF ; Access Rights Block defs. $AQBDEF ; ACP Queue Block $CHFDEF ; Condition Handler Facility. $IRPDEF ; I/O Request Packet $UCBDEF ; Unit Control Block $VCBDEF ; Volume Control Block. .IF NOT_DEFINED VMS_V4 $CPUDEF ; VMS Version 5.0 SMP .ENDC $CRBDEF ; channel request block $DCDEF ; device classes and types $DDBDEF ; device data block $DEVDEF ; device characteristics $IDBDEF ; interrupt data block $IODEF ; I/O function codes $IPLDEF ; hardware IPL definitions $SSDEF ; system status codes $VECDEF ; interrupt vector block $FKBDEF ; define fork block offsets $DYNDEF $PCBDEF $PR780DEF ; Local symbols FALSE = 0 ; Bliss definition of false. IOSB: .LONG 0 .LONG 0 .SBTTL Move Bytes "FAST" with movc3 instruction. ;++ ; ; Function Description: ; ; Move bytes via the movc3 instruction. Copy same number of ; bytes from source to destination. ; ; calling Sequence: ; ; BLISS callable rtn "CALLS" linkage: MOVBYT(Size,SRC,DEST) ; Warning: SRC & DEST are addresses! ; Inputs: ; ; 0(AP) count of args on stack ; 4(AP) Number of bytes to move ; 8(AP) Source address ; 12(AP) Destination address ; ; Outputs: ; ; None. ; ; All registers preserved ; ;-- ; "AP" pointer offsets, calls arguments Size = 4 Src = 8 Dest = 12 MovByt:: .WORD ^M MOVC3 Size(AP),@Src(AP),@Dest(AP) RET .SBTTL Time_Stamp - Get time in hundredths of a second. ;++ ; ; Function: ; ; Get time in hundredths seconds since Jan of cur year. Use this time instead ; of QUAD-word system time since its costs less space & is easier to ; deal with (eg, comparisons). Read system global "EXE$GL_ABSTIM". ; ; Calling Sequence: ; ; CALLS from BLISS standard. ; ; Inputs: ; ; None. ; ; Outputs: ; ; R0 = time in hundredths seconds since Jan 1 of current year ; (longword value). ; ;-- .Entry Time_Stamp, 0 MOVL @#EXE$GL_ABSTIM, R0 ; Get system interval timer MULL2 #100, R0 ; Convert seconds to 100ths RET .SBTTL Swap bytes on word boundaries. ;++ ; ;Function: ; ; Routine will swap the low address byte of a word with the ; high address byte of the word. ; ;Calling Convention: ; ; VMS CALLS ; ;Inputs: ; ; 0(AP) - Number of arguments on stack. ; 4(AP) - Number of contiguous words to swap bytes} in. ; 8(AP) - Word address of where to start swapping. ; ;Outputs: ; ; None ; ;Side Effects: ; ; None ; ;-- WrdCnt = 4 ; AP offset to # of words to swap Start = 8 ; start address. .ENTRY SWAPBYTES,^M<> MOVL Start(AP),R1 ; starting word address. Swp_Loop: MOVB (R1),R0 ; low ==> temp MOVB 1(R1),(R1)+ ; high ==> low MOVB R0,(R1)+ ; temp ==> High SOBGTR WrdCnt(AP),Swp_Loop ; decr word's left to do RET .SBTTL Zero block of bytes. ;++ ; ; Function: ; ; Zero a block of bytes. ; ; Inputs: ; ; 4(AP) # of bytes to zero. ; 8(AP) Starting address. ; ; Outputs: ; ; None. ; ; Side Effects: ; ; Consecuative bytes are zeroed. ; ;-- Count = 0 Size = 4 Adrs = 8 .ENTRY Zero_Blk,^M MOVC5 #0,@Adrs(AP),#0,Size(AP),@Adrs(AP) RET .SBTTL Calculate Checksums ;++ ; ; Function: ; ; Generate a 16-bit one's-complement of the one's complement ; sum of series of 16 bit words. ; See TCP DARPA document for details. ; ; Number Systems Lesson: ; ; One's complement arithemetic is different than Two's complement ; arithmetic. Two's complement arithmetic is what is used on the ; on the VAX. The positive numbers are the same in one's Complement ; and two's complement. For two's complement however, NOT X = - X. ; The complement of a number is the negative of the number. This ; Results in two values for 0, %X'0000' (positive zero) and ; %X'FFFF' (negative zero). ; ; Addition of two's complement can be performed without regard ; to the sign of the numbers involved. This is done by simply ; adding the binary representations and ignoring any carry. ; (-5) + (-2) = 1011 + 1110 = 11001 = 1001 = -7 ; ; Addition of one's complement numbers can be performed without ; regard to the sign of the numbers involved. This is done by ; adding the binary representation of the numbers involved and ; then adding one if a carry occurred. ; (-1) + 6 = 1110 + 0110 = 10100 = 0100 + 1 = 0101 = 5 ; ; Algorithm: ; ; We treat the data to be checksummed as an arry of 32 bit ; integers. We keep a running 32 bit sum of the 32 bit integers. ; ; Sum = 0; ; Incr I FROM 0 TO .Size - 1 DO ; Sum = .Sum + .Data [.I] + Carry; ; ; We can use two's complement addition to keep this running sum ; as long as we add with carry. Then we add in any pieces of the ; data that wasn't a full 32 bits. ; Once the 32 bit sum is computed, we can fold the top 16 bits ; into the bottom 16 bits. ; ; Sum = .Sum <0, 16> + .Sum <16, 16>; ; ; But this folding may produce yet another carry. So we do it twice. ; ; If you want you can think of the bits as being in a ring, where ; any carry (out of the high bits) would be pushed into the low ; bits. ; ; 00 ; 15 01 ; 14 02 ; 13 03 ; 12 04 ; 11 05 ; 10 06 ; 09 07 ; 08 ; ; ; We then complement the result of the summing and check for ; value of 0 and return only -0. ; ; ; Calling Sequence: ; ; Standard CALLS: ; CALC_CHECKSUM(Byte_Count,Start); ; GEN_CHECKSUM(Byte_Count,Start,SrcA,DstA,Ptcl_Type) ; ; Inputs: ; ; 4(AP) : Byte_Count = # of 8-bit bytes to checksum as words. ; 8(AP) : Start = Starting byte address. ; Additionally, for GEN_CHECKSUM: ; 12(AP): SrcA = Source IP address ; 16(AP): DstA = Destination IP address ; 20(AP): Ptcl_Type = Protocol type of packet ; ; Outputs: ; ; ; My interpretation of "one's complement sum" is the true sum ; modulo 2^16-1. Thus, it will be a value in the range 0 .. 2^16-2, ; i.e. -0 is not a legal "one's complement sum", but -0 is. ; ; Thus the "one's complement of the one's complement sum" ; will be a value in the range 1 to 2^16-1. So the IP header ; checksum and TCP checksum should never be zero. (UDP specifies ; the interpretation of putting a zero in the checksum field). ; Under the robustness principle: accept a value of "0" where ; it should be "-0". I don't believe that IP or TCP should ; skip verifying the checksum (as UDP does) when the checksum ; field is 0, because checksumming for IP and TCP is not optional. ; ; The one's complement sum of a good packet will be 0. Of ; course the checksum routines return the "one's complement of ; the one's complement sum", so apply a NOT before comparing ; against 0. But be aware that this routine returns 16 bits in ; R0, so you must NOT only 16 bits worth. ; ; Side Effects: ; ; None. ; ;-- ; Argument Point stack offsets. Byte_Count = 4 Start = 8 Srca = 12 ; Source IP address Srca0 = 12 ; First 16-bit word Srca1 = 14 ; Second 16-bit word Dsta = 16 ; Destination IP address Dsta0 = 16 ; First 16-bit word Dsta1 = 18 ; Second 16-bit word PtclT = 20 ; Protocol type ;++ ; Gen_Checksum - Generate checksum for UDP and TCP. ; Adds in the protocol fields and the IP addresses, then joins Calc_Checksum. ; N.B. We jump directly into Calc_Checksum, so the offsets for Length and Start ; must be the same and we must make sure to save the same registers that ; Calc_Checksum uses. ;-- .Entry Gen_Checksum,^M MOVZWL Byte_Count(AP),R0 ; Put byte count in R0 ASHL #-2,R0,R2 ; Put fullword count in R2 ;; Must use byte count rotated +/- 8 bits ;; Must use protocol rotated +/- 8 bits ;; (Because net stuff wants Big Endian byte order) ASHL #8,R0,R0 ; Start with byte count INSV Ptclt(AP),#24,#8,R0 ; Add in protocol (only 8 bits wide) ADDL Srca(AP),R0 ; Add in source addr ADWC Dsta(AP),R0 ; Add in dest addr (and carry) BRB Calc_Check0 ; Join Calc_Checksum routine ;++ ; Calc_Checksum - one's compliment checksum routine. ;-- .Entry Calc_Checksum,^M CLRL R0 ; Start with a zero checksum MOVZWL Byte_Count(AP),R2 ASHL #-2,R2,R2 ; Put fullword count in R2 ; (and clear Carry) Calc_Check0: ; Point where Gen_Checksum joins in ;; R0 and PSW-Carry contain initial 32 bit checksum ;; R2 contains fullword count ;; Start(AP) is pointer to 1st byte ;; Byte_Count(AP) contains byte count MOVL Start(AP),R1 ; starting address. SOBGEQ R2,CLop ; enter loop BRB Odd_Word ; (no fullwords) Clop: ADWC (R1)+,R0 ; add in next fullword and Carry SOBGEQ R2,CLop ; get next one Odd_Word: ;; Check for extra word BBC #1,Byte_Count(AP),Odd_Byte MOVZWL (R1)+,R2 ; get next word ADWC R2,R0 ; add it in (and the Carry) Odd_Byte: ;; Check for extra byte BLBC Byte_Count(AP),Reduce16 MOVZBL (R1)+,R2 ; get next byte ADWC R2,R0 ; add it in (and the Carry) Reduce16: ;; We have the sum modulo 2**32-1 (actually: protocol and bytecount ;; from Gen_Checksum are strange, but are eqv mod 2**16-1) ;; Now reduce mod 2**16-1 EXTZV #16,#16,R0,R2 ; extract HO word ; INSV #0,#16,#16,R0 ; clear HO word MOVZWL R0,R0 ADWC R2,R0 ; add HO and LO words (and Carry) BBCC #16, R0, Comp ; clear carry out of LO word ; (and branch if short word result) INCW R0 ; add carry in BCC Comp ; branch if no more carry INCW R0 ; else add final carry in ; (this INCW can't produce a carry) Comp: ;; Complement the word MCOMW R0,R0 BEQL ZSum RET ZSum: ;; Return 0 as FFFF MCOMW R0,R0 RET ;.Entry Gen_Checksum,^M ; ; MOVL PtclT(AP),R0 ; Start with the protocol code ; ADDL Byte_Count(AP),R0 ; Add the length ;; Do a byte swap - isn't there an easier way? ; EXTV #8,#8,R0,R1 ; D C B A B ; INSV R0,#8,#8,R0 ; D C A A B ; MOVB R1,R0 ; D C A B B ; ROTL #16,R0,R0 ; A B D C B ; EXTV #8,#8,R0,R1 ; A B D C D ; INSV R0,#8,#8,R0 ; A B C C D ; MOVB R1,R0 ; A B C D D ; MOVZWL Srca0(AP),R1 ; Get first word of source addr ; ADDL R1,R0 ; Add it ; MOVZWL Srca1(AP),R1 ; Get second word of source addr ; ADDL R1,R0 ; MOVZWL Dsta0(AP),R1 ; Get first word of dest addr ; ADDL R1,R0 ; MOVZWL Dsta1(AP),R1 ; Get second word of dest addr ; ADDL R1,R0 ; BRB Calc_Check0 ; Join Calc_Checksum routine ; ;;++ ;; Calc_Checksum - one's compliment checksum routine. ;;-- ; ;.Entry Calc_Checksum,^M ; ; CLRL R0 ; Start with a zero checksum ; ;Calc_Check0: ; Point where Gen_Checksum joins in ; ; MOVL Start(AP),R4 ; starting address. ; MOVL Byte_Count(AP),R3 ; TSTL R3 ; anything to do? ; BGTR 10$ ; >0 = yes. ; RET ; 0 count. ;10$: ; ASHL #-1,R3,R3 ; convert bytes to words. ; TSTL R3 ; any words to do? ; BEQL Odd_BC ; 0 = 1 byte to do.... ; CLRL R1 ; clean up high word of R1 ;Clop: ; MOVW (R4)+,R1 ; get a word ; ADDL R1,R0 ; longword arith. ; SOBGTR R3,Clop ; more to come? ; ;; Check for ODD byte count ; ;ODD_BC: ; BLBC Byte_Count(AP),Chk_OVFL ; MOVZBL (R4),R1 ; get ODD byte padded with 0 byte. ; ADDL R1,R0 ; ;; Add in any overflow ; ;CHk_OVFL: ; EXTZV #16,#16,R0,R1 ; extract overflow. ; BEQL Comp ; 0 = done ; BICL2 #^XFFFF0000,R0 ; AND to 16-bits. ; ADDL R1,R0 ; add to checksum ; BRB Chk_OVFL ; ;; Complement & mask to 16-bits. ; ;Comp: ; XORW #^X0FFFF,R0 ; Complement to 16-bits. ; RET .SBTTL Circular byte queue manipulation routines ; These routines are written in MACRO for two reasons: ; 1) Speed. They are used a lot and should be as fast as possible. ; 2) Precision. Since the circular queues can be modified at AST level, it ; it critical that the pointers and counters be updated atomically. We ; can't really trust the BLISS compiler to take care of that. ; Define the format of the circular queue - must match STRUCTURE.REQ $DEFINI CQ ; Circular queue $DEF CQ$QUEUE .BLKB 0 ; Address of the queue structure $DEF CQ$BASE .BLKL 1 ; Base address of queue buffer $DEF CQ$END .BLKL 1 ; End address of queue buffer $DEF CQ$SIZE .BLKW 1 ; Size of the queue $DEF CQ$COUNT .BLKW 1 ; Number of bytes on the queue $DEF CQ$ENQP .BLKL 1 ; Pointer to first free byte on queue $DEF CQ$DEQP .BLKL 1 ; Pointer to first used byte on queue $DEFEND CQ ; CQ_Enqueue(CQ,SRC,Scount) ; Enqueue bytes onto a circular queue. Called via $CMKRNL when enqueueing from ; user's system buffer. CQ = 4 ; Queue header SRC = 8 ; Destination address SCOUNT = 12 ; Number of bytes .ENTRY CQ_Enqueue,^M MOVL CQ(AP),R7 ; Get queue address SUBL3 CQ$ENQP(R7),CQ$END(R7),R6 ; Find space left to end CMPL R6,SCOUNT(AP) ; Does he want all we have till end? BLEQ 10$ ; Yes - need two moves, then MOVC3 SCOUNT(AP),@SRC(AP),@CQ$ENQP(R7) ; Do the move MOVL R3,CQ$ENQP(R7) ; Update the queue pointer ADDW2 SCOUNT(AP),CQ$COUNT(R7) ; And update the count RET 10$: ; Here on pointer-wrap case MOVC3 R6,@SRC(AP),@CQ$ENQP(R7) ; Move till end of queue MOVL CQ$BASE(R7),R3 ; Reset pointer to start of queue SUBL3 R6,SCOUNT(AP),R6 ; Compute how much we need from Q base BLEQ 20$ ; Have anything left? MOVC3 R6,(R1),(R3) ; Yes - finish the copy 20$: MOVL R3,CQ$ENQP(R7) ; Update the queue pointer ADDW2 SCOUNT(AP),CQ$COUNT(R7) ; And update the count RET ; And done. ; CQ_Dequeue(CQ,Dest,Dcount) ; Dequeue bytes from a cirucular queue. Called via $CMKRNL when dequeuing to ; user's system buffer. CQ = 4 ; Queue header DEST = 8 ; Destination address DCOUNT = 12 ; Number of bytes .ENTRY CQ_Dequeue,^M MOVL CQ(AP),R7 ; Get queue address SUBL3 CQ$DEQP(R7),CQ$END(R7),R6 ; Find space left to end CMPL R6,DCOUNT(AP) ; Does he want all we have till end? BLEQ 10$ ; Yes - need two moves, then MOVC3 DCOUNT(AP),@CQ$DEQP(R7),@DEST(AP) ; Do the move MOVL R1,CQ$DEQP(R7) ; Update the queue pointer SUBW2 DCOUNT(AP),CQ$COUNT(R7) ; And update the count RET 10$: ; Here on pointer-wrap case MOVC3 R6,@CQ$DEQP(R7),@DEST(AP) ; Move till end of queue MOVL CQ$BASE(R7),R1 ; Reset pointer to start of queue SUBL3 R6,DCOUNT(AP),R6 ; Compute count we need from Q base BLEQ 20$ ; Have anything left? MOVC3 R6,(R1),(R3) ; Yes - finish the copy 20$: MOVL R1,CQ$DEQP(R7) ; Update the queue pointer SUBW2 DCOUNT(AP),CQ$COUNT(R7) ; And update the count RET ; And done. ; CQ_DeqCopy(CQ,Dest,Dcount) ; Same as CQ_Dequeue, except queue pointer/count is not updated. CQ = 4 ; Queue header DEST = 8 ; Destination address DCOUNT = 12 ; Number of bytes .ENTRY CQ_DeqCopy,^M MOVL CQ(AP),R7 ; Get queue address SUBL3 CQ$DEQP(R7),CQ$END(R7),R6 ; Find space left to end CMPL R6,DCOUNT(AP) ; Does he want all we have till end? BLEQ 10$ ; Yes - need two moves, then MOVC3 DCOUNT(AP),@CQ$DEQP(R7),@DEST(AP) ; Do the move ;;; MOVL R1,CQ$DEQP(R7) ; Update the queue pointer ;;; SUBW2 DCOUNT(AP),CQ$COUNT(R7) ; And update the count RET 10$: ; Here on pointer-wrap case MOVC3 R6,@CQ$DEQP(R7),@DEST(AP) ; Move till end of queue MOVL CQ$BASE(R7),R1 ; Reset pointer to start of queue SUBL3 R6,DCOUNT(AP),R6 ; Compute count we need from Q base BLEQ 20$ ; Have anything left? MOVC3 R6,(R1),(R3) ; Yes - finish the copy 20$: ;;; MOVL R1,CQ$DEQP(R7) ; Update the queue pointer ;;; SUBW2 DCOUNT(AP),CQ$COUNT(R7) ; And update the count RET ; And done. .END