| /* 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); | |
| } |