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 registers = new() { [0] = 0x40, [8] = 2, [0x5400] = 0x12005452, [0x5404] = 0x80005634 }; readonly Dictionary 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? OnTransmit; public int FailAllocationAt; public uint Bar = 0x10000000; public List 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(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(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(() => new E1000Device(hardware, new TestPlatform())); Assert.Equal(0, hardware.OutstandingDma); } [Fact] public void UnsupportedDeviceOrIoBarDoesNotPretendToBeE1000() { using var missing = new E1000HardwareFixture { Found = false }; Assert.Throws(() => new E1000Device(missing, new TestPlatform())); Assert.Equal(0, missing.OutstandingDma); using var io = new E1000HardwareFixture { Bar = 1 }; Assert.Throws(() => new E1000Device(io, new TestPlatform())); Assert.Equal(0, io.OutstandingDma); } }