123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338 |
- /* SPDX-License-Identifier: GPL-2.0 */
- /*
- * Generate opcode table initializers for the in-kernel disassembler.
- *
- * Copyright IBM Corp. 2017
- *
- */
- #include <stdlib.h>
- #include <string.h>
- #include <ctype.h>
- #include <stdio.h>
- #define STRING_SIZE_MAX 20
- struct insn_type {
- unsigned char byte;
- unsigned char mask;
- char **format;
- };
- struct insn {
- struct insn_type *type;
- char opcode[STRING_SIZE_MAX];
- char name[STRING_SIZE_MAX];
- char upper[STRING_SIZE_MAX];
- char format[STRING_SIZE_MAX];
- unsigned int name_len;
- };
- struct insn_group {
- struct insn_type *type;
- int offset;
- int count;
- char opcode[2];
- };
- struct insn_format {
- char *format;
- int type;
- };
- struct gen_opcode {
- struct insn *insn;
- int nr;
- struct insn_group *group;
- int nr_groups;
- };
- /*
- * Table of instruction format types. Each opcode is defined with at
- * least one byte (two nibbles), three nibbles, or two bytes (four
- * nibbles).
- * The byte member of each instruction format type entry defines
- * within which byte of an instruction the third (and fourth) nibble
- * of an opcode can be found. The mask member is the and-mask that
- * needs to be applied on this byte in order to get the third (and
- * fourth) nibble of the opcode.
- * The format array defines all instruction formats (as defined in the
- * Principles of Operation) which have the same position of the opcode
- * nibbles.
- * A special case are instruction formats with 1-byte opcodes. In this
- * case the byte member always is zero, so that the mask is applied on
- * the (only) byte that contains the opcode.
- */
- static struct insn_type insn_type_table[] = {
- {
- .byte = 0,
- .mask = 0xff,
- .format = (char *[]) {
- "MII",
- "RR",
- "RS",
- "RSI",
- "RX",
- "SI",
- "SMI",
- "SS",
- NULL,
- },
- },
- {
- .byte = 1,
- .mask = 0x0f,
- .format = (char *[]) {
- "RI",
- "RIL",
- "SSF",
- NULL,
- },
- },
- {
- .byte = 1,
- .mask = 0xff,
- .format = (char *[]) {
- "E",
- "IE",
- "RRE",
- "RRF",
- "RRR",
- "S",
- "SIL",
- "SSE",
- NULL,
- },
- },
- {
- .byte = 5,
- .mask = 0xff,
- .format = (char *[]) {
- "RIE",
- "RIS",
- "RRS",
- "RSE",
- "RSL",
- "RSY",
- "RXE",
- "RXF",
- "RXY",
- "SIY",
- "VRI",
- "VRR",
- "VRS",
- "VRV",
- "VRX",
- "VSI",
- NULL,
- },
- },
- };
- static struct insn_type *insn_format_to_type(char *format)
- {
- char tmp[STRING_SIZE_MAX];
- char *base_format, **ptr;
- int i;
- strcpy(tmp, format);
- base_format = tmp;
- base_format = strsep(&base_format, "_");
- for (i = 0; i < sizeof(insn_type_table) / sizeof(insn_type_table[0]); i++) {
- ptr = insn_type_table[i].format;
- while (*ptr) {
- if (!strcmp(base_format, *ptr))
- return &insn_type_table[i];
- ptr++;
- }
- }
- exit(EXIT_FAILURE);
- }
- static void read_instructions(struct gen_opcode *desc)
- {
- struct insn insn;
- int rc, i;
- while (1) {
- rc = scanf("%s %s %s", insn.opcode, insn.name, insn.format);
- if (rc == EOF)
- break;
- if (rc != 3)
- exit(EXIT_FAILURE);
- insn.type = insn_format_to_type(insn.format);
- insn.name_len = strlen(insn.name);
- for (i = 0; i <= insn.name_len; i++)
- insn.upper[i] = toupper((unsigned char)insn.name[i]);
- desc->nr++;
- desc->insn = realloc(desc->insn, desc->nr * sizeof(*desc->insn));
- if (!desc->insn)
- exit(EXIT_FAILURE);
- desc->insn[desc->nr - 1] = insn;
- }
- }
- static int cmpformat(const void *a, const void *b)
- {
- return strcmp(((struct insn *)a)->format, ((struct insn *)b)->format);
- }
- static void print_formats(struct gen_opcode *desc)
- {
- char *format;
- int i, count;
- qsort(desc->insn, desc->nr, sizeof(*desc->insn), cmpformat);
- format = "";
- count = 0;
- printf("enum {\n");
- for (i = 0; i < desc->nr; i++) {
- if (!strcmp(format, desc->insn[i].format))
- continue;
- count++;
- format = desc->insn[i].format;
- printf("\tINSTR_%s,\n", format);
- }
- printf("}; /* %d */\n\n", count);
- }
- static int cmp_long_insn(const void *a, const void *b)
- {
- return strcmp(((struct insn *)a)->name, ((struct insn *)b)->name);
- }
- static void print_long_insn(struct gen_opcode *desc)
- {
- struct insn *insn;
- int i, count;
- qsort(desc->insn, desc->nr, sizeof(*desc->insn), cmp_long_insn);
- count = 0;
- printf("enum {\n");
- for (i = 0; i < desc->nr; i++) {
- insn = &desc->insn[i];
- if (insn->name_len < 6)
- continue;
- printf("\tLONG_INSN_%s,\n", insn->upper);
- count++;
- }
- printf("}; /* %d */\n\n", count);
- printf("#define LONG_INSN_INITIALIZER { \\\n");
- for (i = 0; i < desc->nr; i++) {
- insn = &desc->insn[i];
- if (insn->name_len < 6)
- continue;
- printf("\t[LONG_INSN_%s] = \"%s\", \\\n", insn->upper, insn->name);
- }
- printf("}\n\n");
- }
- static void print_opcode(struct insn *insn, int nr)
- {
- char *opcode;
- opcode = insn->opcode;
- if (insn->type->byte != 0)
- opcode += 2;
- printf("\t[%4d] = { .opfrag = 0x%s, .format = INSTR_%s, ", nr, opcode, insn->format);
- if (insn->name_len < 6)
- printf(".name = \"%s\" ", insn->name);
- else
- printf(".offset = LONG_INSN_%s ", insn->upper);
- printf("}, \\\n");
- }
- static void add_to_group(struct gen_opcode *desc, struct insn *insn, int offset)
- {
- struct insn_group *group;
- group = desc->group ? &desc->group[desc->nr_groups - 1] : NULL;
- if (group && (!strncmp(group->opcode, insn->opcode, 2) || group->type->byte == 0)) {
- group->count++;
- return;
- }
- desc->nr_groups++;
- desc->group = realloc(desc->group, desc->nr_groups * sizeof(*desc->group));
- if (!desc->group)
- exit(EXIT_FAILURE);
- group = &desc->group[desc->nr_groups - 1];
- memcpy(group->opcode, insn->opcode, 2);
- group->type = insn->type;
- group->offset = offset;
- group->count = 1;
- }
- static int cmpopcode(const void *a, const void *b)
- {
- return strcmp(((struct insn *)a)->opcode, ((struct insn *)b)->opcode);
- }
- static void print_opcode_table(struct gen_opcode *desc)
- {
- char opcode[2] = "";
- struct insn *insn;
- int i, offset;
- qsort(desc->insn, desc->nr, sizeof(*desc->insn), cmpopcode);
- printf("#define OPCODE_TABLE_INITIALIZER { \\\n");
- offset = 0;
- for (i = 0; i < desc->nr; i++) {
- insn = &desc->insn[i];
- if (insn->type->byte == 0)
- continue;
- add_to_group(desc, insn, offset);
- if (strncmp(opcode, insn->opcode, 2)) {
- memcpy(opcode, insn->opcode, 2);
- printf("\t/* %.2s */ \\\n", opcode);
- }
- print_opcode(insn, offset);
- offset++;
- }
- printf("\t/* 1-byte opcode instructions */ \\\n");
- for (i = 0; i < desc->nr; i++) {
- insn = &desc->insn[i];
- if (insn->type->byte != 0)
- continue;
- add_to_group(desc, insn, offset);
- print_opcode(insn, offset);
- offset++;
- }
- printf("}\n\n");
- }
- static void print_opcode_table_offsets(struct gen_opcode *desc)
- {
- struct insn_group *group;
- int i;
- printf("#define OPCODE_OFFSET_INITIALIZER { \\\n");
- for (i = 0; i < desc->nr_groups; i++) {
- group = &desc->group[i];
- printf("\t{ .opcode = 0x%.2s, .mask = 0x%02x, .byte = %d, .offset = %d, .count = %d }, \\\n",
- group->opcode, group->type->mask, group->type->byte, group->offset, group->count);
- }
- printf("}\n\n");
- }
- int main(int argc, char **argv)
- {
- struct gen_opcode _desc = { 0 };
- struct gen_opcode *desc = &_desc;
- read_instructions(desc);
- printf("#ifndef __S390_GENERATED_DIS_DEFS_H__\n");
- printf("#define __S390_GENERATED_DIS_DEFS_H__\n");
- printf("/*\n");
- printf(" * DO NOT MODIFY.\n");
- printf(" *\n");
- printf(" * This file was generated by %s\n", __FILE__);
- printf(" */\n\n");
- print_formats(desc);
- print_long_insn(desc);
- print_opcode_table(desc);
- print_opcode_table_offsets(desc);
- printf("#endif\n");
- exit(EXIT_SUCCESS);
- }
|