forked from mirrors/linux
		
	Replace strncpy which has been used to copy a substring into buf
without NUL-termination with memcpy to avoid the following gcc 8
warnings:
arch/s390/tools/gen_opcode_table.c: In function ‘add_to_group.isra.4’:
arch/s390/tools/gen_opcode_table.c:260:2: warning: ‘strncpy’
output may be truncated copying 2 bytes from a string of length 19
[-Wstringop-truncation]
  strncpy(group->opcode, insn->opcode, 2);
In function ‘print_opcode_table’,
    inlined from ‘main’ at arch/s390/tools/gen_opcode_table.c:333:2:
arch/s390/tools/gen_opcode_table.c:286:4: warning: ‘strncpy’
output may be truncated copying 2 bytes from a string of length 19
[-Wstringop-truncation]
    strncpy(opcode, insn->opcode, 2);
Reviewed-by: Heiko Carstens <heiko.carstens@de.ibm.com>
Signed-off-by: Vasily Gorbik <gor@linux.ibm.com>
Signed-off-by: Martin Schwidefsky <schwidefsky@de.ibm.com>
		
	
			
		
			
				
	
	
		
			337 lines
		
	
	
	
		
			7.2 KiB
		
	
	
	
		
			C
		
	
	
	
	
	
			
		
		
	
	
			337 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];
 | 
						|
	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);
 | 
						|
}
 |