Add MemoryData to IR and ASM Tests

This commit is contained in:
Scott Mansell 2020-08-20 23:33:14 +12:00
parent 25b2ced2b8
commit cb6dcf62eb
10 changed files with 184 additions and 20 deletions

2
.gitignore vendored
View File

@ -9,4 +9,4 @@ Config.json
out/
.vscode/
.vs/
*.pyc

View File

@ -123,6 +123,20 @@ ModeStringLookup = {
"64BIT": Mode.MODE_64,
}
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
@ -133,6 +147,7 @@ def parse_json(json_text, output_file):
OptionEntryPoint = 1
OptionRegData = {}
OptionMemoryRegions = {}
OptionMemoryData = {}
json_object = json.loads(json_text)
@ -195,9 +210,9 @@ def parse_json(json_text, output_file):
if ("MEMORYREGIONS" in json_object):
data = json_object["MEMORYREGIONS"]
if not (type(data) is dict):
sys.exit("RegData value must be list of key:value pairs")
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);
OptionMemoryRegions[int(data_key, 0)] = int(data_val, 0)
if ("REGDATA" in json_object):
data = json_object["REGDATA"]
@ -219,10 +234,43 @@ def parse_json(json_text, output_file):
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 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()
# 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)
config_file = open(output_file, "wb")
config_file.write(struct.pack('Q', OptionMatch.value))
config_file.write(struct.pack('Q', OptionIgnore.value))
@ -231,23 +279,29 @@ def parse_json(json_text, output_file):
config_file.write(struct.pack('I', OptionABI.value))
config_file.write(struct.pack('I', OptionMode.value))
# Number of memory regions
# Total length of header, including offsets/counts below
headerLength = (8 * 4) + (4 * 2) + (4 * 6)
offset = headerLength
# memory regions offset/count
config_file.write(struct.pack('I', offset))
config_file.write(struct.pack('I', len(OptionMemoryRegions)))
offset += len(memRegions)
# Number of register values
# register values offset/count
config_file.write(struct.pack('I', offset))
config_file.write(struct.pack('I', len(OptionRegData)))
offset += len(regData)
# Print number of memory regions
for reg_key, reg_val in OptionMemoryRegions.items():
config_file.write(struct.pack('Q', reg_key))
config_file.write(struct.pack('Q', reg_val))
# memory data offset/count
config_file.write(struct.pack('I', offset))
config_file.write(struct.pack('I', len(OptionMemoryData)))
offset += len(memData)
# Print Register values
for reg_key, reg_val in OptionRegData.items():
config_file.write(struct.pack('I', len(reg_val)))
config_file.write(struct.pack('Q', reg_key.value))
for reg_vals in reg_val:
config_file.write(struct.pack('Q', reg_vals))
# 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.close()

View File

@ -157,7 +157,6 @@ namespace FEX::HarnessHelper {
}
if (BaseConfig.OptionRegDataCount > 0) {
uintptr_t DataOffset = sizeof(ConfigStructBase);
constexpr std::array<std::pair<uint64_t, unsigned>, 45> OffsetArray = {{
{offsetof(FEXCore::Core::CPUState, rip), 1},
{offsetof(FEXCore::Core::CPUState, gregs[0]), 1},
@ -206,8 +205,7 @@ namespace FEX::HarnessHelper {
{offsetof(FEXCore::Core::CPUState, mm[8][0]), 2},
}};
// Offset past the Memory regions if there are any
DataOffset += sizeof(MemoryRegionBase) * BaseConfig.OptionMemoryRegionCount;
uintptr_t DataOffset = BaseConfig.OptionRegDataOffset;
for (unsigned i = 0; i < BaseConfig.OptionRegDataCount; ++i) {
RegDataStructBase *RegData = reinterpret_cast<RegDataStructBase*>(RawConfigFile.data() + DataOffset);
[[maybe_unused]] std::bitset<64> RegFlags = RegData->RegKey;
@ -263,6 +261,31 @@ namespace FEX::HarnessHelper {
return Matches;
}
std::map<uintptr_t, size_t> GetMemoryRegions() {
std::map<uintptr_t, size_t> regions;
uintptr_t DataOffset = BaseConfig.OptionMemoryRegionOffset;
for (unsigned i = 0; i < BaseConfig.OptionMemoryRegionCount; ++i) {
MemoryRegionBase *Region = reinterpret_cast<MemoryRegionBase*>(RawConfigFile.data() + DataOffset);
regions[Region->Region] = Region->Size;
DataOffset += sizeof(MemoryRegionBase);
}
return regions;
}
void LoadMemory(uint64_t MemoryBase, FEXCore::CodeLoader::MemoryWriter Writer) {
uintptr_t DataOffset = BaseConfig.OptionMemDataOffset;
for (unsigned i = 0; i < BaseConfig.OptionMemDataCount; ++i) {
MemDataStructBase *MemData = reinterpret_cast<MemDataStructBase*>(RawConfigFile.data() + DataOffset);
Writer(&MemData->data, MemoryBase + MemData->address, MemData->length);
DataOffset += sizeof(MemDataStructBase) + MemData->length;
}
}
bool Is64BitMode() const { return BaseConfig.OptionMode == 1; }
private:
@ -275,8 +298,12 @@ namespace FEX::HarnessHelper {
uint64_t OptionEntryPoint;
uint32_t OptionABI;
uint32_t OptionMode;
uint32_t OptionMemoryRegionOffset;
uint32_t OptionMemoryRegionCount;
uint32_t OptionRegDataOffset;
uint32_t OptionRegDataCount;
uint32_t OptionMemDataOffset;
uint32_t OptionMemDataCount;
uint8_t AdditionalData[];
}__attribute__((packed));
@ -291,6 +318,12 @@ namespace FEX::HarnessHelper {
uint64_t RegValues[];
} __attribute__((packed));
struct MemDataStructBase {
uint64_t address;
uint32_t length;
uint8_t data[];
} __attribute__((packed));
std::vector<char> RawConfigFile;
ConfigStructBase BaseConfig;
};
@ -355,12 +388,19 @@ namespace FEX::HarnessHelper {
// Map in the memory region for the test file
Mapper(CODE_START_PAGE, AlignUp(RawFile.size(), PAGE_SIZE), true, true);
// Map the memory regions the test file asks for
for (auto& [region, size] : Config.GetMemoryRegions()) {
Mapper(region, size, true, true);
}
}
void LoadMemory(MemoryWriter Writer) override {
// Memory base here starts at the start location we passed back with GetLayout()
// This will write at [CODE_START_RANGE + 0, RawFile.size() )
Writer(&RawFile.at(0), MemoryBase + CODE_START_RANGE, RawFile.size());
Config.LoadMemory(MemoryBase, Writer);
}
uint64_t GetFinalRIP() override { return CODE_START_RANGE + RawFile.size(); }

View File

@ -62,6 +62,7 @@ class IRCodeLoader final : public FEXCore::CodeLoader {
}
void SetMemoryBase(uint64_t Base, bool Unified) override {
MemoryBase = Base;
}
uint64_t SetupStack([[maybe_unused]] void *HostPtr, uint64_t GuestPtr) const override {
@ -73,9 +74,12 @@ class IRCodeLoader final : public FEXCore::CodeLoader {
}
void MapMemoryRegion(std::function<void*(uint64_t, uint64_t, bool, bool)> Mapper) override {
// Map the memory regions the test file asks for
IR->MapRegions(Mapper);
}
void LoadMemory(MemoryWriter Writer) override {
IR->LoadMemory(MemoryBase, Writer);
}
uint64_t GetFinalRIP() override { return 0; }
@ -87,6 +91,7 @@ class IRCodeLoader final : public FEXCore::CodeLoader {
private:
FEX::IRLoader::Loader *IR;
constexpr static uint64_t STACK_SIZE = 8 * 1024 * 1024;
uint64_t MemoryBase = 0;
};
int main(int argc, char **argv, char **const envp) {

View File

@ -39,6 +39,16 @@ namespace FEX::IRLoader {
return Config.CompareStates(State, nullptr);
}
void LoadMemory(uint64_t MemoryBase, FEXCore::CodeLoader::MemoryWriter Writer) {
Config.LoadMemory(MemoryBase, Writer);
}
void MapRegions(std::function<void*(uint64_t, uint64_t, bool, bool)> Mapper) {
for (auto& [region, size] : Config.GetMemoryRegions()) {
Mapper(region, size, true, true);
}
}
#define IROP_PARSER_ALLOCATE_HELPERS
#include <FEXCore/IR/IRDefines.inc>
private:

View File

@ -36,6 +36,7 @@ foreach(ASM_SRC ${ASM_SOURCES})
add_custom_command(OUTPUT ${OUTPUT_CONFIG_NAME}
DEPENDS "${ASM_SRC}"
DEPENDS "${CMAKE_SOURCE_DIR}/Scripts/json_asm_config_parse.py"
DEPENDS "${CMAKE_SOURCE_DIR}/Scripts/json_config_parse.py"
COMMAND "python3" ARGS "${CMAKE_SOURCE_DIR}/Scripts/json_asm_config_parse.py" "${ASM_SRC}" "${OUTPUT_CONFIG_NAME}")
list(APPEND ASM_DEPENDS "${OUTPUT_NAME};${OUTPUT_CONFIG_NAME}")

View File

@ -0,0 +1,18 @@
%ifdef CONFIG
{
"RegData": {
"RAX": "0xddccbbaa"
},
"MemoryRegions": {
"0x100000": "4096"
},
"MemoryData": {
"0x100000": "AA BB CC DD"
}
}
%endif
; Simple test to prove that config loader's MemoryData is working
mov rax, [0x100000]
hlt

View File

@ -44,10 +44,17 @@
; - Key indicates the memory base
; - Value indicates the memory region size
; - WARNING: Emulator sets up some default regions that you don't want to intersect with
; - Additionally the VM only has 64GB of virtual memory. If you go past this sizer, expect failure
; - Additionally the VM only has 64GB of virtual memory. If you go past this size, expect failure
; - 0xb000'0000 - FS Memory base
; - 0xc000'0000 - Stack pointer base
; - 0xd000'0000 - Linux BRK memory base
; MemoryData: Prepopulate one or more memory regions with data
; - Default: None
; - Dict of key:value pairs
; - Key is address
; - Value is a string with hex data.
; - No leading 0x needed.
; - Spaces allowed
%ifdef CONFIG
{
@ -58,6 +65,11 @@
},
"MemoryRegions": {
"0x100000000": "4096"
},
"MemoryData": {
"0x100000000" : "00000001 00000000 00000000 00000000",
"0x100000020" : "fa aa 55 33",
"0x100000038" : "0x123456789"
}
}
%endif

View File

@ -0,0 +1,23 @@
;%ifdef CONFIG
;{
; "RegData": {
; "RAX": "0xddccbbaa"
; },
; "MemoryRegions": {
; "0x100000": "4096"
; },
; "MemoryData": {
; "0x100000": "AA BB CC DD"
; }
;}
;%endif
(%ssa1) IRHeader #0x1000, %ssa2, #0
(%ssa2) CodeBlock %start, %end, %ssa1
(%start i0) Dummy
%Addr i64 = Constant #0x100000
%Val i32 = LoadMem %Addr i64, #0x8, #0x8, GPR
(%Store i64) StoreContext %Val i64, #0x08, GPR
(%brk i0) Break #4, #4
(%end i0) EndBlock #0x0

View File

@ -14,7 +14,8 @@ foreach(IR_SRC ${IR_SOURCES})
add_custom_command(OUTPUT ${OUTPUT_CONFIG_NAME}
DEPENDS "${IR_SRC}"
DEPENDS "${CMAKE_SOURCE_DIR}/Scripts/json_asm_config_parse.py"
DEPENDS "${CMAKE_SOURCE_DIR}/Scripts/json_ir_config_parse.py"
DEPENDS "${CMAKE_SOURCE_DIR}/Scripts/json_config_parse.py"
COMMAND "python3" ARGS "${CMAKE_SOURCE_DIR}/Scripts/json_ir_config_parse.py" "${IR_SRC}" "${CMAKE_CURRENT_BINARY_DIR}/${OUTPUT_CONFIG_NAME}")
list(APPEND IR_DEPENDS "${OUTPUT_CONFIG_NAME}")