238 lines
8.5 KiB
Plaintext
238 lines
8.5 KiB
Plaintext
|
//////////////////////////////////////////////////////////////////////////////
|
||
|
//
|
||
|
// 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
|
||
|
{
|
||
|
/// <summary>
|
||
|
/// Structure used to hold Virtual-To-Physical addresses for
|
||
|
/// data blocks passed into an NvMacRingBuffer instance.
|
||
|
/// </summary>
|
||
|
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);
|
||
|
}
|
||
|
}
|
||
|
}
|