/* 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_insn_name(const char *name){ size_t i, len; len = strlen(name); printf("{"); for (i = 0; i < len; i++) printf(" \'%c\',", name[i]); printf(" }");} 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] = ", insn->upper); print_insn_name(insn->name); printf(", \\\n"); } 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 = "); print_insn_name(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);}
/* 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_insn_name(const char *name)
{
size_t i, len;
len = strlen(name);
printf("{");
for (i = 0; i < len; i++)
printf(" \'%c\',", name[i]);
printf(" }");
}
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] = ", insn->upper);
print_insn_name(insn->name);
printf(", \\\n");
}
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 = ");
print_insn_name(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);
}