////////////////////////////////////////////////////////////////////////////// // // Microsoft Research Singularity // // Copyright (c) Microsoft Corporation. All rights reserved. // // File: IntelRingBuffer.cs // // Notes: // // The underlying implementation of the ring buffer code. IntelRxRingBuffer or // IntelTxRingBuffer should be used for actual implementation of these buffers. using Microsoft.Contracts; using Microsoft.Singularity.Io; using Microsoft.Singularity.V1.Services; using System; namespace Microsoft.Singularity.Drivers.Network.Intel { internal class IntelRingBuffer { /// /// Structure used to hold Virtual-To-Physical addresses for /// data blocks passed into an NvMacRingBuffer instance. /// internal struct MapEntry { internal UIntPtr VirtualAddress; internal UIntPtr PhysicalAddress; internal void Initialize(UIntPtr va, UIntPtr pa) { this.VirtualAddress = va; this.PhysicalAddress = pa; } } private const uint DescriptorBytes = 16; private const uint AlignmentBytes = 64; // cache line alignment private IoMemory! region; private MapEntry []! mapEntries; uint capacity; uint head; uint tail; uint count; internal IntelRingBuffer(uint capacity) requires capacity > 0 && IsPowerOf2(capacity); { UIntPtr length = new UIntPtr(capacity * DescriptorBytes); IoMemory! region = (!)IoMemory.AllocatePhysical(length, AlignmentBytes); PlatformService.SetCacheAttributes(region.VirtualAddress, (UIntPtr)region.Length, false, false); this.region = region; this.mapEntries = new MapEntry [capacity]; this.capacity = capacity; this.head = 0; this.tail = 0; this.count = 0; // Clear out ring buffer for (int i = 0; i < region.Length; i += 4) { region.Write32(i, 0); } } internal void Reset() requires this.Count == 0; { this.head = 0; this.tail = 0; for (int i = 0; i < this.region.Length; i += 4) { this.region.Write32(i, 0); } } internal void Push(UIntPtr fragmentVirtualAddress, ulong controlBits) requires this.Count < (this.Capacity - 1); // must keep one slot empty to prevent // head==tail which would signal an // empty buffer. ensures this.Count == old(this.Count) + 1; { UIntPtr pa = GetPhysicalAddress(fragmentVirtualAddress); this.mapEntries[this.head].Initialize(fragmentVirtualAddress, pa); WriteDescriptor(this.head, pa, controlBits); this.head = (this.head + 1) & (this.Capacity - 1); this.count++; } internal void Pop() requires this.Count > 0; ensures this.Count == old(this.count) - 1; { this.tail = (this.tail + 1) & (this.Capacity - 1); this.count--; } // Returns true if hardware is done with this descriptor. internal bool Peek() { if (this.count > 0) { uint cb = this.region.Read32((int) (this.tail * DescriptorBytes + 12)); return (cb & DescriptorStat.DESCRIPTOR_DONE) != 0; } else { return false; } } // Returns true if hardware is done with this descriptor. internal bool Peek(out UIntPtr fragmentVirtualAddress, out ulong controlBits) { if (this.count > 0) { UIntPtr pa; ReadDescriptor(this.tail, out pa, out controlBits); DebugStub.Assert(pa == this.mapEntries[tail].PhysicalAddress); fragmentVirtualAddress = this.mapEntries[tail].VirtualAddress; return ((controlBits & Descriptor.DESCRIPTOR_DONE) != 0); } else { fragmentVirtualAddress = 0; controlBits = 0; return false; } } private void WriteDescriptor(uint index, UIntPtr physicalAddr, ulong controlBits) requires (index >= 0 && index <= this.Capacity); { ulong addrPtr = physicalAddr.ToUInt64(); int descriptorOffset = (int)(index * DescriptorBytes); this.region.Write64(descriptorOffset, addrPtr); this.region.Write64(descriptorOffset + 8, controlBits); #if false PlatformService.CleanAndInvalidateDCache( ((UIntPtr)this.region.PhysicalAddress.Value) + descriptorOffset, DescriptorBytes); #endif // false } private void ReadDescriptor(uint index, out UIntPtr physicalAddr, out ulong controlBits) { int descriptorOffset = (int)(index * DescriptorBytes); #if false PlatformService.InvalidateDCache( ((UIntPtr)this.region.PhysicalAddress.Value) + descriptorOffset, DescriptorBytes); #endif // false ulong addrPtr = this.region.Read64(descriptorOffset); controlBits = this.region.Read64(descriptorOffset + 8); physicalAddr = new UIntPtr(addrPtr); } internal static UIntPtr GetPhysicalAddress(UIntPtr va) { UIntPtr pa; // Physical address UIntPtr paLeft; // Bytes remaining on physical page if (!DeviceService.GetDmaPhysicalAddress(va, out pa, out paLeft) || pa == UIntPtr.Zero || paLeft < Intel.IEEE8023FrameBytes) { throw new ApplicationException("Bad DMA pointer"); } return pa; } internal void Dump(string! preamble, uint count) { if (count > this.capacity) { count = this.capacity; } Intel.DebugWriteLine("Head {0} Tail {1}\n", __arglist(this.Head, this.Tail)); for (uint i = 0; i < count; i++) { ulong address = this.region.Read64((int)(i * 16)); ulong fields = this.region.Read64((int)(i * 16 + 8)); Intel.DebugWriteLine("{0}: [{1}] Address {2:x16} Sp={3:x4} Err={4:x1} Sta={5:x2} Checksum {6:x4} Length {7:x4}", __arglist(preamble, i, address, (fields >> 48) & 0xffff, (fields >> 40) & 0xff, (fields >> 32) & 0xff, (fields >> 16) & 0xffff, fields & 0xffff)); } } internal UIntPtr BaseAddress { get { return this.region.PhysicalAddress.Value; } } [Pure] internal uint Capacity { get { return this.capacity; } } // Note, saves one descriptor so that ring never gets completely full, which would // lead to head == tail, which the hardware takes as meaning the ring is empty. [Pure] internal uint Free { get { return (this.capacity - 1) - this.count; } } [Pure] internal bool IsFull { get { return (this.Free == 0); } } [Pure] internal uint Count { get { return this.count; } } [Pure] internal uint Tail { get { return this.tail; } } [Pure] internal uint Head { get { return this.head; } } [Pure] internal uint DescLength { get { return (this.Capacity * DescriptorBytes); } } [Pure] internal static bool IsPowerOf2(uint n) { return (n > 0) && ((n & (n - 1)) == 0); } } }