338 lines
7.2 KiB
C
338 lines
7.2 KiB
C
/* 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];
|
|
strncpy(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)) {
|
|
strncpy(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_H__\n");
|
|
printf("#define __S390_GENERATED_DIS_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);
|
|
}
|