FEX/Scripts/json_config_parse.py
Ryan Houdek 54fc8cb0bd
TestHarnessRunner: Support querying AFP for features
Also fixes desync of flags
2024-07-20 15:34:54 -07:00

372 lines
11 KiB
Python

from enum import Flag
import json
import struct
import sys
class Regs(Flag):
REG_NONE = 0
REG_RIP = (1 << 0)
REG_RAX = (1 << 1)
REG_RBX = (1 << 2)
REG_RCX = (1 << 3)
REG_RDX = (1 << 4)
REG_RSI = (1 << 5)
REG_RDI = (1 << 6)
REG_RBP = (1 << 7)
REG_RSP = (1 << 8)
REG_R8 = (1 << 9)
REG_R9 = (1 << 10)
REG_R10 = (1 << 11)
REG_R11 = (1 << 12)
REG_R12 = (1 << 13)
REG_R13 = (1 << 14)
REG_R14 = (1 << 15)
REG_R15 = (1 << 16)
REG_XMM0 = (1 << 17)
REG_XMM1 = (1 << 18)
REG_XMM2 = (1 << 19)
REG_XMM3 = (1 << 20)
REG_XMM4 = (1 << 21)
REG_XMM5 = (1 << 22)
REG_XMM6 = (1 << 23)
REG_XMM7 = (1 << 24)
REG_XMM8 = (1 << 25)
REG_XMM9 = (1 << 26)
REG_XMM10 = (1 << 27)
REG_XMM11 = (1 << 28)
REG_XMM12 = (1 << 29)
REG_XMM13 = (1 << 30)
REG_XMM14 = (1 << 31)
REG_XMM15 = (1 << 32)
REG_GS = (1 << 33)
REG_FS = (1 << 34)
REG_MM0 = (1 << 35)
REG_MM1 = (1 << 36)
REG_MM2 = (1 << 37)
REG_MM3 = (1 << 38)
REG_MM4 = (1 << 39)
REG_MM5 = (1 << 40)
REG_MM6 = (1 << 41)
REG_MM7 = (1 << 42)
REG_MM8 = (1 << 43)
REG_ALL = (1 << 44) - 1
REG_INVALID = (1 << 44)
class ABI(Flag) :
ABI_SYSTEMV = 0
ABI_WIN64 = 1
ABI_NONE = 2
class Mode(Flag) :
MODE_32 = 0
MODE_64 = 1
class HostFeatures(Flag) :
FEATURE_ANY = 0
FEATURE_3DNOW = (1 << 0)
FEATURE_SSE4A = (1 << 1)
FEATURE_AVX = (1 << 2)
FEATURE_RAND = (1 << 3)
FEATURE_SHA = (1 << 4)
FEATURE_CLZERO = (1 << 5)
FEATURE_BMI1 = (1 << 6)
FEATURE_BMI2 = (1 << 7)
FEATURE_CLWB = (1 << 8)
FEATURE_LINUX = (1 << 9)
FEATURE_AES256 = (1 << 10)
FEATURE_AFP = (1 << 11)
RegStringLookup = {
"NONE": Regs.REG_NONE,
"RAX": Regs.REG_RAX,
"RIP": Regs.REG_RIP,
"RBX": Regs.REG_RBX,
"RCX": Regs.REG_RCX,
"RDX": Regs.REG_RDX,
"RSI": Regs.REG_RSI,
"RDI": Regs.REG_RDI,
"RBP": Regs.REG_RBP,
"RSP": Regs.REG_RSP,
"R8": Regs.REG_R8,
"R9": Regs.REG_R9,
"R10": Regs.REG_R10,
"R11": Regs.REG_R11,
"R12": Regs.REG_R12,
"R13": Regs.REG_R13,
"R14": Regs.REG_R14,
"R15": Regs.REG_R15,
"XMM0": Regs.REG_XMM0,
"XMM1": Regs.REG_XMM1,
"XMM2": Regs.REG_XMM2,
"XMM3": Regs.REG_XMM3,
"XMM4": Regs.REG_XMM4,
"XMM5": Regs.REG_XMM5,
"XMM6": Regs.REG_XMM6,
"XMM7": Regs.REG_XMM7,
"XMM8": Regs.REG_XMM8,
"XMM9": Regs.REG_XMM9,
"XMM10": Regs.REG_XMM10,
"XMM11": Regs.REG_XMM11,
"XMM12": Regs.REG_XMM12,
"XMM13": Regs.REG_XMM13,
"XMM14": Regs.REG_XMM14,
"XMM15": Regs.REG_XMM15,
"GS": Regs.REG_GS,
"FS": Regs.REG_FS,
"ALL": Regs.REG_ALL,
"MM0": Regs.REG_MM0,
"MM1": Regs.REG_MM1,
"MM2": Regs.REG_MM2,
"MM3": Regs.REG_MM3,
"MM4": Regs.REG_MM4,
"MM5": Regs.REG_MM5,
"MM6": Regs.REG_MM6,
"MM7": Regs.REG_MM7,
"MM8": Regs.REG_MM8,
}
ABIStringLookup = {
"SYSTEMV": ABI.ABI_SYSTEMV,
"WIN64": ABI.ABI_WIN64,
"NONE": ABI.ABI_NONE,
}
ModeStringLookup = {
"32BIT": Mode.MODE_32,
"64BIT": Mode.MODE_64,
}
HostFeaturesLookup = {
"3DNOW" : HostFeatures.FEATURE_3DNOW,
"SSE4A" : HostFeatures.FEATURE_SSE4A,
"AVX" : HostFeatures.FEATURE_AVX,
"RAND" : HostFeatures.FEATURE_RAND,
"SHA" : HostFeatures.FEATURE_SHA,
"CLZERO" : HostFeatures.FEATURE_CLZERO,
"BMI1" : HostFeatures.FEATURE_BMI1,
"BMI2" : HostFeatures.FEATURE_BMI2,
"CLWB" : HostFeatures.FEATURE_CLWB,
"LINUX" : HostFeatures.FEATURE_LINUX,
"AES256" : HostFeatures.FEATURE_AES256,
"AFP" : HostFeatures.FEATURE_AFP,
}
def parse_hexstring(s):
length = 0
byte_data = []
for num in s.split(' '):
if s.startswith("0x"):
num = num[2:]
while len(num) > 0:
byte_num = num[-2:]
byte_data.append(int(byte_num, 16))
length += 1
num = num[0:-2]
return length, byte_data
def parse_json(json_text, output_file):
# Default options
OptionMatch = Regs.REG_INVALID
OptionIgnore = Regs.REG_NONE
OptionABI = ABI.ABI_SYSTEMV
OptionMode = Mode.MODE_64
OptionHostFeatures = HostFeatures.FEATURE_ANY
OptionStackSize = 4096
OptionEntryPoint = 1
OptionRegData = {}
OptionMemoryRegions = {}
OptionMemoryData = {}
OptionEnvironmentVariables = {}
json_object = json.loads(json_text)
json_object = {k.upper(): v for k, v in json_object.items()}
# Begin parsing the JSON
if ("MATCH" in json_object):
data = json_object["MATCH"]
if (type(data) is str):
data = [data]
for data_val in data:
data_val = data_val.upper()
if not (data_val in RegStringLookup):
sys.exit("Invalid Match register option")
if (OptionMatch == Regs.REG_INVALID):
OptionMatch = Regs.REG_NONE
RegOption = RegStringLookup[data_val]
OptionMatch = OptionMatch | RegOption
if ("IGNORE" in json_object):
data = json_object["IGNORE"]
if (type(data) is str):
data = [data]
for data_val in data:
data_val = data_val.upper()
if not (data_val in RegStringLookup):
sys.exit("Invalid Ignore register option")
if (OptionMatch == Regs.REG_INVALID):
OptionMatch = Regs.REG_NONE
RegOption = RegStringLookup[data_val]
OptionIgnore = OptionIgnore | RegOption
if ("ABI" in json_object):
data = json_object["ABI"]
data = data.upper()
if not (data in ABIStringLookup):
sys.exit("Invalid ABI")
OptionABI = ABIStringLookup[data]
if ("MODE" in json_object):
data = json_object["MODE"]
data = data.upper()
if not (data in ModeStringLookup):
sys.exit("Invalid Mode")
OptionMode = ModeStringLookup[data]
if ("HOSTFEATURES" in json_object):
data = json_object["HOSTFEATURES"]
if not (type(data) is list):
sys.exit("HostFeatures value must be list of features")
for data_key in data:
data_key = data_key.upper()
if not (data_key in HostFeaturesLookup):
sys.exit("Invalid host feature")
OptionHostFeatures |= HostFeaturesLookup[data_key]
if ("STACKSIZE" in json_object):
data = json_object["STACKSIZE"]
OptionStackSize = int(data, 0)
if ("ENTRYPOINT" in json_object):
data = json_object["ENTRYPOINT"]
data = int(data, 0)
if (data == 0):
sys.exit("Invalid entrypoint of 0")
OptionEntryPoint = data
if ("MEMORYREGIONS" in json_object):
data = json_object["MEMORYREGIONS"]
if not (type(data) is dict):
sys.exit("MemoryRegions value must be list of key:value pairs")
for data_key, data_val in data.items():
OptionMemoryRegions[int(data_key, 0)] = int(data_val, 0)
if ("REGDATA" in json_object):
data = json_object["REGDATA"]
if not (type(data) is dict):
sys.exit("RegData value must be list of key:value pairs")
for data_key, data_val in data.items():
data_key = data_key.upper()
if not (data_key in RegStringLookup):
sys.exit("Invalid RegData register option")
data_key_index = RegStringLookup[data_key]
data_key_values = []
# Create a list of values for this register as an integer
if (type(data_val) is list):
for data_key_value in data_val:
data_key_values.append(int(data_key_value, 0))
else:
data_key_values.append(int(data_val, 0))
OptionRegData[data_key_index] = data_key_values
if ("MEMORYDATA" in json_object):
data = json_object["MEMORYDATA"]
if not (type(data) is dict):
sys.exit("MemoryData value must be list of key:value pairs")
for data_key, data_val in data.items():
length, byte_data = parse_hexstring(data_val)
OptionMemoryData[int(data_key, 0)] = (length, byte_data)
if ("ENV" in json_object):
data = json_object["ENV"]
if not (type(data) is dict):
sys.exit("Environment variables value must be list of key:value pairs")
for data_key, data_val in data.items():
OptionEnvironmentVariables[data_key] = data_val
# If Match option wasn't touched then set it to the default
if (OptionMatch == Regs.REG_INVALID):
OptionMatch = Regs.REG_NONE
memRegions = bytes()
regData = bytes()
memData = bytes()
envData = bytes()
# Write memory regions
for key, val in OptionMemoryRegions.items():
memRegions += struct.pack('Q', key)
memRegions += struct.pack('Q', val)
# Write Register values
for reg_key, reg_val in OptionRegData.items():
regData += struct.pack('I', len(reg_val))
regData += struct.pack('Q', reg_key.value)
for val in reg_val:
regData += struct.pack('Q', val)
# Write Memory data
for reg_key, reg_val in OptionMemoryData.items():
length, data = reg_val
memData += struct.pack('Q', reg_key) # address
memData += struct.pack('I', length)
for byte in data:
memData += struct.pack('B', byte)
# Write environment variables
for key, val in OptionEnvironmentVariables.items():
envData += key.encode()
envData += struct.pack('B', 0)
envData += val.encode()
envData += struct.pack('B', 0)
config_file = open(output_file, "wb")
config_file.write(struct.pack('Q', OptionMatch.value))
config_file.write(struct.pack('Q', OptionIgnore.value))
config_file.write(struct.pack('Q', OptionStackSize))
config_file.write(struct.pack('Q', OptionEntryPoint))
config_file.write(struct.pack('I', OptionABI.value))
config_file.write(struct.pack('I', OptionMode.value))
config_file.write(struct.pack('I', OptionHostFeatures.value))
# Total length of header, including offsets/counts below
headerLength = (8 * 4) + (4 * 3) + (4 * 8)
offset = headerLength
# memory regions offset/count
config_file.write(struct.pack('I', offset))
config_file.write(struct.pack('I', len(OptionMemoryRegions)))
offset += len(memRegions)
# register values offset/count
config_file.write(struct.pack('I', offset))
config_file.write(struct.pack('I', len(OptionRegData)))
offset += len(regData)
# memory data offset/count
config_file.write(struct.pack('I', offset))
config_file.write(struct.pack('I', len(OptionMemoryData)))
offset += len(memData)
# environment data offset/count
config_file.write(struct.pack('I', offset))
config_file.write(struct.pack('I', len(OptionEnvironmentVariables)))
offset += len(envData)
# write out the actual data for memory regions, reg data and memory data
config_file.write(memRegions)
config_file.write(regData)
config_file.write(memData)
config_file.write(envData)
config_file.close()