[PATCH umr] Add the ability to update the register/bitfield database at runtime.

Tom St Denis tom.stdenis at amd.com
Tue Apr 11 08:45:06 UTC 2017


Support for deleting entries will be added later.  The syntax is fairly
simple but the parser isn't really robust so don't feed it garbage.  It
will happily eat whitespace between tokens so you can indent however you
want.

Each command starts with "add" or "edit" and then "bit" or "reg"
followed by a path and then an address or start/stop pair, e.g.

add reg tonga.uvd5.mmFOO 0x12345678
add bit tonga.uvd5.mmFOO.BAZ_ENABLEMENT 0 1

or

edit reg tonga.uvd5.mmFOO 0x87654321

You use it by specifying "--update /path/to/script" after any
other options/instance commands.  It will ignore any updates for
asics that aren't currently being used so you can specify
multiple asic updates in a single file.

Signed-off-by: Tom St Denis <tom.stdenis at amd.com>
---
 src/app/main.c         |  10 ++
 src/lib/CMakeLists.txt |   1 +
 src/lib/update.c       | 427 +++++++++++++++++++++++++++++++++++++++++++++++++
 src/umr.h              |   1 +
 4 files changed, 439 insertions(+)
 create mode 100644 src/lib/update.c

diff --git a/src/app/main.c b/src/app/main.c
index 3fc1ee947a39..96a790cbec7c 100644
--- a/src/app/main.c
+++ b/src/app/main.c
@@ -363,6 +363,16 @@ int main(int argc, char **argv)
 				printf("--option requires one parameter\n");
 				return EXIT_FAILURE;
 			}
+		} else if (!strcmp(argv[i], "--update") || !strcmp(argv[i], "-u")) {
+			if (!asic)
+				asic = get_asic();
+			if (i + 1 < argc) {
+				umr_update(asic, argv[i+1]);
+				++i;
+			} else {
+				printf("--update requires one parameter\n");
+				return EXIT_FAILURE;
+			}
 		} else if (!strcmp(argv[i], "--help") || !strcmp(argv[i], "-h")) {
 			printf("User Mode Register debugger v%s for AMDGPU devices (build: %s), Copyright (c) 2017, AMD Inc.\n"
 "\n\t--instance, -i <number>\n\t\tSelect a device instance to investigate. (default: 0)"
diff --git a/src/lib/CMakeLists.txt b/src/lib/CMakeLists.txt
index b7c249452c36..dfdf44cf4ad2 100644
--- a/src/lib/CMakeLists.txt
+++ b/src/lib/CMakeLists.txt
@@ -22,6 +22,7 @@ add_library(umrcore STATIC
   scan_config.c
   transfer_soc15.c
   wave_status.c
+  update.c
   $<TARGET_OBJECTS:asic> $<TARGET_OBJECTS:ip>
 )
 
diff --git a/src/lib/update.c b/src/lib/update.c
new file mode 100644
index 000000000000..8b6ff77887cc
--- /dev/null
+++ b/src/lib/update.c
@@ -0,0 +1,427 @@
+/*
+ * Copyright 2017 Advanced Micro Devices, Inc.
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a
+ * copy of this software and associated documentation files (the "Software"),
+ * to deal in the Software without restriction, including without limitation
+ * the rights to use, copy, modify, merge, publish, distribute, sublicense,
+ * and/or sell copies of the Software, and to permit persons to whom the
+ * Software is furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.  IN NO EVENT SHALL
+ * THE COPYRIGHT HOLDER(S) OR AUTHOR(S) BE LIABLE FOR ANY CLAIM, DAMAGES OR
+ * OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE,
+ * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
+ * OTHER DEALINGS IN THE SOFTWARE.
+ *
+ * Authors: Tom St Denis <tom.stdenis at amd.com>
+ *
+ */
+#include "umr.h"
+
+#define BUFLEN 512
+
+static void consume_whitespace(char **ptr)
+{
+	char *p = *ptr;
+	while (*p && (*p == ' ' || *p == '\t' || *p == '\n' || *p == '\r'))
+		++p;
+	*ptr = p;
+}
+
+static void consume_str(char **ptr, char *str)
+{
+	char *p;
+	int i;
+
+	consume_whitespace(ptr);
+
+	// now we're at the address
+	memset(str, 0, BUFLEN);
+	i = 0;
+	p = *ptr;
+	while (*p && !(*p == ' ' || *p == '\t' || *p == '\n' || *p == '\r'))
+		str[i++] = *p++;
+	*ptr = p;
+
+	if (!strlen(str))
+		fprintf(stderr, "[ERROR] Premature end of file\n");
+}
+
+static void parse_regpath(char **ptr, char *asic, char *ip, char *reg)
+{
+	char *p, *op, path[BUFLEN];
+
+	// now were at the intended path, e.g. tonga.uvd5.mmFOO so let's extract that first
+	memset(asic, 0, BUFLEN);
+	memset(ip,   0, BUFLEN);
+	memset(reg,  0, BUFLEN);
+
+	consume_whitespace(ptr);
+	consume_str(ptr, path);
+	consume_whitespace(ptr);
+
+	// tokenize path...
+	p = strstr(path, ".");
+	if (p) {
+		memcpy(asic, path, p - path);
+		op = ++p;
+		p = strstr(p, ".");
+		if (p) {
+			memcpy(ip, op, p - op);
+			strcpy(reg, p + 1);
+		}
+	}
+}
+
+static void parse_bitpath(char **ptr, char *asic, char *ip, char *reg, char *bit)
+{
+	char *p, *op, path[BUFLEN];
+
+	// now were at the intended path, e.g. tonga.uvd5.mmFOO so let's extract that first
+	memset(asic, 0, BUFLEN);
+	memset(ip,   0, BUFLEN);
+	memset(reg,  0, BUFLEN);
+	memset(bit,  0, BUFLEN);
+
+	consume_whitespace(ptr);
+	consume_str(ptr, path);
+	consume_whitespace(ptr);
+
+	// tokenize path...
+	p = strstr(path, ".");
+	if (p) {
+		memcpy(asic, path, p - path);
+		op = ++p;
+		p = strstr(p, ".");
+		if (p) {
+			memcpy(ip, op, p - op);
+			op = ++p;
+			p = strstr(p, ".");
+			if (p) {
+				memcpy(reg, op, p - op);
+				strcpy(bit, p + 1);
+			}
+		}
+	}
+}
+
+static int do_add_reg(char **ptr, struct umr_asic *as)
+{
+	char	asic[BUFLEN], ip[BUFLEN],
+		reg[BUFLEN], addr[BUFLEN];
+	int i, j;
+	struct umr_reg newreg;
+
+	parse_regpath(ptr, asic, ip, reg);
+	consume_str(ptr, addr);
+
+	// now try to find that asic/ip block
+	if (strcmp(as->asicname, asic)) {
+		// ignore asics that don't apply
+		return 0;
+	}
+
+	// now scan for the IP block
+	for (i = 0; i < as->no_blocks; i++) {
+		if (!strcmp(as->blocks[i]->ipname, ip))
+			break;
+	}
+
+	if (i == as->no_blocks) {
+		fprintf(stderr, "[ERROR] ASIC %s has no IP block %s\n", asic, ip);
+		return -1;
+	}
+
+	// make sure the register doesn't exist already
+	for (j = 0; j < as->blocks[i]->no_regs; j++) {
+		if (!strcmp(reg, as->blocks[i]->regs[j].regname)) {
+			fprintf(stderr, "[ERROR] path %s.%s.%s already exists\n", asic, ip, reg);
+			return -1;
+		}
+	}
+
+	// create new register entry
+	memset(&newreg, 0, sizeof(newreg));
+	newreg.regname = calloc(1, strlen(reg)+1);
+	strcpy(newreg.regname, reg);
+	newreg.type = REG_MMIO;
+	sscanf(addr, "%"SCNx32, &newreg.addr);
+
+	// extend array
+	as->blocks[i]->regs = realloc(as->blocks[i]->regs, sizeof(struct umr_reg) * (as->blocks[i]->no_regs + 1));
+	as->blocks[i]->regs[as->blocks[i]->no_regs++] = newreg;
+
+	return 0;
+}
+
+static int do_add_bit(char **ptr, struct umr_asic *as)
+{
+	char	asic[BUFLEN], ip[BUFLEN],
+		reg[BUFLEN], bit[BUFLEN], start[BUFLEN], stop[BUFLEN];
+	int i, j, k;
+	struct umr_bitfield newbit;
+	uint32_t nstart, nstop;
+
+	parse_bitpath(ptr, asic, ip, reg, bit);
+	consume_str(ptr, start);
+	consume_str(ptr, stop);
+
+	// now try to find that asic/ip block
+	if (strcmp(as->asicname, asic)) {
+		// ignore asics that don't apply
+		return 0;
+	}
+
+	// now scan for the IP block
+	for (i = 0; i < as->no_blocks; i++) {
+		if (!strcmp(as->blocks[i]->ipname, ip))
+			break;
+	}
+
+	if (i == as->no_blocks) {
+		fprintf(stderr, "[ERROR] ASIC %s has no IP block %s\n", asic, ip);
+		return -1;
+	}
+
+	// make sure the register DOES exist already
+	for (j = 0; j < as->blocks[i]->no_regs; j++) {
+		if (!strcmp(reg, as->blocks[i]->regs[j].regname))
+			break;
+	}
+
+	if (j == as->blocks[i]->no_regs) {
+		fprintf(stderr, "[ERROR] IP %s has no register %s\n", ip, reg);
+		return -1;
+	}
+
+
+	// make sure the register DOESN'T exist already
+	for (k = 0; k < as->blocks[i]->regs[j].no_bits; k++) {
+		if (!strcmp(bit, as->blocks[i]->regs[j].regname)) {
+			fprintf(stderr, "[ERROR] register %s.%s already has bit %s\n", ip, reg, bit);
+			return -1;
+		}
+	}
+
+	// create new register entry
+	memset(&newbit, 0, sizeof(newbit));
+	newbit.regname = calloc(1, strlen(bit)+1);
+	strcpy(newbit.regname, bit);
+	sscanf(start, "%"SCNu32, &nstart);
+	sscanf(stop, "%"SCNu32, &nstop);
+	newbit.start = nstart;
+	newbit.stop = nstop;
+
+	// extend bits
+	if (as->blocks[i]->regs[j].no_bits == 0) {
+		// empty array so lets populate it
+		as->blocks[i]->regs[j].bits = calloc(1, sizeof(struct umr_bitfield));
+		as->blocks[i]->regs[j].bits[0] = newbit;
+		as->blocks[i]->regs[j].no_bits = 1;
+	} else {
+		as->blocks[i]->regs[j].bits = realloc(as->blocks[i]->regs[j].bits, sizeof(struct umr_bitfield) * (as->blocks[i]->regs[j].no_bits + 1));
+		as->blocks[i]->regs[j].bits[as->blocks[i]->regs[j].no_bits++] = newbit;
+	}
+
+	return 0;
+}
+
+static int do_add(char **ptr, struct umr_asic *asic)
+{
+	consume_whitespace(ptr);
+
+	// now we're pointing at reg or bit
+	if (!memcmp(*ptr, "reg", 3)) {
+		*ptr += 3;
+		do_add_reg(ptr, asic);
+	} else if (!memcmp(*ptr, "bit", 3)) {
+		*ptr += 3;
+		do_add_bit(ptr, asic);
+	} else {
+		fprintf(stderr, "[ERROR] Invalid add command\n");
+		return -1;
+	}
+	return 0;
+}
+
+static int do_edit_reg(char **ptr, struct umr_asic *as)
+{
+	char	asic[BUFLEN], ip[BUFLEN],
+		reg[BUFLEN], addr[BUFLEN];
+	int i, j;
+
+	parse_regpath(ptr, asic, ip, reg);
+	consume_str(ptr, addr);
+
+	// now try to find that asic/ip block
+	if (strcmp(as->asicname, asic)) {
+		// ignore asics that don't apply
+		return 0;
+	}
+
+	// now scan for the IP block
+	for (i = 0; i < as->no_blocks; i++) {
+		if (!strcmp(as->blocks[i]->ipname, ip))
+			break;
+	}
+
+	if (i == as->no_blocks) {
+		fprintf(stderr, "[ERROR] ASIC %s has no IP block %s\n", asic, ip);
+		return -1;
+	}
+
+	// make sure the register DOES exist already
+	for (j = 0; j < as->blocks[i]->no_regs; j++) {
+		if (!strcmp(reg, as->blocks[i]->regs[j].regname))
+			break;
+	}
+
+	if (j == as->blocks[i]->no_regs) {
+		fprintf(stderr, "[ERROR] IP %s has no register %s\n", ip, reg);
+		return -1;
+	}
+
+	// replace address
+	sscanf(addr, "%"SCNx32, &as->blocks[i]->regs[j].addr);
+
+	return 0;
+}
+
+static int do_edit_bit(char **ptr, struct umr_asic *as)
+{
+	char	asic[BUFLEN], ip[BUFLEN],
+		reg[BUFLEN], bit[BUFLEN], start[BUFLEN], stop[BUFLEN];
+	int i, j, k;
+	uint32_t nstart, nstop;
+
+	parse_bitpath(ptr, asic, ip, reg, bit);
+	consume_str(ptr, start);
+	consume_str(ptr, stop);
+
+	// now try to find that asic/ip block
+	if (strcmp(as->asicname, asic)) {
+		// ignore asics that don't apply
+		return 0;
+	}
+
+	// now scan for the IP block
+	for (i = 0; i < as->no_blocks; i++) {
+		if (!strcmp(as->blocks[i]->ipname, ip))
+			break;
+	}
+
+	if (i == as->no_blocks) {
+		fprintf(stderr, "[ERROR] ASIC %s has no IP block %s\n", asic, ip);
+		return -1;
+	}
+
+	// make sure the register DOES exist already
+	for (j = 0; j < as->blocks[i]->no_regs; j++) {
+		if (!strcmp(reg, as->blocks[i]->regs[j].regname))
+			break;
+	}
+
+	if (j == as->blocks[i]->no_regs) {
+		fprintf(stderr, "[ERROR] IP %s has no register %s\n", ip, reg);
+		return -1;
+	}
+
+	if (as->blocks[i]->regs[j].no_bits == 0) {
+		fprintf(stderr, "[ERROR] register %s.%s has no bits!\n", ip, reg);
+		return -1;
+	}
+
+	// make sure bit exists
+	for (k = 0; k < as->blocks[i]->regs[j].no_bits; k++)
+		if (!strcmp(bit, as->blocks[i]->regs[j].bits[k].regname))
+			break;
+
+	if (k == as->blocks[i]->regs[j].no_bits) {
+		fprintf(stderr, "[ERROR] register %s.%s has no bit named %s\n", ip, reg, bit);
+		return -1;
+	}
+
+	sscanf(start, "%"SCNu32, &nstart);
+	sscanf(stop, "%"SCNu32, &nstop);
+	as->blocks[i]->regs[j].bits[k].start = nstart;
+	as->blocks[i]->regs[j].bits[k].stop = nstop;
+	return 0;
+}
+
+static int do_edit(char **ptr, struct umr_asic *asic)
+{
+	consume_whitespace(ptr);
+
+	// now we're pointing at reg or bit
+	if (!memcmp(*ptr, "reg", 3)) {
+		*ptr += 3;
+		do_edit_reg(ptr, asic);
+	} else if (!memcmp(*ptr, "bit", 3)) {
+		*ptr += 3;
+		do_edit_bit(ptr, asic);
+	} else {
+		fprintf(stderr, "[ERROR] Invalid edit command\n");
+		return -1;
+	}
+	return 0;
+}
+
+static void do_del(char **ptr, struct umr_asic *asic)
+{
+	// TODO: Add delete support...
+	(void)ptr;
+	(void)asic;
+}
+
+int umr_update(struct umr_asic *asic, char *script)
+{
+	int fd;
+	void *smem;
+	char *sdata;
+	unsigned len;
+
+	fd = open(script, O_RDWR);
+	if (fd < 0) {
+		fprintf(stderr, "[ERROR] Script <%s> not found\n", script);
+		return -1;
+	}
+	len = lseek(fd, 0, SEEK_END) + 1;
+	sdata = smem = calloc(1, len);
+	lseek(fd, 0, SEEK_SET);
+	if (!smem) {
+		close(fd);
+		fprintf(stderr, "[ERROR] Out of memory...\n");
+		return -1;
+	}
+	read(fd, smem, len);
+	close(fd);
+
+	// parse script
+	while (*sdata) {
+		consume_whitespace(&sdata);
+
+		// should be pointing to "add/edit/del" now
+		if (!memcmp(sdata, "add", 3)) {
+			sdata += 3;
+			if (do_add(&sdata, asic))
+				*sdata = 0;
+		} else if (!memcmp(sdata, "edit", 4)) {
+			sdata += 4;
+			do_edit(&sdata, asic);
+		} else if (!memcmp(sdata, "del", 3)) {
+			sdata += 3;
+			do_del(&sdata, asic);
+		} else if (*sdata) {
+			fprintf(stderr, "[ERROR] Unknown update command [%s]\n", sdata);
+			*sdata = 0;
+		}
+	}
+	free(smem);
+	return 0;
+}
diff --git a/src/umr.h b/src/umr.h
index 33a77fc24c57..27d0015d17a3 100644
--- a/src/umr.h
+++ b/src/umr.h
@@ -444,6 +444,7 @@ void umr_free_asic(struct umr_asic *asic);
 void umr_close_asic(struct umr_asic *asic); // call this to close a fully open asic
 int umr_query_drm(struct umr_asic *asic, int field, uint64_t *ret);
 void umr_enumerate_devices(void);
+int umr_update(struct umr_asic *asic, char *script);
 
 /* lib helpers */
 int umr_get_wave_status(struct umr_asic *asic, unsigned se, unsigned sh, unsigned cu, unsigned simd, unsigned wave, struct umr_wave_status *ws);
-- 
2.12.0



More information about the amd-gfx mailing list