2017-12-04 12:39:38 +03:00
/* SPDX-License-Identifier: GPL-2.0 */
2017-11-06 15:29:56 +03:00
/*
* 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 ( " \t INSTR_%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 ( " \t LONG_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 ] ;
2018-06-29 18:32:09 +03:00
memcpy ( group - > opcode , insn - > opcode , 2 ) ;
2017-11-06 15:29:56 +03:00
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 ) ) {
2018-06-29 18:32:09 +03:00
memcpy ( opcode , insn - > opcode , 2 ) ;
2017-11-06 15:29:56 +03:00
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 ) ;
2018-01-11 14:13:18 +03:00
printf ( " #ifndef __S390_GENERATED_DIS_DEFS_H__ \n " ) ;
printf ( " #define __S390_GENERATED_DIS_DEFS_H__ \n " ) ;
2017-11-06 15:29:56 +03:00
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 ) ;
}