<<>> <> <> <> <> <<>> DIRECTORY TargetArchitecture, Shepherd, Breakpoint, BreakWorldShepherd, PCRMonitorDefs, BreakWorldArchitecture, RuntimeError; BreakWorldShepherdImpl: CEDAR PROGRAM IMPORTS Shepherd, TargetArchitecture, BreakWorldArchitecture, RuntimeError EXPORTS Shepherd ~ { <> Meadow: PUBLIC TYPE ~ BreakWorldShepherd.Meadow _ Shepherd.nullMeadow; MeadowRep: PUBLIC TYPE ~ BreakWorldShepherd.MeadowRep; Patch: PUBLIC TYPE ~ BreakWorldShepherd.Patch _ Shepherd.nullPatch; PatchRep: PUBLIC TYPE ~ BreakWorldShepherd.PatchRep; <> ReservePatch: PUBLIC PROCEDURE [ pc: BreakWorldArchitecture.Address, codeSize: BreakWorldArchitecture.ByteSize] RETURNS [Shepherd.Patch] ~ { ENABLE BreakWorldArchitecture.Cant => ERROR Shepherd.Cant[message]; patch: BreakWorldShepherd.Patch _ BreakWorldShepherd.nullPatch; IF pc.IsNullAddress[] THEN { ERROR Shepherd.Cant[message: "ReservePatch[nullAddress]"]; }; { meadow: BreakWorldShepherd.Meadow ~ MeadowFromPC[pc: pc]; ReserveUnderMonitor: PROCEDURE [] RETURNS [] ~ { Reserve: PROCEDURE [ meadow: BreakWorldShepherd.Meadow, pc: BreakWorldArchitecture.Address, codeSize: BreakWorldArchitecture.ByteSize] ~ { patch _ AllocatePatch[meadow: meadow, codeSize: codeSize]; SetPatchPC[ patch: patch, pc: pc.TargetAddressFromBreakWorldAddress[]]; RETURN; }; IF Shepherd.IsBusyPC[pc: pc] THEN { ERROR Shepherd.BusyPC[message: "That address is busy"]; }; Reserve[meadow: meadow, pc: pc, codeSize: codeSize]; RETURN; }; IF Shepherd.IsNullMeadow[meadow: meadow] THEN { ERROR Shepherd.NoMeadow[message: "No meadow for that address"]; }; { -- extra block to contain EXITS. meadowAddress: BreakWorldArchitecture.Address ~ AddressFromMeadow[ meadow: meadow]; BreakWorldArchitecture.MonitoredCall[ address: meadowAddress, proc: ReserveUnderMonitor ! BreakWorldArchitecture.WouldBlock => { GO TO wouldBlock; }]; EXITS wouldBlock => { ERROR Shepherd.BusyMeadow[message: "Meadow for that address is busy"]; }; }; }; RETURN [patch]; }; ReleasePatch: PUBLIC PROCEDURE [patch: Shepherd.Patch] ~ { IF Shepherd.IsNullPatch[patch: patch] THEN { ERROR Shepherd.Cant[message: "ReleasePatch[nullPatch]"]; }; { meadow: Shepherd.Meadow ~ MeadowFromPatch[patch: patch]; ReleaseUnderMonitor: PROCEDURE [] RETURNS [] ~ { Release: PROCEDURE [patch: Shepherd.Patch] ~ { SetPatchPC[patch: patch, pc: TargetArchitecture.nullAddress]; DeallocatePatch[patch: patch]; }; Release[patch: patch]; }; IF Shepherd.IsNullMeadow[meadow: meadow] THEN { ERROR Shepherd.NoMeadow[message: "No meadow for that address"]; }; { -- extra block to contain EXITS. meadowAddress: BreakWorldArchitecture.Address ~ AddressFromMeadow[ meadow: meadow]; BreakWorldArchitecture.MonitoredCall[ address: meadowAddress, proc: ReleaseUnderMonitor ! BreakWorldArchitecture.WouldBlock => { GO TO wouldBlock; }]; EXITS wouldBlock => { ERROR Shepherd.BusyMeadow[message: "Meadow for that address is busy"]; }; }; }; RETURN; }; IsNullMeadow: PUBLIC PROCEDURE [meadow: Shepherd.Meadow] RETURNS [BOOLEAN] ~ { isNullMeadow: BOOLEAN ~ meadow = Shepherd.nullMeadow; RETURN [isNullMeadow]; }; IsNullPatch: PUBLIC PROCEDURE [patch: Shepherd.Patch] RETURNS [BOOLEAN] ~ { isNullPatch: BOOLEAN ~ patch = Shepherd.nullPatch; RETURN [isNullPatch]; }; PCFromPatch: PUBLIC PROCEDURE [patch: BreakWorldShepherd.Patch] RETURNS [BreakWorldArchitecture.Address] ~ { IF Shepherd.IsNullPatch[patch: patch] THEN { ERROR Shepherd.Cant[message: "PCFromPatch[nullPatch]"]; }; { targetAddress: TargetArchitecture.Address ~ GetPatchPC[patch: patch]; pc: BreakWorldArchitecture.Address ~ BreakWorldArchitecture.NewAddress[ breakWorld: BreakWorldArchitecture.BreakWorldFromBreakWorldAddress[ address: PatchAddressFromPatch[patch: patch]], address: targetAddress]; RETURN [pc]; }; }; AddressFromMeadow: PRIVATE PROCEDURE [meadow: BreakWorldShepherd.Meadow] RETURNS [BreakWorldArchitecture.Address] ~ { IF IsNullMeadow[meadow: meadow] THEN { ERROR Shepherd.Cant[message: "AddressFromMeadow[nullMeadow]"]; }; RETURN [meadow.address]; }; SizeFromMeadow: PRIVATE PROCEDURE [meadow: BreakWorldShepherd.Meadow] RETURNS [BreakWorldArchitecture.ByteSize] ~ { IF IsNullMeadow[meadow: meadow] THEN { ERROR Shepherd.Cant[message: "SizeFromMeadow[nullMeadow]"]; }; RETURN [meadow.byteSize]; }; MeadowFromPatch: PUBLIC PROCEDURE [patch: Shepherd.Patch] RETURNS [Shepherd.Meadow] ~ { meadow: Shepherd.Meadow ~ Shepherd.nullMeadow; IF IsNullPatch[patch: patch] THEN { ERROR Shepherd.Cant[message: "MeadowFromPatch[nullPatch]"]; }; RETURN [patch.meadow]; }; PatchAddressFromPatch: PUBLIC PROCEDURE [patch: BreakWorldShepherd.Patch] RETURNS [BreakWorldArchitecture.Address] ~ { IF IsNullPatch[patch: patch] THEN { ERROR Shepherd.Cant[message: "PatchAddressFromPatch[nullPatch]"]; }; RETURN [patch.patchAddress]; }; CodeAddressFromPatch: PUBLIC PROCEDURE [patch: BreakWorldShepherd.Patch] RETURNS [BreakWorldArchitecture.Address] ~ { IF IsNullPatch[patch: patch] THEN { ERROR Shepherd.Cant[message: "PatchAddressFromPatch[nullPatch]"]; }; RETURN [patch.codeAddress]; }; PatchFromPC: PUBLIC PROCEDURE [pc: BreakWorldArchitecture.Address] RETURNS [Shepherd.Patch] ~ { found: Shepherd.Patch _ Shepherd.nullPatch; IF pc.IsNullAddress[] THEN { ERROR Shepherd.Cant[message: "PatchFromPC[nullAddress]"]; }; { meadow: Shepherd.Meadow ~ MeadowFromPC[pc: pc]; targetPC: TargetArchitecture.Address ~ BreakWorldArchitecture.TargetAddressFromBreakWorldAddress[address: pc]; IF IsNullMeadow[meadow: meadow] THEN { ERROR Shepherd.Cant[message: "PatchFromPC[nullMeadow]"]; }; FOR patch: Shepherd.Patch _ FirstPatch[meadow: meadow], NextPatch[patch: patch] UNTIL Shepherd.IsNullPatch[patch: patch] DO { IF NOT IsAllocatedPatch[patch: patch] THEN { EXIT; }; IF targetPC = GetPatchPC[patch: patch] THEN { found _ patch; EXIT; }; } ENDLOOP; }; RETURN [found]; }; IsAllocatedPatch: PUBLIC PROCEDURE [patch: Shepherd.Patch] RETURNS [BOOLEAN] ~ { IF IsNullPatch[patch: patch] THEN { ERROR Shepherd.Cant[message: "IsAllocatedPatch[nullPatch]"]; }; { size: BreakWorldArchitecture.ByteSize ~ GetPatchSize[patch: patch]; RETURN [size # 0]; }; }; IsBusyPC: PUBLIC PROCEDURE [pc: BreakWorldArchitecture.Address] RETURNS [BOOLEAN] ~ { IF pc.IsNullAddress[] THEN { ERROR Shepherd.Cant[message: "IsBusyPC[nullAddress]"]; }; { patch: Shepherd.Patch ~ PatchFromPC[pc: pc]; isBusy: BOOLEAN ~ NOT IsNullPatch[patch: patch]; RETURN [isBusy]; }; }; <> NoRoom: PUBLIC ERROR [message: Shepherd.ErrorMessage] ~ CODE; NoMeadow: PUBLIC ERROR [message: Shepherd.ErrorMessage] ~ CODE; BusyMeadow: PUBLIC ERROR [message: Shepherd.ErrorMessage] ~ CODE; BusyPC: PUBLIC ERROR [message: Shepherd.ErrorMessage] ~ CODE; CantReach: PUBLIC ERROR [message: Shepherd.ErrorMessage] ~ CODE; Cant: PUBLIC ERROR [message: Shepherd.ErrorMessage] ~ CODE; <> NewMeadow: PRIVATE PROCEDURE [ address: BreakWorldArchitecture.Address, byteSize: BreakWorldArchitecture.ByteSize] RETURNS [Shepherd.Meadow] ~ { meadow: BreakWorldShepherd.Meadow ~ NEW[BreakWorldShepherd.MeadowRep _ [ address: address, byteSize: byteSize]]; RETURN [meadow]; }; NewPatch: PRIVATE PROCEDURE [ meadow: Shepherd.Meadow, address: BreakWorldArchitecture.Address] RETURNS [Shepherd.Patch] ~ { IF meadow.IsNullMeadow[] THEN { ERROR Shepherd.Cant[message: "NewPatch[nullMeadow]"]; }; IF address.IsNullAddress[] THEN { ERROR Shepherd.Cant[message: "NewPatch[nullAddress]"]; }; { PatchCodeOffset: PROCEDURE [] RETURNS [TargetArchitecture.Displacement] ~ TRUSTED { PatchStruct: TYPE ~ MACHINE DEPENDENT RECORD [ header: Shepherd.PatchHeaderStruct, code: TargetArchitecture.Instruction]; patchStruct: PatchStruct; patchStructAddress: LONG POINTER TO PatchStruct ~ @patchStruct; patchStructCard: CARD32 ~ LOOPHOLE[patchStructAddress]; codeAddress: LONG POINTER TO TargetArchitecture.Instruction ~ @patchStruct.code; codeCard: CARD32 ~ LOOPHOLE[codeAddress]; offset: TargetArchitecture.Displacement ~ (codeCard - patchStructCard) * BYTES[UNIT]; RETURN [offset]; }; codeAddress: BreakWorldArchitecture.Address ~ BreakWorldArchitecture.AddressFromDisplacement[ address: address, displacement: PatchCodeOffset[]]; patch: BreakWorldShepherd.Patch ~ NEW[BreakWorldShepherd.PatchRep _ [ meadow: meadow, patchAddress: address, codeAddress: codeAddress]]; RETURN [patch]; }; }; MeadowFromPC: PRIVATE PROCEDURE [pc: BreakWorldArchitecture.Address] RETURNS [Shepherd.Meadow] ~ { ENABLE BreakWorldArchitecture.Cant => ERROR Shepherd.Cant[message]; meadow: Shepherd.Meadow _ Shepherd.nullMeadow; IF pc.IsNullAddress[] THEN { ERROR Shepherd.Cant[message: "MeadowFromPC[nullAddress]"]; }; { patchArea: BreakWorldArchitecture.PatchArea ~ BreakWorldArchitecture.GetPatchArea[address: pc]; IF NOT BreakWorldArchitecture.IsNullPatchArea[patchArea: patchArea] THEN { meadow _ NewMeadow[address: patchArea.address, byteSize: patchArea.byteSize]; }; }; RETURN [meadow]; }; AllocatePatch: PRIVATE PROCEDURE [ meadow: Shepherd.Meadow, codeSize: BreakWorldArchitecture.ByteSize] RETURNS [Shepherd.Patch] ~ { patch: Shepherd.Patch _ Shepherd.nullPatch; IF meadow.IsNullMeadow[] THEN { ERROR Shepherd.Cant[message: "AllocatePatch[nullMeadow]"]; }; { meadowAddress: TargetArchitecture.Address ~ BreakWorldArchitecture.TargetAddressFromBreakWorldAddress[ address: AddressFromMeadow[meadow: meadow]]; meadowSize: BreakWorldArchitecture.ByteSize ~ SizeFromMeadow[meadow: meadow]; patchSize: BreakWorldArchitecture.ByteSize ~ codeSize + BYTES[Shepherd.PatchHeaderStruct]; <> FOR newPatch: Shepherd.Patch _ FirstPatch[meadow: meadow], NextPatch[patch: newPatch] DO { IF Shepherd.IsNullPatch[patch: newPatch] THEN { ERROR Shepherd.NoRoom[message: "No more patches in the meadow"]; }; IF NOT IsAllocatedPatch[patch: newPatch] THEN { patchAddress: TargetArchitecture.Address ~ BreakWorldArchitecture.TargetAddressFromBreakWorldAddress[ address: PatchAddressFromPatch[patch: newPatch]]; patchDisplacement: TargetArchitecture.Displacement ~ TargetArchitecture.DisplacementFromAddresses[ here: meadowAddress, there: patchAddress]; requiredSize: BreakWorldArchitecture.ByteSize ~ patchDisplacement + patchSize; IF requiredSize > meadowSize THEN { ERROR Shepherd.NoRoom[message: "No room for patch in the meadow"]; }; SetPatchSize[patch: newPatch, size: patchSize]; patch _ newPatch; EXIT; }; } ENDLOOP; }; RETURN [patch]; }; DeallocatePatch: PRIVATE PROCEDURE [patch: Shepherd.Patch] RETURNS [] ~ { <> IF Shepherd.IsNullPatch[patch: patch] THEN { ERROR Shepherd.Cant[message: "DeallocatePatch[nullPatch]"]; }; RETURN; }; MeadowHeader: PRIVATE TYPE ~ MACHINE DEPENDENT RECORD [ monitorLock: PCRMonitorDefs.MonitorLock, <> next: TargetArchitecture.Address <> ]; FirstPatch: PRIVATE PROCEDURE [meadow: Shepherd.Meadow] RETURNS [Shepherd.Patch] ~ { patch: BreakWorldShepherd.Patch _ Shepherd.nullPatch; IF meadow.IsNullMeadow[] THEN { ERROR Shepherd.Cant[message: "FirstPatch[nullMeadow]"]; }; { <> meadowAddress: TargetArchitecture.Address ~ BreakWorldArchitecture.TargetAddressFromBreakWorldAddress[ address: AddressFromMeadow[meadow: meadow]]; meadowSize: BreakWorldArchitecture.ByteSize ~ SizeFromMeadow[meadow: meadow]; patchAddress: TargetArchitecture.Address ~ TargetArchitecture.AddressFromDisplacement[ address: meadowAddress, displacement: BYTES[MeadowHeader]]; patchDisplacement: TargetArchitecture.Displacement ~ TargetArchitecture.DisplacementFromAddresses[ here: meadowAddress, there: patchAddress]; IF BreakWorldArchitecture.ByteSize[patchDisplacement] < meadowSize THEN { patchBreakWorldAddress: BreakWorldArchitecture.Address ~ BreakWorldArchitecture.NewAddress[ breakWorld: BreakWorldArchitecture.BreakWorldFromBreakWorldAddress[ address: AddressFromMeadow[meadow: meadow]], address: patchAddress]; patch _ NewPatch[meadow: meadow, address: patchBreakWorldAddress]; }; }; RETURN [patch]; }; NextPatch: PRIVATE PROCEDURE [patch: Shepherd.Patch] RETURNS [Shepherd.Patch] ~ { nextPatch: BreakWorldShepherd.Patch _ Shepherd.nullPatch; IF patch.IsNullPatch[] THEN { ERROR Shepherd.Cant[message: "NextPatch[nullPatch]"]; }; { <> meadow: BreakWorldShepherd.Meadow ~ MeadowFromPatch[patch: patch]; meadowAddress: TargetArchitecture.Address ~ BreakWorldArchitecture.TargetAddressFromBreakWorldAddress[ address: AddressFromMeadow[meadow: meadow]]; meadowSize: BreakWorldArchitecture.ByteSize ~ SizeFromMeadow[meadow: meadow]; patchAddress: TargetArchitecture.Address ~ BreakWorldArchitecture.TargetAddressFromBreakWorldAddress[ address: PatchAddressFromPatch[patch: patch]]; patchSize: BreakWorldArchitecture.ByteSize ~ GetPatchSize[patch: patch]; newTargetAddress: TargetArchitecture.Address ~ TargetArchitecture.AddressFromDisplacement[ address: patchAddress, displacement: patchSize]; newDisplacement: TargetArchitecture.Displacement ~ TargetArchitecture.DisplacementFromAddresses[ here: meadowAddress, there: newTargetAddress]; IF BreakWorldArchitecture.ByteSize[newDisplacement] < meadowSize THEN { newBreakWorldAddress: BreakWorldArchitecture.Address ~ BreakWorldArchitecture.NewAddress[ breakWorld: BreakWorldArchitecture.BreakWorldFromBreakWorldAddress[ address: PatchAddressFromPatch[patch: patch]], address: newTargetAddress]; nextPatch _ NewPatch[meadow: meadow, address: newBreakWorldAddress]; }; }; RETURN [nextPatch]; }; GetPatchHeader: PRIVATE PROCEDURE [patch: Shepherd.Patch] RETURNS [Shepherd.PatchHeaderStruct] ~ { IF patch.IsNullPatch[] THEN { ERROR Shepherd.Cant[message: "GetPatchHeader[nullPatch]"]; }; { contents: TargetArchitecture.Contents ~ BreakWorldArchitecture.PeekContents[ address: PatchAddressFromPatch[patch: patch]]; patchHeaderStruct: Shepherd.PatchHeaderStruct ~ LOOPHOLE[contents]; RETURN [patchHeaderStruct]; }; }; SetPatchHeader: PRIVATE PROCEDURE [ patch: Shepherd.Patch, patchHeaderStruct: Shepherd.PatchHeaderStruct] RETURNS [] ~ { IF patch.IsNullPatch[] THEN { ERROR Shepherd.Cant[message: "SetPatchHeader[nullPatch]"]; }; { contents: TargetArchitecture.Contents ~ LOOPHOLE[patchHeaderStruct]; BreakWorldArchitecture.PokeContents[ address: PatchAddressFromPatch[patch: patch], contents: contents]; }; RETURN; }; GetPatchSize: PRIVATE PROCEDURE [patch: Shepherd.Patch] RETURNS [Shepherd.PatchSize] ~ { IF patch.IsNullPatch[] THEN { ERROR Shepherd.Cant[message: "GetPatchSize[nullPatch]"]; }; { patchHeaderStruct: Shepherd.PatchHeaderStruct ~ GetPatchHeader[patch: patch]; RETURN [patchHeaderStruct.size * BYTES[TargetArchitecture.Contents]]; }; }; SetPatchSize: PRIVATE PROCEDURE [patch: Shepherd.Patch, size: BreakWorldArchitecture.ByteSize] RETURNS [] ~ { IF patch.IsNullPatch[] THEN { ERROR Shepherd.Cant[message: "SetPatchSize[nullPatch]"]; }; { patchHeaderStruct: Shepherd.PatchHeaderStruct _ GetPatchHeader[patch: patch]; wordSize: Shepherd.PatchSize ~ (size + BYTES[TargetArchitecture.Contents] - 1) / BYTES[TargetArchitecture.Contents]; patchHeaderStruct.size _ wordSize; SetPatchHeader[patch: patch, patchHeaderStruct: patchHeaderStruct]; RETURN; }; }; GetPatchPC: PUBLIC PROCEDURE [patch: Shepherd.Patch] RETURNS [TargetArchitecture.Address] ~ { address: TargetArchitecture.Address _ TargetArchitecture.nullAddress; IF patch.IsNullPatch[] THEN { ERROR Shepherd.Cant[message: "GetPatchPC[nullPatch]"]; }; { relPC: Shepherd.RelPC ~ GetPatchRelPC[patch: patch]; IF relPC # TargetArchitecture.nullDisplacement THEN { patchAddress: TargetArchitecture.Address ~ BreakWorldArchitecture.TargetAddressFromBreakWorldAddress[ address: PatchAddressFromPatch[patch: patch]]; displacementBytes: TargetArchitecture.Displacement ~ relPC * BYTES[TargetArchitecture.Contents]; address _ TargetArchitecture.AddressFromDisplacement[ address: patchAddress, displacement: displacementBytes]; }; }; RETURN [address]; }; GetPatchRelPC: PRIVATE PROCEDURE [patch: Shepherd.Patch] RETURNS [Shepherd.RelPC] ~ { IF patch.IsNullPatch[] THEN { ERROR Shepherd.Cant[message: "GetPatchRelPC[nullPatch]"]; }; { patchHeaderStruct: Shepherd.PatchHeaderStruct ~ GetPatchHeader[patch: patch]; relPCLS: Shepherd.RelPCLS ~ patchHeaderStruct.relPCLS; relPCMS: Shepherd.RelPCMS ~ patchHeaderStruct.relPCMS; card: CARD32 ~ (relPCMS * (CARD32[Shepherd.lastRelPCLS] + 1)) + relPCLS; signBits: CARD32 ~ (LAST[CARD32] / ((CARD32[Shepherd.lastRelPCMS] + 1) * (CARD32[Shepherd.lastRelPCLS] + 1))) * ((CARD32[Shepherd.lastRelPCMS] + 1) * (CARD32[Shepherd.lastRelPCLS] + 1)); relPC: Shepherd.RelPC ~ IF card <= CARD32[Shepherd.lastRelPC] THEN card ELSE LOOPHOLE[card + signBits]; IF relPC NOT IN [Shepherd.firstRelPC .. Shepherd.lastRelPC] THEN { ERROR RuntimeError.BoundsFault; }; RETURN [relPC]; }; }; SetPatchPC: PUBLIC PROCEDURE [patch: Shepherd.Patch, pc: TargetArchitecture.Address] RETURNS [] ~ { relPC: Shepherd.RelPC _ TargetArchitecture.nullDisplacement; IF patch.IsNullPatch[] THEN { ERROR Shepherd.Cant[message: "GetPatchRelPC[nullPatch]"]; }; IF pc # TargetArchitecture.nullAddress THEN { patchAddress: TargetArchitecture.Address ~ BreakWorldArchitecture.TargetAddressFromBreakWorldAddress[ address: PatchAddressFromPatch[patch: patch]]; displacementBytes: TargetArchitecture.Displacement ~ TargetArchitecture.DisplacementFromAddresses[here: patchAddress, there: pc]; relPC _ displacementBytes / BYTES[TargetArchitecture.Contents]; IF relPC NOT IN [Shepherd.firstRelPC .. Shepherd.lastRelPC] THEN { ERROR Shepherd.CantReach[message: "Patch too far from PC"]; }; }; SetPatchRelPC[patch: patch, relPC: relPC]; RETURN; }; SetPatchRelPC: PRIVATE PROCEDURE [patch: Shepherd.Patch, relPC: Shepherd.RelPC] RETURNS [] ~ { IF patch.IsNullPatch[] THEN { ERROR Shepherd.Cant[message: "GetPatchRelPC[nullPatch]"]; }; { patchHeaderStruct: Shepherd.PatchHeaderStruct _ GetPatchHeader[patch: patch]; card: CARD32 ~ LOOPHOLE[relPC]; relPCLS: Shepherd.RelPCLS ~ card MOD (CARD32[Shepherd.lastRelPCLS] + 1); relPCMS: Shepherd.RelPCMS ~ (card / (CARD32[Shepherd.lastRelPCLS] + 1)) MOD (CARD32[Shepherd.lastRelPCMS] + 1); IF relPC NOT IN [Shepherd.firstRelPC .. Shepherd.lastRelPC] THEN { ERROR RuntimeError.BoundsFault; }; patchHeaderStruct.relPCLS _ relPCLS; patchHeaderStruct.relPCMS _ relPCMS; SetPatchHeader[patch: patch, patchHeaderStruct: patchHeaderStruct]; RETURN; }; }; }.