summaryrefslogtreecommitdiffstats
path: root/tools/cuda-bridge/cuda_bridge.c
diff options
context:
space:
mode:
authorArthur Hanson Rasmusson <arthur@vgpu.io>2026-10-04 16:19:31 -0400
committerArthur Hanson Rasmusson <arthur@vgpu.io>2026-10-04 16:19:31 -0400
commitaa3520f252182aaf64ea0b8856a3ac5e4466ce0e (patch)
treef5382da924162e6b0705c2eeff60361cf3911cda /tools/cuda-bridge/cuda_bridge.c
parent5d305e94d028f14edffa042f006770494fda1d86 (diff)
downloadopen-iov-aa3520f252182aaf64ea0b8856a3ac5e4466ce0e.tar.gz
open-iov-aa3520f252182aaf64ea0b8856a3ac5e4466ce0e.zip
vgsp: end-to-end CUDA acceptance in the guest
The no-claim emulation path now runs a stock CUDA program end to end: device properties, cudaMalloc, a GPU-authored string kernel read back byte-for-byte, two 4 MB host-to-device copies, a 1 M-element vectorAdd executed on the physical GPU through the host bridge, and the device-to-host copy-back verified element by element. Three consecutive supervised cycles report MAIL + VADD bad=0 checksum=392344090.500000 + STATUS OK with identical input/output hashes. Two data-path correctness fixes make the transfers sound: - Once-only op execution per GPFIFO entry. The pump replays recently consumed ring entries when translation batches are injected so missed releases can land; re-executing a CE copy is not idempotent because its source is a client staging buffer that may have been refilled. Slots now record their raw entry words and a replayed entry matching that identity suppresses operations while still processing releases. - Per-page translation of virtual operands. svc_copy resolved only the first page of a VA range and walked the PA linearly; client staging ranges are frequently split (observed 195 + 61 pages), so the tail of alternating chunks was read from unrelated memory. Multi-page copies now re-resolve each 4 KB page through the trusted resolver. Also carries the earlier uncommitted emulation fixes that got the client to the launch (QMD address-window gate, counter registration persistence, 512 MiB emulated FB, bridge slot hygiene) and bounded diagnostics (sema-bigop, sema-bigcopy, bridge-in/out hashes, daemon INCHECK/OUTCHECK/dumps).
Diffstat (limited to 'tools/cuda-bridge/cuda_bridge.c')
-rw-r--r--tools/cuda-bridge/cuda_bridge.c92
1 files changed, 92 insertions, 0 deletions
diff --git a/tools/cuda-bridge/cuda_bridge.c b/tools/cuda-bridge/cuda_bridge.c
index 86823e6..cfd2ab7 100644
--- a/tools/cuda-bridge/cuda_bridge.c
+++ b/tools/cuda-bridge/cuda_bridge.c
@@ -1026,6 +1026,64 @@ static int run_bridge_vadd(const cubb2_job_t *j, const uint8_t *in,
uint32_t bad = 0, i;
int ni;
+ /*
+ * Diagnostic (input-path proof): the acceptance client fills a with
+ * (i%1000)*0.5 and b with (i%997)*0.25. Verify the bytes this daemon
+ * actually received against that pattern, so an input-path corruption
+ * is separated from a kernel/execution corruption. Bounded output.
+ */
+ {
+ uint32_t ba = 0, bb = 0, nan_in = 0, first = 0xffffffffu, k;
+
+ for (k = 0; k < n; k++) {
+ float ea = (float)(k % 1000) * 0.5f;
+ float eb = (float)(k % 997) * 0.25f;
+
+ if (ha[k] != ea) {
+ if (first == 0xffffffffu)
+ first = k;
+ ba++;
+ }
+ if (hb[k] != eb)
+ bb++;
+ if (ha[k] != ha[k] || hb[k] != hb[k])
+ nan_in++;
+ }
+ printf("CUBB2 seq=%llu INCHECK n=%u bad_a=%u bad_b=%u nan=%u first_bad=%u a0=%08x b0=%08x a_last=%08x b_last=%08x\n",
+ (unsigned long long)j->seq, n, ba, bb, nan_in, first,
+ ((const uint32_t *)ha)[0], ((const uint32_t *)hb)[0],
+ ((const uint32_t *)ha)[n - 1], ((const uint32_t *)hb)[n - 1]);
+ fflush(stdout);
+ if (first != 0xffffffffu) {
+ printf("CUBB2 seq=%llu INSAMPLE first=%u a[first..+8]=",
+ (unsigned long long)j->seq, first);
+ for (k = first; k < first + 8 && k < n; k++)
+ printf(" %08x", ((const uint32_t *)ha)[k]);
+ printf(" b[first..+8]=");
+ for (k = first; k < first + 8 && k < n; k++)
+ printf(" %08x", ((const uint32_t *)hb)[k]);
+ printf("\n");
+ fflush(stdout);
+ }
+ /* Raw blob dump for offline analysis (bounded, overwrite). */
+ {
+ char path[64];
+ FILE *fp;
+
+ snprintf(path, sizeof(path), "/tmp/cubb2-in-seq%llu.bin",
+ (unsigned long long)j->seq);
+ fp = fopen(path, "wb");
+ if (fp) {
+ fwrite(in, 1, (size_t)bytes * 2, fp);
+ fclose(fp);
+ printf("CUBB2 seq=%llu INDMP %s bytes=%llu\n",
+ (unsigned long long)j->seq, path,
+ (unsigned long long)bytes * 2);
+ fflush(stdout);
+ }
+ }
+ }
+
hc = malloc(bytes);
if (!hc) {
d->status = ST_RAM;
@@ -1070,6 +1128,40 @@ static int run_bridge_vadd(const cubb2_job_t *j, const uint8_t *in,
}
d->bad = bad;
d->checksum = double_to_bits(sum);
+ {
+ uint64_t h = 1469598103934665603ULL;
+ uint32_t k, nan_out = 0;
+
+ for (k = 0; k < n; k++) {
+ uint32_t w;
+
+ memcpy(&w, hc + (size_t)k * 4, 4);
+ h ^= w;
+ h *= 1099511628211ULL;
+ if (hf[k] != hf[k])
+ nan_out++;
+ }
+ printf("CUBB2 seq=%llu OUTCHECK n=%u hash=%016llx nan=%u bad=%u c0=%08x c_last=%08x\n",
+ (unsigned long long)j->seq, n,
+ (unsigned long long)h, nan_out, bad,
+ ((const uint32_t *)hc)[0], ((const uint32_t *)hc)[n - 1]);
+ fflush(stdout);
+ {
+ char path[64];
+ FILE *fp;
+
+ snprintf(path, sizeof(path), "/tmp/cubb2-out-seq%llu.bin",
+ (unsigned long long)j->seq);
+ fp = fopen(path, "wb");
+ if (fp) {
+ fwrite(hc, 1, (size_t)n * 4, fp);
+ fclose(fp);
+ printf("CUBB2 seq=%llu OUTDMP %s bytes=%u\n",
+ (unsigned long long)j->seq, path, n * 4);
+ fflush(stdout);
+ }
+ }
+ }
*outp = hc;
*out_len = bytes;
hc = NULL;