162306a36Sopenharmony_ci// SPDX-License-Identifier: MIT
262306a36Sopenharmony_ci/* utility to create the register check tables
362306a36Sopenharmony_ci * this includes inlined list.h safe for userspace.
462306a36Sopenharmony_ci *
562306a36Sopenharmony_ci * Copyright 2009 Jerome Glisse
662306a36Sopenharmony_ci * Copyright 2009 Red Hat Inc.
762306a36Sopenharmony_ci *
862306a36Sopenharmony_ci * Authors:
962306a36Sopenharmony_ci * 	Jerome Glisse
1062306a36Sopenharmony_ci * 	Dave Airlie
1162306a36Sopenharmony_ci */
1262306a36Sopenharmony_ci
1362306a36Sopenharmony_ci#include <sys/types.h>
1462306a36Sopenharmony_ci#include <stdlib.h>
1562306a36Sopenharmony_ci#include <string.h>
1662306a36Sopenharmony_ci#include <stdio.h>
1762306a36Sopenharmony_ci#include <regex.h>
1862306a36Sopenharmony_ci#include <libgen.h>
1962306a36Sopenharmony_ci
2062306a36Sopenharmony_ci#define offsetof(TYPE, MEMBER) ((size_t) &((TYPE *)0)->MEMBER)
2162306a36Sopenharmony_ci/**
2262306a36Sopenharmony_ci * container_of - cast a member of a structure out to the containing structure
2362306a36Sopenharmony_ci * @ptr:    the pointer to the member.
2462306a36Sopenharmony_ci * @type:   the type of the container struct this is embedded in.
2562306a36Sopenharmony_ci * @member: the name of the member within the struct.
2662306a36Sopenharmony_ci *
2762306a36Sopenharmony_ci */
2862306a36Sopenharmony_ci#define container_of(ptr, type, member) ({          \
2962306a36Sopenharmony_ci	const typeof(((type *)0)->member)*__mptr = (ptr);    \
3062306a36Sopenharmony_ci		     (type *)((char *)__mptr - offsetof(type, member)); })
3162306a36Sopenharmony_ci
3262306a36Sopenharmony_ci/*
3362306a36Sopenharmony_ci * Simple doubly linked list implementation.
3462306a36Sopenharmony_ci *
3562306a36Sopenharmony_ci * Some of the internal functions ("__xxx") are useful when
3662306a36Sopenharmony_ci * manipulating whole lists rather than single entries, as
3762306a36Sopenharmony_ci * sometimes we already know the next/prev entries and we can
3862306a36Sopenharmony_ci * generate better code by using them directly rather than
3962306a36Sopenharmony_ci * using the generic single-entry routines.
4062306a36Sopenharmony_ci */
4162306a36Sopenharmony_ci
4262306a36Sopenharmony_cistruct list_head {
4362306a36Sopenharmony_ci	struct list_head *next, *prev;
4462306a36Sopenharmony_ci};
4562306a36Sopenharmony_ci
4662306a36Sopenharmony_ci
4762306a36Sopenharmony_cistatic inline void INIT_LIST_HEAD(struct list_head *list)
4862306a36Sopenharmony_ci{
4962306a36Sopenharmony_ci	list->next = list;
5062306a36Sopenharmony_ci	list->prev = list;
5162306a36Sopenharmony_ci}
5262306a36Sopenharmony_ci
5362306a36Sopenharmony_ci/*
5462306a36Sopenharmony_ci * Insert a new entry between two known consecutive entries.
5562306a36Sopenharmony_ci *
5662306a36Sopenharmony_ci * This is only for internal list manipulation where we know
5762306a36Sopenharmony_ci * the prev/next entries already!
5862306a36Sopenharmony_ci */
5962306a36Sopenharmony_ci#ifndef CONFIG_DEBUG_LIST
6062306a36Sopenharmony_cistatic inline void __list_add(struct list_head *new,
6162306a36Sopenharmony_ci			      struct list_head *prev, struct list_head *next)
6262306a36Sopenharmony_ci{
6362306a36Sopenharmony_ci	next->prev = new;
6462306a36Sopenharmony_ci	new->next = next;
6562306a36Sopenharmony_ci	new->prev = prev;
6662306a36Sopenharmony_ci	prev->next = new;
6762306a36Sopenharmony_ci}
6862306a36Sopenharmony_ci#else
6962306a36Sopenharmony_ciextern void __list_add(struct list_head *new,
7062306a36Sopenharmony_ci		       struct list_head *prev, struct list_head *next);
7162306a36Sopenharmony_ci#endif
7262306a36Sopenharmony_ci
7362306a36Sopenharmony_ci/**
7462306a36Sopenharmony_ci * list_add_tail - add a new entry
7562306a36Sopenharmony_ci * @new: new entry to be added
7662306a36Sopenharmony_ci * @head: list head to add it before
7762306a36Sopenharmony_ci *
7862306a36Sopenharmony_ci * Insert a new entry before the specified head.
7962306a36Sopenharmony_ci * This is useful for implementing queues.
8062306a36Sopenharmony_ci */
8162306a36Sopenharmony_cistatic inline void list_add_tail(struct list_head *new, struct list_head *head)
8262306a36Sopenharmony_ci{
8362306a36Sopenharmony_ci	__list_add(new, head->prev, head);
8462306a36Sopenharmony_ci}
8562306a36Sopenharmony_ci
8662306a36Sopenharmony_ci/**
8762306a36Sopenharmony_ci * list_entry - get the struct for this entry
8862306a36Sopenharmony_ci * @ptr:	the &struct list_head pointer.
8962306a36Sopenharmony_ci * @type:	the type of the struct this is embedded in.
9062306a36Sopenharmony_ci * @member:	the name of the list_head within the struct.
9162306a36Sopenharmony_ci */
9262306a36Sopenharmony_ci#define list_entry(ptr, type, member) \
9362306a36Sopenharmony_ci	container_of(ptr, type, member)
9462306a36Sopenharmony_ci
9562306a36Sopenharmony_ci/**
9662306a36Sopenharmony_ci * list_for_each_entry	-	iterate over list of given type
9762306a36Sopenharmony_ci * @pos:	the type * to use as a loop cursor.
9862306a36Sopenharmony_ci * @head:	the head for your list.
9962306a36Sopenharmony_ci * @member:	the name of the list_head within the struct.
10062306a36Sopenharmony_ci */
10162306a36Sopenharmony_ci#define list_for_each_entry(pos, head, member)				\
10262306a36Sopenharmony_ci	for (pos = list_entry((head)->next, typeof(*pos), member);	\
10362306a36Sopenharmony_ci	     &pos->member != (head); 	\
10462306a36Sopenharmony_ci	     pos = list_entry(pos->member.next, typeof(*pos), member))
10562306a36Sopenharmony_ci
10662306a36Sopenharmony_cistruct offset {
10762306a36Sopenharmony_ci	struct list_head list;
10862306a36Sopenharmony_ci	unsigned offset;
10962306a36Sopenharmony_ci};
11062306a36Sopenharmony_ci
11162306a36Sopenharmony_cistruct table {
11262306a36Sopenharmony_ci	struct list_head offsets;
11362306a36Sopenharmony_ci	unsigned offset_max;
11462306a36Sopenharmony_ci	unsigned nentry;
11562306a36Sopenharmony_ci	unsigned *table;
11662306a36Sopenharmony_ci	char *gpu_prefix;
11762306a36Sopenharmony_ci};
11862306a36Sopenharmony_ci
11962306a36Sopenharmony_cistatic struct offset *offset_new(unsigned o)
12062306a36Sopenharmony_ci{
12162306a36Sopenharmony_ci	struct offset *offset;
12262306a36Sopenharmony_ci
12362306a36Sopenharmony_ci	offset = (struct offset *)malloc(sizeof(struct offset));
12462306a36Sopenharmony_ci	if (offset) {
12562306a36Sopenharmony_ci		INIT_LIST_HEAD(&offset->list);
12662306a36Sopenharmony_ci		offset->offset = o;
12762306a36Sopenharmony_ci	}
12862306a36Sopenharmony_ci	return offset;
12962306a36Sopenharmony_ci}
13062306a36Sopenharmony_ci
13162306a36Sopenharmony_cistatic void table_offset_add(struct table *t, struct offset *offset)
13262306a36Sopenharmony_ci{
13362306a36Sopenharmony_ci	list_add_tail(&offset->list, &t->offsets);
13462306a36Sopenharmony_ci}
13562306a36Sopenharmony_ci
13662306a36Sopenharmony_cistatic void table_init(struct table *t)
13762306a36Sopenharmony_ci{
13862306a36Sopenharmony_ci	INIT_LIST_HEAD(&t->offsets);
13962306a36Sopenharmony_ci	t->offset_max = 0;
14062306a36Sopenharmony_ci	t->nentry = 0;
14162306a36Sopenharmony_ci	t->table = NULL;
14262306a36Sopenharmony_ci}
14362306a36Sopenharmony_ci
14462306a36Sopenharmony_cistatic void table_print(struct table *t)
14562306a36Sopenharmony_ci{
14662306a36Sopenharmony_ci	unsigned nlloop, i, j, n, c, id;
14762306a36Sopenharmony_ci
14862306a36Sopenharmony_ci	nlloop = (t->nentry + 3) / 4;
14962306a36Sopenharmony_ci	c = t->nentry;
15062306a36Sopenharmony_ci	printf("static const unsigned %s_reg_safe_bm[%d] = {\n", t->gpu_prefix,
15162306a36Sopenharmony_ci	       t->nentry);
15262306a36Sopenharmony_ci	for (i = 0, id = 0; i < nlloop; i++) {
15362306a36Sopenharmony_ci		n = 4;
15462306a36Sopenharmony_ci		if (n > c)
15562306a36Sopenharmony_ci			n = c;
15662306a36Sopenharmony_ci		c -= n;
15762306a36Sopenharmony_ci		for (j = 0; j < n; j++) {
15862306a36Sopenharmony_ci			if (j == 0)
15962306a36Sopenharmony_ci				printf("\t");
16062306a36Sopenharmony_ci			else
16162306a36Sopenharmony_ci				printf(" ");
16262306a36Sopenharmony_ci			printf("0x%08X,", t->table[id++]);
16362306a36Sopenharmony_ci		}
16462306a36Sopenharmony_ci		printf("\n");
16562306a36Sopenharmony_ci	}
16662306a36Sopenharmony_ci	printf("};\n");
16762306a36Sopenharmony_ci}
16862306a36Sopenharmony_ci
16962306a36Sopenharmony_cistatic int table_build(struct table *t)
17062306a36Sopenharmony_ci{
17162306a36Sopenharmony_ci	struct offset *offset;
17262306a36Sopenharmony_ci	unsigned i, m;
17362306a36Sopenharmony_ci
17462306a36Sopenharmony_ci	t->nentry = ((t->offset_max >> 2) + 31) / 32;
17562306a36Sopenharmony_ci	t->table = (unsigned *)malloc(sizeof(unsigned) * t->nentry);
17662306a36Sopenharmony_ci	if (t->table == NULL)
17762306a36Sopenharmony_ci		return -1;
17862306a36Sopenharmony_ci	memset(t->table, 0xff, sizeof(unsigned) * t->nentry);
17962306a36Sopenharmony_ci	list_for_each_entry(offset, &t->offsets, list) {
18062306a36Sopenharmony_ci		i = (offset->offset >> 2) / 32;
18162306a36Sopenharmony_ci		m = (offset->offset >> 2) & 31;
18262306a36Sopenharmony_ci		m = 1 << m;
18362306a36Sopenharmony_ci		t->table[i] ^= m;
18462306a36Sopenharmony_ci	}
18562306a36Sopenharmony_ci	return 0;
18662306a36Sopenharmony_ci}
18762306a36Sopenharmony_ci
18862306a36Sopenharmony_cistatic char gpu_name[10];
18962306a36Sopenharmony_cistatic int parser_auth(struct table *t, const char *filename)
19062306a36Sopenharmony_ci{
19162306a36Sopenharmony_ci	FILE *file;
19262306a36Sopenharmony_ci	regex_t mask_rex;
19362306a36Sopenharmony_ci	regmatch_t match[4];
19462306a36Sopenharmony_ci	char buf[1024];
19562306a36Sopenharmony_ci	size_t end;
19662306a36Sopenharmony_ci	int len;
19762306a36Sopenharmony_ci	int done = 0;
19862306a36Sopenharmony_ci	int r;
19962306a36Sopenharmony_ci	unsigned o;
20062306a36Sopenharmony_ci	struct offset *offset;
20162306a36Sopenharmony_ci	char last_reg_s[10];
20262306a36Sopenharmony_ci	int last_reg;
20362306a36Sopenharmony_ci
20462306a36Sopenharmony_ci	if (regcomp
20562306a36Sopenharmony_ci	    (&mask_rex, "(0x[0-9a-fA-F]*) *([_a-zA-Z0-9]*)", REG_EXTENDED)) {
20662306a36Sopenharmony_ci		fprintf(stderr, "Failed to compile regular expression\n");
20762306a36Sopenharmony_ci		return -1;
20862306a36Sopenharmony_ci	}
20962306a36Sopenharmony_ci	file = fopen(filename, "r");
21062306a36Sopenharmony_ci	if (file == NULL) {
21162306a36Sopenharmony_ci		fprintf(stderr, "Failed to open: %s\n", filename);
21262306a36Sopenharmony_ci		return -1;
21362306a36Sopenharmony_ci	}
21462306a36Sopenharmony_ci	fseek(file, 0, SEEK_END);
21562306a36Sopenharmony_ci	end = ftell(file);
21662306a36Sopenharmony_ci	fseek(file, 0, SEEK_SET);
21762306a36Sopenharmony_ci
21862306a36Sopenharmony_ci	/* get header */
21962306a36Sopenharmony_ci	if (fgets(buf, 1024, file) == NULL) {
22062306a36Sopenharmony_ci		fclose(file);
22162306a36Sopenharmony_ci		return -1;
22262306a36Sopenharmony_ci	}
22362306a36Sopenharmony_ci
22462306a36Sopenharmony_ci	/* first line will contain the last register
22562306a36Sopenharmony_ci	 * and gpu name */
22662306a36Sopenharmony_ci	sscanf(buf, "%9s %9s", gpu_name, last_reg_s);
22762306a36Sopenharmony_ci	t->gpu_prefix = gpu_name;
22862306a36Sopenharmony_ci	last_reg = strtol(last_reg_s, NULL, 16);
22962306a36Sopenharmony_ci
23062306a36Sopenharmony_ci	do {
23162306a36Sopenharmony_ci		if (fgets(buf, 1024, file) == NULL) {
23262306a36Sopenharmony_ci			fclose(file);
23362306a36Sopenharmony_ci			return -1;
23462306a36Sopenharmony_ci		}
23562306a36Sopenharmony_ci		len = strlen(buf);
23662306a36Sopenharmony_ci		if (ftell(file) == end)
23762306a36Sopenharmony_ci			done = 1;
23862306a36Sopenharmony_ci		if (len) {
23962306a36Sopenharmony_ci			r = regexec(&mask_rex, buf, 4, match, 0);
24062306a36Sopenharmony_ci			if (r == REG_NOMATCH) {
24162306a36Sopenharmony_ci			} else if (r) {
24262306a36Sopenharmony_ci				fprintf(stderr,
24362306a36Sopenharmony_ci					"Error matching regular expression %d in %s\n",
24462306a36Sopenharmony_ci					r, filename);
24562306a36Sopenharmony_ci				fclose(file);
24662306a36Sopenharmony_ci				return -1;
24762306a36Sopenharmony_ci			} else {
24862306a36Sopenharmony_ci				buf[match[0].rm_eo] = 0;
24962306a36Sopenharmony_ci				buf[match[1].rm_eo] = 0;
25062306a36Sopenharmony_ci				buf[match[2].rm_eo] = 0;
25162306a36Sopenharmony_ci				o = strtol(&buf[match[1].rm_so], NULL, 16);
25262306a36Sopenharmony_ci				offset = offset_new(o);
25362306a36Sopenharmony_ci				table_offset_add(t, offset);
25462306a36Sopenharmony_ci				if (o > t->offset_max)
25562306a36Sopenharmony_ci					t->offset_max = o;
25662306a36Sopenharmony_ci			}
25762306a36Sopenharmony_ci		}
25862306a36Sopenharmony_ci	} while (!done);
25962306a36Sopenharmony_ci	fclose(file);
26062306a36Sopenharmony_ci	if (t->offset_max < last_reg)
26162306a36Sopenharmony_ci		t->offset_max = last_reg;
26262306a36Sopenharmony_ci	return table_build(t);
26362306a36Sopenharmony_ci}
26462306a36Sopenharmony_ci
26562306a36Sopenharmony_ciint main(int argc, char *argv[])
26662306a36Sopenharmony_ci{
26762306a36Sopenharmony_ci	struct table t;
26862306a36Sopenharmony_ci
26962306a36Sopenharmony_ci	if (argc != 2) {
27062306a36Sopenharmony_ci		fprintf(stderr, "Usage: %s <authfile>\n", argv[0]);
27162306a36Sopenharmony_ci		exit(1);
27262306a36Sopenharmony_ci	}
27362306a36Sopenharmony_ci	table_init(&t);
27462306a36Sopenharmony_ci	if (parser_auth(&t, argv[1])) {
27562306a36Sopenharmony_ci		fprintf(stderr, "Failed to parse file %s\n", argv[1]);
27662306a36Sopenharmony_ci		return -1;
27762306a36Sopenharmony_ci	}
27862306a36Sopenharmony_ci	table_print(&t);
27962306a36Sopenharmony_ci	return 0;
28062306a36Sopenharmony_ci}
281