18c2ecf20Sopenharmony_ci/* SPDX-License-Identifier: GPL-2.0 */
28c2ecf20Sopenharmony_ci/*
38c2ecf20Sopenharmony_ci * Generate opcode table initializers for the in-kernel disassembler.
48c2ecf20Sopenharmony_ci *
58c2ecf20Sopenharmony_ci *    Copyright IBM Corp. 2017
68c2ecf20Sopenharmony_ci *
78c2ecf20Sopenharmony_ci */
88c2ecf20Sopenharmony_ci
98c2ecf20Sopenharmony_ci#include <stdlib.h>
108c2ecf20Sopenharmony_ci#include <string.h>
118c2ecf20Sopenharmony_ci#include <ctype.h>
128c2ecf20Sopenharmony_ci#include <stdio.h>
138c2ecf20Sopenharmony_ci
148c2ecf20Sopenharmony_ci#define STRING_SIZE_MAX 20
158c2ecf20Sopenharmony_ci
168c2ecf20Sopenharmony_cistruct insn_type {
178c2ecf20Sopenharmony_ci	unsigned char byte;
188c2ecf20Sopenharmony_ci	unsigned char mask;
198c2ecf20Sopenharmony_ci	char **format;
208c2ecf20Sopenharmony_ci};
218c2ecf20Sopenharmony_ci
228c2ecf20Sopenharmony_cistruct insn {
238c2ecf20Sopenharmony_ci	struct insn_type *type;
248c2ecf20Sopenharmony_ci	char opcode[STRING_SIZE_MAX];
258c2ecf20Sopenharmony_ci	char name[STRING_SIZE_MAX];
268c2ecf20Sopenharmony_ci	char upper[STRING_SIZE_MAX];
278c2ecf20Sopenharmony_ci	char format[STRING_SIZE_MAX];
288c2ecf20Sopenharmony_ci	unsigned int name_len;
298c2ecf20Sopenharmony_ci};
308c2ecf20Sopenharmony_ci
318c2ecf20Sopenharmony_cistruct insn_group {
328c2ecf20Sopenharmony_ci	struct insn_type *type;
338c2ecf20Sopenharmony_ci	int offset;
348c2ecf20Sopenharmony_ci	int count;
358c2ecf20Sopenharmony_ci	char opcode[2];
368c2ecf20Sopenharmony_ci};
378c2ecf20Sopenharmony_ci
388c2ecf20Sopenharmony_cistruct insn_format {
398c2ecf20Sopenharmony_ci	char *format;
408c2ecf20Sopenharmony_ci	int type;
418c2ecf20Sopenharmony_ci};
428c2ecf20Sopenharmony_ci
438c2ecf20Sopenharmony_cistruct gen_opcode {
448c2ecf20Sopenharmony_ci	struct insn *insn;
458c2ecf20Sopenharmony_ci	int nr;
468c2ecf20Sopenharmony_ci	struct insn_group *group;
478c2ecf20Sopenharmony_ci	int nr_groups;
488c2ecf20Sopenharmony_ci};
498c2ecf20Sopenharmony_ci
508c2ecf20Sopenharmony_ci/*
518c2ecf20Sopenharmony_ci * Table of instruction format types. Each opcode is defined with at
528c2ecf20Sopenharmony_ci * least one byte (two nibbles), three nibbles, or two bytes (four
538c2ecf20Sopenharmony_ci * nibbles).
548c2ecf20Sopenharmony_ci * The byte member of each instruction format type entry defines
558c2ecf20Sopenharmony_ci * within which byte of an instruction the third (and fourth) nibble
568c2ecf20Sopenharmony_ci * of an opcode can be found. The mask member is the and-mask that
578c2ecf20Sopenharmony_ci * needs to be applied on this byte in order to get the third (and
588c2ecf20Sopenharmony_ci * fourth) nibble of the opcode.
598c2ecf20Sopenharmony_ci * The format array defines all instruction formats (as defined in the
608c2ecf20Sopenharmony_ci * Principles of Operation) which have the same position of the opcode
618c2ecf20Sopenharmony_ci * nibbles.
628c2ecf20Sopenharmony_ci * A special case are instruction formats with 1-byte opcodes. In this
638c2ecf20Sopenharmony_ci * case the byte member always is zero, so that the mask is applied on
648c2ecf20Sopenharmony_ci * the (only) byte that contains the opcode.
658c2ecf20Sopenharmony_ci */
668c2ecf20Sopenharmony_cistatic struct insn_type insn_type_table[] = {
678c2ecf20Sopenharmony_ci	{
688c2ecf20Sopenharmony_ci		.byte = 0,
698c2ecf20Sopenharmony_ci		.mask = 0xff,
708c2ecf20Sopenharmony_ci		.format = (char *[]) {
718c2ecf20Sopenharmony_ci			"MII",
728c2ecf20Sopenharmony_ci			"RR",
738c2ecf20Sopenharmony_ci			"RS",
748c2ecf20Sopenharmony_ci			"RSI",
758c2ecf20Sopenharmony_ci			"RX",
768c2ecf20Sopenharmony_ci			"SI",
778c2ecf20Sopenharmony_ci			"SMI",
788c2ecf20Sopenharmony_ci			"SS",
798c2ecf20Sopenharmony_ci			NULL,
808c2ecf20Sopenharmony_ci		},
818c2ecf20Sopenharmony_ci	},
828c2ecf20Sopenharmony_ci	{
838c2ecf20Sopenharmony_ci		.byte = 1,
848c2ecf20Sopenharmony_ci		.mask = 0x0f,
858c2ecf20Sopenharmony_ci		.format = (char *[]) {
868c2ecf20Sopenharmony_ci			"RI",
878c2ecf20Sopenharmony_ci			"RIL",
888c2ecf20Sopenharmony_ci			"SSF",
898c2ecf20Sopenharmony_ci			NULL,
908c2ecf20Sopenharmony_ci		},
918c2ecf20Sopenharmony_ci	},
928c2ecf20Sopenharmony_ci	{
938c2ecf20Sopenharmony_ci		.byte = 1,
948c2ecf20Sopenharmony_ci		.mask = 0xff,
958c2ecf20Sopenharmony_ci		.format = (char *[]) {
968c2ecf20Sopenharmony_ci			"E",
978c2ecf20Sopenharmony_ci			"IE",
988c2ecf20Sopenharmony_ci			"RRE",
998c2ecf20Sopenharmony_ci			"RRF",
1008c2ecf20Sopenharmony_ci			"RRR",
1018c2ecf20Sopenharmony_ci			"S",
1028c2ecf20Sopenharmony_ci			"SIL",
1038c2ecf20Sopenharmony_ci			"SSE",
1048c2ecf20Sopenharmony_ci			NULL,
1058c2ecf20Sopenharmony_ci		},
1068c2ecf20Sopenharmony_ci	},
1078c2ecf20Sopenharmony_ci	{
1088c2ecf20Sopenharmony_ci		.byte = 5,
1098c2ecf20Sopenharmony_ci		.mask = 0xff,
1108c2ecf20Sopenharmony_ci		.format = (char *[]) {
1118c2ecf20Sopenharmony_ci			"RIE",
1128c2ecf20Sopenharmony_ci			"RIS",
1138c2ecf20Sopenharmony_ci			"RRS",
1148c2ecf20Sopenharmony_ci			"RSE",
1158c2ecf20Sopenharmony_ci			"RSL",
1168c2ecf20Sopenharmony_ci			"RSY",
1178c2ecf20Sopenharmony_ci			"RXE",
1188c2ecf20Sopenharmony_ci			"RXF",
1198c2ecf20Sopenharmony_ci			"RXY",
1208c2ecf20Sopenharmony_ci			"SIY",
1218c2ecf20Sopenharmony_ci			"VRI",
1228c2ecf20Sopenharmony_ci			"VRR",
1238c2ecf20Sopenharmony_ci			"VRS",
1248c2ecf20Sopenharmony_ci			"VRV",
1258c2ecf20Sopenharmony_ci			"VRX",
1268c2ecf20Sopenharmony_ci			"VSI",
1278c2ecf20Sopenharmony_ci			NULL,
1288c2ecf20Sopenharmony_ci		},
1298c2ecf20Sopenharmony_ci	},
1308c2ecf20Sopenharmony_ci};
1318c2ecf20Sopenharmony_ci
1328c2ecf20Sopenharmony_cistatic struct insn_type *insn_format_to_type(char *format)
1338c2ecf20Sopenharmony_ci{
1348c2ecf20Sopenharmony_ci	char tmp[STRING_SIZE_MAX];
1358c2ecf20Sopenharmony_ci	char *base_format, **ptr;
1368c2ecf20Sopenharmony_ci	int i;
1378c2ecf20Sopenharmony_ci
1388c2ecf20Sopenharmony_ci	strcpy(tmp, format);
1398c2ecf20Sopenharmony_ci	base_format = tmp;
1408c2ecf20Sopenharmony_ci	base_format = strsep(&base_format, "_");
1418c2ecf20Sopenharmony_ci	for (i = 0; i < sizeof(insn_type_table) / sizeof(insn_type_table[0]); i++) {
1428c2ecf20Sopenharmony_ci		ptr = insn_type_table[i].format;
1438c2ecf20Sopenharmony_ci		while (*ptr) {
1448c2ecf20Sopenharmony_ci			if (!strcmp(base_format, *ptr))
1458c2ecf20Sopenharmony_ci				return &insn_type_table[i];
1468c2ecf20Sopenharmony_ci			ptr++;
1478c2ecf20Sopenharmony_ci		}
1488c2ecf20Sopenharmony_ci	}
1498c2ecf20Sopenharmony_ci	exit(EXIT_FAILURE);
1508c2ecf20Sopenharmony_ci}
1518c2ecf20Sopenharmony_ci
1528c2ecf20Sopenharmony_cistatic void read_instructions(struct gen_opcode *desc)
1538c2ecf20Sopenharmony_ci{
1548c2ecf20Sopenharmony_ci	struct insn insn;
1558c2ecf20Sopenharmony_ci	int rc, i;
1568c2ecf20Sopenharmony_ci
1578c2ecf20Sopenharmony_ci	while (1) {
1588c2ecf20Sopenharmony_ci		rc = scanf("%s %s %s", insn.opcode, insn.name, insn.format);
1598c2ecf20Sopenharmony_ci		if (rc == EOF)
1608c2ecf20Sopenharmony_ci			break;
1618c2ecf20Sopenharmony_ci		if (rc != 3)
1628c2ecf20Sopenharmony_ci			exit(EXIT_FAILURE);
1638c2ecf20Sopenharmony_ci		insn.type = insn_format_to_type(insn.format);
1648c2ecf20Sopenharmony_ci		insn.name_len = strlen(insn.name);
1658c2ecf20Sopenharmony_ci		for (i = 0; i <= insn.name_len; i++)
1668c2ecf20Sopenharmony_ci			insn.upper[i] = toupper((unsigned char)insn.name[i]);
1678c2ecf20Sopenharmony_ci		desc->nr++;
1688c2ecf20Sopenharmony_ci		desc->insn = realloc(desc->insn, desc->nr * sizeof(*desc->insn));
1698c2ecf20Sopenharmony_ci		if (!desc->insn)
1708c2ecf20Sopenharmony_ci			exit(EXIT_FAILURE);
1718c2ecf20Sopenharmony_ci		desc->insn[desc->nr - 1] = insn;
1728c2ecf20Sopenharmony_ci	}
1738c2ecf20Sopenharmony_ci}
1748c2ecf20Sopenharmony_ci
1758c2ecf20Sopenharmony_cistatic int cmpformat(const void *a, const void *b)
1768c2ecf20Sopenharmony_ci{
1778c2ecf20Sopenharmony_ci	return strcmp(((struct insn *)a)->format, ((struct insn *)b)->format);
1788c2ecf20Sopenharmony_ci}
1798c2ecf20Sopenharmony_ci
1808c2ecf20Sopenharmony_cistatic void print_formats(struct gen_opcode *desc)
1818c2ecf20Sopenharmony_ci{
1828c2ecf20Sopenharmony_ci	char *format;
1838c2ecf20Sopenharmony_ci	int i, count;
1848c2ecf20Sopenharmony_ci
1858c2ecf20Sopenharmony_ci	qsort(desc->insn, desc->nr, sizeof(*desc->insn), cmpformat);
1868c2ecf20Sopenharmony_ci	format = "";
1878c2ecf20Sopenharmony_ci	count = 0;
1888c2ecf20Sopenharmony_ci	printf("enum {\n");
1898c2ecf20Sopenharmony_ci	for (i = 0; i < desc->nr; i++) {
1908c2ecf20Sopenharmony_ci		if (!strcmp(format, desc->insn[i].format))
1918c2ecf20Sopenharmony_ci			continue;
1928c2ecf20Sopenharmony_ci		count++;
1938c2ecf20Sopenharmony_ci		format = desc->insn[i].format;
1948c2ecf20Sopenharmony_ci		printf("\tINSTR_%s,\n", format);
1958c2ecf20Sopenharmony_ci	}
1968c2ecf20Sopenharmony_ci	printf("}; /* %d */\n\n", count);
1978c2ecf20Sopenharmony_ci}
1988c2ecf20Sopenharmony_ci
1998c2ecf20Sopenharmony_cistatic int cmp_long_insn(const void *a, const void *b)
2008c2ecf20Sopenharmony_ci{
2018c2ecf20Sopenharmony_ci	return strcmp(((struct insn *)a)->name, ((struct insn *)b)->name);
2028c2ecf20Sopenharmony_ci}
2038c2ecf20Sopenharmony_ci
2048c2ecf20Sopenharmony_cistatic void print_long_insn(struct gen_opcode *desc)
2058c2ecf20Sopenharmony_ci{
2068c2ecf20Sopenharmony_ci	struct insn *insn;
2078c2ecf20Sopenharmony_ci	int i, count;
2088c2ecf20Sopenharmony_ci
2098c2ecf20Sopenharmony_ci	qsort(desc->insn, desc->nr, sizeof(*desc->insn), cmp_long_insn);
2108c2ecf20Sopenharmony_ci	count = 0;
2118c2ecf20Sopenharmony_ci	printf("enum {\n");
2128c2ecf20Sopenharmony_ci	for (i = 0; i < desc->nr; i++) {
2138c2ecf20Sopenharmony_ci		insn = &desc->insn[i];
2148c2ecf20Sopenharmony_ci		if (insn->name_len < 6)
2158c2ecf20Sopenharmony_ci			continue;
2168c2ecf20Sopenharmony_ci		printf("\tLONG_INSN_%s,\n", insn->upper);
2178c2ecf20Sopenharmony_ci		count++;
2188c2ecf20Sopenharmony_ci	}
2198c2ecf20Sopenharmony_ci	printf("}; /* %d */\n\n", count);
2208c2ecf20Sopenharmony_ci
2218c2ecf20Sopenharmony_ci	printf("#define LONG_INSN_INITIALIZER { \\\n");
2228c2ecf20Sopenharmony_ci	for (i = 0; i < desc->nr; i++) {
2238c2ecf20Sopenharmony_ci		insn = &desc->insn[i];
2248c2ecf20Sopenharmony_ci		if (insn->name_len < 6)
2258c2ecf20Sopenharmony_ci			continue;
2268c2ecf20Sopenharmony_ci		printf("\t[LONG_INSN_%s] = \"%s\", \\\n", insn->upper, insn->name);
2278c2ecf20Sopenharmony_ci	}
2288c2ecf20Sopenharmony_ci	printf("}\n\n");
2298c2ecf20Sopenharmony_ci}
2308c2ecf20Sopenharmony_ci
2318c2ecf20Sopenharmony_cistatic void print_opcode(struct insn *insn, int nr)
2328c2ecf20Sopenharmony_ci{
2338c2ecf20Sopenharmony_ci	char *opcode;
2348c2ecf20Sopenharmony_ci
2358c2ecf20Sopenharmony_ci	opcode = insn->opcode;
2368c2ecf20Sopenharmony_ci	if (insn->type->byte != 0)
2378c2ecf20Sopenharmony_ci		opcode += 2;
2388c2ecf20Sopenharmony_ci	printf("\t[%4d] = { .opfrag = 0x%s, .format = INSTR_%s, ", nr, opcode, insn->format);
2398c2ecf20Sopenharmony_ci	if (insn->name_len < 6)
2408c2ecf20Sopenharmony_ci		printf(".name = \"%s\" ", insn->name);
2418c2ecf20Sopenharmony_ci	else
2428c2ecf20Sopenharmony_ci		printf(".offset = LONG_INSN_%s ", insn->upper);
2438c2ecf20Sopenharmony_ci	printf("}, \\\n");
2448c2ecf20Sopenharmony_ci}
2458c2ecf20Sopenharmony_ci
2468c2ecf20Sopenharmony_cistatic void add_to_group(struct gen_opcode *desc, struct insn *insn, int offset)
2478c2ecf20Sopenharmony_ci{
2488c2ecf20Sopenharmony_ci	struct insn_group *group;
2498c2ecf20Sopenharmony_ci
2508c2ecf20Sopenharmony_ci	group = desc->group ? &desc->group[desc->nr_groups - 1] : NULL;
2518c2ecf20Sopenharmony_ci	if (group && (!strncmp(group->opcode, insn->opcode, 2) || group->type->byte == 0)) {
2528c2ecf20Sopenharmony_ci		group->count++;
2538c2ecf20Sopenharmony_ci		return;
2548c2ecf20Sopenharmony_ci	}
2558c2ecf20Sopenharmony_ci	desc->nr_groups++;
2568c2ecf20Sopenharmony_ci	desc->group = realloc(desc->group, desc->nr_groups * sizeof(*desc->group));
2578c2ecf20Sopenharmony_ci	if (!desc->group)
2588c2ecf20Sopenharmony_ci		exit(EXIT_FAILURE);
2598c2ecf20Sopenharmony_ci	group = &desc->group[desc->nr_groups - 1];
2608c2ecf20Sopenharmony_ci	memcpy(group->opcode, insn->opcode, 2);
2618c2ecf20Sopenharmony_ci	group->type = insn->type;
2628c2ecf20Sopenharmony_ci	group->offset = offset;
2638c2ecf20Sopenharmony_ci	group->count = 1;
2648c2ecf20Sopenharmony_ci}
2658c2ecf20Sopenharmony_ci
2668c2ecf20Sopenharmony_cistatic int cmpopcode(const void *a, const void *b)
2678c2ecf20Sopenharmony_ci{
2688c2ecf20Sopenharmony_ci	return strcmp(((struct insn *)a)->opcode, ((struct insn *)b)->opcode);
2698c2ecf20Sopenharmony_ci}
2708c2ecf20Sopenharmony_ci
2718c2ecf20Sopenharmony_cistatic void print_opcode_table(struct gen_opcode *desc)
2728c2ecf20Sopenharmony_ci{
2738c2ecf20Sopenharmony_ci	char opcode[2] = "";
2748c2ecf20Sopenharmony_ci	struct insn *insn;
2758c2ecf20Sopenharmony_ci	int i, offset;
2768c2ecf20Sopenharmony_ci
2778c2ecf20Sopenharmony_ci	qsort(desc->insn, desc->nr, sizeof(*desc->insn), cmpopcode);
2788c2ecf20Sopenharmony_ci	printf("#define OPCODE_TABLE_INITIALIZER { \\\n");
2798c2ecf20Sopenharmony_ci	offset = 0;
2808c2ecf20Sopenharmony_ci	for (i = 0; i < desc->nr; i++) {
2818c2ecf20Sopenharmony_ci		insn = &desc->insn[i];
2828c2ecf20Sopenharmony_ci		if (insn->type->byte == 0)
2838c2ecf20Sopenharmony_ci			continue;
2848c2ecf20Sopenharmony_ci		add_to_group(desc, insn, offset);
2858c2ecf20Sopenharmony_ci		if (strncmp(opcode, insn->opcode, 2)) {
2868c2ecf20Sopenharmony_ci			memcpy(opcode, insn->opcode, 2);
2878c2ecf20Sopenharmony_ci			printf("\t/* %.2s */ \\\n", opcode);
2888c2ecf20Sopenharmony_ci		}
2898c2ecf20Sopenharmony_ci		print_opcode(insn, offset);
2908c2ecf20Sopenharmony_ci		offset++;
2918c2ecf20Sopenharmony_ci	}
2928c2ecf20Sopenharmony_ci	printf("\t/* 1-byte opcode instructions */ \\\n");
2938c2ecf20Sopenharmony_ci	for (i = 0; i < desc->nr; i++) {
2948c2ecf20Sopenharmony_ci		insn = &desc->insn[i];
2958c2ecf20Sopenharmony_ci		if (insn->type->byte != 0)
2968c2ecf20Sopenharmony_ci			continue;
2978c2ecf20Sopenharmony_ci		add_to_group(desc, insn, offset);
2988c2ecf20Sopenharmony_ci		print_opcode(insn, offset);
2998c2ecf20Sopenharmony_ci		offset++;
3008c2ecf20Sopenharmony_ci	}
3018c2ecf20Sopenharmony_ci	printf("}\n\n");
3028c2ecf20Sopenharmony_ci}
3038c2ecf20Sopenharmony_ci
3048c2ecf20Sopenharmony_cistatic void print_opcode_table_offsets(struct gen_opcode *desc)
3058c2ecf20Sopenharmony_ci{
3068c2ecf20Sopenharmony_ci	struct insn_group *group;
3078c2ecf20Sopenharmony_ci	int i;
3088c2ecf20Sopenharmony_ci
3098c2ecf20Sopenharmony_ci	printf("#define OPCODE_OFFSET_INITIALIZER { \\\n");
3108c2ecf20Sopenharmony_ci	for (i = 0; i < desc->nr_groups; i++) {
3118c2ecf20Sopenharmony_ci		group = &desc->group[i];
3128c2ecf20Sopenharmony_ci		printf("\t{ .opcode = 0x%.2s, .mask = 0x%02x, .byte = %d, .offset = %d, .count = %d }, \\\n",
3138c2ecf20Sopenharmony_ci		       group->opcode, group->type->mask, group->type->byte, group->offset, group->count);
3148c2ecf20Sopenharmony_ci	}
3158c2ecf20Sopenharmony_ci	printf("}\n\n");
3168c2ecf20Sopenharmony_ci}
3178c2ecf20Sopenharmony_ci
3188c2ecf20Sopenharmony_ciint main(int argc, char **argv)
3198c2ecf20Sopenharmony_ci{
3208c2ecf20Sopenharmony_ci	struct gen_opcode _desc = { 0 };
3218c2ecf20Sopenharmony_ci	struct gen_opcode *desc = &_desc;
3228c2ecf20Sopenharmony_ci
3238c2ecf20Sopenharmony_ci	read_instructions(desc);
3248c2ecf20Sopenharmony_ci	printf("#ifndef __S390_GENERATED_DIS_DEFS_H__\n");
3258c2ecf20Sopenharmony_ci	printf("#define __S390_GENERATED_DIS_DEFS_H__\n");
3268c2ecf20Sopenharmony_ci	printf("/*\n");
3278c2ecf20Sopenharmony_ci	printf(" * DO NOT MODIFY.\n");
3288c2ecf20Sopenharmony_ci	printf(" *\n");
3298c2ecf20Sopenharmony_ci	printf(" * This file was generated by %s\n", __FILE__);
3308c2ecf20Sopenharmony_ci	printf(" */\n\n");
3318c2ecf20Sopenharmony_ci	print_formats(desc);
3328c2ecf20Sopenharmony_ci	print_long_insn(desc);
3338c2ecf20Sopenharmony_ci	print_opcode_table(desc);
3348c2ecf20Sopenharmony_ci	print_opcode_table_offsets(desc);
3358c2ecf20Sopenharmony_ci	printf("#endif\n");
3368c2ecf20Sopenharmony_ci	exit(EXIT_SUCCESS);
3378c2ecf20Sopenharmony_ci}
338