//////////////////////////////////////////////////////////////////////////////// // // Microsoft Research Singularity // // Copyright (c) Microsoft Corporation. All rights reserved. // // File: EndpointCore.cs // // HACK: Because we currently compile this file as part of the Kernel with C#, we can't // make EndpointCore a rep struct, which is necessary for inheriting from it. // The compiler and Bartok recognize EndpointCore as special and treat it as // unsealed. // This hack can be removed, once we compile it with Sing#. // DON'T change the name until then! using System; using System.Runtime.CompilerServices; using System.Runtime.InteropServices; using System.Diagnostics; using Microsoft.Singularity.Channels; using Microsoft.Singularity.Security; using Microsoft.Singularity.Memory; namespace Microsoft.Singularity.Channels { using Microsoft.Singularity.V1.Threads; using Microsoft.Singularity.V1.Security; using Microsoft.Singularity.V1.Services; using Microsoft.Singularity.V1.Types; using Allocation = Microsoft.Singularity.Memory.SharedHeap.Allocation; using System.Threading; using SharedHeap = Microsoft.Singularity.Memory.SharedHeap; [CLSCompliant(false)] public enum EndpointCoreEvent : ushort { Connect = 4, TransferToProcess = 5, } [CLSCompliant(false)] [CCtorIsRunDuringStartup] unsafe public struct EndpointCore { //////////////////////////////////////////////////////////////////// // Fields //////////////////////////////////////////////////////////////////// // // NOTE: The fields specified here must match those in: // Kernel/Singularity/Channels/EndpointCore.cs // Kernel/Singularity/V1/Services/ChannelServices.cs // Libraries/Singuarity.V1/Services/ChannelServices.cs /// /// Handle to the actual message delivery mechanism /// private DeliveryHandle deliveryHandle; /// /// Event handle in case this endpoint is part of a collection /// private AutoResetEventHandle collectionEvent; // // These "cached" fields are directly accessable by user programs, // but are not trusted by the kernel (as they could be modified by untrusted // code). The kernel relies on the trusted shadow copies held in the // deliveryImpl object, but updates these fields to reflect any changes to user // apps. // /// /// Event on which sends are signaled to this endpoint. /// The handle is owned by the kernel, since the endpoint can move. /// The kernel deallocates the handle when the channel is deallocated. /// NOTE: stays valid until the entire channel gets collected. /// private AutoResetEventHandle cachedMessageEvent; /// /// Closed flag /// private bool cachedClosed; /// /// Contains the process id of the process currently owning this end of the /// channel. /// private int cachedOwnerProcessId; /// /// Contains the channelId (positive on the EXP endpoint, negative on the imp endpoint) /// private int cachedChannelId; /// /// Whether to marshall or not /// private bool cachedMarshall; /// /// Points to the peer endpoint /// private Allocation* /*EndpointCore* opt(ExHeap)*/ cachedPeer; /// /// If true then the peer state can be queried directly from cachedPeer /// private bool peerStateValid; //////////////////////////////////////////////////////////////////// // Static Fields //////////////////////////////////////////////////////////////////// /// /// Number of open channels (using any delivery mechanism) /// private static int openChannelCount; // TODO open channel count!!! /// /// Channel id generator used to create unique channel id's accross delivery /// mechanisms. /// private static int channelIdGenerator; //////////////////////////////////////////////////////////////////// // Types //////////////////////////////////////////////////////////////////// public enum DelegationState {None, ByCapability, ByMediation, Mediated}; //////////////////////////////////////////////////////////////////// // Methods //////////////////////////////////////////////////////////////////// /// /// Retrieve underlying delivery mechanism for a given endpoint allocation pointer /// [NoHeapAllocation] internal static DeliveryImpl AllocationEndpointDeliveryImpl( Allocation* /*EndpointCore* opt(ExHeap)!*/ endpoint) { EndpointCore * ep = (EndpointCore *) Allocation.GetData(endpoint); if (ep == null) { return null; } else { return ep->EndpointDeliveryImpl; } } /// /// Retrieve underlying delivery mechanism for a given endpoint allocation pointer /// using GetDataUnchecked. /// [NoHeapAllocation] internal static DeliveryImpl AllocationEndpointDeliveryImplUnchecked( Allocation* /*EndpointCore* opt(ExHeap)!*/ endpoint) { EndpointCore * ep = (EndpointCore *) Allocation.GetDataUnchecked(endpoint); if (ep == null) { return null; } else { return ep->EndpointDeliveryImpl; } } /// /// Retrieve underlying delivery mechanism for this endpoint /// internal DeliveryImpl EndpointDeliveryImpl { [NoHeapAllocation] get { if (deliveryHandle != DeliveryHandle.Zero) { return DeliveryHandle.GetImpl(deliveryHandle); } else { unsafe { DebugStub.WriteLine("deliveryHandle value is {0,8:x}\n", __arglist((uint) deliveryHandle.id)); } DebugStub.Break(); return null; } } } /// /// Performs the initialization of the core part of each endpoint and cross links /// them to form a channel. Uses the standard shared address space delivery /// mechanism. /// public static void Connect( Allocation* /*EndpointCore* opt(ExHeap)!*/ imp, Allocation* /*EndpointCore* opt(ExHeap)!*/ exp, Allocation* /*EndpointCore* opt(ExHeap)!*/ securityEp) { Connect(imp, exp, securityEp, SingleAddrSpaceDelivery.ImplName); } /// /// Performs the initialization of the core part of each endpoint and cross links /// them to form a channel. Uses the given delivery mechanism. /// public static void Connect( Allocation* /*EndpointCore* opt(ExHeap)!*/ imp, Allocation* /*EndpointCore* opt(ExHeap)!*/ exp, Allocation* /*EndpointCore* opt(ExHeap)!*/ securityEp, string deliveryImplType) { if (imp == null || exp == null) { throw new ApplicationException("Connect called with null endpoints"); } EndpointCore* impData = (EndpointCore*)Allocation.GetData(imp); EndpointCore* expData = (EndpointCore*)Allocation.GetData(exp); if (impData == null || expData == null) { throw new ApplicationException("SharedHeap.GetData return null"); } Tracing.Log(Tracing.Debug, "connect {0:x8} and {1:x8}", (UIntPtr)imp, (UIntPtr)exp); if (!(DeliveryHandle.Create(deliveryImplType, imp, out impData->deliveryHandle) && DeliveryHandle.Create(deliveryImplType, exp, out expData->deliveryHandle))) { throw new EndpointCoreException( "Error trying to create EndpointCore using \"" + deliveryImplType + "\" delivery implementation"); } #if false DebugStub.Print("imp handle {0,8:x} exp handle {1,8:x}\n", __arglist((uint)impData->deliveryHandle.id, (uint)expData->deliveryHandle.id)); #endif DeliveryImpl impDi = impData->EndpointDeliveryImpl; DeliveryImpl expDi = expData->EndpointDeliveryImpl; VTable.Assert(impDi != null && expDi != null); impDi.Connect(expDi, securityEp); // keep track of how many channels are open Interlocked.Increment(ref openChannelCount); #if CHANNEL_COUNT PerfCounters.IncrementChannelsCreated(); #endif Monitoring.Log(Monitoring.Provider.EndpointCore, (ushort)EndpointCoreEvent.Connect, 0, (uint)expData->ChannelId, 0, 0, 0, 0); } /// /// Set this end to closed /// public void Close() { DeliveryImpl di = EndpointDeliveryImpl; if (di != null) { di.Close(); } } [NoHeapAllocation] public bool Closed() { DeliveryImpl di = EndpointDeliveryImpl; if (di != null) { return di.Closed; } else { // endpoint has not yet been connected return true; } } /// /// Closes this end of the channel and frees associated resources, EXCEPT the block /// of memory for this endpoint. It must be released by the caller. Sing# does this /// for the programmer. /// /// This runs in the kernel to avoid a race condition with Process.Stop. /// public bool Dispose() { DeliveryImpl di = EndpointDeliveryImpl; if (di != null) { return EndpointDeliveryImpl.Dispose(); } else { return true; // endpoint was not yet connected } } /// /// Explicitly frees this end of the channel. /// /// Since both threads on the channel could try to do this simultaneously, /// we use the ref counting on the underlying endpoints to let the last /// free operation (the one pulling the ref count to 0) to free the associated /// event. /// public unsafe static void Free(Allocation* /*EndpointCore* opt(ExHeap)!*/ endpoint) { // Use unchecked GetData here, since this may be called from the // cleanup threading running in the kernel. EndpointCore * ep = ((EndpointCore*)Allocation.GetDataUnchecked(endpoint)); if (ep->deliveryHandle != DeliveryHandle.Zero) { if (DeliveryHandle.Free(ep->deliveryHandle)) { Interlocked.Decrement(ref openChannelCount); } } else { // was never connected, just free this endpoint AutoResetEventHandle areHandle = ep->cachedMessageEvent; SharedHeap.KernelSharedHeap.Free(endpoint, SharedHeap.CurrentProcessSharedHeap.EndpointPeerOwnerId); if (areHandle.id != UIntPtr.Zero) { Process.kernelProcess.ReleaseHandle(areHandle.id); } } } /// /// The event to wait for messages on this endpoint. Used by Select. /// public SyncHandle GetWaitHandle() { DeliveryImpl di = EndpointDeliveryImpl; VTable.Assert(di != null); return di.MessageEvent; } /// /// Get the AutoResetEventHandle /// public AutoResetEventHandle GetAreHandle() { DeliveryImpl di = EndpointDeliveryImpl; VTable.Assert(di != null); return di.AreHandle; } /// /// Notify the peer of this endpoint that a message is ready. /// Notifies the set owner if this endpoint is part of a set. /// public void NotifyPeer() { DeliveryImpl di = EndpointDeliveryImpl; VTable.Assert(di != null); di.NotifyPeer(); } /// /// Used internally by the kernel to transfer an endpoint to a new owner /// /// Can be used to transfer ANY kind of shared heap data, not just endpoints. /// public static Allocation* MoveEndpoint(SharedHeap fromHeap, SharedHeap toHeap, Process newOwner, Allocation *ep) { return DeliveryImpl.MoveData(fromHeap, toHeap, newOwner, ep); } public static void AcceptDelegation(Allocation* /*EndpointCore* opt(ExHeap)!*/ imp, Allocation* /*EndpointCore* opt(ExHeap)!*/ exp, Allocation* /*EndpointCore* opt(ExHeap)!*/ ep) { DeliveryImpl impDi = AllocationEndpointDeliveryImpl(imp); DeliveryImpl expDi = AllocationEndpointDeliveryImpl(exp); DeliveryImpl epDi = AllocationEndpointDeliveryImpl(ep); VTable.Assert((impDi != null && expDi != null && epDi != null)); VTable.Assert((impDi.GetType() == expDi.GetType()) && (impDi.GetType() == epDi.GetType())); impDi.AcceptDelegation(expDi, epDi); } public void EnableDelegation(bool allowMediation) { EndpointDeliveryImpl.EnableDelegation(allowMediation); } public DelegationState OwnerDelegationState { [NoHeapAllocation] get { return EndpointDeliveryImpl.OwnerDelegationState; } } public PrincipalHandle OwnerPrincipalHandle { [NoHeapAllocation] get { return EndpointDeliveryImpl.OwnerPrincipalHandle; } } public PrincipalHandle PeerPrincipalHandle { [NoHeapAllocation] get { return EndpointDeliveryImpl.PeerPrincipalHandle; } } public int PeerReceiveCount { [NoHeapAllocation] get { DeliveryImpl di = EndpointDeliveryImpl; if (di != null) { return di.PeerReceiveCount; } else { // not yet connected, therefore no peer yet return 0; } } } //versions of begin update and marshallpointer that handle objects with pointers //greater than one level deep internal void BeginUpdate(byte* basep, byte* source, int* tagAddress, int msgSize) { DeliveryImpl di = EndpointDeliveryImpl; VTable.Assert(di != null); // DebugStub.Print("BeginUpdate delivery handle {0,8:x}\n", __arglist((uint)deliveryHandle.id)); di.BeginUpdate(basep, source, tagAddress, msgSize); } public void MarshallPointer(byte* basep, byte** target, SystemType type, byte* parent, int offset) { if (target == null) return; DeliveryImpl di = EndpointDeliveryImpl; // DebugStub.Print("Marshall Pointer delivery handle {0,8:x}\n", __arglist((uint)deliveryHandle.id)); VTable.Assert(di != null); di.MarshallPointer(basep, target, type, parent, offset); } public static PrincipalHandle TransferPrincipal(PrincipalHandle oldH, int processId, ref DelegationState delegationState) { Process p = Process.GetProcessByID(processId); if (p == null) throw new ApplicationException("Delegate endpoint process is null"); DelegationState newstate; Principal pr; switch (delegationState) { case DelegationState.ByMediation: pr = PrincipalImpl.NewDelegation( PrincipalImpl.MakePrincipal(oldH.val), p.Principal); newstate = DelegationState.Mediated; break; case DelegationState.ByCapability: pr = PrincipalImpl.NewDelegation( PrincipalImpl.MakePrincipal(oldH.val), p.Principal); newstate = DelegationState.None; break; case DelegationState.Mediated: pr = p.Principal; newstate = DelegationState.None; break; case DelegationState.None: default: pr = p.Principal; newstate = DelegationState.None; break; } delegationState = newstate; return new PrincipalHandle(pr.Val); } #if SINGULARITY_KERNEL && CHANNEL_COUNT internal static void IncreaseBytesSentCount(long bytes) { PerfCounters.AddBytesSent(bytes); } #endif // SINGULARITY_KERNEL /// /// Transfer the given Allocation block to the target endpoint /// public static void TransferBlockOwnership(Allocation* ptr, ref EndpointCore target) { Allocation.SetOwnerProcessId(ptr, target.cachedOwnerProcessId); // TODO MAKE THIS APROPRIATE TO BOTH SINGLE AND PAGED IMPLS DeliveryImpl di = target.EndpointDeliveryImpl; VTable.Assert(di != null); //Monitoring.Log(Monitoring.Provider.ChannelService, // (ushort)ChannelServiceEvent.TransferBlockOwnership, 0, // (uint)di.ChannelId, // (uint)di.ProcessId, // 0, 0, 0); #if CHANNEL_COUNT IncreaseBytesSentCount((long) Allocation.GetSize(ptr)); #endif Allocation.SetOwnerProcessId(ptr, di.ProcessId); } /// /// Transfer any contents that needs to be adjusted from the transferee to the target /// endpoint. /// // TODO: change "ref EndpointCore" to "EndpointCore" public static void TransferContentOwnership( ref EndpointCore transferee, ref EndpointCore target) { // TODO MAKE THIS APROPRIATE TO BOTH SINGLE AND PAGED IMPLS DeliveryImpl transfereeDi = transferee.EndpointDeliveryImpl; // XXX BUG? BUG? BUG? // targetDi = transferee.EndpointDeliveryImpl // should be: // targetDi = target.EndpointDeliveryImpl DeliveryImpl targetDi = transferee.EndpointDeliveryImpl; VTable.Assert((transfereeDi != null) && (targetDi != null)); //Monitoring.Log(Monitoring.Provider.ChannelService, // (ushort)ChannelServiceEvent.TransferContentOwnership, 0, // (uint)transfereeDi.ProcessId, // (uint)targetDi.ProcessId, // (uint)transfereeDi.ChannelId, // (uint)targetDi.ChannelId, // (uint)targetDi.Peer.ChannelId); int toProcessId = targetDi.ProcessId; transfereeDi.ProcessId = toProcessId; DelegationState newstate = transfereeDi.OwnerDelegationState; transfereeDi.OwnerPrincipalHandle = TransferPrincipal(transfereeDi.OwnerPrincipalHandle, toProcessId, ref newstate); transfereeDi.OwnerDelegationState = newstate; Allocation* transfereePeerAllocation = transfereeDi.Peer(); // also transfer the peer allocation Allocation.SetOwnerProcessId(transfereePeerAllocation, toProcessId); } public static int OpenChannelCount { [NoHeapAllocation] get { return openChannelCount; } } public static void IncOpenChannelCount () { Interlocked.Increment(ref openChannelCount); } public static void DecOpenChannelCount () { Interlocked.Decrement(ref openChannelCount); } // // These getter defer to the delivery implementation, as the kernel only // trusts the shadow versions held in Kernel memory space. // public bool PeerClosed() { DeliveryImpl di = EndpointDeliveryImpl; if (di != null) { return di.PeerClosed(); } else { // endpoint has not yet been connected return true; } } /// /// Return a handle for the peer /// public Allocation* Peer(out bool marshall) { DeliveryImpl di = EndpointDeliveryImpl; VTable.Assert(di != null); return di.Peer(out marshall); } public int ChannelId { [NoHeapAllocation] get { DeliveryImpl di = EndpointDeliveryImpl; VTable.Assert(di != null); return di.ChannelId; } } public int ProcessId { [NoHeapAllocation] get { DeliveryImpl di = EndpointDeliveryImpl; VTable.Assert(di != null); return di.ProcessId; } } public int ReceiveCount { get { DeliveryImpl di = EndpointDeliveryImpl; VTable.Assert(di != null); return di.ReceiveCount; } } public int PeerProcessId { [NoHeapAllocation] get { DeliveryImpl di = EndpointDeliveryImpl; VTable.Assert(di != null); return di.PeerProcessId; } } public DeliveryHandle dImpHandle { get { return deliveryHandle; } set { deliveryHandle = value; } } public AutoResetEventHandle CollectionEvent { get { return collectionEvent; } set { collectionEvent = value; } } internal static int GetNextChannelId() { return Interlocked.Increment(ref channelIdGenerator); } // // These methods only set the user accessable cached copies, real changes should // be made to the delivery implementation copies. // [NoHeapAllocation] public void SetCachedClose(bool closed) { cachedClosed = closed; } [NoHeapAllocation] public void SetCachedProcessId(int pid) { cachedOwnerProcessId = pid; } [NoHeapAllocation] public void SetCachedChannelId(int cid) { cachedChannelId = cid; } [NoHeapAllocation] public void SetCachedMarshall(bool marshall) { cachedMarshall = marshall; } [NoHeapAllocation] public void SetCachedMessageEvent(AutoResetEventHandle mh) { cachedMessageEvent = mh; } [NoHeapAllocation] public void SetCachedPeer(Allocation * /* EndpointCore */ peer) { cachedPeer = peer; } [NoHeapAllocation] public void SetPeerStateValid(bool valid) { peerStateValid = valid; } } public class EndpointCoreException: Exception { public EndpointCoreException (string message) : base(message) { } } }