using System.Runtime.InteropServices;
using XFESystem.Network;
namespace XFESystem.Network.Tests;
internal sealed unsafe class E1000HardwareFixture : IE1000Hardware, IDisposable
{
public const nuint RegisterBase = 0x20000000;
readonly Dictionary<uint, uint> registers = new() { [0] = 0x40, [8] = 2, [0x5400] = 0x12005452, [0x5404] = 0x80005634 };
readonly Dictionary<ulong, DmaMemory> memory = [];
ulong nextPhysical = 0x110000000;
uint command = 0x100; int allocations, txHead, rxHead;
public bool Found = true, CompleteTx = true;
public bool LinkUp = true, ResetStuck;
public Action? PciAccess;
public Action<byte[]>? OnTransmit;
public int FailAllocationAt;
public uint Bar = 0x10000000;
public List<byte[]> Transmitted { get; } = [];
public int OutstandingDma => memory.Count;
public uint Register(uint offset) => registers.GetValueOrDefault(offset);
public uint ReadPci(uint bus, uint device, uint function, uint offset)
{
PciAccess?.Invoke();
if (!Found || bus != 0 || device != 3 || function != 0) return uint.MaxValue;
return offset switch { 0 => 0x100e8086, 4 => command | 0xf9000000, 0x10 => Bar, _ => 0 };
}
public void WritePci(uint bus, uint device, uint function, uint offset, uint value)
{ Assert.Equal(4u, offset); Assert.Equal(0u, value & 0xffff0000); command = value; }
public nuint MapMmio(ulong physical, uint bytes)
{ Assert.Equal(Bar & 0xfffffff0u, physical); Assert.Equal(0x20000u, bytes); return RegisterBase; }
public uint ReadMmio(nuint address) => (uint)(address - RegisterBase) == 8 && !LinkUp ? 0 : Register((uint)(address - RegisterBase));
public void WriteMmio(nuint address, uint value)
{
uint offset = (uint)(address - RegisterBase); registers[offset] = offset == 0 && !ResetStuck ? value & ~(1u << 26) : value;
if (offset != 0x3818 || !CompleteTx) return;
while (txHead != value)
{
byte* descriptor = Pointer(((ulong)Register(0x3804) << 32) | Register(0x3800)) + txHead * 16;
byte* buffer = Pointer(*(ulong*)descriptor); int length = *(ushort*)(descriptor + 8);
Assert.Equal(0x0b, descriptor[11]); byte[] frame = new ReadOnlySpan<byte>(buffer, length).ToArray(); Transmitted.Add(frame); OnTransmit?.Invoke(frame);
descriptor[12] = 1; txHead = (txHead + 1) % 32;
}
}
public DmaMemory AllocateDma(uint pages)
{
if (++allocations == FailAllocationAt) return default;
nuint address = (nuint)NativeMemory.AlignedAlloc(pages * 4096, 4096);
var result = new DmaMemory(address, nextPhysical, pages); memory.Add(nextPhysical, result); nextPhysical += (ulong)pages * 4096;
return result;
}
public void FreeDma(DmaMemory value)
{
Assert.Equal(0u, command & 4); Assert.True(memory.Remove(value.PhysicalAddress)); NativeMemory.AlignedFree((void*)value.Address);
}
byte* Pointer(ulong physical)
{
foreach (DmaMemory value in memory.Values)
if (physical >= value.PhysicalAddress && physical - value.PhysicalAddress < value.Pages * 4096UL)
return (byte*)(value.Address + (nuint)(physical - value.PhysicalAddress));
throw new InvalidOperationException("The controller used an unowned physical DMA address.");
}
public void Receive(byte[] frame, byte errors = 0, bool completePacket = true)
{
byte* descriptor = Pointer(((ulong)Register(0x2804) << 32) | Register(0x2800)) + rxHead * 16;
byte* buffer = Pointer(*(ulong*)descriptor); frame.CopyTo(new Span<byte>(buffer, 2048));
*(ushort*)(descriptor + 8) = (ushort)frame.Length; descriptor[13] = errors; descriptor[12] = completePacket ? (byte)3 : (byte)1;
rxHead = (rxHead + 1) % 64;
}
public void Dispose()
{ foreach (DmaMemory value in memory.Values) NativeMemory.AlignedFree((void*)value.Address); memory.Clear(); }
}
public sealed class E1000Tests
{
[Fact]
public void RealDescriptorLayoutUsesOwnedPhysicalAddressesAndReturnsExactBytes()
{
using var hardware = new E1000HardwareFixture(); var clock = new TestPlatform();
using (var device = new E1000Device(hardware, clock))
{
Assert.Equal("525400123456", Convert.ToHexString(device.MacAddress));
Assert.Equal(1024u, hardware.Register(0x2808)); Assert.Equal(512u, hardware.Register(0x3808));
Assert.NotEqual(0u, hardware.Register(0x2804)); Assert.NotEqual(0u, hardware.Register(0x3804));
Assert.Equal(63u, hardware.Register(0x2818));
byte[] frame = Enumerable.Range(0, 60).Select(i => (byte)i).ToArray();
Assert.True(device.Send(frame, clock.Milliseconds + 100)); Assert.Equal(frame, Assert.Single(hardware.Transmitted));
hardware.Receive(frame); byte[] output = new byte[2048]; Assert.Equal(60, device.Receive(output));
Assert.Equal(frame, output[..60]); Assert.Equal(0, device.Receive(output));
}
Assert.Equal(0, hardware.OutstandingDma);
}
[Fact]
public void CorruptOrIncompleteReceiveDescriptorsAreReturnedWithoutDeliveringBytes()
{
using var hardware = new E1000HardwareFixture(); using var device = new E1000Device(hardware, new TestPlatform());
hardware.Receive(new byte[60], errors: 1); Assert.Equal(-1, device.Receive(new byte[2048]));
hardware.Receive(new byte[60], completePacket: false); Assert.Equal(-1, device.Receive(new byte[2048]));
hardware.Receive(new byte[60]); Assert.Equal(60, device.Receive(new byte[2048])); Assert.Equal(2u, hardware.Register(0x2818));
}
[Fact]
public void RingWrapDoesNotReuseTheWrongTransmitBuffer()
{
using var hardware = new E1000HardwareFixture(); using var device = new E1000Device(hardware, new TestPlatform());
for (int i = 0; i < 70; i++) { byte[] frame = Enumerable.Repeat((byte)i, 60).ToArray(); Assert.True(device.Send(frame, 100)); }
Assert.Equal(70, hardware.Transmitted.Count);
for (int i = 0; i < 70; i++) Assert.All(hardware.Transmitted[i], value => Assert.Equal((byte)i, value));
}
[Fact]
public void NoTransmitCompletionTimesOutWithoutReportingSuccess()
{
using var hardware = new E1000HardwareFixture(); var clock = new TestPlatform();
using var device = new E1000Device(hardware, clock); hardware.CompleteTx = false;
Assert.False(device.Send(new byte[60], 40)); Assert.Equal(40, clock.Milliseconds);
}
[Fact]
public void AllocationFailureStopsBusMasteringBeforeReleasingEarlierRings()
{
using var hardware = new E1000HardwareFixture { FailAllocationAt = 3 };
Assert.Throws<IOException>(() => new E1000Device(hardware, new TestPlatform())); Assert.Equal(0, hardware.OutstandingDma);
}
[Fact]
public void UnsupportedDeviceOrIoBarDoesNotPretendToBeE1000()
{
using var missing = new E1000HardwareFixture { Found = false };
Assert.Throws<IOException>(() => new E1000Device(missing, new TestPlatform())); Assert.Equal(0, missing.OutstandingDma);
using var io = new E1000HardwareFixture { Bar = 1 };
Assert.Throws<IOException>(() => new E1000Device(io, new TestPlatform())); Assert.Equal(0, io.OutstandingDma);
}
}
using System.Runtime.InteropServices;
using XFESystem.Network;
namespace XFESystem.Network.Tests;
internal sealed unsafe class E1000HardwareFixture : IE1000Hardware, IDisposable
{
public const nuint RegisterBase = 0x20000000;
readonly Dictionary<uint, uint> registers = new() { [0] = 0x40, [8] = 2, [0x5400] = 0x12005452, [0x5404] = 0x80005634 };
readonly Dictionary<ulong, DmaMemory> memory = [];
ulong nextPhysical = 0x110000000;
uint command = 0x100; int allocations, txHead, rxHead;
public bool Found = true, CompleteTx = true;
public bool LinkUp = true, ResetStuck;
public Action? PciAccess;
public Action<byte[]>? OnTransmit;
public int FailAllocationAt;
public uint Bar = 0x10000000;
public List<byte[]> Transmitted { get; } = [];
public int OutstandingDma => memory.Count;
public uint Register(uint offset) => registers.GetValueOrDefault(offset);
public uint ReadPci(uint bus, uint device, uint function, uint offset)
{
PciAccess?.Invoke();
if (!Found || bus != 0 || device != 3 || function != 0) return uint.MaxValue;
return offset switch { 0 => 0x100e8086, 4 => command | 0xf9000000, 0x10 => Bar, _ => 0 };
}
public void WritePci(uint bus, uint device, uint function, uint offset, uint value)
{ Assert.Equal(4u, offset); Assert.Equal(0u, value & 0xffff0000); command = value; }
public nuint MapMmio(ulong physical, uint bytes)
{ Assert.Equal(Bar & 0xfffffff0u, physical); Assert.Equal(0x20000u, bytes); return RegisterBase; }
public uint ReadMmio(nuint address) => (uint)(address - RegisterBase) == 8 && !LinkUp ? 0 : Register((uint)(address - RegisterBase));
public void WriteMmio(nuint address, uint value)
{
uint offset = (uint)(address - RegisterBase); registers[offset] = offset == 0 && !ResetStuck ? value & ~(1u << 26) : value;
if (offset != 0x3818 || !CompleteTx) return;
while (txHead != value)
{
byte* descriptor = Pointer(((ulong)Register(0x3804) << 32) | Register(0x3800)) + txHead * 16;
byte* buffer = Pointer(*(ulong*)descriptor); int length = *(ushort*)(descriptor + 8);
Assert.Equal(0x0b, descriptor[11]); byte[] frame = new ReadOnlySpan<byte>(buffer, length).ToArray(); Transmitted.Add(frame); OnTransmit?.Invoke(frame);
descriptor[12] = 1; txHead = (txHead + 1) % 32;
}
}
public DmaMemory AllocateDma(uint pages)
{
if (++allocations == FailAllocationAt) return default;
nuint address = (nuint)NativeMemory.AlignedAlloc(pages * 4096, 4096);
var result = new DmaMemory(address, nextPhysical, pages); memory.Add(nextPhysical, result); nextPhysical += (ulong)pages * 4096;
return result;
}
public void FreeDma(DmaMemory value)
{
Assert.Equal(0u, command & 4); Assert.True(memory.Remove(value.PhysicalAddress)); NativeMemory.AlignedFree((void*)value.Address);
}
byte* Pointer(ulong physical)
{
foreach (DmaMemory value in memory.Values)
if (physical >= value.PhysicalAddress && physical - value.PhysicalAddress < value.Pages * 4096UL)
return (byte*)(value.Address + (nuint)(physical - value.PhysicalAddress));
throw new InvalidOperationException("The controller used an unowned physical DMA address.");
}
public void Receive(byte[] frame, byte errors = 0, bool completePacket = true)
{
byte* descriptor = Pointer(((ulong)Register(0x2804) << 32) | Register(0x2800)) + rxHead * 16;
byte* buffer = Pointer(*(ulong*)descriptor); frame.CopyTo(new Span<byte>(buffer, 2048));
*(ushort*)(descriptor + 8) = (ushort)frame.Length; descriptor[13] = errors; descriptor[12] = completePacket ? (byte)3 : (byte)1;
rxHead = (rxHead + 1) % 64;
}
public void Dispose()
{ foreach (DmaMemory value in memory.Values) NativeMemory.AlignedFree((void*)value.Address); memory.Clear(); }
}
public sealed class E1000Tests
{
[Fact]
public void RealDescriptorLayoutUsesOwnedPhysicalAddressesAndReturnsExactBytes()
{
using var hardware = new E1000HardwareFixture(); var clock = new TestPlatform();
using (var device = new E1000Device(hardware, clock))
{
Assert.Equal("525400123456", Convert.ToHexString(device.MacAddress));
Assert.Equal(1024u, hardware.Register(0x2808)); Assert.Equal(512u, hardware.Register(0x3808));
Assert.NotEqual(0u, hardware.Register(0x2804)); Assert.NotEqual(0u, hardware.Register(0x3804));
Assert.Equal(63u, hardware.Register(0x2818));
byte[] frame = Enumerable.Range(0, 60).Select(i => (byte)i).ToArray();
Assert.True(device.Send(frame, clock.Milliseconds + 100)); Assert.Equal(frame, Assert.Single(hardware.Transmitted));
hardware.Receive(frame); byte[] output = new byte[2048]; Assert.Equal(60, device.Receive(output));
Assert.Equal(frame, output[..60]); Assert.Equal(0, device.Receive(output));
}
Assert.Equal(0, hardware.OutstandingDma);
}
[Fact]
public void CorruptOrIncompleteReceiveDescriptorsAreReturnedWithoutDeliveringBytes()
{
using var hardware = new E1000HardwareFixture(); using var device = new E1000Device(hardware, new TestPlatform());
hardware.Receive(new byte[60], errors: 1); Assert.Equal(-1, device.Receive(new byte[2048]));
hardware.Receive(new byte[60], completePacket: false); Assert.Equal(-1, device.Receive(new byte[2048]));
hardware.Receive(new byte[60]); Assert.Equal(60, device.Receive(new byte[2048])); Assert.Equal(2u, hardware.Register(0x2818));
}
[Fact]
public void RingWrapDoesNotReuseTheWrongTransmitBuffer()
{
using var hardware = new E1000HardwareFixture(); using var device = new E1000Device(hardware, new TestPlatform());
for (int i = 0; i < 70; i++) { byte[] frame = Enumerable.Repeat((byte)i, 60).ToArray(); Assert.True(device.Send(frame, 100)); }
Assert.Equal(70, hardware.Transmitted.Count);
for (int i = 0; i < 70; i++) Assert.All(hardware.Transmitted[i], value => Assert.Equal((byte)i, value));
}
[Fact]
public void NoTransmitCompletionTimesOutWithoutReportingSuccess()
{
using var hardware = new E1000HardwareFixture(); var clock = new TestPlatform();
using var device = new E1000Device(hardware, clock); hardware.CompleteTx = false;
Assert.False(device.Send(new byte[60], 40)); Assert.Equal(40, clock.Milliseconds);
}
[Fact]
public void AllocationFailureStopsBusMasteringBeforeReleasingEarlierRings()
{
using var hardware = new E1000HardwareFixture { FailAllocationAt = 3 };
Assert.Throws<IOException>(() => new E1000Device(hardware, new TestPlatform())); Assert.Equal(0, hardware.OutstandingDma);
}
[Fact]
public void UnsupportedDeviceOrIoBarDoesNotPretendToBeE1000()
{
using var missing = new E1000HardwareFixture { Found = false };
Assert.Throws<IOException>(() => new E1000Device(missing, new TestPlatform())); Assert.Equal(0, missing.OutstandingDma);
using var io = new E1000HardwareFixture { Bar = 1 };
Assert.Throws<IOException>(() => new E1000Device(io, new TestPlatform())); Assert.Equal(0, io.OutstandingDma);
}
}