qemu with hax to log dma reads & writes jcs.org/2018/11/12/vfio

decodetree: Allow grouping of overlapping patterns

Signed-off-by: Richard Henderson <richard.henderson@linaro.org>

+207 -22
+58
docs/devel/decodetree.rst
··· 161 161 and:: 162 162 163 163 trans_addl_i(ctx, &arg_opi, insn) 164 + 165 + Pattern Groups 166 + ============== 167 + 168 + Syntax:: 169 + 170 + group := '{' ( pat_def | group )+ '}' 171 + 172 + A *group* begins with a lone open-brace, with all subsequent lines 173 + indented two spaces, and ending with a lone close-brace. Groups 174 + may be nested, increasing the required indentation of the lines 175 + within the nested group to two spaces per nesting level. 176 + 177 + Unlike ungrouped patterns, grouped patterns are allowed to overlap. 178 + Conflicts are resolved by selecting the patterns in order. If all 179 + of the fixedbits for a pattern match, its translate function will 180 + be called. If the translate function returns false, then subsequent 181 + patterns within the group will be matched. 182 + 183 + The following example from PA-RISC shows specialization of the *or* 184 + instruction:: 185 + 186 + { 187 + { 188 + nop 000010 ----- ----- 0000 001001 0 00000 189 + copy 000010 00000 r1:5 0000 001001 0 rt:5 190 + } 191 + or 000010 rt2:5 r1:5 cf:4 001001 0 rt:5 192 + } 193 + 194 + When the *cf* field is zero, the instruction has no side effects, 195 + and may be specialized. When the *rt* field is zero, the output 196 + is discarded and so the instruction has no effect. When the *rt2* 197 + field is zero, the operation is ``reg[rt] | 0`` and so encodes 198 + the canonical register copy operation. 199 + 200 + The output from the generator might look like:: 201 + 202 + switch (insn & 0xfc000fe0) { 203 + case 0x08000240: 204 + /* 000010.. ........ ....0010 010..... */ 205 + if ((insn & 0x0000f000) == 0x00000000) { 206 + /* 000010.. ........ 00000010 010..... */ 207 + if ((insn & 0x0000001f) == 0x00000000) { 208 + /* 000010.. ........ 00000010 01000000 */ 209 + extract_decode_Fmt_0(&u.f_decode0, insn); 210 + if (trans_nop(ctx, &u.f_decode0)) return true; 211 + } 212 + if ((insn & 0x03e00000) == 0x00000000) { 213 + /* 00001000 000..... 00000010 010..... */ 214 + extract_decode_Fmt_1(&u.f_decode1, insn); 215 + if (trans_copy(ctx, &u.f_decode1)) return true; 216 + } 217 + } 218 + extract_decode_Fmt_2(&u.f_decode2, insn); 219 + if (trans_or(ctx, &u.f_decode2)) return true; 220 + return false; 221 + }
+143 -22
scripts/decodetree.py
··· 31 31 arguments = {} 32 32 formats = {} 33 33 patterns = [] 34 + allpatterns = [] 34 35 35 36 translate_prefix = 'trans' 36 37 translate_scope = 'static ' ··· 300 301 self.fields = flds 301 302 302 303 def __str__(self): 303 - r = self.name 304 - if self.base: 305 - r = r + ' ' + self.base.name 306 - else: 307 - r = r + ' ' + str(self.fields) 308 - r = r + ' ' + str_match_bits(self.fixedbits, self.fixedmask) 309 - return r 304 + return self.name + ' ' + str_match_bits(self.fixedbits, self.fixedmask) 310 305 311 306 def str1(self, i): 312 307 return str_indent(i) + self.__str__() ··· 353 348 # end Pattern 354 349 355 350 351 + class MultiPattern(General): 352 + """Class representing an overlapping set of instruction patterns""" 353 + 354 + def __init__(self, lineno, pats, fixb, fixm, udfm): 355 + self.file = input_file 356 + self.lineno = lineno 357 + self.pats = pats 358 + self.base = None 359 + self.fixedbits = fixb 360 + self.fixedmask = fixm 361 + self.undefmask = udfm 362 + 363 + def __str__(self): 364 + r = "{" 365 + for p in self.pats: 366 + r = r + ' ' + str(p) 367 + return r + "}" 368 + 369 + def output_decl(self): 370 + for p in self.pats: 371 + p.output_decl() 372 + 373 + def output_code(self, i, extracted, outerbits, outermask): 374 + global translate_prefix 375 + ind = str_indent(i) 376 + for p in self.pats: 377 + if outermask != p.fixedmask: 378 + innermask = p.fixedmask & ~outermask 379 + innerbits = p.fixedbits & ~outermask 380 + output(ind, 'if ((insn & ', 381 + '0x{0:08x}) == 0x{1:08x}'.format(innermask, innerbits), 382 + ') {\n') 383 + output(ind, ' /* ', 384 + str_match_bits(p.fixedbits, p.fixedmask), ' */\n') 385 + p.output_code(i + 4, extracted, p.fixedbits, p.fixedmask) 386 + output(ind, '}\n') 387 + else: 388 + p.output_code(i, extracted, p.fixedbits, p.fixedmask) 389 + #end MultiPattern 390 + 391 + 356 392 def parse_field(lineno, name, toks): 357 393 """Parse one instruction field from TOKS at LINENO""" 358 394 global fields ··· 505 541 global arguments 506 542 global formats 507 543 global patterns 544 + global allpatterns 508 545 global re_ident 509 546 global insnwidth 510 547 global insnmask ··· 649 686 pat = Pattern(name, lineno, fmt, fixedbits, fixedmask, 650 687 undefmask, fieldmask, flds) 651 688 patterns.append(pat) 689 + allpatterns.append(pat) 652 690 653 691 # Validate the masks that we have assembled. 654 692 if fieldmask & fixedmask: ··· 667 705 .format(allbits ^ insnmask)) 668 706 # end parse_general 669 707 708 + def build_multi_pattern(lineno, pats): 709 + """Validate the Patterns going into a MultiPattern.""" 710 + global patterns 711 + global insnmask 712 + 713 + if len(pats) < 2: 714 + error(lineno, 'less than two patterns within braces') 715 + 716 + fixedmask = insnmask 717 + undefmask = insnmask 718 + 719 + # Collect fixed/undefmask for all of the children. 720 + # Move the defining lineno back to that of the first child. 721 + for p in pats: 722 + fixedmask &= p.fixedmask 723 + undefmask &= p.undefmask 724 + if p.lineno < lineno: 725 + lineno = p.lineno 726 + 727 + repeat = True 728 + while repeat: 729 + if fixedmask == 0: 730 + error(lineno, 'no overlap in patterns within braces') 731 + fixedbits = None 732 + for p in pats: 733 + thisbits = p.fixedbits & fixedmask 734 + if fixedbits is None: 735 + fixedbits = thisbits 736 + elif fixedbits != thisbits: 737 + fixedmask &= ~(fixedbits ^ thisbits) 738 + break 739 + else: 740 + repeat = False 741 + 742 + mp = MultiPattern(lineno, pats, fixedbits, fixedmask, undefmask) 743 + patterns.append(mp) 744 + # end build_multi_pattern 670 745 671 746 def parse_file(f): 672 747 """Parse all of the patterns within a file""" 673 748 749 + global patterns 750 + 674 751 # Read all of the lines of the file. Concatenate lines 675 752 # ending in backslash; discard empty lines and comments. 676 753 toks = [] 677 754 lineno = 0 755 + nesting = 0 756 + saved_pats = [] 757 + 678 758 for line in f: 679 759 lineno += 1 680 760 761 + # Expand and strip spaces, to find indent. 762 + line = line.rstrip() 763 + line = line.expandtabs() 764 + len1 = len(line) 765 + line = line.lstrip() 766 + len2 = len(line) 767 + 681 768 # Discard comments 682 769 end = line.find('#') 683 770 if end >= 0: ··· 687 774 if len(toks) != 0: 688 775 # Next line after continuation 689 776 toks.extend(t) 690 - elif len(t) == 0: 691 - # Empty line 692 - continue 693 777 else: 778 + # Allow completely blank lines. 779 + if len1 == 0: 780 + continue 781 + indent = len1 - len2 782 + # Empty line due to comment. 783 + if len(t) == 0: 784 + # Indentation must be correct, even for comment lines. 785 + if indent != nesting: 786 + error(lineno, 'indentation ', indent, ' != ', nesting) 787 + continue 788 + start_lineno = lineno 694 789 toks = t 695 790 696 791 # Continuation? ··· 698 793 toks.pop() 699 794 continue 700 795 701 - if len(toks) < 2: 702 - error(lineno, 'short line') 703 - 704 796 name = toks[0] 705 797 del toks[0] 706 798 799 + # End nesting? 800 + if name == '}': 801 + if nesting == 0: 802 + error(start_lineno, 'mismatched close brace') 803 + if len(toks) != 0: 804 + error(start_lineno, 'extra tokens after close brace') 805 + nesting -= 2 806 + if indent != nesting: 807 + error(start_lineno, 'indentation ', indent, ' != ', nesting) 808 + pats = patterns 809 + patterns = saved_pats.pop() 810 + build_multi_pattern(lineno, pats) 811 + toks = [] 812 + continue 813 + 814 + # Everything else should have current indentation. 815 + if indent != nesting: 816 + error(start_lineno, 'indentation ', indent, ' != ', nesting) 817 + 818 + # Start nesting? 819 + if name == '{': 820 + if len(toks) != 0: 821 + error(start_lineno, 'extra tokens after open brace') 822 + saved_pats.append(patterns) 823 + patterns = [] 824 + nesting += 2 825 + toks = [] 826 + continue 827 + 707 828 # Determine the type of object needing to be parsed. 708 829 if name[0] == '%': 709 - parse_field(lineno, name[1:], toks) 830 + parse_field(start_lineno, name[1:], toks) 710 831 elif name[0] == '&': 711 - parse_arguments(lineno, name[1:], toks) 832 + parse_arguments(start_lineno, name[1:], toks) 712 833 elif name[0] == '@': 713 - parse_generic(lineno, True, name[1:], toks) 834 + parse_generic(start_lineno, True, name[1:], toks) 714 835 else: 715 - parse_generic(lineno, False, name, toks) 836 + parse_generic(start_lineno, False, name, toks) 716 837 toks = [] 717 838 # end parse_file 718 839 ··· 789 910 innermask &= i.fixedmask 790 911 791 912 if innermask == 0: 792 - pnames = [] 913 + text = 'overlapping patterns:' 793 914 for p in pats: 794 - pnames.append(p.name + ':' + p.file + ':' + str(p.lineno)) 795 - error_with_file(pats[0].file, pats[0].lineno, 796 - 'overlapping patterns:', pnames) 915 + text += '\n' + p.file + ':' + str(p.lineno) + ': ' + str(p) 916 + error_with_file(pats[0].file, pats[0].lineno, text) 797 917 798 918 fullmask = outermask | innermask 799 919 ··· 846 966 global arguments 847 967 global formats 848 968 global patterns 969 + global allpatterns 849 970 global translate_scope 850 971 global translate_prefix 851 972 global output_fd ··· 907 1028 # Make sure that the argument sets are the same, and declare the 908 1029 # function only once. 909 1030 out_pats = {} 910 - for i in patterns: 1031 + for i in allpatterns: 911 1032 if i.name in out_pats: 912 1033 p = out_pats[i.name] 913 1034 if i.base.base != p.base.base:
+6
tests/decode/err_pattern_group_overlap1.decode
··· 1 + one 00000000000000000000000000000000 2 + { 3 + two 0000000000000000000000000000000 s:1 4 + three 000000000000000000000000000000 s:1 0 5 + } 6 +