XFE Git
XFE Studio Git
Git 首页 全局搜索
XFE 主站 文档 NuGet

XFESystem

【Rust】XFE系统,基于Rust和汇编

公开
关注 0 Fork 0 Star 0
UTF-8
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);
    }
}