WVMRemote.mesa - TeleDebug protocol
Copyright © 1985 by Xerox Corporation. All rights reserved.
Andrew Birrell July 29, 1983 3:56 pm
Russ Atkinson, February 6, 1985 9:37:28 pm PST
DIRECTORY
BufferDefs USING[ Buffer ],
DebuggerFormat USING[ LabelChecksum ],
DiskFace USING[ Label, Type ],
PrincOps,
Process USING[ EnableAborts ],
PupDefs USING[ GetFreePupBuffer, GetPupAddress, GetPupContentsBytes, MsToTocks, PupBuffer, PupNameTrouble, PupPackageDestroy, PupPackageMake, PupSocket, PupSocketDestroy, PupSocketMake, ReturnFreePupBuffer, SetPupContentsWords ],
PupTypes USING[ fillInSocketID, Pair, PupAddress, PupType ],
Rope USING[ ROPE ],
TeledebugProtocol,
WorldVM USING[ ],
WVMPrivate;
WVMRemote:
MONITOR
IMPORTS DebuggerFormat, Process, PupDefs
EXPORTS WorldVM, WVMPrivate = {
Machine: PUBLIC TYPE = PupTypes.PupAddress;
Public procedures
ReadRemoteCore:
PUBLIC
PROC[host: Machine, data:
REF WVMPrivate.PageData, mempage: WVMPrivate.PageNumber]
RETURNS[map: WVMPrivate.MapEntry, ok:
BOOL] =
{
in: LONG POINTER TO TeledebugProtocol.CoreFetchAcknowledgement;
out: LONG POINTER TO TeledebugProtocol.CoreFetchRequest;
Lock[host];
DO
ENABLE
UNWIND => Unlock[];
p: PupDefs.PupBuffer ← PupDefs.GetFreePupBuffer[];
out ← LOOPHOLE[@p.pupWords];
out^ ← [page: mempage];
p ← SendAndReceive[p, TeledebugProtocol.coreFetchRequest,
TeledebugProtocol.coreFetchRequestSize];
in ← LOOPHOLE[@p.pupWords];
IF PupWords[p] # TeledebugProtocol.coreFetchAcknowledgementSize
OR WVMPrivate.PageNumber[in.page] # mempage
THEN { PupDefs.ReturnFreePupBuffer[p]; LOOP };
data^ ← LOOPHOLE[in.data, WVMPrivate.PageData];
map.state.flags ← LOOPHOLE[in.flags, PrincOps.PageState].flags;
PupDefs.ReturnFreePupBuffer[p];
ok ← LOOPHOLE[in.flags, PrincOps.PageState].flags#PrincOps.flagsVacant;
EXIT
ENDLOOP;
Unlock[];
};
WriteRemoteCore:
PUBLIC
PROC[host: Machine, data:
REF WVMPrivate.PageData, mempage: WVMPrivate.PageNumber, map: WVMPrivate.MapEntry]
RETURNS[readOnly:
BOOL] = {
in: LONG POINTER TO TeledebugProtocol.CoreStoreAcknowledgement;
out: LONG POINTER TO TeledebugProtocol.CoreStoreRequest;
readOnly ← map.state.flags.readonly;
IF NOT readOnly THEN map.state.flags.dirty ← TRUE;
Lock[host];
DO
ENABLE
UNWIND => Unlock[];
p: PupDefs.PupBuffer ← PupDefs.GetFreePupBuffer[];
out ← LOOPHOLE[@p.pupWords];
out.page ← mempage;
out.flags ← LOOPHOLE[map.state.flags];
LOOPHOLE[@out.data, LONG POINTER TO WVMPrivate.PageData]^ ← data^;
p ← SendAndReceive[p, TeledebugProtocol.coreStoreRequest,
TeledebugProtocol.coreStoreRequestSize];
in ← LOOPHOLE[@p.pupWords];
IF PupWords[p] # TeledebugProtocol.coreStoreAcknowledgementSize
OR WVMPrivate.PageNumber[in.page] # mempage
THEN { PupDefs.ReturnFreePupBuffer[p]; LOOP };
IF in.flags#LOOPHOLE[map.state.flags, WORD] THEN ERROR;
PupDefs.ReturnFreePupBuffer[p];
EXIT;
ENDLOOP;
Unlock[];
};
ClientDiskTellsLies: ERROR = CODE;
ReadRemoteDisk:
PUBLIC
PROC[host: Machine, data:
REF WVMPrivate.PageData, addr: WVMPrivate.DiskAddress, label:
LONG
POINTER
TO DiskFace.Label] = {
in: LONG POINTER TO TeledebugProtocol.DiskFetchAcknowledgement;
out: LONG POINTER TO TeledebugProtocol.DiskAddressSetRequest;
Lock[host];
DO
ENABLE
UNWIND => Unlock[];
p: PupDefs.PupBuffer ← PupDefs.GetFreePupBuffer[];
out ← LOOPHOLE[@p.pupWords];
out^ ← [addr.diskPage + addr.offset, addr.deviceType, addr.deviceOrdinal];
Sending diskFetchRequest with diskAddressSetRequestSize causes
a combined addressSet and diskFetch. (I didn't design it!)
p ← SendAndReceive[p, TeledebugProtocol.diskFetchRequest,
TeledebugProtocol.diskAddressSetRequestSize];
IF PupWords[p] # TeledebugProtocol.diskFetchAcknowledgementSize
THEN { PupDefs.ReturnFreePupBuffer[p]; LOOP };
in ← LOOPHOLE[@p.pupWords];
IF data # NIL THEN data^ ← LOOPHOLE[in.data, WVMPrivate.PageData];
label^ ← LOOPHOLE[in.label, DiskFace.Label];
PupDefs.ReturnFreePupBuffer[p];
IF addr.labelCheck # DebuggerFormat.LabelChecksum[label^, addr.offset]
THEN ERROR ClientDiskTellsLies[];
EXIT
ENDLOOP;
Unlock[];
};
WriteRemoteDisk:
PUBLIC
PROC[host: Machine, data:
REF WVMPrivate.PageData, addr: WVMPrivate.DiskAddress, label:
LONG
POINTER
TO DiskFace.Label] = {
p: PupDefs.PupBuffer;
in: LONG POINTER TO TeledebugProtocol.DiskStoreAcknowledgement;
out: LONG POINTER TO TeledebugProtocol.DiskStoreRequest;
Lock[host];
DO
ENABLE
UNWIND => Unlock[];
SetDiskAddress[addr.deviceType, addr.deviceOrdinal, addr.diskPage + addr.offset];
p ← PupDefs.GetFreePupBuffer[];
out ← LOOPHOLE[@p.pupWords];
LOOPHOLE[@out.label, LONG POINTER TO DiskFace.Label]^ ← label^;
LOOPHOLE[@out.data, LONG POINTER TO WVMPrivate.PageData]^ ← data^;
p ← SendAndReceive[p, TeledebugProtocol.diskStoreRequest,
TeledebugProtocol.diskStoreRequestSize];
IF PupWords[p] # TeledebugProtocol.diskStoreAcknowledgementSize
THEN { PupDefs.ReturnFreePupBuffer[p]; LOOP };
in ← LOOPHOLE[@p.pupWords];
IF in.label = TeledebugProtocol.noLabel THEN ERROR ClientDiskTellsLies[];
PupDefs.ReturnFreePupBuffer[p];
EXIT
ENDLOOP;
Unlock[];
};
GoRemote:
PUBLIC
PROC[host: Machine] = {
p: PupDefs.PupBuffer ← PupDefs.GetFreePupBuffer[];
Lock[host];
{
ENABLE UNWIND => Unlock[];
p ← SendAndReceive[p, TeledebugProtocol.go, 0];
p.pupID ← IncPair[p.pupID];
Send[p, TeledebugProtocol.goReply, 0];
};
Unlock[];
};
SetDiskAddress:
PROC [device: DiskFace.Type, deviceOrdinal:
CARDINAL, page:
LONG
CARDINAL] = {
in: LONG POINTER TO TeledebugProtocol.DiskAddressSetAcknowledgement;
out: LONG POINTER TO TeledebugProtocol.DiskAddressSetRequest;
DO p: PupDefs.PupBuffer ← PupDefs.GetFreePupBuffer[];
out ← LOOPHOLE[@p.pupWords];
out^ ← [page, device, deviceOrdinal];
p ← SendAndReceive[p, TeledebugProtocol.diskAddressSetRequest,
TeledebugProtocol.diskAddressSetRequestSize];
in ← LOOPHOLE[@p.pupWords];
IF PupWords[p] # TeledebugProtocol.diskAddressSetAcknowledgementSize
OR in^ # [page, device, deviceOrdinal]
THEN { PupDefs.ReturnFreePupBuffer[p]; LOOP };
PupDefs.ReturnFreePupBuffer[p];
EXIT
ENDLOOP;
};
Actual packet transmission/reception! --
lastPupID: PupTypes.Pair ← [0, 0];
GetNextPupID:
ENTRY
PROC
RETURNS [PupTypes.Pair] =
INLINE {
RETURN[lastPupID←IncPair[IncPair[lastPupID]]]; };
IncPair:
PROC [id: PupTypes.Pair]
RETURNS [PupTypes.Pair] =
INLINE {
OPEN id;
RETURN [[a+(IF b=LAST[CARDINAL] THEN 1 ELSE 0), b+1]];
};
PupWords:
PROC [p: PupDefs.PupBuffer]
RETURNS [
CARDINAL] =
INLINE {
w: CARDINAL = PupDefs.GetPupContentsBytes[p];
IF w MOD 2 # 0 THEN RETURN[LAST[CARDINAL]];
RETURN[w/2];
};
SendAndReceive:
PROC [x: PupDefs.PupBuffer, t: PupTypes.PupType, words:
CARDINAL]
RETURNS [answer: PupDefs.PupBuffer] = {
requeued: BOOL ← TRUE;
done: CONDITION;
MyRequeue:
ENTRY
PROC[BufferDefs.Buffer] =
{ requeued ← TRUE; NOTIFY done };
WaitRequeue:
ENTRY
PROC =
{ ENABLE UNWIND => NULL; UNTIL requeued DO WAIT done ENDLOOP };
x.requeueProcedure ← MyRequeue;
x.pupID ← GetNextPupID[];
DO WaitRequeue[]; requeued ←
FALSE; Send[x, t, words];
answer ← Receive[x.pupID ! ReceiveTimeout => LOOP];
EXIT
ENDLOOP;
WaitRequeue[]; PupDefs.ReturnFreePupBuffer[x];
};
Send:
PROC [x: PupDefs.PupBuffer, t: PupTypes.PupType, words:
CARDINAL] = {
x.pupType ← t;
PupDefs.SetPupContentsWords[x, words];
socket.put[x];
};
ReceiveTimeout: ERROR = CODE;
getMeAPup: CONDITION;
receivedPup: PupDefs.PupBuffer;
wantPup: BOOL;
Receive:
ENTRY
PROC[thisPupID: PupTypes.Pair]
RETURNS [PupDefs.PupBuffer] =
{
ENABLE UNWIND => NULL;
DO wantPup ←
TRUE;
receivedPup ← NIL;
WAIT getMeAPup;
IF receivedPup=NIL THEN RETURN WITH ERROR ReceiveTimeout;
IF receivedPup.pupID = thisPupID THEN RETURN[receivedPup];
PupDefs.ReturnFreePupBuffer[receivedPup];
ENDLOOP;
};
keepReceiving: BOOL;
ReceiveProcess:
PROC = {
x: PupDefs.PupBuffer;
Pass:
ENTRY
PROC =
INLINE {
IF wantPup
THEN { wantPup ← FALSE; receivedPup ← x; NOTIFY getMeAPup }
ELSE PupDefs.ReturnFreePupBuffer[x];
};
Notify: ENTRY PROC = INLINE {NOTIFY getMeAPup};
Like:
PROC
RETURNS [
BOOL] =
INLINE {
first level of screening (don't care about others)
RETURN [
x.source = remote AND x.dest = socket.getLocalAddress[] AND
x.pupType = TeledebugProtocol.acknowledgement];
};
WHILE keepReceiving
DO
x ← socket.get[];
IF x = NIL
THEN Notify[] -- timeout
ELSE IF Like[] THEN Pass[] ELSE PupDefs.ReturnFreePupBuffer[x];
ENDLOOP;
};
There is only one socket, protected by Lock/Unlock during remote calls --
locked: BOOL ← FALSE;
unlocked: CONDITION;
socket: PupDefs.PupSocket;
remote: PupTypes.PupAddress;
Lock:
ENTRY
PROC[host: Machine] = {
ENABLE UNWIND => NULL;
WHILE locked DO WAIT unlocked ENDLOOP; locked ← TRUE;
socket.setRemoteAddress[remote ← host];
};
Unlock:
ENTRY
PROC = { locked ←
FALSE;
NOTIFY unlocked };
LookupFailed: PUBLIC ERROR = CODE;
LocateRemote:
PUBLIC
PROC [name: Rope.
ROPE]
RETURNS[ host: Machine ] = {
host ← PupDefs.GetPupAddress[TeledebugProtocol.teleSwatSocket, name !
PupDefs.PupNameTrouble => ERROR LookupFailed[] ];
};
receiveProcess: PROCESS;
StartTeledebugging:
PROC = {
PupDefs.PupPackageMake[];
Process.EnableAborts[@getMeAPup];
remote.net ← [0]; remote.host ← [0];
socket ← PupDefs.PupSocketMake[
local: PupTypes.fillInSocketID, remote: remote,
ticks: PupDefs.MsToTocks[500] ];
keepReceiving ← TRUE; wantPup ← FALSE;
receiveProcess ← FORK ReceiveProcess;
Unlock[];
};
StopTeledebugging:
PROC = {
Lock[remote];
keepReceiving ← FALSE; JOIN receiveProcess;
PupDefs.PupSocketDestroy[socket];
PupDefs.PupPackageDestroy[];
};
StartTeledebugging[];
}.