返回提交历史
Added
Source/XFESystem.Gpu.Nvidia/NvidiaBlackwellCommands.cs
+95
-0
Added
Source/XFESystem.Gpu.Nvidia/NvidiaGpuRange.cs
+49
-0
Added
Source/XFESystem.Gpu.Nvidia/NvidiaMappedCopyChannel.cs
+143
-0
Added
Source/XFESystem.Gpu.Nvidia/Packages.lock.json
+6
-0
Added
Source/XFESystem.Gpu.Nvidia/XFESystem.Gpu.Nvidia.csproj
+11
-0
Modified
THIRD_PARTY.md
+3
-1
Added
Tests/XFESystem.Gpu.Nvidia.Tests/NvidiaProtocolTests.cs
+199
-0
Added
Tests/XFESystem.Gpu.Nvidia.Tests/Packages.lock.json
+95
-0
Added
Tests/XFESystem.Gpu.Nvidia.Tests/XFESystem.Gpu.Nvidia.Tests.csproj
+20
-0
Added
Tools/Gpu/Licenses/NvidiaPublicHeaders.txt
+41
-0
Added
Tools/Gpu/NvidiaProtocolProbe/NvidiaProtocolProbe.csproj
+12
-0
Added
Tools/Gpu/NvidiaProtocolProbe/Packages.lock.json
+10
-0
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
XFEstudio/XFESystem
Add pinned NVIDIA command protocol components and offline oracle
46756fd
代码差异
19 个文件
+1001
-2
@@ -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
}
@@ -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
}
@@ -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
}
@@ -0,0 +1,6 @@
1
{
2
"version": 1,
3
"dependencies": {
4
"net10.0": {}
5
}
6
}
@@ -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>
@@ -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 执行通过记录。
@@ -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
}
@@ -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
}
@@ -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>
@@ -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.
@@ -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>
@@ -0,0 +1,10 @@
1
{
2
"version": 1,
3
"dependencies": {
4
"net10.0": {
5
"xfesystem.gpu.nvidia": {
6
"type": "Project"
7
}
8
}
9
}
10
}