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
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
|
/* SPDX-License-Identifier: BSD-3-Clause */
/* Copyright (c) 2026 Open-IOV contributors */
/* mem.c - rmtool mem stats: read-only FB/heap/BAR1 information.
*
* Allocation, mapping, pinning and arbitrary-handle inspection are
* deliberately not implemented: they are write operations against the live
* driver and are out of scope for this read-only oracle.
*/
#include <getopt.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include "rmtool.h"
static const NvU32 fb_indices[] = {
NV2080_CTRL_FB_INFO_INDEX_TOTAL_RAM_SIZE,
NV2080_CTRL_FB_INFO_INDEX_USABLE_RAM_SIZE,
NV2080_CTRL_FB_INFO_INDEX_HEAP_SIZE,
NV2080_CTRL_FB_INFO_INDEX_HEAP_FREE,
NV2080_CTRL_FB_INFO_INDEX_LARGEST_FREE_REGION_SIZE_KB,
NV2080_CTRL_FB_INFO_INDEX_BAR1_SIZE,
NV2080_CTRL_FB_INFO_INDEX_BAR1_AVAIL_SIZE,
};
static const char *fb_names[] = {
"vram_total_kib",
"vram_usable_kib",
"heap_size_kib",
"heap_free_kib",
"largest_free_region_kib",
"bar1_size_kib",
"bar1_avail_kib",
};
#define FB_N (sizeof(fb_indices) / sizeof(fb_indices[0]))
static void usage(void)
{
printf("usage: rmtool mem stats [-d N|BDF] [--format=...]\n"
" rmtool mem info -h HANDLE (not implemented: write "
"path)\n"
"rmtool is read-only; alloc/map/unmap/pin are refused.\n");
}
static int mem_stats(struct rm_ctx *ctx)
{
NvU32 values[FB_N];
struct rm_kv kv[FB_N + 4];
char v[FB_N + 4][128];
NvU32 status;
int rc, n = 0;
unsigned int i;
rc = rm_ctx_open(ctx);
if (rc != 0) {
fprintf(stderr, "rmtool: cannot open RM client: rc=%d\n", rc);
return 2;
}
memset(values, 0, sizeof(values));
status = rm_query_fb(ctx, fb_indices, values, FB_N);
if (status != 0) {
fprintf(stderr, "rmtool: FB_GET_INFO_V2: %s\n",
rm_status_str(status));
return 2;
}
for (i = 0; i < FB_N; i++) {
char human[32];
rm_human_kib(values[i], human, sizeof(human));
snprintf(v[n], sizeof(v[n]), "%u (%s)", values[i], human);
kv[n].key = fb_names[i];
kv[n].value = v[n];
n++;
}
if (values[2] >= values[3]) {
unsigned int used = values[2] - values[3];
char human[32];
rm_human_kib(used, human, sizeof(human));
snprintf(v[n], sizeof(v[n]), "%u (%s)", used, human);
kv[n].key = "heap_used_kib (derived)";
kv[n].value = v[n];
n++;
}
rm_print_kv("VRAM / heap (FB_GET_INFO_V2, KiB units)", kv, n,
ctx->format);
return 0;
}
static int parse_common(int argc, char **argv, struct rm_ctx *ctx)
{
static const struct option long_opts[] = {
{ "device-spec", required_argument, NULL, 'd' },
{ "format", required_argument, NULL, 'f' },
{ "device", required_argument, NULL, 1000 },
{ "help", no_argument, NULL, 'h' },
{ NULL, 0, NULL, 0 },
};
int opt;
optind = 1;
opterr = 0;
while ((opt = getopt_long(argc, argv, "d:f:h", long_opts, NULL)) != -1) {
int rc;
if (opt == 'h')
return 1;
rc = rm_apply_common_opt(ctx, opt, optarg);
if (rc < 0)
return -1;
}
return 0;
}
int cmd_mem(int argc, char **argv, struct rm_ctx *ctx)
{
int rc;
if (argc < 2) {
usage();
return 2;
}
if (strcmp(argv[1], "stats") == 0) {
rc = parse_common(argc - 1, argv + 1, ctx);
if (rc < 0)
return 2;
if (rc == 1) {
usage();
return 0;
}
return mem_stats(ctx);
}
if (strcmp(argv[1], "alloc") == 0 ||
strcmp(argv[1], "map") == 0 ||
strcmp(argv[1], "unmap") == 0 ||
strcmp(argv[1], "pin") == 0 ||
strcmp(argv[1], "info") == 0) {
fprintf(stderr,
"rmtool: 'mem %s' is a write/ownership operation and "
"is refused by policy (read-only host oracle). "
"See debug/risc-v-gsp-rm-vm/docs/17-RM-HARNESS.md.\n",
argv[1]);
return 4;
}
usage();
return 2;
}
|