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)))