From mboxrd@z Thu Jan 1 00:00:00 1970 Return-Path: Received: from mga06.intel.com (mga06b.intel.com [134.134.136.31]) by gabe.freedesktop.org (Postfix) with ESMTPS id A4BDC10E3A3 for ; Tue, 21 Feb 2023 11:46:43 +0000 (UTC) From: =?UTF-8?q?Zbigniew=20Kempczy=C5=84ski?= To: igt-dev@lists.freedesktop.org Date: Tue, 21 Feb 2023 12:46:18 +0100 Message-Id: <20230221114621.32785-5-zbigniew.kempczynski@intel.com> In-Reply-To: <20230221114621.32785-1-zbigniew.kempczynski@intel.com> References: <20230221114621.32785-1-zbigniew.kempczynski@intel.com> MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Subject: [igt-dev] [PATCH i-g-t 4/7] tools/xe_reg: Add Xe register read/write tool List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , Errors-To: igt-dev-bounces@lists.freedesktop.org Sender: "igt-dev" List-ID: Signed-off-by: Jason Ekstrand --- 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 d2defec8..4c45f16b 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 00000000..1f7b384d --- /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 +#include +#include + +#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