2019-02-21 22:28:49 +03:00
#!/usr/bin/env python3
2017-02-01 19:48:41 +03:00
2021-01-04 14:30:11 +03:00
import argparse
2018-01-05 16:35:42 +03:00
import os
2017-02-01 19:48:41 +03:00
import sys
import json
2021-01-04 14:30:12 +03:00
import xml . etree . ElementTree
2017-02-01 19:48:41 +03:00
2019-09-24 15:55:56 +03:00
2021-01-04 14:30:16 +03:00
_KEYS = {
" cpuid " : [ " eax_in " , " ecx_in " ] ,
" msr " : [ " index " ] ,
}
_REGS = {
" cpuid " : [ " eax " , " ebx " , " ecx " , " edx " ] ,
" msr " : [ " eax " , " edx " ] ,
}
2019-04-02 20:02:04 +03:00
def checkFeature ( cpuData , feature ) :
2021-01-04 14:30:16 +03:00
for key in [ " type " ] + _KEYS . get ( feature [ " type " ] , list ( ) ) :
2021-01-04 14:30:15 +03:00
if feature [ key ] not in cpuData :
return False
cpuData = cpuData [ feature [ key ] ]
2019-04-02 20:02:04 +03:00
2021-01-04 14:30:16 +03:00
for reg in _REGS . get ( feature [ " type " ] , list ( ) ) :
2021-01-04 14:30:15 +03:00
if feature [ reg ] > 0 and feature [ reg ] == feature [ reg ] & cpuData [ reg ] :
return True
return False
2019-04-02 20:02:04 +03:00
def addFeature ( cpuData , feature ) :
2021-01-04 14:30:16 +03:00
for key in [ " type " ] + _KEYS . get ( feature [ " type " ] , list ( ) ) :
2021-01-04 14:30:14 +03:00
if feature [ key ] not in cpuData :
cpuData [ feature [ key ] ] = dict ( )
cpuData = cpuData [ feature [ key ] ]
2021-01-04 14:30:16 +03:00
for reg in _REGS . get ( feature [ " type " ] , list ( ) ) :
2021-01-04 14:30:14 +03:00
cpuData [ reg ] = cpuData . get ( reg , 0 ) | feature [ reg ]
2019-04-02 20:02:04 +03:00
2017-03-16 14:25:30 +03:00
def parseQemu ( path , features ) :
2019-04-01 18:06:59 +03:00
cpuData = { }
2017-03-16 14:25:30 +03:00
with open ( path , " r " ) as f :
2017-09-26 22:08:37 +03:00
data , pos = json . JSONDecoder ( ) . raw_decode ( f . read ( ) )
2017-03-16 14:25:30 +03:00
2019-02-21 22:28:49 +03:00
for ( prop , val ) in data [ " return " ] [ " model " ] [ " props " ] . items ( ) :
2017-03-16 14:25:30 +03:00
if val and prop in features :
2019-04-01 18:06:59 +03:00
addFeature ( cpuData , features [ prop ] )
2017-03-16 14:25:30 +03:00
2019-04-01 18:06:59 +03:00
return cpuData
2017-03-16 14:25:30 +03:00
2019-04-01 18:06:59 +03:00
def parseCPUData ( path ) :
2021-01-04 14:30:13 +03:00
cpuData = dict ( )
for f in xml . etree . ElementTree . parse ( path ) . getroot ( ) :
2021-01-04 14:30:16 +03:00
if f . tag not in ( " cpuid " , " msr " ) :
2021-01-04 14:30:13 +03:00
continue
2017-03-16 14:25:30 +03:00
2021-01-04 14:30:13 +03:00
feature = { " type " : f . tag }
2021-01-04 14:30:16 +03:00
for reg in _KEYS [ f . tag ] + _REGS [ f . tag ] :
2021-01-04 14:30:13 +03:00
feature [ reg ] = int ( f . attrib . get ( reg , " 0 " ) , 0 )
2019-04-01 18:06:59 +03:00
addFeature ( cpuData , feature )
return cpuData
2017-03-16 14:25:30 +03:00
2018-01-05 16:35:42 +03:00
def parseMap ( ) :
path = os . path . dirname ( sys . argv [ 0 ] )
2019-02-21 22:10:48 +03:00
path = os . path . join ( path , " .. " , " .. " , " src " , " cpu_map " , " x86_features.xml " )
2018-01-05 16:35:42 +03:00
2021-01-04 14:30:12 +03:00
cpuMap = dict ( )
for f in xml . etree . ElementTree . parse ( path ) . getroot ( ) . iter ( " feature " ) :
2021-01-04 14:30:16 +03:00
if f [ 0 ] . tag not in ( " cpuid " , " msr " ) :
2021-01-04 14:30:12 +03:00
continue
2018-01-05 16:35:42 +03:00
2021-01-04 14:30:12 +03:00
feature = { " type " : f [ 0 ] . tag }
2021-01-04 14:30:16 +03:00
for reg in _KEYS [ f [ 0 ] . tag ] + _REGS [ f [ 0 ] . tag ] :
2021-01-04 14:30:12 +03:00
feature [ reg ] = int ( f [ 0 ] . attrib . get ( reg , " 0 " ) , 0 )
cpuMap [ f . attrib [ " name " ] ] = feature
2018-01-05 16:35:42 +03:00
return cpuMap
2019-04-02 20:02:04 +03:00
def formatCPUData ( cpuData , path , comment ) :
2019-02-21 22:28:49 +03:00
print ( path )
2017-03-16 14:25:30 +03:00
with open ( path , " w " ) as f :
f . write ( " <!-- " + comment + " --> \n " )
f . write ( " <cpudata arch= ' x86 ' > \n " )
2019-04-02 20:02:04 +03:00
cpuid = cpuData [ " cpuid " ]
2019-04-01 20:23:01 +03:00
for eax_in in sorted ( cpuid . keys ( ) ) :
for ecx_in in sorted ( cpuid [ eax_in ] . keys ( ) ) :
leaf = cpuid [ eax_in ] [ ecx_in ]
2017-03-16 14:25:30 +03:00
line = ( " <cpuid eax_in= ' 0x %08x ' ecx_in= ' 0x %02x ' "
" eax= ' 0x %08x ' ebx= ' 0x %08x ' "
" ecx= ' 0x %08x ' edx= ' 0x %08x ' /> \n " )
2018-03-20 09:48:47 +03:00
f . write ( line % (
2019-04-01 20:23:01 +03:00
eax_in , ecx_in ,
2017-03-16 14:25:30 +03:00
leaf [ " eax " ] , leaf [ " ebx " ] , leaf [ " ecx " ] , leaf [ " edx " ] ) )
2019-04-01 19:24:05 +03:00
if " msr " in cpuData :
msr = cpuData [ " msr " ]
for index in sorted ( msr . keys ( ) ) :
2019-09-24 18:47:02 +03:00
f . write ( " <msr index= ' 0x %x ' edx= ' 0x %08x ' eax= ' 0x %08x ' /> \n " %
( index , msr [ index ] [ ' edx ' ] , msr [ index ] [ ' eax ' ] ) )
2019-04-01 19:24:05 +03:00
2017-03-16 14:25:30 +03:00
f . write ( " </cpudata> \n " )
2021-01-04 14:30:11 +03:00
def diff ( args ) :
2018-01-05 16:35:42 +03:00
cpuMap = parseMap ( )
2021-01-04 14:30:11 +03:00
for jsonFile in args . json_files :
cpuDataFile = jsonFile . replace ( " .json " , " .xml " )
enabledFile = jsonFile . replace ( " .json " , " -enabled.xml " )
disabledFile = jsonFile . replace ( " .json " , " -disabled.xml " )
cpuData = parseCPUData ( cpuDataFile )
qemu = parseQemu ( jsonFile , cpuMap )
enabled = dict ( )
disabled = dict ( )
for feature in cpuMap . values ( ) :
if checkFeature ( qemu , feature ) :
addFeature ( enabled , feature )
elif checkFeature ( cpuData , feature ) :
addFeature ( disabled , feature )
formatCPUData ( enabled , enabledFile , " Features enabled by QEMU " )
formatCPUData ( disabled , disabledFile , " Features disabled by QEMU " )
def main ( ) :
parser = argparse . ArgumentParser ( description = " Diff cpuid results " )
subparsers = parser . add_subparsers ( dest = " action " , required = True )
diffparser = subparsers . add_parser (
" diff " ,
help = " Diff json description of CPU model against known features. " )
diffparser . add_argument (
" json_files " ,
nargs = " + " ,
metavar = " FILE " ,
type = os . path . realpath ,
help = " Path to one or more json CPU model descriptions. " )
args = parser . parse_args ( )
diff ( args )
if __name__ == " __main__ " :
main ( )