github.com/google/syzkaller@v0.0.0-20240517125934-c0f1611a36d6/pkg/ifuzz/powerpc/gen/powerisa31_tex_to_syz (about)

     1  #! /usr/bin/env python3
     2  
     3  # Copyright 2021 syzkaller project authors. All rights reserved.
     4  # Use of this source code is governed by Apache 2 LICENSE that can be found in the LICENSE file.
     5  
     6  import re
     7  import os
     8  import sys
     9  import pprint
    10  import subprocess
    11  
    12  pp = pprint.PrettyPrinter(indent = 0, compact = True, width = 300)
    13  pe = pprint.PrettyPrinter(indent = 0, compact = True, width = 300, stream = sys.stderr)
    14  
    15  def read_file(fname):
    16  	if not os.access(fname, os.O_RDONLY):
    17  		return []
    18  	f = open(fname, 'r')
    19  	ret = f.read()
    20  	ret = ret.split("\n")[:-1]
    21  	f.close()
    22  	return ret
    23  
    24  # Returns map<str, str>:  'layoutxxivbform': ([6, 5, 5, 5, 5, 2, 1, 1, 1, 1],
    25  #	[None, 'T', A', 'B', 'C', None, 'CX', 'AX', 'BX'm 'TX'])
    26  def get_layouts(layout_file):
    27  	layout_content = read_file(layout_file)
    28  	last_comment = ""
    29  	layouts = {}
    30  	for cur in layout_content:
    31  		if len(cur) > 0 and cur[0] == '%':
    32  			last_comment = re.sub(r'(^%|bits.*$)', "", cur).strip()
    33  			continue
    34  		l = re.match(r'\\newcommand{\\(layout\w+)}.*{', cur)
    35  		if l:
    36  			a = last_comment.split(" ")
    37  			pos = []
    38  			names = []
    39  			for a1 in a:
    40  				tmp = re.match(r'(\d+)<(\S+)>', a1)
    41  				if tmp:
    42  					pos += [int(tmp.group(1), 10)]
    43  					names += [tmp.group(2)]
    44  					continue
    45  				pos += [int(a1, 10)]
    46  				names += [None]
    47  			layouts[l.group(1)] = (pos, names)
    48  #	pe.pprint(layouts)
    49  	return layouts
    50  
    51  # Expands names of fields
    52  # (list<n>, list<n>), string -> list<n>
    53  def complete_layout(layout, insn_layout):
    54  	bb = []
    55  	b_ = 0
    56  	b = re.findall(r'{([^}]*)}', insn_layout)
    57  	for i in range(len(layout[0])):
    58  		if layout[1][i]:
    59  			bb += [layout[1][i]]
    60  			continue
    61  
    62  		# "pnop" is special: {any value\textsuperscript{*}
    63  		tmpname = re.sub(r'([{}?]+|any value\\textsuperscript{\*)', "", b[b_])
    64  		tmpname = re.sub(r'/+', '/', tmpname)
    65  		bb += [tmpname]
    66  		b_ += 1
    67  	return bb
    68  
    69  # Finds instructions in a latex file
    70  # Returns map<str, list>:
    71  # 'addc.': [([6, 5, 5, 5, 1, 9, 1], ['31', 'RT', 'RA', 'RB', 'OE', '10', 'Rc'], [-1, -1, -1, -1, 0, -1, 1])],
    72  # layouts: array of tuples
    73  def find_insns(tex_file, layouts):
    74  
    75  	def add_insns(insn_list, layout):
    76  		if len(layout) != 1 and len(layout) != 2:
    77  			pe.pprint("!!!Error: broken layout {} for {}".format(layout, insn_list))
    78  			sys.exit(-1)
    79  		r = {}
    80  		for ins in insn_list:
    81  			tmp = ins.split(" ", 1)
    82  
    83  			ll = []
    84  			for l in layout:
    85  				par = []
    86  				for j in range(len(l[1])):
    87  					defval = -1
    88  					# This is dealing with OE/Rc from "addc. RT,RA,RB (OE=0 Rc=1)"
    89  					if len(tmp) > 1 and ('?' not in l[1][j]):
    90  						ptmp = re.match(r'.*{}=(\d).*'.format(l[1][j]), tmp[1])
    91  						if ptmp:
    92  							defval = int(ptmp.group(1), 10)
    93  					par += [defval]
    94  				ll += [(l[0], l[1], par)]
    95  			pe.pprint("{}".format(tmp[0]))
    96  			r[tmp[0]] = ll
    97  		return r
    98  
    99  	tex_content = read_file(tex_file)
   100  	ret = {}
   101  	layout = []
   102  	insn_list = []
   103  	for cur in tex_content:
   104  		# \instrsyntax{pmxvf16ger2np AT,XA,XB,XMSK,YMSK,PMSK}
   105  		l = re.match(r'\\instrsyntax{(.*)}', cur)
   106  		if l:
   107  			if insn_list != [] and layout != []:
   108  				ret.update(add_insns(insn_list, layout))
   109  				insn_list = []
   110  				layout = []
   111  
   112  			insn_list += [l.group(1)]
   113  			continue
   114  		if not insn_list:
   115  			continue
   116  
   117  		# \layoutxxiiidform{59}{AT}{//}{A}{B}{82}{AX}{BX}{/}
   118  		l = re.match(r'\\(layout\w+)(.*)$', cur)
   119  		if l:
   120  			if len(layout) > 2:
   121  				pe.pprint("! Wrong layout")
   122  				sys.exit(-1)
   123  			layout += [(layouts[l.group(1)][0],
   124  				complete_layout(layouts[l.group(1)], l.group(2)))]
   125  
   126  	if layout:
   127  		ret.update(add_insns(insn_list, layout))
   128  
   129  	return ret
   130  
   131  # Extracts priv. flag from Table H.1: Power ISA Instruction Set Sorted by Mnemonic
   132  # Returns priv insns list
   133  def collect_priv(tex_file, insns):
   134  	tex_cont = read_file(tex_file)
   135  	ret = []
   136  	cur = ""
   137  	for tcur in tex_cont:
   138  		if tcur != '\hline':
   139  			cur += tcur
   140  			continue
   141  		# Merge all lines between \hline and split by tab (which '&' in latex)
   142  		cur = re.sub(r'\\&', 'AND', cur) # but '&' may occur in the instruction name
   143  		tmp = cur.split('&')
   144  		if len(tmp) == 11:
   145  			ins = re.sub(r'.*{([^{}]+)}$', r'\1', tmp[5])
   146  			if ins in insns:
   147  				if re.match(r'.+{(P|H|HV|HV\/P|UV|64)}$', tmp[7]):
   148  					ret += [ins]
   149  			cur = ""
   150  	return ret
   151  
   152  def generate_go(insns, priv):
   153  	def ppcmask(val, start, len):
   154  		return (val & ((1 << len) - 1)) << (31 - (start + len - 1))
   155  
   156  	def generate_opcode(ins, layout):
   157  		pos_, names_, defval_ = layout[0], layout[1], layout[2]
   158  		opcode = 0
   159  		opmask = 0
   160  		pos = 0
   161  		bits = 0
   162  		fields = {}
   163  		for i in range(len(pos_)):
   164  			pos += bits
   165  			bits = pos_[i]
   166  
   167  			# Fields marked `/` must be 0
   168  			if names_[i] == '/':
   169  				opmask |= ppcmask(0xffffffff, pos, pos_[i])
   170  				continue
   171  
   172  			if names_[i] == '':
   173  				continue
   174  
   175  			try:
   176  				num = int(names_[i], 10)
   177  				opcode |= ppcmask(num, pos, pos_[i])
   178  				opmask |= ppcmask(0xffffffff, pos, pos_[i])
   179  				continue
   180  			except:
   181  				pass
   182  
   183  			if defval_[i] >= 0:
   184  				opcode |= ppcmask(defval_[i], pos, pos_[i])
   185  				opmask |= ppcmask(0xffffffff, pos, pos_[i])
   186  				continue
   187  
   188  			fval = [(pos, pos_[i])]
   189  			if (ins in ['rldcl', 'rldcl.', 'rldic', 'rldic.', 'rldicl', 'rldicl.',
   190  				'rldimi', 'rldimi.', 'rldcr', 'rldcr.', 'rldicr', 'rldicr.'] and
   191  				names_[i] in ["me", "mb"] and fval == [(21, 6)]):
   192  				fval = [(21, 5), (26, 1)]
   193  			elif ins in ['mfspr', 'mtspr'] and names_[i] == "spr" and fval == [(11, 10)]:
   194  				fval = [(16, 5), (11, 5)]
   195  
   196  			if names_[i] not in fields:
   197  				fields[names_[i]] = []
   198  			fields[names_[i]] += fval
   199  
   200  
   201  		# Fix up fields
   202  		fields_str = ""
   203  		for f, fval in sorted(fields.items()):
   204  			fields_str += '{'
   205  			fields_str += 'Name: "{}", Bits: []powerpc.InsnBits'.format(f)
   206  			fields_str += '{'
   207  			for ff in fval:
   208  				fields_str += '{{{}, {}}}, '.format(ff[0], ff[1])
   209  				if ff[1] == 0:
   210  					pe.pprint("!Wrong length!")
   211  					sys.exit(-1)
   212  			fields_str = fields_str[:-2] + '}}, '
   213  
   214  		return opcode, opmask, fields_str[:-2]
   215  
   216  	for ins, ival in sorted(insns.items()):
   217  		tmp = '\t{Name: "'
   218  		tmp += ins
   219  		tmp += '", '
   220  		if len(ival) >= 1:
   221  			opcode, opmask, fields = generate_opcode(ins, ival[0])
   222  			tmp += 'Opcode: 0x{:08x}, Mask: 0x{:08x}, Fields: []powerpc.InsnField{{{}}}'.format(opcode, opmask, fields)
   223  			if ins in priv:
   224  				tmp += ', Priv: true'
   225  		if len(ival) == 2:
   226  			opcode, opmask, fields = generate_opcode(ins, ival[1])
   227  			tmp += ',\n\t\tOpcodeSuffix: 0x{:08x}, MaskSuffix: 0x{:08x}, FieldsSuffix: []powerpc.InsnField{{{}}}'.format(opcode, opmask, fields)
   228  
   229  		tmp += "},"
   230  		print(tmp)
   231  
   232  isa_dir = sys.argv[1]
   233  layouts = get_layouts(isa_dir + '/ilayouts.tex')
   234  texfiles = subprocess.check_output(["find", isa_dir, "-iname", "*.tex"]).decode("utf-8").split("\n")[:-1]
   235  
   236  insns = {}
   237  for tex in texfiles:
   238  	insns.update(find_insns(tex, layouts))
   239  
   240  print('// Code generated by {}. DO NOT EDIT.'.format(sys.argv[0]))
   241  print('')
   242  print('//go:build !codeanalysis')
   243  print('// +build !codeanalysis')
   244  print('')
   245  print('package generated')
   246  print('')
   247  print('import "github.com/google/syzkaller/pkg/ifuzz/powerpc"')
   248  print('')
   249  print('func init() {')
   250  print('\tpowerpc.Register(insns)')
   251  print('}')
   252  print('')
   253  print('var insns = []*powerpc.Insn{')
   254  generate_go(insns, collect_priv(isa_dir + "/Appendices/inst-mnem.tex", insns))
   255  print("}")
   256  
   257  pe.pprint("Processed {} instructions".format(len(insns)))