-- Cedar Remote Debugging: word-level implementation -- WVMImpl.mesa -- Andrew Birrell March 3, 1983 12:42 pm DIRECTORY Inline USING[ LongCOPY ], Mopcodes USING[ op, zBRK ], PageMap USING[ flagsVacant, GetF, SetF, Value ], PrincOps USING[ BytePC, InstWord ], ProcessInternal USING[ DisableInterrupts, EnableInterrupts], Space USING[ PageFromLongPointer, PageNumber ], SpecialSpace USING[ MakeGlobalFrameResident, MakeProcedureResident ], VMMapLog USING[ PatchTable, PatchTableEntry, PatchTableEntryPointer ], WorldVM USING[ Address, LocalWorld, World ], WVMPrivate USING[ GetPage, PageData, PageHandle, PageNumber, pageSize, PatchTable, ReleasePage, WriteAndReleasePage ]; WVMImpl: MONITOR--protects the patch table-- IMPORTS Inline, PageMap, ProcessInternal, Space, SpecialSpace, WorldVM, WVMPrivate EXPORTS WorldVM SHARES PageMap = BEGIN OPEN WorldVM; local: WorldVM.World = WorldVM.LocalWorld[]; Read: PUBLIC PROC[world: World, addr: Address] RETURNS[ i: CARDINAL ] = BEGIN n: WVMPrivate.PageNumber; w: [0..WVMPrivate.pageSize); h: WVMPrivate.PageHandle; d: REF WVMPrivate.PageData; IF world = local THEN RETURN[LOOPHOLE[addr, LONG POINTER TO CARDINAL]^]; [n, w, h, d] _ PageFromAddr[world, addr]; i _ d[w]; WVMPrivate.ReleasePage[h]; END; Write: PUBLIC PROC[world: World, addr: Address, value: CARDINAL] = BEGIN n: WVMPrivate.PageNumber; w: [0..WVMPrivate.pageSize); h: WVMPrivate.PageHandle; d: REF WVMPrivate.PageData; IF world = local THEN { LOOPHOLE[addr, LONG POINTER TO CARDINAL]^ _ value; RETURN }; [n, w, h, d] _ PageFromAddr[world, addr]; d[w] _ value; WVMPrivate.WriteAndReleasePage[h]; END; LongRead: PUBLIC PROC[world: World, addr: Address] RETURNS[ i: LONG CARDINAL ] = BEGIN n: WVMPrivate.PageNumber; w: [0..WVMPrivate.pageSize); h: WVMPrivate.PageHandle; d: REF WVMPrivate.PageData; IF world = local THEN RETURN[LOOPHOLE[addr, LONG POINTER TO LONG CARDINAL]^]; [n, w, h, d] _ PageFromAddr[world, addr]; IF w = WVMPrivate.pageSize-1 THEN { WVMPrivate.ReleasePage[h]; CopyRead[world,addr,SIZE[LONG CARDINAL],@i] } ELSE BEGIN i _ LOOPHOLE[@d[w],LONG POINTER TO LONG CARDINAL]^; WVMPrivate.ReleasePage[h]; END; END; LongWrite: PUBLIC PROC[world: World, addr: Address, value: LONG CARDINAL] = BEGIN n: WVMPrivate.PageNumber; w: [0..WVMPrivate.pageSize); h: WVMPrivate.PageHandle; d: REF WVMPrivate.PageData; IF world = local THEN { LOOPHOLE[addr, LONG POINTER TO LONG CARDINAL]^ _ value; RETURN }; [n, w, h, d] _ PageFromAddr[world, addr]; IF w = WVMPrivate.pageSize-1 THEN { WVMPrivate.ReleasePage[h]; CopyWrite[world,@value,SIZE[LONG CARDINAL],addr] } ELSE BEGIN LOOPHOLE[@d[w],LONG POINTER TO LONG CARDINAL]^ _ value; WVMPrivate.WriteAndReleasePage[h]; END; END; CopyRead: PUBLIC PROC[world: World, from: Address, nwords: INT, to: LONG POINTER] = BEGIN n: WVMPrivate.PageNumber; w: [0..WVMPrivate.pageSize); h: WVMPrivate.PageHandle; d: REF WVMPrivate.PageData; IF world = local THEN { Inline.LongCOPY[from: LOOPHOLE[from], nwords: nwords, to: to]; RETURN }; WHILE nwords > 0 DO amount: (0..WVMPrivate.pageSize]; [n, w, h, d] _ PageFromAddr[world, from]; amount _ MIN[nwords,WVMPrivate.pageSize-w]; Inline.LongCOPY[from: @d[w], to: to, nwords: amount]; nwords _ nwords - amount; from _ from + amount; to _ to + amount; WVMPrivate.ReleasePage[h]; ENDLOOP; END; CopyWrite: PUBLIC PROC[world: World, from: LONG POINTER, nwords: INT, to: Address] = BEGIN n: WVMPrivate.PageNumber; w: [0..WVMPrivate.pageSize); h: WVMPrivate.PageHandle; d: REF WVMPrivate.PageData; IF world = local THEN { Inline.LongCOPY[from: from, nwords: nwords, to: LOOPHOLE[to]]; RETURN }; WHILE nwords > 0 DO amount: [0..WVMPrivate.pageSize); [n, w, h, d] _ PageFromAddr[world, to]; amount _ MIN[nwords,WVMPrivate.pageSize-w]; Inline.LongCOPY[from: from, to: @d[w], nwords: amount]; nwords _ nwords - amount; from _ from + amount; to _ to + amount; WVMPrivate.WriteAndReleasePage[h]; ENDLOOP; END; PageFromAddr: PROC[world: WorldVM.World, addr: WorldVM.Address, coreOnly: BOOLEAN _ FALSE] RETURNS[n: WVMPrivate.PageNumber, w: [0..WVMPrivate.pageSize), h: WVMPrivate.PageHandle, d: REF WVMPrivate.PageData] = BEGIN n _ addr / WVMPrivate.pageSize; w _ addr - n * LONG[WVMPrivate.pageSize]; [d, h] _ WVMPrivate.GetPage[world, n, coreOnly]; END; SetBreak: PUBLIC PROC[world: World, addr: Address, offset: PrincOps.BytePC] RETURNS[ oldByte: Mopcodes.op ] = { oldByte _ Patch[world, addr+offset/2, offset MOD 2, Mopcodes.zBRK] }; ClearBreak: PUBLIC PROC[world: World, addr: Address, offset: PrincOps.BytePC, oldByte: Mopcodes.op] = { [] _ Patch[world, addr+offset/2, offset MOD 2, oldByte] }; PatchTableFull: ERROR = CODE; IllegalPatch: PUBLIC ERROR = CODE; Patch: ENTRY PROC[world: World, addr: Address, offset: [0..1], byte: Mopcodes.op] RETURNS[oldByte: Mopcodes.op] = BEGIN ENABLE UNWIND => NULL; value: PrincOps.InstWord; -- new memory word contents -- patch: Address = WVMPrivate.PatchTable[world]; header: VMMapLog.PatchTable; base: Address = patch + SIZE[VMMapLog.PatchTable]; free: CARDINAL; pti: CARDINAL; entry: VMMapLog.PatchTableEntry; CopyRead[world: world, from: patch, to: @header, nwords: SIZE[VMMapLog.PatchTable]]; free _ LOOPHOLE[header.limit]; FOR pti _ 0, pti + SIZE[VMMapLog.PatchTableEntry] UNTIL pti = LOOPHOLE[header.limit, CARDINAL] DO CopyRead[world: world, from: base+pti, to: @entry, nwords: SIZE[VMMapLog.PatchTableEntry]]; IF entry.address = NIL THEN free _ pti; IF LOOPHOLE[entry.address, Address] = addr THEN EXIT; REPEAT FINISHED => { pti _ free; entry.address _ NIL } ENDLOOP; IF pti = LOOPHOLE[header.limit, CARDINAL] AND header.limit = header.maxLimit THEN ERROR PatchTableFull[]; value _ IF LOOPHOLE[entry.address, Address] = addr THEN entry.value ELSE LOOPHOLE[Read[world, addr]]; oldByte _ IF offset=0 THEN value.evenbyte ELSE value.oddbyte; IF oldByte # Mopcodes.zBRK AND byte # Mopcodes.zBRK THEN ERROR IllegalPatch[]; IF oldByte = Mopcodes.zBRK AND byte = Mopcodes.zBRK THEN ERROR IllegalPatch[]; IF offset=0 THEN value.evenbyte _ byte ELSE value.oddbyte _ byte; entry.value _ value; IF value.evenbyte # Mopcodes.zBRK AND value.oddbyte # Mopcodes.zBRK THEN entry.address _ NIL -- no patching needed -- ELSE entry.address _ LOOPHOLE[addr]; IF world = local THEN LocalEntry[from: entry, to: LOOPHOLE[base, LONG POINTER] + pti] ELSE CopyWrite[world: world, from: @entry, to: base+pti, nwords: SIZE[VMMapLog.PatchTableEntry]]; IF pti = LOOPHOLE[header.limit, CARDINAL] THEN -- extend table -- BEGIN header.limit _ LOOPHOLE[pti + SIZE[VMMapLog.PatchTableEntry]]; CopyWrite[world: world, from: @header, to: patch, nwords: SIZE[VMMapLog.PatchTable]]; END; -- Now we've done the patch table. Next, fix real memory. -- IF world = local THEN LocalPatch[LOOPHOLE[addr, LONG POINTER], entry.value] ELSE BEGIN n: WVMPrivate.PageNumber; w: [0..WVMPrivate.pageSize); h: WVMPrivate.PageHandle; d: REF WVMPrivate.PageData; [n, w, h, d] _ PageFromAddr[world, addr, --coreOnly:-- TRUE]; IF h # NIL THEN -- swapped in, so patch it -- BEGIN d[w] _ entry.value; WVMPrivate.WriteAndReleasePage[h--, clean--]; END; END; END; LocalEntry: INTERNAL PROC[from: VMMapLog.PatchTableEntry, to: LONG POINTER TO VMMapLog.PatchTableEntry] = BEGIN -- Unfortunately, we can't synchronize with the swapper. ProcessInternal.DisableInterrupts[]; to^ _ from; -- assume our local frame and patch table are resident. ProcessInternal.EnableInterrupts[]; END; LocalPatch: INTERNAL PROC[addr: LONG POINTER, value: CARDINAL] = BEGIN -- patch given address in our real memory, cleanly. page: Space.PageNumber = Space.PageFromLongPointer[addr]; oldVal, newVal: PageMap.Value; UNTIL addr^ = value -- in practice, only once! DO ProcessInternal.DisableInterrupts[]; newVal _ oldVal _ PageMap.GetF[page]; IF oldVal.flags # PageMap.flagsVacant THEN BEGIN newVal.flags.writeProtected _ FALSE; [] _ PageMap.SetF[page, newVal]; addr^ _ value; --write the word! [] _ PageMap.SetF[page, oldVal]; END; ProcessInternal.EnableInterrupts[]; ENDLOOP; END; SpecialSpace.MakeGlobalFrameResident[WVMImpl]; SpecialSpace.MakeProcedureResident[LocalPatch]; SpecialSpace.MakeProcedureResident[LocalEntry]; END.