-- File: TeledebugImplNoDisk.mesa - last edit: -- AOF 11-Feb-88 16:44:31 -- HGM 14-Nov-83 19:20:32, Discard disk stuff -- DKntusen 14-Nov-83 10:31:45 -- Copyright (C) 1983, 1988 by Xerox Corporation. All rights reserved. << This module implements a teledebugging server using the Packet Exchange Protocol over the Ethernet, device # 0. The implementation only talks to the first PilotDisk. Later versions could support other device types and ordinals. At present, this teledebug server is promiscuous: it is willing to indiscriminately talk to several debuggers simultaneously. This is not much of a problem at present. In the future, we could envision it latching on to the "master" - the first debugger that gave it a write-type request; it would still allow other debuggers to read (only). This would require that the protocol be enhanced with a "speakToMe" PacketType, which would override the identity of the debugger that currently was the master. There are provisions here for DEBUGGING THIS IN CoPilot. To do so: o run any debugger client (Othello is good); o get from the client to the debugger (by breakpoint is good); o Run SimpleNSIOEthernetImpl and TeledebugImpl, in that order. o Interpret call TestTeledebugging in this module. Bottom line, this doesn't work very good. The remote debugger looks at the ESV in the PDA; this tells about CoPilot's debugee. However, it reads memory directly from CoPilot, itself. Not too good. This debugging technique may be useful for debugging the basics of communication, if they are not working. Alternatively, you can insert code which displays values of key variables in the maintenance panel. See GermOps.ShowCardinalInMP, etc. >> DIRECTORY Boot USING [Location], BootChannel USING [Result], DeviceTypes USING [ethernet], Environment USING [ Base, bytesPerWord, Long, LongPointerFromPage, PageFromLongPointer, PageCount, PageNumber, wordsPerPage], GermOps USING [GermWorldError], IEEE8023 USING [EncapObject], NSConstants USING [teleDebugSocket], NSTypes USING [ BufferBody, bytesPerExchangeHeader, maxDataBytesPerExchange, PacketType], PacketExchange USING [ExchangeClientType, ExchangeID], PageMap USING [ExchangeFlags, Flags, GetState, IsMapped], PilotMP USING [ cGermDeviceError, cGermERROR, cGermFunnyPacket, Code, cRespondingToEtherDebugger, cWaitingForEtherDebugger], ProcessorFace USING [BootButton, GetNextAvailableVM, SetMP], SimpleNSIO USING [ ByteCount, Finalize, fromAnyHost, fromAnyNetwork, fromAnySocket, GetRawBufferSize, Initialize, noTimeOut, ReceivePacket, ReturnPacket, SendPacket, timedOutBytes], System USING [ broadcastHostNumber, GetClockPulses, Microseconds, MicrosecondsToPulses, NetworkAddress, nullNetworkNumber, Pulses], TeledebugProtocol; TeledebugImplNoDisk: PROGRAM IMPORTS Environment, GermOps, PageMap, ProcessorFace, SimpleNSIO, System EXPORTS GermOps SHARES GermOps, PageMap = BEGIN -- Parameters: broadcastForDebuggerInterval: System.Microseconds = 30000000; proceedSequenceTimeout: System.Microseconds = 60000000; -- for go, remoteDestruct exchangeIDTimeout: -- a trojan horse ExchangeID will get thrown out after this delay. System.Microseconds = 120000000; -- TYPES and Constants: MaxSize: TYPE = CARDINAL [0..NSTypes.maxDataBytesPerExchange]; bytesPerWord: CARDINAL = Environment.bytesPerWord; nil: Environment.Base RELATIVE POINTER = LOOPHOLE[0]; nullExchangeID: PacketExchange.ExchangeID = [0, 0]; -- should be in PacketExchange! RequesterPacketType: TYPE = -- SHOULD be in TeledebugProtocol! TeledebugProtocol .PacketType[coreStore..remoteDestructReply]; -- Variables: -- The following ethernet buffer is allocated here statically because -- (1) if this module is present, we need it at any time -- (2) the buffer used by BootChannelSPP is only temporarily allocated, -- thus there is never more than one buffer permanently allocated. rawNSBufferSize: CARDINAL = -- if not enough, dies with mp code. SIZE[ethernet IEEE8023.EncapObject] + NSTypes.maxDataBytesPerExchange/Environment.bytesPerWord + 36--say--; rawNSBuffer: ARRAY [0..rawNSBufferSize) OF WORD; b: LONG POINTER TO NSTypes.BufferBody; -- the network buffer. firstUnimplementedVMPage: Environment.PageNumber; beingDebuggedInCopilot: BOOLEAN ← FALSE; protocolFlagsToPageMapFlags: ARRAY TeledebugProtocol .PageFlags[clean..vacant] OF PageMap.Flags = [ clean: [readonly: FALSE, dirty: FALSE, referenced: FALSE], referenced: [readonly: FALSE, dirty: FALSE, referenced: TRUE], dirty: [readonly: FALSE, dirty: TRUE, referenced: FALSE], dirtyReferenced: [readonly: FALSE, dirty: TRUE, referenced: TRUE], writeProtected: [readonly: TRUE, dirty: FALSE, referenced: FALSE], writeProtectedReferenced: [readonly: TRUE, dirty: FALSE, referenced: TRUE], vacant: [readonly: TRUE, dirty: TRUE, referenced: FALSE]]; coreStoreReqSize: MaxSize = TeledebugProtocol.coreStoreReqSize*bytesPerWord; coreStoreAckSize: MaxSize = TeledebugProtocol.coreStoreAckSize*bytesPerWord; coreFetchReqSize: MaxSize = TeledebugProtocol.coreFetchReqSize*bytesPerWord; coreFetchAckSize: MaxSize = TeledebugProtocol.coreFetchAckSize*bytesPerWord; diskStoreReqSize: MaxSize = TeledebugProtocol.diskStoreReqSize*bytesPerWord; diskStoreAckSize: MaxSize = TeledebugProtocol.diskStoreAckSize*bytesPerWord; diskFetchReqSize: MaxSize = TeledebugProtocol.diskFetchReqSize*bytesPerWord; diskFetchAckSize: MaxSize = TeledebugProtocol.diskFetchAckSize*bytesPerWord; nullPacketSize: MaxSize = SIZE[null TeledebugProtocol.TeledebugBuffer]*bytesPerWord; -- ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ -- Main Teledebugee server -- ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ GetTeledebugged: PUBLIC --GermOps.-- PROC [ pLocation: LONG POINTER TO Boot.Location, scratchPage: Environment.PageNumber] = BEGIN -- (scratchPage is used for reading page labels.) packet: LONG POINTER TO TeledebugProtocol.TeledebugBuffer; gotADebugger: BOOLEAN ← FALSE; receiveTimeout: System.Microseconds ← broadcastForDebuggerInterval; -- Information about current request packet. dataBytes: SimpleNSIO.ByteCount; thisExchangeID: PacketExchange.ExchangeID; prevExchangeID: PacketExchange.ExchangeID ← nullExchangeID; timePrevExchangeID: System.Pulses; nilRequest: TeledebugProtocol.PacketType = TeledebugProtocol.PacketType.last; requiredRequest: TeledebugProtocol.PacketType ← -- -- If non-nil, next packet must be this type (or requiredRequest.PRED). nilRequest; ProcessorFace.SetMP[PilotMP.cWaitingForEtherDebugger]; -- Initialize Network I/O: -- SOMEDAY, we could make the call to Debug be passed a device -- and type (just like BootChannel), and thus allow teledebugging -- over a variety of networks. BEGIN result: BootChannel.Result; IF rawNSBufferSize < SimpleNSIO.GetRawBufferSize[] THEN GermOps.GermWorldError[PilotMP.cGermERROR]; [bufferBody: b, result: result] ← SimpleNSIO.Initialize[ DeviceTypes.ethernet, 0, @rawNSBuffer, IF beingDebuggedInCopilot THEN doCleanup ELSE avoidCleanup]; IF result # [ok[]] THEN GermOps.GermWorldError[PilotMP.cGermDeviceError]; packet ← LOOPHOLE[@b.exchangeWords]; END; DO --until go or remoteDestruct request-- -- Get next request: DO -- until got a request packet IF NOT gotADebugger THEN BEGIN -- send mayday packet -- don't use a constructor here unless you want a LARGE frame -- packet↑ ← [type: mayday, body: null[]]]; packet.type ← mayday; packet.spare ← 0; packet.body ← null[]; PacketExchangeSend[ dataBytes: nullPacketSize, exchangeID: LOOPHOLE[System.GetClockPulses[]], dest: [ System.nullNetworkNumber, System.broadcastHostNumber, NSConstants.teleDebugSocket]]; receiveTimeout ← broadcastForDebuggerInterval; END; [dataBytes, thisExchangeID] ← PacketExchangeReceive[ minBytes: nullPacketSize, prevExchangeID: prevExchangeID, timePrevExchangeID: timePrevExchangeID, timeout: IF gotADebugger THEN SimpleNSIO.noTimeOut ELSE broadcastForDebuggerInterval, getFrom: [ SimpleNSIO.fromAnyNetwork, SimpleNSIO.fromAnyHost, SimpleNSIO.fromAnySocket]]; --ASSERT: timed out or got a packet of at least nullPacketSize. IF --UNTIL-- dataBytes # SimpleNSIO.timedOutBytes THEN EXIT; --ASSERT: timed out. IF requiredRequest # nilRequest THEN BEGIN -- Fabricate desired request packet and process it: packet.type ← requiredRequest; dataBytes ← nullPacketSize; packet.body ← null[]; EXIT; END; ENDLOOP; -- Process request: BEGIN --scope of BadPacket-- IF requiredRequest # nilRequest AND (packet.type # requiredRequest AND packet.type # requiredRequest.PRED) THEN GOTO BadPacket; SELECT packet.type FROM -- Each arm below sets dataBytes and the buffer body for the appropriate -- response and falls through to send it back to the requestor. mayday, ack, nak => GOTO IgnorePacket; -- broadcast from some other debugee. coreFetch => BEGIN -- this depends upon FetchReq and FetchAck pageNumber -- being in the same place. OPEN pAck: LOOPHOLE[packet, TeledebugProtocol.CoreFetchAckHandle]; IF dataBytes # coreFetchReqSize THEN GOTO BadPacket; WITH pReq: packet SELECT FROM coreFetchReq => BEGIN page: Environment.PageNumber = pReq.page; IF ImplementedAndMapped[page] THEN BEGIN pData: LONG POINTER TO TeledebugProtocol.PageData = Environment.LongPointerFromPage[page]; flags: PageMap.Flags = PageMap.GetState[page].state.flags; pAck.flags ← ProtocolFlagsFromPageMapFlags[flags]; pAck.data ← pData↑; [] ← PageMap.ExchangeFlags[ -- restore referenced bit. virtual: page, newFlags: flags]; END ELSE pAck.flags ← vacant; END; ENDCASE => GOTO BadPacket; packet.body ← coreFetchAck[ -- (body all already set) page: NULL, flags: NULL, data: NULL]; dataBytes ← coreFetchAckSize; packet.type ← ack; receiveTimeout ← SimpleNSIO.noTimeOut; END; --coreFetch-- coreStore => BEGIN -- this depends upon both StoreReq and StoreAck -- page and flags being in the same place IF dataBytes # coreStoreReqSize THEN GOTO BadPacket; WITH pReq: packet SELECT FROM coreStoreReq => BEGIN page: Environment.PageNumber = pReq.page; IF pReq.flags ~IN [clean..vacant] THEN GOTO BadPacket; IF ImplementedAndMapped[page] THEN BEGIN pData: LONG POINTER TO TeledebugProtocol.PageData = Environment.LongPointerFromPage[page]; MakeWritable[page]; pData↑ ← pReq.data; [] ← PageMap.ExchangeFlags[ virtual: page, newFlags: protocolFlagsToPageMapFlags[pReq.flags]] END ELSE pReq.flags ← vacant; END; ENDCASE => GOTO BadPacket; packet.body ← coreStoreAck[page: NULL, flags: NULL]; -- (body all already set) dataBytes ← coreStoreAckSize; packet.type ← ack; receiveTimeout ← SimpleNSIO.noTimeOut; END; --coreStore-- go, remoteDestruct => BEGIN goReplyIsSUCCGo: BOOLEAN [TRUE.. (TeledebugProtocol.PacketType.goReply = TeledebugProtocol.PacketType.go.SUCC)] = TRUE; remoteDestructReplyIsSUCCGo: BOOLEAN [TRUE.. (TeledebugProtocol.PacketType.remoteDestructReply = TeledebugProtocol.PacketType.remoteDestruct.SUCC)] = TRUE; requiredRequest ← packet.type.SUCC; IF dataBytes # nullPacketSize OR packet.request # null THEN GOTO BadPacket; packet.type ← ack; packet.body ← null[]; dataBytes ← nullPacketSize; receiveTimeout ← proceedSequenceTimeout; END; --go, remoteDestruct-- goReply => BEGIN IF dataBytes # nullPacketSize OR packet.request # null THEN GOTO BadPacket; SimpleNSIO.Finalize[ IF beingDebuggedInCopilot THEN doCleanup ELSE avoidCleanup]; RETURN; END; --goReply-- remoteDestructReply => BEGIN IF dataBytes # nullPacketSize OR packet.request # null THEN GOTO BadPacket; ProcessorFace.BootButton[]; END; --remoteDestructReply-- ENDCASE => GOTO BadPacket; -- unknown request type. -- Return response packet sitting in buffer: prevExchangeID ← thisExchangeID; -- remember ID of good packets. timePrevExchangeID ← System.GetClockPulses[]; PacketExchangeReturn[dataBytes: dataBytes]; ProcessorFace.SetMP[PilotMP.cRespondingToEtherDebugger]; gotADebugger ← TRUE; EXITS BadPacket => ProcessorFace.SetMP[PilotMP.cGermFunnyPacket]; IgnorePacket => NULL; END; --scope of BadPacket-- ENDLOOP; --until go or remoteDestruct request-- END; --Debug-- ImplementedAndMapped: PROC [page: Environment.PageNumber] RETURNS [BOOLEAN] = { IF page >= firstUnimplementedVMPage THEN RETURN[FALSE] ELSE RETURN[PageMap.IsMapped[page]]}; MakeWritable: PROC [page: LONG CARDINAL] = { flags: PageMap.Flags ← PageMap.GetState[page].state.flags; flags.readonly ← FALSE; [] ← PageMap.ExchangeFlags[virtual: page, newFlags: flags]}; ProtocolFlagsFromPageMapFlags: PROC [in: PageMap.Flags] RETURNS [out: TeledebugProtocol.PageFlags] = BEGIN out ← clean; DO IF protocolFlagsToPageMapFlags[out] = in THEN RETURN; IF (out ← SUCC[out]) = illegal THEN RETURN; ENDLOOP; END; -- ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ -- PacketExchange Network I/O -- ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ -- This supports sending and receiving level three packets -- using the Packet Exchange Protocol. -- Note this isn't very clean since we know some about teledebug socket etc. PacketExchangeReceive: PROC [ timeout: LONG CARDINAL, getFrom: System.NetworkAddress, minBytes: SimpleNSIO.ByteCount, prevExchangeID: PacketExchange.ExchangeID, timePrevExchangeID: System.Pulses] RETURNS [ dataBytes: SimpleNSIO.ByteCount, exchangeID: PacketExchange.ExchangeID, source: LONG POINTER TO System.NetworkAddress] = -- The returned dataBytes is the number of client bytes in the level -- three packet. Returns dataBytes = SimpleNSIO.timedOutBytes if -- times out. In this case, exchangeID will be TRASH. -- Supresses duplicates of packets with exchangeID previous to -- prevExchangeID, and packets shorter than minBytes. -- ExchangeIDs which look bad are discarded for a maximum of two minutes, -- then are accepted as good. -- Duplicates of packets with exchangeID equal to prevExchangeID are -- returned to caller; he can suppress them if he wants. -- prevExchangeID = nullExchangeID means accept any exchangeID. BEGIN type: NSTypes.PacketType; levelTwoBytes: SimpleNSIO.ByteCount; result: BootChannel.Result; DO --until correct teleDebug packet arrives-- [dataBytes: levelTwoBytes, type: type, source: source, result: result] ← SimpleNSIO.ReceivePacket[ getFrom: getFrom, timeout: timeout, mySocket: NSConstants.teleDebugSocket]; IF levelTwoBytes = SimpleNSIO.timedOutBytes THEN RETURN[SimpleNSIO.timedOutBytes, TRASH, NIL]; HandleResult[result]; -- This packet was sent to teleDebugSocket. It should be correct. IF --UNTIL-- type = packetExchange AND levelTwoBytes >= NSTypes.bytesPerExchangeHeader + minBytes AND b.exchangeType = teledebug AND (prevExchangeID = nullExchangeID OR NewerOrSame[ thisExchangeID: b.exchangeID, prevExchangeID: prevExchangeID] OR System.GetClockPulses[] - timePrevExchangeID > System.MicrosecondsToPulses[exchangeIDTimeout]) THEN EXIT; ProcessorFace.SetMP[PilotMP.cGermFunnyPacket]; ENDLOOP; RETURN[levelTwoBytes - NSTypes.bytesPerExchangeHeader, b.exchangeID, source]; END; --PacketExchangeReceive-- PacketExchangeReturn: PROC [dataBytes: SimpleNSIO.ByteCount] = INLINE -- Returns the contents of the buffer to where it came from. BEGIN -- Note that the exchangeID, teledebugExchangeType already set HandleResult[ SimpleNSIO.ReturnPacket[ dataBytes: dataBytes + NSTypes.bytesPerExchangeHeader, type: packetExchange].result]; END; PacketExchangeSend: PROC [ dataBytes: SimpleNSIO.ByteCount, exchangeID: PacketExchange.ExchangeID, dest: System.NetworkAddress] = INLINE BEGIN b.exchangeID ← exchangeID; b.exchangeType ← teledebug; HandleResult[ SimpleNSIO.SendPacket[ dataBytes: dataBytes + NSTypes.bytesPerExchangeHeader, type: packetExchange, sourceSocket: NSConstants.teleDebugSocket, dest: dest].result]; END; HandleResult: PROC [result: BootChannel.Result] = BEGIN WITH r: result SELECT FROM ok => NULL; error => GermOps.GermWorldError[r.code]; ENDCASE => GermOps.GermWorldError[PilotMP.cGermERROR]; END; NewerOrSame: PROC [thisExchangeID, prevExchangeID: PacketExchange.ExchangeID] RETURNS [BOOLEAN] = --INLINE-- -- Returns TRUE if thisExchangeID is newer than or the same as prevExchangeID -- (and therefore should not be discarded). BEGIN << We call an exchangeID older if it happened in the recent past; that is, its value is within the range [thisExchangeID - LAST[ExchangeID]/8..thisExchangeID). This means that duplicates of recent previous packets will be discarded with high reliability. >> LongCardFromExchangeID: PROC [exchangeID: PacketExchange.ExchangeID] RETURNS [LONG CARDINAL] = INLINE { RETURN[LOOPHOLE[ Environment.Long[num[lowbits: exchangeID.b, highbits: exchangeID.a]]]]}; newerLimit: LONG CARDINAL = 7*(LAST[LONG CARDINAL]/8); -- utilizes wraparound amountThisNewer: LONG CARDINAL = -- the amount that thisExchangeID -- is newer than prevExchangeID (the usual case) LongCardFromExchangeID[thisExchangeID] - LongCardFromExchangeID[ prevExchangeID]; -- underflow is fine here. -- TEMP DEBUG --IF amountThisNewer >= newerLimit THEN { -- ShowCodeInMP[2222]; -- ShowCardinalInMP[Inline.HighHalf[amountThisNewer]]; -- ShowCardinalInMP[Inline.LowHalf[amountThisNewer]]; -- ShowCodeInMP[2224]; -- ShowCardinalInMP[Inline.HighHalf[newerLimit]]; -- ShowCardinalInMP[Inline.LowHalf[newerLimit]]; -- }; RETURN[ --newerOrSame:-- amountThisNewer < newerLimit]; END; -- ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ -- Main body, debugging code -- ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ TestTeledebugging: PROC[deviceOrdinal: CARDINAL ← 0] = -- To test this module in CoPilot, interpret call this proc from the debugger. -- See comments at head of module. BEGIN location: Boot.Location; storage: ARRAY -- need one word-aligned page of storage. [0..2*Environment.wordsPerPage) OF WORD; beingDebuggedInCopilot ← TRUE; location.deviceOrdinal ← deviceOrdinal; -- only relevant value in location GetTeledebugged[pLocation: @location, scratchPage: Environment.PageFromLongPointer[ @storage + Environment.wordsPerPage-1]]; END; -- MAIN BODY: -- Find the size of VM for this processor: (sets firstUnimplementedVMPage) BEGIN page: Environment.PageNumber; count: Environment.PageCount; FOR firstUnimplementedVMPage ← FIRST[Environment.PageNumber], page + count DO [firstPage: page, count: count] ← ProcessorFace.GetNextAvailableVM[ firstUnimplementedVMPage]; IF count = 0 THEN EXIT; ENDLOOP; END; END. LOG (For previous log entries, please see Mesa 10.0 archive version.) 1-Sep-81 15:17:25 Forrest 8.0c; add DetermineDiskShape 6-Dec-81 4:27:00 Forrest NS Teledebug lives 7-Jun-83 23:18:09 DKnutsen OISCP => NS. Get LongPointerFromPage from Environment. Changed IsVacant to IsMapped. Convert to new PageMap. Make compatible with new SimpleNSIO interface. Use official type definitions rather than private copies. Some responses didn't set ack packet.type. Doubled proceedSequenceTimeout. Supress copies of previous request packets. Now retransmits "shutUp" packet. Documented operation of go sequencing. Use informative mp codes. Allocate ethernet buffer here. 20-Jul-83 10:16:14 Luniewski NSTypes.maxDataBytePerExchange => NSTypes.maxDataBytesPerExchange, long page numbers in map log entries. 11-Nov-83 13:55:50 DKnutsen Must not use SendPacket to talk to remote network. Simplify Proceed logic and use timeout. 14-Nov-83 10:31:50 DKnutsen Handle duplicate go packets.