| 1 | // SPDX-License-Identifier: MIT |
| 2 | /* utility to create the register check tables |
| 3 | * this includes inlined list.h safe for userspace. |
| 4 | * |
| 5 | * Copyright 2009 Jerome Glisse |
| 6 | * Copyright 2009 Red Hat Inc. |
| 7 | * |
| 8 | * Authors: |
| 9 | * Jerome Glisse |
| 10 | * Dave Airlie |
| 11 | */ |
| 12 | |
| 13 | #include <sys/types.h> |
| 14 | #include <stdlib.h> |
| 15 | #include <string.h> |
| 16 | #include <stdio.h> |
| 17 | #include <regex.h> |
| 18 | #include <libgen.h> |
| 19 | |
| 20 | #define offsetof(TYPE, MEMBER) ((size_t) &((TYPE *)0)->MEMBER) |
| 21 | /** |
| 22 | * container_of - cast a member of a structure out to the containing structure |
| 23 | * @ptr: the pointer to the member. |
| 24 | * @type: the type of the container struct this is embedded in. |
| 25 | * @member: the name of the member within the struct. |
| 26 | * |
| 27 | */ |
| 28 | #define container_of(ptr, type, member) ({ \ |
| 29 | const typeof(((type *)0)->member)*__mptr = (ptr); \ |
| 30 | (type *)((char *)__mptr - offsetof(type, member)); }) |
| 31 | |
| 32 | /* |
| 33 | * Simple doubly linked list implementation. |
| 34 | * |
| 35 | * Some of the internal functions ("__xxx") are useful when |
| 36 | * manipulating whole lists rather than single entries, as |
| 37 | * sometimes we already know the next/prev entries and we can |
| 38 | * generate better code by using them directly rather than |
| 39 | * using the generic single-entry routines. |
| 40 | */ |
| 41 | |
| 42 | struct list_head { |
| 43 | struct list_head *next, *prev; |
| 44 | }; |
| 45 | |
| 46 | |
| 47 | static inline void INIT_LIST_HEAD(struct list_head *list) |
| 48 | { |
| 49 | list->next = list; |
| 50 | list->prev = list; |
| 51 | } |
| 52 | |
| 53 | /* |
| 54 | * Insert a new entry between two known consecutive entries. |
| 55 | * |
| 56 | * This is only for internal list manipulation where we know |
| 57 | * the prev/next entries already! |
| 58 | */ |
| 59 | #ifndef CONFIG_DEBUG_LIST |
| 60 | static inline void __list_add(struct list_head *new, |
| 61 | struct list_head *prev, struct list_head *next) |
| 62 | { |
| 63 | next->prev = new; |
| 64 | new->next = next; |
| 65 | new->prev = prev; |
| 66 | prev->next = new; |
| 67 | } |
| 68 | #else |
| 69 | extern void __list_add(struct list_head *new, |
| 70 | struct list_head *prev, struct list_head *next); |
| 71 | #endif |
| 72 | |
| 73 | /** |
| 74 | * list_add_tail - add a new entry |
| 75 | * @new: new entry to be added |
| 76 | * @head: list head to add it before |
| 77 | * |
| 78 | * Insert a new entry before the specified head. |
| 79 | * This is useful for implementing queues. |
| 80 | */ |
| 81 | static inline void list_add_tail(struct list_head *new, struct list_head *head) |
| 82 | { |
| 83 | __list_add(new, prev: head->prev, next: head); |
| 84 | } |
| 85 | |
| 86 | /** |
| 87 | * list_entry - get the struct for this entry |
| 88 | * @ptr: the &struct list_head pointer. |
| 89 | * @type: the type of the struct this is embedded in. |
| 90 | * @member: the name of the list_head within the struct. |
| 91 | */ |
| 92 | #define list_entry(ptr, type, member) \ |
| 93 | container_of(ptr, type, member) |
| 94 | |
| 95 | /** |
| 96 | * list_for_each_entry - iterate over list of given type |
| 97 | * @pos: the type * to use as a loop cursor. |
| 98 | * @head: the head for your list. |
| 99 | * @member: the name of the list_head within the struct. |
| 100 | */ |
| 101 | #define list_for_each_entry(pos, head, member) \ |
| 102 | for (pos = list_entry((head)->next, typeof(*pos), member); \ |
| 103 | &pos->member != (head); \ |
| 104 | pos = list_entry(pos->member.next, typeof(*pos), member)) |
| 105 | |
| 106 | struct offset { |
| 107 | struct list_head list; |
| 108 | unsigned offset; |
| 109 | }; |
| 110 | |
| 111 | struct table { |
| 112 | struct list_head offsets; |
| 113 | unsigned offset_max; |
| 114 | unsigned nentry; |
| 115 | unsigned *table; |
| 116 | char *gpu_prefix; |
| 117 | }; |
| 118 | |
| 119 | static struct offset *offset_new(unsigned o) |
| 120 | { |
| 121 | struct offset *offset; |
| 122 | |
| 123 | offset = (struct offset *)malloc(size: sizeof(struct offset)); |
| 124 | if (offset) { |
| 125 | INIT_LIST_HEAD(list: &offset->list); |
| 126 | offset->offset = o; |
| 127 | } |
| 128 | return offset; |
| 129 | } |
| 130 | |
| 131 | static void table_offset_add(struct table *t, struct offset *offset) |
| 132 | { |
| 133 | list_add_tail(new: &offset->list, head: &t->offsets); |
| 134 | } |
| 135 | |
| 136 | static void table_init(struct table *t) |
| 137 | { |
| 138 | INIT_LIST_HEAD(list: &t->offsets); |
| 139 | t->offset_max = 0; |
| 140 | t->nentry = 0; |
| 141 | t->table = NULL; |
| 142 | } |
| 143 | |
| 144 | static void table_print(struct table *t) |
| 145 | { |
| 146 | unsigned nlloop, i, j, n, c, id; |
| 147 | |
| 148 | nlloop = (t->nentry + 3) / 4; |
| 149 | c = t->nentry; |
| 150 | printf(format: "static const unsigned %s_reg_safe_bm[%d] = {\n" , t->gpu_prefix, |
| 151 | t->nentry); |
| 152 | for (i = 0, id = 0; i < nlloop; i++) { |
| 153 | n = 4; |
| 154 | if (n > c) |
| 155 | n = c; |
| 156 | c -= n; |
| 157 | for (j = 0; j < n; j++) { |
| 158 | if (j == 0) |
| 159 | printf(format: "\t" ); |
| 160 | else |
| 161 | printf(format: " " ); |
| 162 | printf(format: "0x%08X," , t->table[id++]); |
| 163 | } |
| 164 | printf(format: "\n" ); |
| 165 | } |
| 166 | printf(format: "};\n" ); |
| 167 | } |
| 168 | |
| 169 | static int table_build(struct table *t) |
| 170 | { |
| 171 | struct offset *offset; |
| 172 | unsigned i, m; |
| 173 | |
| 174 | t->nentry = ((t->offset_max >> 2) + 31) / 32; |
| 175 | t->table = (unsigned *)malloc(size: sizeof(unsigned) * t->nentry); |
| 176 | if (t->table == NULL) |
| 177 | return -1; |
| 178 | memset(s: t->table, c: 0xff, n: sizeof(unsigned) * t->nentry); |
| 179 | list_for_each_entry(offset, &t->offsets, list) { |
| 180 | i = (offset->offset >> 2) / 32; |
| 181 | m = (offset->offset >> 2) & 31; |
| 182 | m = 1 << m; |
| 183 | t->table[i] ^= m; |
| 184 | } |
| 185 | return 0; |
| 186 | } |
| 187 | |
| 188 | static char gpu_name[10]; |
| 189 | static int parser_auth(struct table *t, const char *filename) |
| 190 | { |
| 191 | FILE *file; |
| 192 | regex_t mask_rex; |
| 193 | regmatch_t match[4]; |
| 194 | char buf[1024]; |
| 195 | size_t end; |
| 196 | int len; |
| 197 | int done = 0; |
| 198 | int r; |
| 199 | unsigned o; |
| 200 | struct offset *offset; |
| 201 | char last_reg_s[10]; |
| 202 | int last_reg; |
| 203 | |
| 204 | if (regcomp |
| 205 | (preg: &mask_rex, pattern: "(0x[0-9a-fA-F]*) *([_a-zA-Z0-9]*)" , REG_EXTENDED)) { |
| 206 | fprintf(stderr, format: "Failed to compile regular expression\n" ); |
| 207 | return -1; |
| 208 | } |
| 209 | file = fopen(filename: filename, modes: "r" ); |
| 210 | if (file == NULL) { |
| 211 | fprintf(stderr, format: "Failed to open: %s\n" , filename); |
| 212 | return -1; |
| 213 | } |
| 214 | fseek(stream: file, off: 0, SEEK_END); |
| 215 | end = ftell(stream: file); |
| 216 | fseek(stream: file, off: 0, SEEK_SET); |
| 217 | |
| 218 | /* get header */ |
| 219 | if (fgets(s: buf, n: 1024, stream: file) == NULL) { |
| 220 | fclose(stream: file); |
| 221 | return -1; |
| 222 | } |
| 223 | |
| 224 | /* first line will contain the last register |
| 225 | * and gpu name */ |
| 226 | sscanf(s: buf, format: "%9s %9s" , gpu_name, last_reg_s); |
| 227 | t->gpu_prefix = gpu_name; |
| 228 | last_reg = strtol(nptr: last_reg_s, NULL, base: 16); |
| 229 | |
| 230 | do { |
| 231 | if (fgets(s: buf, n: 1024, stream: file) == NULL) { |
| 232 | fclose(stream: file); |
| 233 | return -1; |
| 234 | } |
| 235 | len = strlen(s: buf); |
| 236 | if (ftell(stream: file) == end) |
| 237 | done = 1; |
| 238 | if (len) { |
| 239 | r = regexec(preg: &mask_rex, String: buf, nmatch: 4, pmatch: match, eflags: 0); |
| 240 | if (r == REG_NOMATCH) { |
| 241 | } else if (r) { |
| 242 | fprintf(stderr, |
| 243 | format: "Error matching regular expression %d in %s\n" , |
| 244 | r, filename); |
| 245 | fclose(stream: file); |
| 246 | return -1; |
| 247 | } else { |
| 248 | buf[match[0].rm_eo] = 0; |
| 249 | buf[match[1].rm_eo] = 0; |
| 250 | buf[match[2].rm_eo] = 0; |
| 251 | o = strtol(nptr: &buf[match[1].rm_so], NULL, base: 16); |
| 252 | offset = offset_new(o); |
| 253 | table_offset_add(t, offset); |
| 254 | if (o > t->offset_max) |
| 255 | t->offset_max = o; |
| 256 | } |
| 257 | } |
| 258 | } while (!done); |
| 259 | fclose(stream: file); |
| 260 | if (t->offset_max < last_reg) |
| 261 | t->offset_max = last_reg; |
| 262 | return table_build(t); |
| 263 | } |
| 264 | |
| 265 | int main(int argc, char *argv[]) |
| 266 | { |
| 267 | struct table t; |
| 268 | |
| 269 | if (argc != 2) { |
| 270 | fprintf(stderr, format: "Usage: %s <authfile>\n" , argv[0]); |
| 271 | exit(status: 1); |
| 272 | } |
| 273 | table_init(t: &t); |
| 274 | if (parser_auth(t: &t, filename: argv[1])) { |
| 275 | fprintf(stderr, format: "Failed to parse file %s\n" , argv[1]); |
| 276 | return -1; |
| 277 | } |
| 278 | table_print(t: &t); |
| 279 | return 0; |
| 280 | } |
| 281 | |