From 002e3d7d13c414ea874abbee8d82803dd3a72fe5 Mon Sep 17 00:00:00 2001 From: Arthur Hanson Rasmusson Date: Fri, 2 Oct 2026 10:58:21 -0400 Subject: debug: green-eye wave 3 - RM harness, local simulator, device map, MCP plugin, value-validation demos, CI pipeline, toolchain docs; add host RM tools rmtool/rmtop --- tools/rmtool/src/engine.c | 252 ++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 252 insertions(+) create mode 100644 tools/rmtool/src/engine.c (limited to 'tools/rmtool/src/engine.c') diff --git a/tools/rmtool/src/engine.c b/tools/rmtool/src/engine.c new file mode 100644 index 0000000..c247e47 --- /dev/null +++ b/tools/rmtool/src/engine.c @@ -0,0 +1,252 @@ +/* engine.c - rmtool engine list/info via read-only GPU/engine controls. */ +#include +#include +#include +#include +#include + +#include "rmtool.h" + +static void usage(void) +{ + printf("usage: rmtool engine list [-d N|BDF] [--format=...]\n" + " rmtool engine info [-d N|BDF] -e ENGINE\n" + "ENGINE is a FIFO table name (gr, ce0, nvdec0, ...).\n"); +} + +static int parse_list(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; + } + if (optind < argc) { + fprintf(stderr, "rmtool: unexpected argument '%s'\n", + argv[optind]); + return -1; + } + return 0; +} + +static int engine_list(struct rm_ctx *ctx) +{ + NV2080_CTRL_GPU_GET_ENGINES_V2_PARAMS engines; + NV2080_FIFO_DEVICE_ENTRY *fifo; + const char *headers[] = { "IDX", "TYPE", "NAME", "PBDMAS", + "PBDMA IDS" }; + char (*cells)[64]; + int rc; + NvU32 status, n_fifo = 0; + unsigned int i; + + fifo = calloc(1, sizeof(*fifo) * 512); + cells = calloc(1, sizeof(*cells) * (NV2080_GPU_MAX_ENGINES_LIST_SIZE * + 5)); + if (fifo == NULL || cells == NULL) { + free(fifo); + free(cells); + return 2; + } + rc = rm_ctx_open(ctx); + if (rc != 0) { + fprintf(stderr, "rmtool: cannot open RM client: rc=%d\n", rc); + free(fifo); + free(cells); + return 2; + } + status = rm_query_engines(ctx, &engines); + if (status != 0) { + fprintf(stderr, "rmtool: GPU_GET_ENGINES_V2: %s\n", + rm_status_str(status)); + free(fifo); + free(cells); + return 2; + } + status = rm_query_fifo_table(ctx, fifo, 512, &n_fifo); + + for (i = 0; i < engines.engineCount; i++) { + char type[24]; + + snprintf(type, sizeof(type), "0x%x", engines.engineList[i]); + snprintf(cells[i * 5 + 0], sizeof(cells[0]), "%u", i); + snprintf(cells[i * 5 + 1], sizeof(cells[0]), "%s", type); + snprintf(cells[i * 5 + 2], sizeof(cells[0]), "%s", + rm_engine_name(fifo, n_fifo, engines.engineList[i])); + snprintf(cells[i * 5 + 3], sizeof(cells[0]), "-"); + snprintf(cells[i * 5 + 4], sizeof(cells[0]), "-"); + } + if (status == 0) { + NvU32 j; + + for (j = 0; j < n_fifo && j < engines.engineCount; j++) { + snprintf(cells[j * 5 + 2], sizeof(cells[0]), "%s", + fifo[j].engineName); + snprintf(cells[j * 5 + 3], sizeof(cells[0]), "%u", + fifo[j].numPbdmas); + snprintf(cells[j * 5 + 4], sizeof(cells[0]), + "%u,%u", fifo[j].pbdmaIds[0], + fifo[j].pbdmaIds[1]); + } + } + if (ctx->format == RM_FMT_TABLE) + printf("%u engine(s); FIFO table %s\n", engines.engineCount, + status == 0 ? "read" : "unavailable"); + rm_print_table("engines", headers, &cells[0][0], sizeof(cells[0]), + (int)engines.engineCount, 5, ctx->format); + free(fifo); + free(cells); + return 0; +} + +static int engine_info(struct rm_ctx *ctx, const char *want) +{ + NV2080_FIFO_DEVICE_ENTRY *fifo; + struct rm_kv kv[8]; + char v[8][256]; + NvU32 n_fifo = 0, i; + int rc, n = 0; + NvU32 status; + + fifo = calloc(1, sizeof(*fifo) * 512); + if (fifo == NULL) + return 2; + rc = rm_ctx_open(ctx); + if (rc != 0) { + fprintf(stderr, "rmtool: cannot open RM client: rc=%d\n", rc); + free(fifo); + return 2; + } + status = rm_query_fifo_table(ctx, fifo, 512, &n_fifo); + if (status != 0) { + fprintf(stderr, "rmtool: FIFO_GET_DEVICE_INFO_TABLE: %s\n", + rm_status_str(status)); + free(fifo); + return 2; + } + for (i = 0; i < n_fifo; i++) { + if (strcasecmp(fifo[i].engineName, want) != 0) + continue; + snprintf(v[0], sizeof(v[0]), "%s", fifo[i].engineName); + kv[n].key = "Engine"; + kv[n++].value = v[0]; + snprintf(v[1], sizeof(v[1]), "%u", i); + kv[n].key = "Table index"; + kv[n++].value = v[1]; + snprintf(v[2], sizeof(v[2]), "%u", fifo[i].numPbdmas); + kv[n].key = "PBDMA count"; + kv[n++].value = v[2]; + snprintf(v[3], sizeof(v[3]), "%u,%u", fifo[i].pbdmaIds[0], + fifo[i].pbdmaIds[1]); + kv[n].key = "PBDMA ids"; + kv[n++].value = v[3]; + snprintf(v[4], sizeof(v[4]), "%u,%u", + fifo[i].pbdmaFaultIds[0], fifo[i].pbdmaFaultIds[1]); + kv[n].key = "PBDMA fault ids"; + kv[n++].value = v[4]; + snprintf(v[5], sizeof(v[5]),"%u", fifo[i].engineData[0]); + kv[n].key = "engineData[0] (type)"; + kv[n++].value = v[5]; + { + size_t off = 0; + unsigned int j; + + for (j = 0; j < 16 && off < sizeof(v[6]); j++) + off += (size_t)snprintf(v[6] + off, + sizeof(v[6]) - off, + "%s%u", j ? "," : "", + fifo[i].engineData[j]); + } + kv[n].key = "engineData[0..15]"; + kv[n++].value = v[6]; + rm_print_kv("engine info (FIFO device table)", kv, n, + ctx->format); + free(fifo); + return 0; + } + fprintf(stderr, "rmtool: engine '%s' not found in FIFO table " + "(%u entries)\n", want, n_fifo); + free(fifo); + return 2; +} + +int cmd_engine(int argc, char **argv, struct rm_ctx *ctx) +{ + const char *want = NULL; + int rc; + + if (argc < 2) { + usage(); + return 2; + } + if (strcmp(argv[1], "list") == 0) { + rc = parse_list(argc - 1, argv + 1, ctx); + if (rc < 0) + return 2; + if (rc == 1) { + usage(); + return 0; + } + return engine_list(ctx); + } + if (strcmp(argv[1], "info") == 0) { + int opt; + static const struct option long_opts[] = { + { "device-spec", required_argument, NULL, 'd' }, + { "format", required_argument, NULL, 'f' }, + { "engine", required_argument, NULL, 'e' }, + { "device", required_argument, NULL, 1000 }, + { "help", no_argument, NULL, 'h' }, + { NULL, 0, NULL, 0 }, + }; + + optind = 1; + opterr = 0; + while ((opt = getopt_long(argc - 1, argv + 1, "d:f:e:h", + long_opts, NULL)) != -1) { + if (opt == 'h') { + usage(); + return 0; + } + if (opt == 'e') { + want = optarg; + continue; + } + if (rm_apply_common_opt(ctx, opt, optarg) < 0) + return 2; + } + if (want == NULL) { + usage(); + return 2; + } + return engine_info(ctx, want); + } + if (strcmp(argv[1], "allocate") == 0 || + strcmp(argv[1], "start") == 0 || + strcmp(argv[1], "stop") == 0 || + strcmp(argv[1], "config") == 0) { + fprintf(stderr, + "rmtool: 'engine %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; +} -- cgit v1.2.3