summaryrefslogtreecommitdiffstats
path: root/tools/test/gpage_test.c
blob: 1ad2212b897464490e48cf803c6ea31855b91dba (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
/* SPDX-License-Identifier: BSD-3-Clause */
/* Copyright (c) 2026 Open-IOV contributors */
/* gpage_test.c - guest-page GPA probe for the physical-backend guest-memory path.
 *
 * Run as root inside the guest (plain gcc on Ubuntu 24.04, no CUDA).
 *
 * mmap+mlock one 4 KiB anonymous page, fill it with the 0xa5 ^ (i & 0xff)
 * pattern, resolve its guest-physical address from /proc/self/pagemap and
 * print
 *   GPAGE gpa=0x<hex>
 * to stdout (flushed) so the host harness can hand the GPA to the module via
 * the gsp-phys-guest-gpa debugfs file.
 *
 * The page is then polled every 100 ms for up to 120 s. The host-side W2G
 * trigger copies the module SRC over this page, so byte 0 becomes 0x5a
 * (0xa5 ^ (0 & 0xff) = 0xa5 -> 0x5a). On seeing p[0] == 0x5a print
 * "GPAGE SEEN_W2G" and exit 0; on timeout print "GPAGE TIMEOUT" and exit 1.
 * If the PFN cannot be resolved or is zero, print "GPAGE NO_PFN" and exit 2.
 *
 * Build (guest): gcc -O2 -o /tmp/gpage /tmp/gpage.c
 */
#define _GNU_SOURCE
#include <errno.h>
#include <fcntl.h>
#include <stdint.h>
#include <stdio.h>
#include <string.h>
#include <sys/mman.h>
#include <time.h>
#include <unistd.h>

#define PAGE_SIZE        4096u
#define POLL_INTERVAL_MS 100
#define POLL_TIMEOUT_MS  120000

static int resolve_gpa(const void *addr, uint64_t *gpa)
{
    size_t offset = ((uintptr_t)addr / PAGE_SIZE) * sizeof(uint64_t);
    uint64_t entry = 0;
    ssize_t n;
    int fd = open("/proc/self/pagemap", O_RDONLY);
    if (fd < 0) {
        fprintf(stderr, "open pagemap: %s\n", strerror(errno));
        return -1;
    }
    n = pread(fd, &entry, sizeof(entry), (off_t)offset);
    close(fd);
    if (n != (ssize_t)sizeof(entry)) {
        fprintf(stderr, "read pagemap: %s\n", n < 0 ? strerror(errno) : "short read");
        return -1;
    }
    if (!(entry & (1ULL << 63))) {
        fprintf(stderr, "pagemap: page not present\n");
        return -1;
    }
    {
        uint64_t pfn = entry & ((1ULL << 55) - 1);
        if (pfn == 0) {
            fprintf(stderr, "pagemap: pfn is zero\n");
            return -1;
        }
        *gpa = pfn * PAGE_SIZE;
    }
    return 0;
}

int main(void)
{
    struct timespec ts = { .tv_sec = 0, .tv_nsec = POLL_INTERVAL_MS * 1000000L };
    unsigned char *p;
    uint64_t gpa = 0;
    size_t i;
    int waited = 0;

    setvbuf(stdout, NULL, _IONBF, 0);

    p = mmap(NULL, PAGE_SIZE, PROT_READ | PROT_WRITE,
             MAP_PRIVATE | MAP_ANONYMOUS, -1, 0);
    if (p == MAP_FAILED) {
        printf("GPAGE MMAP_FAILED\n");
        return 2;
    }
    if (mlock(p, PAGE_SIZE) != 0) {
        printf("GPAGE MLOCK_FAILED\n");
        return 2;
    }

    for (i = 0; i < PAGE_SIZE; i++)
        p[i] = (unsigned char)(0xa5 ^ (i & 0xff));

    if (resolve_gpa(p, &gpa) != 0) {
        printf("GPAGE NO_PFN\n");
        return 2;
    }

    printf("GPAGE gpa=0x%llx\n", (unsigned long long)gpa);
    fflush(stdout);

    while (waited < POLL_TIMEOUT_MS) {
        if (*(volatile unsigned char *)p == 0x5a) {
            printf("GPAGE SEEN_W2G\n");
            fflush(stdout);
            return 0;
        }
        nanosleep(&ts, NULL);
        waited += POLL_INTERVAL_MS;
    }

    printf("GPAGE TIMEOUT\n");
    fflush(stdout);
    return 1;
}