[igt-dev] [PATCH i-g-t v8 5/7] tools/xe_reg: Add Xe register read/write tool

Zbigniew Kempczyński zbigniew.kempczynski at intel.com
Wed Mar 8 07:53:32 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 &reg_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, &reg_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