[igt-dev] [PATCH i-g-t v4 5/8] tools/xe_reg: Add Xe register read/write tool
Zbigniew Kempczyński
zbigniew.kempczynski at intel.com
Thu Mar 2 11:09:44 UTC 2023
Add xe_reg tool which allows to manipulate Xe registers.
Sample use:
./xe_reg read --ring rcs0 --all
./xe_reg read --ring rcs0 EXECLIST_STATUS
At this moment rings rcs0 and bcs0 are supported.
Signed-off-by: Jason Ekstrand <jason at jlekstrand.net>
Signed-off-by: Zbigniew Kempczyński <zbigniew.kempczynski at intel.com>
Acked-by: Mauro Carvalho Chehab <mchehab at kernel.org>
---
tools/meson.build | 1 +
tools/xe_reg.c | 366 ++++++++++++++++++++++++++++++++++++++++++++++
2 files changed, 367 insertions(+)
create mode 100644 tools/xe_reg.c
diff --git a/tools/meson.build b/tools/meson.build
index d2defec870..4c45f16b91 100644
--- a/tools/meson.build
+++ b/tools/meson.build
@@ -42,6 +42,7 @@ tools_progs = [
'intel_gvtg_test',
'dpcd_reg',
'lsgpu',
+ 'xe_reg',
]
tool_deps = igt_deps
tool_deps += zlib
diff --git a/tools/xe_reg.c b/tools/xe_reg.c
new file mode 100644
index 0000000000..1f7b384d32
--- /dev/null
+++ b/tools/xe_reg.c
@@ -0,0 +1,366 @@
+// SPDX-License-Identifier: MIT
+/*
+ * Copyright © 2021 Intel Corporation
+ */
+
+#include "igt.h"
+#include "igt_device_scan.h"
+
+#include "xe_drm.h"
+
+#include <stdlib.h>
+#include <stdio.h>
+#include <string.h>
+
+#define DECL_XE_MMIO_READ_FN(bits) \
+static inline uint##bits##_t \
+xe_mmio_read##bits(int fd, uint32_t reg) \
+{ \
+ struct drm_xe_mmio mmio = { \
+ .addr = reg, \
+ .flags = DRM_XE_MMIO_READ | DRM_XE_MMIO_##bits##BIT, \
+ }; \
+\
+ igt_assert_eq(igt_ioctl(fd, DRM_IOCTL_XE_MMIO, &mmio), 0); \
+\
+ return mmio.value;\
+}\
+static inline void \
+xe_mmio_write##bits(int fd, uint32_t reg, uint##bits##_t value) \
+{ \
+ struct drm_xe_mmio mmio = { \
+ .addr = reg, \
+ .flags = DRM_XE_MMIO_WRITE | DRM_XE_MMIO_##bits##BIT, \
+ .value = value, \
+ }; \
+\
+ igt_assert_eq(igt_ioctl(fd, DRM_IOCTL_XE_MMIO, &mmio), 0); \
+}
+
+DECL_XE_MMIO_READ_FN(8)
+DECL_XE_MMIO_READ_FN(16)
+DECL_XE_MMIO_READ_FN(32)
+DECL_XE_MMIO_READ_FN(64)
+
+static void print_help(FILE *fp)
+{
+ fprintf(fp, "usage: xe_reg read REG1 [REG2]...\n");
+ fprintf(fp, " xe_reg write REG VALUE\n");
+}
+
+enum ring {
+ RING_UNKNOWN = -1,
+ RING_RCS0,
+ RING_BCS0,
+};
+
+static const struct ring_info {
+ enum ring ring;
+ const char *name;
+ uint32_t mmio_base;
+} ring_info[] = {
+ {RING_RCS0, "rcs0", 0x02000, },
+ {RING_BCS0, "bcs0", 0x22000, },
+};
+
+static const struct ring_info *ring_info_for_name(const char *name)
+{
+ int i;
+
+ for (i = 0; i < ARRAY_SIZE(ring_info); i++)
+ if (strcmp(name, ring_info[i].name) == 0)
+ return &ring_info[i];
+
+ return NULL;
+}
+
+struct reg_info {
+ const char *name;
+ bool is_ring;
+ uint32_t addr_low;
+ uint32_t addr_high;
+} reg_info[] = {
+#define REG32(name, addr) { #name, false, addr }
+#define REG64(name, low, high) { #name, false, low, high }
+#define RING_REG32(name, addr) { #name, true, addr }
+#define RING_REG64(name, low, high) { #name, true, low, high }
+
+ RING_REG64(ACTHD, 0x74, 0x5c),
+ RING_REG32(BB_ADDR_DIFF, 0x154),
+ RING_REG64(BB_ADDR, 0x140, 0x168),
+ RING_REG32(BB_PER_CTX_PTR, 0x2c0),
+ RING_REG64(EXECLIST_STATUS, 0x234, 0x238),
+ RING_REG64(EXECLIST_SQ0, 0x510, 0x514),
+ RING_REG64(EXECLIST_SQ1, 0x518, 0x51c),
+ RING_REG32(HWS_PGA, 0x80),
+ RING_REG32(INDIRECT_CTX, 0x1C4),
+ RING_REG32(INDIRECT_CTX_OFFSET, 0x1C8),
+ RING_REG32(NOPID, 0x94),
+ RING_REG64(PML4E, 0x270, 0x274),
+ RING_REG32(RING_BUFFER_CTL, 0x3c),
+ RING_REG32(RING_BUFFER_HEAD, 0x34),
+ RING_REG32(RING_BUFFER_START, 0x38),
+ RING_REG32(RING_BUFFER_TAIL, 0x30),
+ RING_REG64(SBB_ADDR, 0x114, 0x11c),
+ RING_REG32(SBB_STATE, 0x118),
+
+#undef REG32
+#undef REG64
+#undef RING_REG32
+#undef RING_REG64
+};
+
+static const struct reg_info *reg_info_for_name(const char *name)
+{
+ int i;
+
+ for (i = 0; i < ARRAY_SIZE(reg_info); i++)
+ if (strcmp(name, reg_info[i].name) == 0)
+ return ®_info[i];
+
+ return NULL;
+}
+
+static int print_reg_for_info(int xe, FILE *fp, const struct reg_info *reg,
+ const struct ring_info *ring)
+{
+ if (reg->is_ring) {
+ if (!ring) {
+ fprintf(stderr, "%s is a ring register but --ring "
+ "not set\n", reg->name);
+ return EXIT_FAILURE;
+ }
+
+ if (reg->addr_high) {
+ uint32_t low = xe_mmio_read32(xe, reg->addr_low +
+ ring->mmio_base);
+ uint32_t high = xe_mmio_read32(xe, reg->addr_high +
+ ring->mmio_base);
+
+ fprintf(fp, "%s[%s] = 0x%08x %08x\n", reg->name,
+ ring->name, high, low);
+ } else {
+ uint32_t value = xe_mmio_read32(xe, reg->addr_low +
+ ring->mmio_base);
+
+ fprintf(fp, "%s[%s] = 0x%08x\n", reg->name,
+ ring->name, value);
+ }
+ } else {
+ if (reg->addr_high) {
+ uint32_t low = xe_mmio_read32(xe, reg->addr_low);
+ uint32_t high = xe_mmio_read32(xe, reg->addr_high);
+
+ fprintf(fp, "%s = 0x%08x %08x\n", reg->name, high, low);
+ } else {
+ uint32_t value = xe_mmio_read32(xe, reg->addr_low);
+
+ fprintf(fp, "%s = 0x%08x\n", reg->name, value);
+ }
+ }
+
+ return 0;
+}
+
+static void print_reg_for_addr(int xe, FILE *fp, uint32_t addr)
+{
+ uint32_t value = xe_mmio_read32(xe, addr);
+
+ fprintf(fp, "MMIO[0x%05x] = 0x%08x\n", addr, value);
+}
+
+enum opt {
+ OPT_UNKNOWN = '?',
+ OPT_END = -1,
+ OPT_DEVICE,
+ OPT_RING,
+ OPT_ALL,
+};
+
+static int read_reg(int argc, char *argv[])
+{
+ int xe, i, err, index;
+ unsigned long reg_addr;
+ char *endp = NULL;
+ const struct ring_info *ring = NULL;
+ enum opt opt;
+ bool dump_all = false;
+
+ static struct option options[] = {
+ { "device", required_argument, NULL, OPT_DEVICE },
+ { "ring", required_argument, NULL, OPT_RING },
+ { "all", no_argument, NULL, OPT_ALL },
+ };
+
+ for (opt = 0; opt != OPT_END; ) {
+ opt = getopt_long(argc, argv, "", options, &index);
+
+ switch (opt) {
+ case OPT_DEVICE:
+ igt_device_filter_add(optarg);
+ break;
+ case OPT_RING:
+ ring = ring_info_for_name(optarg);
+ if (!ring) {
+ fprintf(stderr, "invalid ring: %s\n", optarg);
+ return EXIT_FAILURE;
+ }
+ break;
+ case OPT_ALL:
+ dump_all = true;
+ break;
+ case OPT_END:
+ break;
+ case OPT_UNKNOWN:
+ return EXIT_FAILURE;
+ }
+ }
+
+ argc -= optind;
+ argv += optind;
+
+ xe = drm_open_driver(DRIVER_XE);
+ if (dump_all) {
+ for (i = 0; i < ARRAY_SIZE(reg_info); i++) {
+ if (reg_info[i].is_ring != !!ring)
+ continue;
+
+ print_reg_for_info(xe, stdout, ®_info[i], ring);
+ }
+ } else {
+ for (i = 0; i < argc; i++) {
+ const struct reg_info *reg = reg_info_for_name(argv[i]);
+ if (reg) {
+ err = print_reg_for_info(xe, stdout, reg, ring);
+ if (err)
+ return err;
+ continue;
+ }
+ reg_addr = strtoul(argv[i], &endp, 16);
+ if (!reg_addr || reg_addr >= (4 << 20) || *endp) {
+ fprintf(stderr, "invalid reg address '%s'\n",
+ argv[i]);
+ return EXIT_FAILURE;
+ }
+ print_reg_for_addr(xe, stdout, reg_addr);
+ }
+ }
+
+ return 0;
+}
+
+static int write_reg_for_info(int xe, const struct reg_info *reg,
+ const struct ring_info *ring,
+ uint64_t value)
+{
+ if (reg->is_ring) {
+ if (!ring) {
+ fprintf(stderr, "%s is a ring register but --ring "
+ "not set\n", reg->name);
+ return EXIT_FAILURE;
+ }
+
+ xe_mmio_write32(xe, reg->addr_low + ring->mmio_base, value);
+ if (reg->addr_high) {
+ xe_mmio_write32(xe, reg->addr_high + ring->mmio_base,
+ value >> 32);
+ }
+ } else {
+ xe_mmio_write32(xe, reg->addr_low, value);
+ if (reg->addr_high)
+ xe_mmio_write32(xe, reg->addr_high, value >> 32);
+ }
+
+ return 0;
+}
+
+static void write_reg_for_addr(int xe, uint32_t addr, uint32_t value)
+{
+ xe_mmio_write32(xe, addr, value);
+}
+
+static int write_reg(int argc, char *argv[])
+{
+ int xe, index;
+ unsigned long reg_addr;
+ char *endp = NULL;
+ const struct ring_info *ring = NULL;
+ enum opt opt;
+ const char *reg_name;
+ const struct reg_info *reg;
+ uint64_t value;
+
+ static struct option options[] = {
+ { "device", required_argument, NULL, OPT_DEVICE },
+ { "ring", required_argument, NULL, OPT_RING },
+ };
+
+ for (opt = 0; opt != OPT_END; ) {
+ opt = getopt_long(argc, argv, "", options, &index);
+
+ switch (opt) {
+ case OPT_DEVICE:
+ igt_device_filter_add(optarg);
+ break;
+ case OPT_RING:
+ ring = ring_info_for_name(optarg);
+ if (!ring) {
+ fprintf(stderr, "invalid ring: %s\n", optarg);
+ return EXIT_FAILURE;
+ }
+ break;
+ case OPT_END:
+ break;
+ case OPT_UNKNOWN:
+ return EXIT_FAILURE;
+ default:
+ break;
+ }
+ }
+
+ argc -= optind;
+ argv += optind;
+
+ if (argc != 2) {
+ print_help(stderr);
+ return EXIT_FAILURE;
+ }
+
+ reg_name = argv[0];
+ value = strtoull(argv[1], &endp, 0);
+ if (*endp) {
+ fprintf(stderr, "Invalid register value: %s\n", argv[1]);
+ return EXIT_FAILURE;
+ }
+
+ xe = drm_open_driver(DRIVER_XE);
+
+ reg = reg_info_for_name(reg_name);
+ if (reg)
+ return write_reg_for_info(xe, reg, ring, value);
+
+ reg_addr = strtoul(reg_name, &endp, 16);
+ if (!reg_addr || reg_addr >= (4 << 20) || *endp) {
+ fprintf(stderr, "invalid reg address '%s'\n", reg_name);
+ return EXIT_FAILURE;
+ }
+ write_reg_for_addr(xe, reg_addr, value);
+
+ return 0;
+}
+
+int main(int argc, char *argv[])
+{
+ if (argc < 2) {
+ print_help(stderr);
+ return EXIT_FAILURE;
+ }
+
+ if (strcmp(argv[1], "read") == 0)
+ return read_reg(argc - 1, argv + 1);
+ else if (strcmp(argv[1], "write") == 0)
+ return write_reg(argc - 1, argv + 1);
+
+ fprintf(stderr, "invalid sub-command: %s", argv[1]);
+ return EXIT_FAILURE;
+}
--
2.34.1
More information about the igt-dev
mailing list