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

XFESystem

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

公开
关注 0 Fork 0 Star 0
返回提交历史

XFEstudio/XFESystem

Add pinned NVIDIA command protocol components and offline oracle

46756fd
XFE工作室室长 <mail@xfegzs.com>
提交于

代码差异

19 个文件 +1001 -2
Added Source/XFESystem.Gpu.Nvidia/NvidiaBlackwellCommands.cs +95 -0
@@ -0,0 +1,95 @@
1 using System.Buffers.Binary;
2
3 namespace XFESystem.Gpu.Nvidia;
4
5 /// <summary>
6 /// Native NVIDIA method/GPFIFO encoding, derived from the MIT-licensed public
7 /// headers pinned in Tools/Gpu/NvidiaSources.lock.json. This emits GPU commands;
8 /// it does not emulate, execute or claim CUDA compatibility.
9 /// </summary>
10 public static class NvidiaBlackwellCommands
11 {
12 public const uint CopyClassA = 0xC9B5;
13 public const uint CopyClassB = 0xCAB5;
14 public const int Fill32CommandBytes = 72;
15 public const int GpfifoEntryBytes = 8;
16 public const uint GpPutOffset = 0x8C;
17
18 public static uint IncrementingMethodHeader(uint methodByteOffset, int subchannel, int valueCount)
19 {
20 if ((methodByteOffset & 3) != 0 || methodByteOffset > 0x3FFC || subchannel is < 0 or > 7 ||
21 valueCount is < 1 or > 0x1FFF || (ulong)methodByteOffset + (ulong)(valueCount - 1) * 4 > 0x3FFC)
22 throw new ArgumentOutOfRangeException(nameof(methodByteOffset), "Method fields or incremented method range do not fit.");
23 return 1U << 29 | (uint)valueCount << 16 | (uint)subchannel << 13 | methodByteOffset >> 2;
24 }
25
26 /// <summary>
27 /// Four-byte constant fill followed by a system-flushed release on the same
28 /// copy engine. The fence is a 32-bit mapped word; it is not a frontend-only
29 /// semaphore that could run ahead of asynchronous copy-engine work.
30 /// </summary>
31 public static int WriteFill32(Span<byte> destination, uint copyClass, NvidiaGpuRange target,
32 uint pixelCount, uint color, NvidiaGpuRange fence, uint completionPayload)
33 {
34 if (copyClass != CopyClassA && copyClass != CopyClassB) throw new ArgumentOutOfRangeException(nameof(copyClass));
35 target.RequireAlignment(4); fence.RequireAlignment(4);
36 if (pixelCount == 0 || (ulong)pixelCount * 4 > target.Length || fence.Length < 4 || completionPayload == 0)
37 throw new ArgumentOutOfRangeException(nameof(pixelCount));
38 if (target.Overlaps(fence)) throw new ArgumentException("Fill target overlaps its completion fence.");
39 if (destination.Length < Fill32CommandBytes) throw new ArgumentException("Pushbuffer is too short.", nameof(destination));
40
41 var writer = new MethodWriter(destination);
42 writer.Write(0x000, [copyClass]); // SET_OBJECT, fixed copy subchannel 4.
43 writer.Write(0x704, [color, 5U | 3U << 16]); // CONST_B, one four-byte destination component.
44 writer.Write(0x408, [(uint)(target.Address >> 32), (uint)target.Address]);
45 writer.Write(0x418, [pixelCount]);
46 // Non-pipelined; pitched source/destination; remap; system flush; PLC off;
47 // virtual destination, default copy type, no scrub or physical aperture.
48 writer.Write(0x300, [2U | 1U << 2 | 1U << 7 | 1U << 8 | 1U << 10 | 1U << 26]);
49 writer.Write(0x240, [(uint)(fence.Address >> 32), (uint)fence.Address, completionPayload]);
50 // No data transfer; system flush; one-word semaphore without timestamp.
51 writer.Write(0x300, [1U << 2 | 1U << 3 | 1U << 26]);
52 return writer.BytesWritten;
53 }
54
55 /// <summary>Set the 17 high pushbuffer-address bits before its ordinary entry.</summary>
56 public static void WriteSegmentBase(Span<byte> destination, NvidiaGpuRange pushbuffer)
57 {
58 pushbuffer.RequireAlignment(4);
59 if (destination.Length < GpfifoEntryBytes) throw new ArgumentException("Entry is too short.", nameof(destination));
60 BinaryPrimitives.WriteUInt32LittleEndian(destination, (uint)(pushbuffer.Address >> 40) << 8);
61 BinaryPrimitives.WriteUInt32LittleEndian(destination[4..], 4);
62 }
63
64 public static void WriteGpfifoEntry(Span<byte> destination, NvidiaGpuRange pushbuffer, int commandBytes)
65 {
66 pushbuffer.RequireAlignment(4);
67 if (commandBytes <= 0 || (commandBytes & 3) != 0 || commandBytes / 4 > 0x1FFFFF ||
68 (ulong)commandBytes > pushbuffer.Length ||
69 (pushbuffer.Address >> 40) != ((pushbuffer.Address + (ulong)commandBytes - 1) >> 40))
70 throw new ArgumentOutOfRangeException(nameof(commandBytes), "Pushbuffer length, alignment or 40-bit segment boundary is invalid.");
71 if (destination.Length < GpfifoEntryBytes) throw new ArgumentException("Entry is too short.", nameof(destination));
72 BinaryPrimitives.WriteUInt32LittleEndian(destination, (uint)pushbuffer.Address);
73 // Main level, unconditional fetch, SYNC_WAIT. Higher address bits were
74 // supplied by the preceding SET_PB_SEGMENT_EXTENDED_BASE control entry.
75 BinaryPrimitives.WriteUInt32LittleEndian(destination[4..],
76 (uint)(pushbuffer.Address >> 32) & 0xFF | (uint)(commandBytes / 4) << 10 | 1U << 31);
77 }
78
79 private ref struct MethodWriter
80 {
81 private readonly Span<byte> buffer;
82 public MethodWriter(Span<byte> buffer) => this.buffer = buffer;
83 public int BytesWritten { get; private set; }
84 public void Write(uint method, scoped ReadOnlySpan<uint> values)
85 {
86 BinaryPrimitives.WriteUInt32LittleEndian(buffer[BytesWritten..], IncrementingMethodHeader(method, 4, values.Length));
87 BytesWritten += 4;
88 foreach (uint value in values)
89 {
90 BinaryPrimitives.WriteUInt32LittleEndian(buffer[BytesWritten..], value);
91 BytesWritten += 4;
92 }
93 }
94 }
95 }
Added Source/XFESystem.Gpu.Nvidia/NvidiaGpuRange.cs +49 -0
@@ -0,0 +1,49 @@
1 namespace XFESystem.Gpu.Nvidia;
2
3 /// <summary>Validated GPU virtual range, not a physical address or proof of an MMU mapping.</summary>
4 public readonly struct NvidiaGpuRange
5 {
6 public const ulong AddressLimit = 1UL << 57;
7 public ulong Address { get; }
8 public ulong Length { get; }
9 public ulong End => Address + Length;
10
11 public NvidiaGpuRange(ulong address, ulong length)
12 {
13 if (address == 0 || address >= AddressLimit || length == 0 || length > AddressLimit - address)
14 throw new ArgumentOutOfRangeException(nameof(length), "Range must fit the published 57-bit virtual-address fields.");
15 Address = address; Length = length;
16 }
17
18 public void RequireAlignment(uint alignment)
19 {
20 if (Address == 0 || Length == 0 || alignment == 0 || (alignment & (alignment - 1)) != 0 || (Address & (alignment - 1)) != 0)
21 throw new ArgumentException("GPU range is empty or not aligned.");
22 }
23
24 public bool Overlaps(NvidiaGpuRange other) => Address < other.End && other.Address < End;
25 }
26
27 /// <summary>
28 /// Caller-owned, pinned native mapping. The caller must retain the CPU allocation,
29 /// GPU VA mapping, cache policy and channel ownership until channel disposal.
30 /// Constructing this value does not create any of those resources.
31 /// </summary>
32 public readonly struct NvidiaMappedRegion
33 {
34 public nuint CpuAddress { get; }
35 public NvidiaGpuRange Gpu { get; }
36 public int Length { get; }
37
38 public NvidiaMappedRegion(nuint cpuAddress, NvidiaGpuRange gpu, int length)
39 {
40 gpu.RequireAlignment(4);
41 if (cpuAddress == 0 || (cpuAddress & 3) != 0 || length <= 0 ||
42 (nuint)length > nuint.MaxValue - cpuAddress || (ulong)length > gpu.Length)
43 throw new ArgumentOutOfRangeException(nameof(length), "CPU/GPU mapping is empty, misaligned or overflows.");
44 CpuAddress = cpuAddress; Gpu = gpu; Length = length;
45 }
46
47 public bool CpuOverlaps(NvidiaMappedRegion other) =>
48 CpuAddress < other.CpuAddress + (nuint)other.Length && other.CpuAddress < CpuAddress + (nuint)Length;
49 }
Added Source/XFESystem.Gpu.Nvidia/NvidiaMappedCopyChannel.cs +143 -0
@@ -0,0 +1,143 @@
1 namespace XFESystem.Gpu.Nvidia;
2
3 /// <summary>
4 /// Publishes real Blackwell copy commands into caller-owned native mappings.
5 /// Prerequisites: GSP/RM initialized the device/channel, GPU MMU mapped these
6 /// ranges, and RM supplied GPPut and the work-submission register/token. This
7 /// class does not perform PCI discovery, map BARs, or invent a doorbell offset.
8 /// The current XFESystem runtime has no such resource provider and does not
9 /// construct this class. Host tests use private unmanaged memory, not a GPU.
10 /// </summary>
11 public sealed unsafe class NvidiaMappedCopyChannel : IDisposable
12 {
13 private readonly object gate = new();
14 private readonly NvidiaMappedRegion commands, fifo, fence;
15 private readonly nuint gpPut, workSubmission, drainBar1;
16 private readonly uint workToken, copyClass;
17 private readonly int entryCount;
18 private int producer;
19 private uint payload;
20 private bool inFlight, disposed;
21
22 public bool InFlight => Volatile.Read(ref inFlight);
23
24 public NvidiaMappedCopyChannel(uint copyClass, NvidiaMappedRegion commands, NvidiaMappedRegion fifo,
25 int entryCount, NvidiaMappedRegion fence, nuint gpPut, nuint workSubmission,
26 uint workToken, int initialProducer = 0, nuint drainBar1 = 0)
27 {
28 if (copyClass != NvidiaBlackwellCommands.CopyClassA && copyClass != NvidiaBlackwellCommands.CopyClassB)
29 throw new ArgumentOutOfRangeException(nameof(copyClass));
30 if (commands.Length < NvidiaBlackwellCommands.Fill32CommandBytes || entryCount < 4 ||
31 entryCount > 65536 || (entryCount & (entryCount - 1)) != 0 ||
32 fifo.Length < (long)entryCount * NvidiaBlackwellCommands.GpfifoEntryBytes || fence.Length != 4 ||
33 initialProducer < 0 || initialProducer >= entryCount)
34 throw new ArgumentException("Invalid command, FIFO, fence or producer geometry.");
35 fifo.Gpu.RequireAlignment(8);
36 if ((fifo.CpuAddress & 7) != 0) throw new ArgumentException("GPFIFO CPU mapping must be eight-byte aligned.");
37 if (commands.Gpu.Overlaps(fifo.Gpu) || commands.Gpu.Overlaps(fence.Gpu) || fifo.Gpu.Overlaps(fence.Gpu) ||
38 commands.CpuOverlaps(fifo) || commands.CpuOverlaps(fence) || fifo.CpuOverlaps(fence))
39 throw new ArgumentException("Channel memory ranges overlap.");
40 RequireRegister(gpPut); RequireRegister(workSubmission);
41 if (drainBar1 != 0) RequireRegister(drainBar1);
42 if (gpPut == workSubmission) throw new ArgumentException("GPPut and the work-submission register alias.");
43 foreach (var memory in new[] { commands, fifo, fence })
44 if (Contains(memory, gpPut) || Contains(memory, workSubmission) || (drainBar1 != 0 && Contains(memory, drainBar1)))
45 throw new ArgumentException("A control register aliases command, FIFO or fence memory.");
46 // Validate the complete pushbuffer range before exposing an instance.
47 Span<byte> scratch = stackalloc byte[NvidiaBlackwellCommands.GpfifoEntryBytes];
48 NvidiaBlackwellCommands.WriteGpfifoEntry(scratch, commands.Gpu, NvidiaBlackwellCommands.Fill32CommandBytes);
49 this.copyClass = copyClass; this.commands = commands; this.fifo = fifo; this.fence = fence;
50 this.entryCount = entryCount; this.gpPut = gpPut; this.workSubmission = workSubmission;
51 this.workToken = workToken; this.drainBar1 = drainBar1; producer = initialProducer;
52 }
53
54 public uint SubmitFill32(NvidiaGpuRange target, uint pixels, uint color)
55 {
56 Monitor.Enter(gate);
57 uint result;
58 // The native runtime does not unwind arbitrary finally handlers. Release
59 // explicitly on both paths; compiler-generated lock/finally is unsafe here.
60 try { result = SubmitFill32Core(target, pixels, color); }
61 catch { Monitor.Exit(gate); throw; }
62 Monitor.Exit(gate);
63 return result;
64 }
65
66 private uint SubmitFill32Core(NvidiaGpuRange target, uint pixels, uint color)
67 {
68 ObjectDisposedException.ThrowIf(disposed, this);
69 if (inFlight) throw new InvalidOperationException("The prior GPU fence has not completed; its mappings cannot be reused.");
70 if (payload == uint.MaxValue) throw new InvalidOperationException("Fence payload space is exhausted; recreate the channel after draining it.");
71 if (target.Overlaps(commands.Gpu) || target.Overlaps(fifo.Gpu) || target.Overlaps(fence.Gpu))
72 throw new ArgumentException("Fill target aliases live channel memory.");
73 uint next = payload + 1;
74 // Validate/encode privately first. Failed validation leaves all DMA
75 // mappings and registers untouched, including the previous fence.
76 Span<byte> packet = stackalloc byte[NvidiaBlackwellCommands.Fill32CommandBytes];
77 NvidiaBlackwellCommands.WriteFill32(packet, copyClass, target, pixels, color, fence.Gpu, next);
78 Span<byte> entries = stackalloc byte[NvidiaBlackwellCommands.GpfifoEntryBytes * 2];
79 NvidiaBlackwellCommands.WriteSegmentBase(entries, commands.Gpu);
80 NvidiaBlackwellCommands.WriteGpfifoEntry(entries[8..], commands.Gpu, packet.Length);
81 // From here onwards, conservatively retain ownership even if a
82 // mapping faults while publishing. Never recycle a partial submit.
83 payload = next; inFlight = true;
84 Volatile.Write(ref *(uint*)fence.CpuAddress, 0);
85 packet.CopyTo(new Span<byte>((void*)commands.CpuAddress, packet.Length));
86 int second = (producer + 1) & (entryCount - 1);
87 entries[..8].CopyTo(new Span<byte>((void*)(fifo.CpuAddress + (nuint)(producer * 8)), 8));
88 entries[8..].CopyTo(new Span<byte>((void*)(fifo.CpuAddress + (nuint)(second * 8)), 8));
89 Thread.MemoryBarrier();
90 if (drainBar1 != 0) _ = Volatile.Read(ref *(uint*)drainBar1);
91 producer = (producer + 2) & (entryCount - 1);
92 Volatile.Write(ref *(uint*)gpPut, (uint)producer);
93 Thread.MemoryBarrier();
94 Volatile.Write(ref *(uint*)workSubmission, workToken);
95 return payload;
96 }
97
98 /// <summary>
99 /// Reads the mapped GPU-written fence. A timeout never implies completion and no
100 /// CPU-side simulator writes this word. Callers must retain mappings on timeout.
101 /// </summary>
102 public bool TryRetire()
103 {
104 Monitor.Enter(gate);
105 bool result;
106 try { result = TryRetireCore(); }
107 catch { Monitor.Exit(gate); throw; }
108 Monitor.Exit(gate);
109 return result;
110 }
111
112 private bool TryRetireCore()
113 {
114 ObjectDisposedException.ThrowIf(disposed, this);
115 if (!inFlight) return true;
116 if (Volatile.Read(ref *(uint*)fence.CpuAddress) != payload) return false;
117 Thread.MemoryBarrier(); inFlight = false;
118 return true;
119 }
120
121 public void Dispose()
122 {
123 Monitor.Enter(gate);
124 try { DisposeCore(); }
125 catch { Monitor.Exit(gate); throw; }
126 Monitor.Exit(gate);
127 }
128
129 private void DisposeCore()
130 {
131 if (disposed) return;
132 if (inFlight) throw new InvalidOperationException("Wait for the GPU fence or reset the owning channel before unmapping memory.");
133 disposed = true;
134 }
135
136 private static bool Contains(NvidiaMappedRegion memory, nuint word) =>
137 word < memory.CpuAddress + (nuint)memory.Length && memory.CpuAddress < word + 4;
138 private static void RequireRegister(nuint word)
139 {
140 if (word == 0 || (word & 3) != 0 || word > nuint.MaxValue - 4)
141 throw new ArgumentException("A mapped control register is null, unaligned or overflows.");
142 }
143 }
Added Source/XFESystem.Gpu.Nvidia/Packages.lock.json +6 -0
@@ -0,0 +1,6 @@
1 {
2 "version": 1,
3 "dependencies": {
4 "net10.0": {}
5 }
6 }
Added Source/XFESystem.Gpu.Nvidia/XFESystem.Gpu.Nvidia.csproj +11 -0
@@ -0,0 +1,11 @@
1 <Project Sdk="Microsoft.NET.Sdk">
2 <PropertyGroup>
3 <TargetFramework>net10.0</TargetFramework>
4 <ImplicitUsings>enable</ImplicitUsings>
5 <Nullable>enable</Nullable>
6 <AllowUnsafeBlocks>true</AllowUnsafeBlocks>
7 <TreatWarningsAsErrors>true</TreatWarningsAsErrors>
8 <RestorePackagesWithLockFile>true</RestorePackagesWithLockFile>
9 <NuGetLockFilePath>Packages.lock.json</NuGetLockFilePath>
10 </PropertyGroup>
11 </Project>
Modified THIRD_PARTY.md +3 -1
@@ -54,4 +54,6 @@ QEMU 安装包按发布站 SHA512 验证后仅解包;Limine/LLVM 和固件下
54 54
55 55 带第三方二进制、脚本、字体或资源的发行包须同时携带适用的完整许可证、版权与第三方声明,并履行适用的源码提供要求。`Tools/CSharpBoot/Licenses/Collected/Manifest.json`、字体清单和网站许可清单分别保留其来源与校验值;未标明精确源码 commit 的补充许可文本应明确记录为另行固定的官方来源,不能冒称源码版本完全一致。
56 56
57 组件存在或可以在宿主运行,不证明在 XFESystem 中完成初始化。真实来宾网络、TLS、存储、GPU/CUDA、独立程序和性能是否通过,以对应镜像的验收记录为准。当前仅参考 NVIDIA 官方硬件/驱动文档;没有捆绑 NVIDIA 内核驱动、GSP 固件或 CUDA 运行时,也没有将其标记为当前可用依赖。
57 组件存在或可以在宿主运行,不证明在 XFESystem 中完成初始化。真实来宾网络、TLS、存储、GPU/CUDA、独立程序和性能是否通过,以对应镜像的验收记录为准。
58
59 NVIDIA 原生命令协议组件参考 `open-gpu-kernel-modules` 615.78.08 的公开命令头文件及队列发布算法,固定提交为 `ca2d03e6fafd4d4b56b383497d931c25b283bfcf`。13 个来源文件的 SHA256、Git blob 和官方链接见 [NvidiaSources.lock.json](Tools/Gpu/NvidiaSources.lock.json),完整 MIT 许可和相关版权见 [NvidiaPublicHeaders.txt](Tools/Gpu/Licenses/NvidiaPublicHeaders.txt)。本项目没有捆绑 NVIDIA 内核驱动、GSP 固件或 CUDA 运行时;协议组件尚未接入内核,也没有实际 GPU 执行通过记录。
Added Tests/XFESystem.Gpu.Nvidia.Tests/NvidiaProtocolTests.cs +199 -0
@@ -0,0 +1,199 @@
1 using System.Buffers.Binary;
2 using System.Runtime.InteropServices;
3 using System.Reflection;
4 using XFESystem.Gpu.Nvidia;
5
6 namespace XFESystem.Gpu.Nvidia.Tests;
7
8 public sealed class NvidiaProtocolTests
9 {
10 [Theory]
11 [InlineData(0x000U, 4, 1, 0x20018000U)]
12 [InlineData(0x704U, 4, 2, 0x200281C1U)]
13 [InlineData(0x3FFCU, 7, 1, 0x2001EFFFU)]
14 public void MethodHeadersUsePublishedBitFields(uint method, int subchannel, int count, uint expected) =>
15 Assert.Equal(expected, NvidiaBlackwellCommands.IncrementingMethodHeader(method, subchannel, count));
16
17 [Theory]
18 [InlineData(1U, 4, 1)]
19 [InlineData(0x4000U, 4, 1)]
20 [InlineData(0U, 8, 1)]
21 [InlineData(0U, -1, 1)]
22 [InlineData(0U, 4, 0)]
23 [InlineData(0U, 4, 8192)]
24 [InlineData(0x3FFCU, 4, 2)]
25 public void InvalidMethodFieldsAreRejected(uint method, int subchannel, int count) =>
26 Assert.Throws<ArgumentOutOfRangeException>(() => NvidiaBlackwellCommands.IncrementingMethodHeader(method, subchannel, count));
27
28 [Theory]
29 [InlineData(NvidiaBlackwellCommands.CopyClassA)]
30 [InlineData(NvidiaBlackwellCommands.CopyClassB)]
31 public void FillAndSystemReleaseAreExactLittleEndianCommands(uint copyClass)
32 {
33 var bytes = new byte[72];
34 NvidiaBlackwellCommands.WriteFill32(bytes, copyClass, new(0x123456789ABC, 8), 2, 0xFFAC99EA,
35 new(0x0111222233334400, 4), 0x98765432);
36 uint[] words = [
37 0x20018000, copyClass,
38 0x200281C1, 0xFFAC99EA, 0x00030005,
39 0x20028102, 0x00001234, 0x56789ABC,
40 0x20018106, 2,
41 0x200180C0, 0x04000586,
42 0x20038090, 0x01112222, 0x33334400, 0x98765432,
43 0x200180C0, 0x0400000C
44 ];
45 Assert.Equal(words, Enumerable.Range(0, 18).Select(index => BinaryPrimitives.ReadUInt32LittleEndian(bytes.AsSpan(index * 4))));
46 Assert.Equal(new byte[] { 0, 0x80, 1, 0x20 }, bytes[..4]);
47 }
48
49 [Fact]
50 public void SegmentBaseAndGpfifoEntryRetainTheWhole57BitAddress()
51 {
52 var push = new NvidiaGpuRange(0x0123456789ABC000, 72);
53 var bytes = new byte[16];
54 NvidiaBlackwellCommands.WriteSegmentBase(bytes, push);
55 NvidiaBlackwellCommands.WriteGpfifoEntry(bytes.AsSpan(8), push, 72);
56 Assert.Equal("004523010400000000C0AB8967480080", Convert.ToHexString(bytes));
57 }
58
59 [Theory]
60 [InlineData(0UL, 4UL)]
61 [InlineData(1UL, 0UL)]
62 [InlineData(ulong.MaxValue, 4UL)]
63 [InlineData(NvidiaGpuRange.AddressLimit - 4, 8UL)]
64 [InlineData(NvidiaGpuRange.AddressLimit, 4UL)]
65 public void EmptyOverflowingOrTooWideGpuRangesAreRejected(ulong address, ulong length) =>
66 Assert.Throws<ArgumentOutOfRangeException>(() => new NvidiaGpuRange(address, length));
67
68 [Fact]
69 public void InvalidPacketsDoNotPartiallyModifyCallerStorage()
70 {
71 byte[] bytes = Enumerable.Repeat((byte)0xCD, 80).ToArray();
72 var target = new NvidiaGpuRange(0x10000, 16);
73 var fence = new NvidiaGpuRange(0x20000, 4);
74 Assert.Throws<ArgumentException>(() => NvidiaBlackwellCommands.WriteFill32(bytes.AsSpan(0, 71), 0xCAB5, target, 4, 0, fence, 1));
75 Assert.Throws<ArgumentOutOfRangeException>(() => NvidiaBlackwellCommands.WriteFill32(bytes, 0xCEC0, target, 4, 0, fence, 1));
76 Assert.Throws<ArgumentOutOfRangeException>(() => NvidiaBlackwellCommands.WriteFill32(bytes, 0xCAB5, target, 5, 0, fence, 1));
77 Assert.Throws<ArgumentException>(() => NvidiaBlackwellCommands.WriteFill32(bytes, 0xCAB5, target, 4, 0, target, 1));
78 Assert.All(bytes, value => Assert.Equal((byte)0xCD, value));
79 }
80
81 [Fact]
82 public void GpfifoRejectsSegmentCrossingTruncationAndUnalignedLengths()
83 {
84 var bytes = new byte[8];
85 Assert.Throws<ArgumentOutOfRangeException>(() => NvidiaBlackwellCommands.WriteGpfifoEntry(bytes, new((1UL << 40) - 4, 72), 72));
86 Assert.Throws<ArgumentOutOfRangeException>(() => NvidiaBlackwellCommands.WriteGpfifoEntry(bytes, new(0x1000, 72), 73));
87 Assert.Throws<ArgumentOutOfRangeException>(() => NvidiaBlackwellCommands.WriteGpfifoEntry(bytes, new(0x1000, 72), 76));
88 Assert.Throws<ArgumentOutOfRangeException>(() => NvidiaBlackwellCommands.WriteGpfifoEntry(bytes, new(0x1000, 1UL << 24), 1 << 23));
89 Assert.Equal(new byte[8], bytes);
90 }
91
92 [Fact]
93 public unsafe void PrivateUnmanagedMemoryPublicationProtectsInFlightLifetimeAndWrapsFifo()
94 {
95 using var arena = new PrivateArena();
96 using var channel = arena.Create(initialProducer: 3);
97 uint first = channel.SubmitFill32(new(0x40000, 16), 4, 0xFFAC99EA);
98 Assert.Equal(1U, first);
99 Assert.True(channel.InFlight);
100 Assert.Equal(1U, arena.Read(320));
101 Assert.Equal(0xBAADF00DU, arena.Read(324));
102 Assert.Equal(0U, arena.Read(256));
103 Assert.Equal(0x00452300U, arena.Read(128 + 3 * 8));
104 Assert.Equal(4U, arena.Read(128 + 3 * 8 + 4));
105 Assert.Equal(0x80004800U, arena.Read(128 + 4));
106 Assert.False(channel.TryRetire());
107 Assert.Throws<InvalidOperationException>(() => channel.SubmitFill32(new(0x40000, 16), 4, 0));
108 Assert.Throws<InvalidOperationException>(() => channel.Dispose());
109 // This explicit CPU write is solely an offline fence-observation test.
110 // It is not a GPU result and does not validate hardware command execution.
111 arena.Write(256, first + 1);
112 Assert.False(channel.TryRetire());
113 arena.Write(256, first);
114 Assert.True(channel.TryRetire());
115 Assert.False(channel.InFlight);
116 uint second = channel.SubmitFill32(new(0x40000, 16), 4, 0x12345678);
117 Assert.Equal(2U, second);
118 Assert.Equal(3U, arena.Read(320));
119 Assert.Equal(0U, arena.Read(256));
120 arena.Write(256, second);
121 Assert.True(channel.TryRetire());
122 }
123
124 [Fact]
125 public void InvalidSubmissionLeavesEveryMappingAndRegisterUnmodified()
126 {
127 using var arena = new PrivateArena();
128 using var channel = arena.Create();
129 byte[] before = arena.Snapshot();
130 Assert.Throws<ArgumentOutOfRangeException>(() => channel.SubmitFill32(new(0x40000, 16), 5, 0));
131 Assert.Throws<ArgumentException>(() => channel.SubmitFill32(new(0x30000, 4), 1, 0));
132 Assert.Equal(before, arena.Snapshot());
133 Assert.False(channel.InFlight);
134 }
135
136 [Fact]
137 public void CpuGpuAndControlAliasesAreRejectedBeforeAnyWrite()
138 {
139 using var arena = new PrivateArena();
140 var command = arena.Region(0, 0x0045230000000000, 72);
141 var fifo = arena.Region(128, 0x20000, 32);
142 var fence = arena.Region(256, 0x30000, 4);
143 byte[] before = arena.Snapshot();
144 Assert.Throws<ArgumentException>(() => new NvidiaMappedCopyChannel(0xCAB5, command, fifo, 4,
145 arena.Region(0, 0x30000, 4), arena.Pointer(320), arena.Pointer(324), 1));
146 Assert.Throws<ArgumentException>(() => new NvidiaMappedCopyChannel(0xCAB5, command, fifo, 4,
147 arena.Region(256, 0x20000, 4), arena.Pointer(320), arena.Pointer(324), 1));
148 Assert.Throws<ArgumentException>(() => new NvidiaMappedCopyChannel(0xCAB5, command, fifo, 4,
149 fence, arena.Pointer(0), arena.Pointer(324), 1));
150 Assert.Equal(before, arena.Snapshot());
151 }
152
153 [Fact]
154 public void DisposedChannelCannotPublishOrReadFreedMappings()
155 {
156 using var arena = new PrivateArena();
157 var channel = arena.Create();
158 channel.Dispose(); channel.Dispose();
159 Assert.Throws<ObjectDisposedException>(() => channel.SubmitFill32(new(0x40000, 16), 4, 0));
160 Assert.Throws<ObjectDisposedException>(() => channel.TryRetire());
161 }
162
163 [Fact]
164 public async Task ValidationAndInFlightExceptionsReleaseTheGateForAnotherThread()
165 {
166 using var arena = new PrivateArena();
167 using var channel = arena.Create();
168 Assert.Throws<ArgumentOutOfRangeException>(() => channel.SubmitFill32(new(0x40000, 16), 5, 0));
169 Assert.True(await Task.Run(channel.TryRetire).WaitAsync(TimeSpan.FromSeconds(2)));
170 uint payload = channel.SubmitFill32(new(0x40000, 16), 4, 0);
171 Assert.Throws<InvalidOperationException>(() => channel.Dispose());
172 Assert.False(await Task.Run(channel.TryRetire).WaitAsync(TimeSpan.FromSeconds(2)));
173 arena.Write(256, payload);
174 Assert.True(await Task.Run(channel.TryRetire).WaitAsync(TimeSpan.FromSeconds(2)));
175 }
176
177 [Fact]
178 public void NativeChannelGateDoesNotDependOnUnsupportedFinallyUnwind()
179 {
180 foreach (var method in typeof(NvidiaMappedCopyChannel).GetMethods(BindingFlags.Public | BindingFlags.NonPublic | BindingFlags.Instance | BindingFlags.DeclaredOnly))
181 if (method.GetMethodBody() is { } body)
182 Assert.DoesNotContain(body.ExceptionHandlingClauses, clause => clause.Flags == ExceptionHandlingClauseOptions.Finally);
183 }
184
185 private sealed unsafe class PrivateArena : IDisposable
186 {
187 private readonly nint allocation = Marshal.AllocHGlobal(512);
188 public PrivateArena() => new Span<byte>((void*)allocation, 512).Fill(0xCD);
189 public nuint Pointer(int offset) => (nuint)(allocation + offset);
190 public NvidiaMappedRegion Region(int offset, ulong gpu, int length) => new(Pointer(offset), new(gpu, (ulong)length), length);
191 public uint Read(int offset) => *(uint*)(allocation + offset);
192 public void Write(int offset, uint value) => *(uint*)(allocation + offset) = value;
193 public byte[] Snapshot() => new Span<byte>((void*)allocation, 512).ToArray();
194 public NvidiaMappedCopyChannel Create(int initialProducer = 0) => new(0xCAB5,
195 Region(0, 0x0045230000000000, 72), Region(128, 0x20000, 32), 4,
196 Region(256, 0x30000, 4), Pointer(320), Pointer(324), 0xBAADF00D, initialProducer, Pointer(328));
197 public void Dispose() => Marshal.FreeHGlobal(allocation);
198 }
199 }
Added Tests/XFESystem.Gpu.Nvidia.Tests/Packages.lock.json +95 -0
@@ -0,0 +1,95 @@
1 {
2 "version": 1,
3 "dependencies": {
4 "net10.0": {
5 "Microsoft.NET.Test.Sdk": {
6 "type": "Direct",
7 "requested": "[18.9.0, )",
8 "resolved": "18.9.0",
9 "contentHash": "xIzVXa/VpKXqDWzyC/5Hw8JbFY5u9k3Jc53CpcjBiOXvkQGio484LD9FNwOiGUSMDcYL3Rcw9sNgGi5WrswlPQ==",
10 "dependencies": {
11 "Microsoft.CodeCoverage": "18.9.0",
12 "Microsoft.TestPlatform.TestHost": "18.9.0"
13 }
14 },
15 "xunit": {
16 "type": "Direct",
17 "requested": "[2.9.3, )",
18 "resolved": "2.9.3",
19 "contentHash": "TlXQBinK35LpOPKHAqbLY4xlEen9TBafjs0V5KnA4wZsoQLQJiirCR4CbIXvOH8NzkW4YeJKP5P/Bnrodm0h9Q==",
20 "dependencies": {
21 "xunit.analyzers": "1.18.0",
22 "xunit.assert": "2.9.3",
23 "xunit.core": "[2.9.3]"
24 }
25 },
26 "xunit.runner.visualstudio": {
27 "type": "Direct",
28 "requested": "[3.1.5, )",
29 "resolved": "3.1.5",
30 "contentHash": "tKi7dSTwP4m5m9eXPM2Ime4Kn7xNf4x4zT9sdLO/G4hZVnQCRiMTWoSZqI/pYTVeI27oPPqHBKYI/DjJ9GsYgA=="
31 },
32 "Microsoft.CodeCoverage": {
33 "type": "Transitive",
34 "resolved": "18.9.0",
35 "contentHash": "MtegCIKGuG0r/LCdIjoR1HgzCGIUBhR3aNdbtQpts5vMXQuqBDZ2Jsd2uFKoHFM2XrQV6ZrDf3F5oLi3SLTp8w=="
36 },
37 "Microsoft.TestPlatform.ObjectModel": {
38 "type": "Transitive",
39 "resolved": "18.9.0",
40 "contentHash": "Fz/qXC52VXXopzHj7v2reCKcCqSxkTDCkEDfkNAH0vni/7qK/KLMiEYtRmnUanxO2F2g2zZZ60qCpxF0AF7URA=="
41 },
42 "Microsoft.TestPlatform.TestHost": {
43 "type": "Transitive",
44 "resolved": "18.9.0",
45 "contentHash": "Oq+Pma/J86aZL72t71bcESvDszDsTjUfl6PIsuDdPoKTHZfNMhKamCfLD5fKx51W/u0KozXtJo2/3fnt0sgPxg==",
46 "dependencies": {
47 "Microsoft.TestPlatform.ObjectModel": "18.9.0"
48 }
49 },
50 "xunit.abstractions": {
51 "type": "Transitive",
52 "resolved": "2.0.3",
53 "contentHash": "pot1I4YOxlWjIb5jmwvvQNbTrZ3lJQ+jUGkGjWE3hEFM0l5gOnBWS+H3qsex68s5cO52g+44vpGzhAt+42vwKg=="
54 },
55 "xunit.analyzers": {
56 "type": "Transitive",
57 "resolved": "1.18.0",
58 "contentHash": "OtFMHN8yqIcYP9wcVIgJrq01AfTxijjAqVDy/WeQVSyrDC1RzBWeQPztL49DN2syXRah8TYnfvk035s7L95EZQ=="
59 },
60 "xunit.assert": {
61 "type": "Transitive",
62 "resolved": "2.9.3",
63 "contentHash": "/Kq28fCE7MjOV42YLVRAJzRF0WmEqsmflm0cfpMjGtzQ2lR5mYVj1/i0Y8uDAOLczkL3/jArrwehfMD0YogMAA=="
64 },
65 "xunit.core": {
66 "type": "Transitive",
67 "resolved": "2.9.3",
68 "contentHash": "BiAEvqGvyme19wE0wTKdADH+NloYqikiU0mcnmiNyXaF9HyHmE6sr/3DC5vnBkgsWaE6yPyWszKSPSApWdRVeQ==",
69 "dependencies": {
70 "xunit.extensibility.core": "[2.9.3]",
71 "xunit.extensibility.execution": "[2.9.3]"
72 }
73 },
74 "xunit.extensibility.core": {
75 "type": "Transitive",
76 "resolved": "2.9.3",
77 "contentHash": "kf3si0YTn2a8J8eZNb+zFpwfoyvIrQ7ivNk5ZYA5yuYk1bEtMe4DxJ2CF/qsRgmEnDr7MnW1mxylBaHTZ4qErA==",
78 "dependencies": {
79 "xunit.abstractions": "2.0.3"
80 }
81 },
82 "xunit.extensibility.execution": {
83 "type": "Transitive",
84 "resolved": "2.9.3",
85 "contentHash": "yMb6vMESlSrE3Wfj7V6cjQ3S4TXdXpRqYeNEI3zsX31uTsGMJjEw6oD5F5u1cHnMptjhEECnmZSsPxB6ChZHDQ==",
86 "dependencies": {
87 "xunit.extensibility.core": "[2.9.3]"
88 }
89 },
90 "xfesystem.gpu.nvidia": {
91 "type": "Project"
92 }
93 }
94 }
95 }
Added Tests/XFESystem.Gpu.Nvidia.Tests/XFESystem.Gpu.Nvidia.Tests.csproj +20 -0
@@ -0,0 +1,20 @@
1 <Project Sdk="Microsoft.NET.Sdk">
2 <PropertyGroup>
3 <TargetFramework>net10.0</TargetFramework>
4 <ImplicitUsings>enable</ImplicitUsings>
5 <Nullable>enable</Nullable>
6 <AllowUnsafeBlocks>true</AllowUnsafeBlocks>
7 <IsTestProject>true</IsTestProject>
8 <IsPackable>false</IsPackable>
9 <TreatWarningsAsErrors>true</TreatWarningsAsErrors>
10 <RestorePackagesWithLockFile>true</RestorePackagesWithLockFile>
11 <NuGetLockFilePath>Packages.lock.json</NuGetLockFilePath>
12 </PropertyGroup>
13 <ItemGroup>
14 <PackageReference Include="Microsoft.NET.Test.Sdk" Version="18.9.0" />
15 <PackageReference Include="xunit" Version="2.9.3" />
16 <PackageReference Include="xunit.runner.visualstudio" Version="3.1.5" PrivateAssets="all" />
17 <ProjectReference Include="../../Source/XFESystem.Gpu.Nvidia/XFESystem.Gpu.Nvidia.csproj" />
18 </ItemGroup>
19 <ItemGroup><Using Include="Xunit" /></ItemGroup>
20 </Project>
Added Tools/Gpu/Licenses/NvidiaPublicHeaders.txt +41 -0
@@ -0,0 +1,41 @@
1 NVIDIA public command headers and UVM reference algorithms
2 Repository: https://github.com/NVIDIA/open-gpu-kernel-modules
3 Pinned commit: ca2d03e6fafd4d4b56b383497d931c25b283bfcf (615.78.08)
4
5 Copyright (c) 2003-2023 NVIDIA CORPORATION & AFFILIATES
6 Copyright (c) 2020-2022 NVIDIA CORPORATION & AFFILIATES. All rights reserved.
7 Copyright (c) 2003-2022 NVIDIA CORPORATION & AFFILIATES
8 Copyright (c) 2024 NVIDIA CORPORATION & AFFILIATES
9 Copyright (c) 2024 NVIDIA CORPORATION & AFFILIATES. All rights reserved.
10 Copyright (c) 2003-2022 NVIDIA CORPORATION & AFFILIATES. All rights reserved.
11 Copyright (c) 2020-2025 NVIDIA Corporation
12 Copyright (c) 2020-2024 NVIDIA Corporation
13 Copyright (c) 2018-2023 NVIDIA Corporation
14 Copyright (c) 2017-2025 NVIDIA Corporation
15 Copyright (c) 2025 NVIDIA Corporation
16 Copyright (c) 2015-2025 NVIDIA Corporation
17 SPDX-License-Identifier: MIT
18
19 Permission is hereby granted, free of charge, to any person obtaining a
20 copy of this software and associated documentation files (the "Software"),
21 to deal in the Software without restriction, including without limitation
22 the rights to use, copy, modify, merge, publish, distribute, sublicense,
23 and/or sell copies of the Software, and to permit persons to whom the
24 Software is furnished to do so, subject to the following conditions:
25
26 The above copyright notice and this permission notice shall be included in
27 all copies or substantial portions of the Software.
28
29 THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
30 IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
31 FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
32 THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
33 LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
34 FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
35 DEALINGS IN THE SOFTWARE.
36
37 The protocol implementation in Source/XFESystem.Gpu.Nvidia is a C# port of
38 the referenced public method encoding/publication sequence. No NVIDIA
39 firmware, Windows driver, Linux kernel module or CUDA runtime is bundled.
40 The exact individual source notices remain in their hash-verified reference
41 files downloaded by Tools/Gpu/VerifyNvidiaProtocol.py --download.
Added Tools/Gpu/NvidiaProtocolProbe/NvidiaProtocolProbe.csproj +12 -0
@@ -0,0 +1,12 @@
1 <Project Sdk="Microsoft.NET.Sdk">
2 <PropertyGroup>
3 <OutputType>Exe</OutputType>
4 <TargetFramework>net10.0</TargetFramework>
5 <ImplicitUsings>enable</ImplicitUsings>
6 <Nullable>enable</Nullable>
7 <TreatWarningsAsErrors>true</TreatWarningsAsErrors>
8 <RestorePackagesWithLockFile>true</RestorePackagesWithLockFile>
9 <NuGetLockFilePath>Packages.lock.json</NuGetLockFilePath>
10 </PropertyGroup>
11 <ItemGroup><ProjectReference Include="../../../Source/XFESystem.Gpu.Nvidia/XFESystem.Gpu.Nvidia.csproj" /></ItemGroup>
12 </Project>
Added Tools/Gpu/NvidiaProtocolProbe/Packages.lock.json +10 -0
@@ -0,0 +1,10 @@
1 {
2 "version": 1,
3 "dependencies": {
4 "net10.0": {
5 "xfesystem.gpu.nvidia": {
6 "type": "Project"
7 }
8 }
9 }
10 }
Added Tools/Gpu/NvidiaProtocolProbe/Program.cs +24 -0
Added Tools/Gpu/NvidiaSources.lock.json +115 -0
Added Tools/Gpu/Readme.md +26 -0
Added Tools/Gpu/VerifyNvidiaProtocol.py +146 -0
Modified Tools/TestCSharp.ps1 +1 -1
Modified XFESystem.slnx +3 -0
Modified docs/NativeCudaPort.md +2 -0