/* SPDX-License-Identifier: BSD-3-Clause */ /* Copyright (c) 2026 Open-IOV contributors */ /* 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; }