Merge branch 'for-linus' of git://git.kernel.org/pub/scm/linux/kernel/git/s390/linux
Pull s390 updates from Heiko Carstens: "Since Martin is on vacation you get the s390 pull request for the v4.15 merge window this time from me. Besides a lot of cleanups and bug fixes these are the most important changes: - a new regset for runtime instrumentation registers - hardware accelerated AES-GCM support for the aes_s390 module - support for the new CEX6S crypto cards - support for FORTIFY_SOURCE - addition of missing z13 and new z14 instructions to the in-kernel disassembler - generate opcode tables for the in-kernel disassembler out of a simple text file instead of having to manually maintain those tables - fast memset16, memset32 and memset64 implementations - removal of named saved segment support - hardware counter support for z14 - queued spinlocks and queued rwlocks implementations for s390 - use the stack_depth tracking feature for s390 BPF JIT - a new s390_sthyi system call which emulates the sthyi (store hypervisor information) instruction - removal of the old KVM virtio transport - an s390 specific CPU alternatives implementation which is used in the new spinlock code" * 'for-linus' of git://git.kernel.org/pub/scm/linux/kernel/git/s390/linux: (88 commits) MAINTAINERS: add virtio-ccw.h to virtio/s390 section s390/noexec: execute kexec datamover without DAT s390: fix transactional execution control register handling s390/bpf: take advantage of stack_depth tracking s390: simplify transactional execution elf hwcap handling s390/zcrypt: Rework struct ap_qact_ap_info. s390/virtio: remove unused header file kvm_virtio.h s390: avoid undefined behaviour s390/disassembler: generate opcode tables from text file s390/disassembler: remove insn_to_mnemonic() s390/dasd: avoid calling do_gettimeofday() s390: vfio-ccw: Do not attempt to free no-op, test and tic cda. s390: remove named saved segment support s390/archrandom: Reconsider s390 arch random implementation s390/pci: do not require AIS facility s390/qdio: sanitize put_indicator s390/qdio: use atomic_cmpxchg s390/nmi: avoid using long-displacement facility s390: pass endianness info to sparse s390/decompressor: remove informational messages ...
This commit is contained in:
@@ -4,11 +4,21 @@
|
||||
#
|
||||
|
||||
hostprogs-y += gen_facilities
|
||||
hostprogs-y += gen_opcode_table
|
||||
|
||||
HOSTCFLAGS_gen_facilities.o += -Wall $(LINUXINCLUDE)
|
||||
HOSTCFLAGS_gen_opcode_table.o += -Wall $(LINUXINCLUDE)
|
||||
|
||||
define filechk_facilities.h
|
||||
$(obj)/gen_facilities
|
||||
endef
|
||||
|
||||
define filechk_dis.h
|
||||
( $(obj)/gen_opcode_table < $(srctree)/arch/$(ARCH)/tools/opcodes.txt )
|
||||
endef
|
||||
|
||||
include/generated/facilities.h: $(obj)/gen_facilities FORCE
|
||||
$(call filechk,facilities.h)
|
||||
|
||||
include/generated/dis.h: $(obj)/gen_opcode_table FORCE
|
||||
$(call filechk,dis.h,__FUN)
|
||||
|
336
arch/s390/tools/gen_opcode_table.c
Normal file
336
arch/s390/tools/gen_opcode_table.c
Normal file
@@ -0,0 +1,336 @@
|
||||
/*
|
||||
* 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);
|
||||
}
|
1183
arch/s390/tools/opcodes.txt
Normal file
1183
arch/s390/tools/opcodes.txt
Normal file
File diff suppressed because it is too large
Load Diff
Reference in New Issue
Block a user